From f687d197ead268040d7f396eb26ea0035a6dac35 Mon Sep 17 00:00:00 2001 From: HafidBuroiroh Date: Fri, 6 Mar 2026 14:24:00 +0700 Subject: change request sourcing job order --- indoteknik_custom/__manifest__.py | 2 +- indoteknik_custom/models/sourcing_job_order.py | 475 +++++++++++++++++-------- indoteknik_custom/security/ir.model.access.csv | 2 + indoteknik_custom/views/ir_sequence.xml | 9 + indoteknik_custom/views/sourcing.xml | 247 ++++++++++--- 5 files changed, 535 insertions(+), 200 deletions(-) diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 9458cc41..d0fd4c22 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -192,7 +192,7 @@ 'views/update_depreciation_move_wizard_view.xml', 'views/commission_internal.xml', 'views/keywords.xml', - 'views/sourcing.xml' + 'views/sourcing.xml', 'views/token_log.xml', 'views/gudang_service.xml', 'views/kartu_stock.xml', diff --git a/indoteknik_custom/models/sourcing_job_order.py b/indoteknik_custom/models/sourcing_job_order.py index e015eaaa..6bb59c62 100644 --- a/indoteknik_custom/models/sourcing_job_order.py +++ b/indoteknik_custom/models/sourcing_job_order.py @@ -158,7 +158,6 @@ class SourcingJobOrder(models.Model): if self.env.uid != self.create_uid.id and not self.env.user.has_group('indoteknik_custom.group_role_merchandiser'): raise UserError("❌ Hanya Sales dan Merchandiser yang boleh mengedit Sourcing Job.") - # --- Simpan data lama sebelum write (buat pembanding) old_data = {} for rec in self: old_data[rec.id] = { @@ -179,18 +178,15 @@ class SourcingJobOrder(models.Model): for rec in self: rec._log_product_assets_upload() - # --- Bandingkan setelah write dan buat log for rec in self: changes = [] old = old_data.get(rec.id, {}) - # === Perubahan di field parent === if old.get('state') != rec.state: changes.append(f"State: {old.get('state')}{rec.state}") if old.get('approval_sales') != rec.approval_sales: changes.append(f"Approval Status: {old.get('approval_sales')}{rec.approval_sales}") - # === Perubahan di line === old_lines = old.get('line_data', {}) for line in rec.line_ids: old_line = old_lines.get(line.id) @@ -221,7 +217,6 @@ class SourcingJobOrder(models.Model): joined = "
".join(sub_changes) changes.append(f"{line.product_name}:
{joined}") - # Post ke chatter if changes: message = "

".join(changes) rec.message_post( @@ -278,11 +273,16 @@ class SourcingJobOrderLine(models.Model): order_id = fields.Many2one('sourcing.job.order', string='Job Order', ondelete='cascade') product_id = fields.Many2one('product.product', string='Product', ondelete='cascade') md_person_ids = fields.Many2one('res.users', string='MD Person', ondelete='cascade') - product_name = fields.Char(string='Nama Barang', required=True) + brand_id = fields.Many2one('x_manufactures', string='Manufactures', ondelete='cascade') + so_id = fields.Many2one('sale.order', string='SO Number', tracking=True, readonly=True) + product_name_md = fields.Char(string='Nama Barang') + descriptions_md = fields.Text(string='Deskripsi Barang') + + product_name = fields.Char(string='Nama Barang') + brand = fields.Char(string='Brand') code = fields.Char(string='SKU') budget = fields.Char(string='Expected Price') note = fields.Text(string='Note Sourcing') - brand = fields.Char(string='Brand') attachment_type = fields.Selection([ ('none', 'None'), ('pdf', '.PDF'), @@ -293,6 +293,7 @@ class SourcingJobOrderLine(models.Model): product_attachment_img = fields.Binary(string="Product Attachment") product_attachment_other = fields.Binary(string="Product Attachment") product_attachment_filename = fields.Char(string="Filename") + descriptions = fields.Text(string='Deskripsi / Spesifikasi') reason = fields.Text(string='Reason Unavailable') sla = fields.Char(string='SLA Product') @@ -309,8 +310,7 @@ class SourcingJobOrderLine(models.Model): ('draft', 'Unsource'), ('sourcing', 'On Sourcing'), ('sent', 'Approval Sent'), - ('approve', 'Approved'), - ('done', 'Done Sourcing'), + ('approve', 'Done Sourcing'), ('cancel', 'Unavailable') ], default='draft', tracking=True) product_type = fields.Selection([ @@ -323,16 +323,68 @@ class SourcingJobOrderLine(models.Model): string="Show for Sales", compute="_compute_show_for_sales", ) + show_salesperson = fields.Many2one( + 'res.users', + string="Salesperson", + ) + + so_state = fields.Selection( + [ + ('draft', 'Draft'), + ('cancel', 'Cancel'), + ('sale', 'Sale') + ], + string="SO State", + compute="_compute_so_data" + ) + + so_name = fields.Char( + string="SO Number", + compute="_compute_so_data" + ) + is_md_person = fields.Boolean( + string="Is MD Person", + compute="_compute_is_md_person" + ) + is_receiver = fields.Boolean( + string="Is MD Receiver", + compute="_compute_is_md_person" + ) + + is_given = fields.Boolean(string='Is Given', tracking=True) + given_to_id = fields.Many2one('res.users', string='Given To') + previous_md_id = fields.Many2one('res.users', string='Previous MD') @api.depends('quantity', 'price', 'tax_id') def _compute_subtotal(self): - """Menghitung subtotal termasuk pajak.""" for line in self: subtotal = (line.quantity or 0.0) * (line.price or 0.0) + if line.tax_id: - subtotal += subtotal * (line.tax_id.amount / 100) + tax = line.tax_id.amount / 100 + + if line.tax_id.price_include: + subtotal = subtotal / (1 + tax) + line.subtotal = subtotal + @api.depends('order_id.so_id.user_id', 'order_id.so_id.state', 'order_id.so_id.name') + def _compute_so_data(self): + for rec in self: + so = rec.order_id.so_id + if so: + rec.so_state = so.state if so.state in ['draft', 'sale'] else False + rec.so_name = so.name + else: + rec.so_state = False + rec.so_name = False + + def _compute_is_md_person(self): + current_user = self.env.user + for rec in self: + rec.is_md_person = bool(rec.md_person_ids == current_user) + rec.is_receiver = bool(rec.given_to_id == current_user) + @api.constrains('product_type', 'product_category', 'product_class') def _check_required_fields_for_md(self): for rec in self: @@ -356,6 +408,9 @@ class SourcingJobOrderLine(models.Model): order = self.env['sourcing.job.order'].browse(order_id) if order.state == 'taken' and order.line_ids.md_person_ids != self.env.user: raise UserError("❌ SJO sudah taken. Tidak boleh tambah line.") + if order.so_id: + vals['so_id'] = order.so_id.id + vals['show_salesperson'] = order.so_id.user_id.id rec = super().create(vals) return rec @@ -388,7 +443,7 @@ class SourcingJobOrderLine(models.Model): if total == 1: line = lines[0] - if line.state == 'done': + if line.state == 'approve': order.state = 'done' elif line.state == 'cancel': order.state = 'cancel' @@ -399,9 +454,9 @@ class SourcingJobOrderLine(models.Model): states = lines.mapped('state') all_cancel = all(s == 'cancel' for s in states) - all_done_or_cancel = all(s in ['done', 'cancel'] for s in states) - any_done = any(s == 'done' for s in states) - any_progress = any(s not in ['done', 'cancel', 'draft'] for s in states) + all_done_or_cancel = all(s in ['approve', 'cancel'] for s in states) + any_done = any(s == 'approve' for s in states) + any_progress = any(s not in ['approve', 'cancel', 'draft'] for s in states) if all_cancel: order.state = 'cancel' @@ -496,10 +551,12 @@ class SourcingJobOrderLine(models.Model): for line in self: if line.state != 'sourcing': - raise UserError(f"⚠️ Produk '{line.product_name}' bukan status Sourcing.") + raise UserError(f"⚠️ Produk '{line.product_name_md}' bukan status Sourcing.") if ( not line.vendor_id + or not line.product_name_md + or not brand_id or not line.price or line.price <= 0 or not line.tax_id or not line.subtotal or line.subtotal <= 0 @@ -507,7 +564,7 @@ class SourcingJobOrderLine(models.Model): or not line.product_category or not line.product_class ): - raise UserError(f"❌ Data produk '{line.product_name}' belum lengkap.") + raise UserError(f"❌ Data produk '{line.product_name_md}' belum lengkap.") activity_type = self.env.ref('mail.mail_activity_data_todo') @@ -519,10 +576,10 @@ class SourcingJobOrderLine(models.Model): line.activity_schedule( activity_type_id=activity_type.id, user_id=job.create_uid.id, - note=f"{self.env.user.name} meminta approval untuk produk '{line.product_name}' di SJO '{job.name}'.", + note=f"{self.env.user.name} meminta approval untuk produk '{line.product_name_md}' di SJO '{job.name}'.", ) - approved_lines_text += f"
  • {line.product_name} - {line.price or 0}
  • " + approved_lines_text += f"
  • {line.product_name_md} - {line.price or 0}
  • " line.message_post( body=f"📤 Request approval dikirim (Multi)", @@ -543,7 +600,7 @@ class SourcingJobOrderLine(models.Model): title="Multi Request Sent" ) - return {'type': 'ir.actions.client', 'tag': 'reload'} + # return {'type': 'ir.actions.client', 'tag': 'reload'} def action_ask_approval(self): @@ -562,6 +619,8 @@ class SourcingJobOrderLine(models.Model): if ( not line.vendor_id + or not line.product_name_md + or not line.brand_id or not line.price or line.price <= 0 or not line.tax_id or not line.subtotal or line.subtotal <= 0 @@ -578,14 +637,14 @@ class SourcingJobOrderLine(models.Model): line.activity_schedule( activity_type_id=activity_type.id, user_id=job.create_uid.id, - note=f"{self.env.user.name} meminta approval untuk produk '{line.product_name}' di SJO '{job.name}'.", + note=f"{self.env.user.name} meminta approval untuk produk '{line.product_name_md}' di SJO '{job.name}'.", ) line.message_post( body=( f"📤 Request approval dikirim
    " f"Kepada: {job.create_uid.name}
    " - f"Produk: {line.product_name}" + f"Produk: {line.product_name_md}" ), subtype_xmlid="mail.mt_comment" ) @@ -594,7 +653,7 @@ class SourcingJobOrderLine(models.Model): body=( f"📤 Request approval line
    " f"