From e926482af5f2b95ff465445215c77161223ee671 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 27 Mar 2025 14:38:57 +0700 Subject: push --- indoteknik_custom/models/purchase_order.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'indoteknik_custom/models/purchase_order.py') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index d90c4a8a..b107f389 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -74,6 +74,7 @@ class PurchaseOrder(models.Model): date_done_picking = fields.Datetime(string='Date Done Picking', compute='get_date_done') bills_dp_id = fields.Many2one('account.move', string='Bills DP') bills_pelunasan_id = fields.Many2one('account.move', string='Bills Pelunasan') + product_bom_id = fields.Many2one('product.product', string='Product Bom') grand_total = fields.Monetary(string='Grand Total', help='Amount total + amount delivery', compute='_compute_grand_total') total_margin_match = fields.Float(string='Total Margin Match', compute='_compute_total_margin_match') approve_by = fields.Many2one('res.users', string='Approve By') @@ -726,9 +727,25 @@ class PurchaseOrder(models.Model): self.unlink_purchasing_job_state() self._check_qty_plafon_product() + if self.product_bom_id: + self._remove_product_bom() return res + def _remove_product_bom(self): + pj = self.env['v.purchasing.job'].search([ + ('product_id', '=', self.product_bom_id.id) + ]) + + if pj: + pj_state = self.env['purchasing.job.state'].search([ + ('purchasing_job_id', '=', pj.id) + ]) + + if pj_state: + pj_state.note = 'Product BOM Sudah Di PO' + pj_state.date_po = datetime.utcnow() + def check_ppn_mix(self): reference_taxes = self.order_line[0].taxes_id -- cgit v1.2.3 From fc5ed4aab391eb3ec09cae6179209fe30c1bc57f Mon Sep 17 00:00:00 2001 From: AndriFP Date: Tue, 22 Apr 2025 10:57:24 +0700 Subject: (andri) add min amount vendor payment --- indoteknik_custom/models/purchase_order.py | 45 ++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'indoteknik_custom/models/purchase_order.py') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index b107f389..5e9e509f 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -89,6 +89,39 @@ class PurchaseOrder(models.Model): store_name = fields.Char(string='Nama Toko') purchase_order_count = fields.Integer('Purchase Order Count', related='partner_id.purchase_order_count') + # cek payment term + def _check_payment_term(self): + _logger.info("Check Payment Term Terpanggil") + + cbd_term = self.env['account.payment.term'].search([ + ('name', 'ilike', 'Cash Before Delivery') + ], limit=1) + + for order in self: + if not order.partner_id or not order.partner_id.minimum_amount: + continue + + if not order.order_line or order.amount_total == 0: + continue + + if order.amount_total < order.partner_id.minimum_amount: + if cbd_term and order.payment_term_id != cbd_term: + order.payment_term_id = cbd_term.id + self.env.user.notify_info( + message="Total belanja PO belum mencapai minimum yang ditentukan vendor. " + "Payment Term telah otomatis diubah menjadi Cash Before Delivery (C.B.D).", + title="Payment Term Diperbarui" + ) + else: + vendor_term = order.partner_id.property_supplier_payment_term_id + if vendor_term and order.payment_term_id != vendor_term: + order.payment_term_id = vendor_term.id + self.env.user.notify_info( + message=f"Total belanja PO telah memenuhi jumlah minimum vendor. " + f"Payment Term otomatis dikembalikan ke pengaturan vendor awal: *{vendor_term.name}*.", + title="Payment Term Diperbarui" + ) + @api.onchange('total_cost_service') def _onchange_total_cost_service(self): for order in self: @@ -672,6 +705,7 @@ class PurchaseOrder(models.Model): raise UserError("Produk "+line.product_id.name+" memiliki vendor berbeda dengan SO (Vendor PO: "+str(self.partner_id.name)+", Vendor SO: "+str(line.so_line_id.vendor_id.name)+")") def button_confirm(self): + # self._check_payment_term() # check payment term res = super(PurchaseOrder, self).button_confirm() current_time = datetime.now() self.check_ppn_mix() @@ -1079,6 +1113,17 @@ class PurchaseOrder(models.Model): return super(PurchaseOrder, self).button_unlock() + @api.model #override custom create & write for check payment term + def create(self, vals): + order = super().create(vals) + order.with_context(skip_check_payment=True)._check_payment_term() + return order + + def write(self, vals): + res = super().write(vals) + if not self.env.context.get('skip_check_payment'): + self.with_context(skip_check_payment=True)._check_payment_term() + return res class PurchaseOrderUnlockWizard(models.TransientModel): _name = 'purchase.order.unlock.wizard' -- cgit v1.2.3 From 30382037882e15bb43f56fc0d81500faeb364fa5 Mon Sep 17 00:00:00 2001 From: AndriFP Date: Wed, 23 Apr 2025 08:59:23 +0700 Subject: (andri) add field min amt tax vendor PO --- indoteknik_custom/models/purchase_order.py | 76 ++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) (limited to 'indoteknik_custom/models/purchase_order.py') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 5e9e509f..60d26105 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -122,6 +122,80 @@ class PurchaseOrder(models.Model): title="Payment Term Diperbarui" ) + def _check_tax_rule(self): + _logger.info("Check Tax Rule Terpanggil") + + # Pajak 11% + tax_11 = self.env['account.tax'].search([ + ('type_tax_use', '=', 'purchase'), + ('name', 'ilike', '11%') + ], limit=1) + + # Pajak "No Tax" + no_tax = self.env['account.tax'].search([ + ('type_tax_use', '=', 'purchase'), + ('name', 'ilike', 'no tax') + ], limit=1) + + if not tax_11: + raise UserError("Pajak 11% tidak ditemukan. Mohon pastikan pajak 11% tersedia.") + + if not no_tax: + raise UserError("Pajak 'No Tax' tidak ditemukan. Harap buat tax dengan nama 'No Tax' dan tipe 'Purchase'.") + + for order in self: + partner = order.partner_id + minimum_tax = partner.minimum_amount_tax + + _logger.info("Partner ID: %s, Minimum Tax: %s, Untaxed Total: %s", partner.id, minimum_tax, order.amount_untaxed) + + if not minimum_tax or not order.order_line: + continue + + if order.amount_untaxed < minimum_tax: + _logger.info(">>> Total di bawah minimum → apply No Tax") + for line in order.order_line: + line.taxes_id = [(6, 0, [no_tax.id])] + + if self.env.context.get('notify_tax'): + self.env.user.notify_info( + message="Total belanja PO belum mencapai minimum pajak vendor. " + "Pajak diganti menjadi 'No Tax'.", + title="Pajak Diperbarui", + sticky=True + ) + else: + _logger.info(">>> Total memenuhi minimum → apply Pajak 11%") + for line in order.order_line: + line.taxes_id = [(6, 0, [tax_11.id])] + + if self.env.context.get('notify_tax'): + self.env.user.notify_info( + message="Total belanja sebelum pajak telah memenuhi minimum. " + "Pajak 11%% diterapkan", + title="Pajak Diperbarui", + sticky=True + ) + + @api.onchange('order_line') + def _onchange_order_line_tax_default(self): + _logger.info("Onchange Order Line Tax Default Terpanggil") + + no_tax = self.env['account.tax'].search([ + ('type_tax_use', '=', 'purchase'), + ('name', 'ilike', 'no tax') + ], limit=1) + + if not no_tax: + _logger.info("No Tax tidak ditemukan") + return + + for order in self: + for line in order.order_line: + if not line.taxes_id: + line.taxes_id = [(6, 0, [no_tax.id])] + _logger.info("Auto-set No tax ke baris product: %s", line.product_id.name) + @api.onchange('total_cost_service') def _onchange_total_cost_service(self): for order in self: @@ -1117,12 +1191,14 @@ class PurchaseOrder(models.Model): def create(self, vals): order = super().create(vals) order.with_context(skip_check_payment=True)._check_payment_term() + order.with_context(notify_tax=True)._check_tax_rule() return order def write(self, vals): res = super().write(vals) if not self.env.context.get('skip_check_payment'): self.with_context(skip_check_payment=True)._check_payment_term() + self.with_context(notify_tax=True)._check_tax_rule() return res class PurchaseOrderUnlockWizard(models.TransientModel): -- cgit v1.2.3 From f3854677b0f74db2fbb54ad012b3a6a6fc9e11df Mon Sep 17 00:00:00 2001 From: AndriFP Date: Wed, 23 Apr 2025 11:53:12 +0700 Subject: (andri) fix notify ganda pada PO --- indoteknik_custom/models/purchase_order.py | 39 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'indoteknik_custom/models/purchase_order.py') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 60d26105..98b367d0 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -152,7 +152,7 @@ class PurchaseOrder(models.Model): if not minimum_tax or not order.order_line: continue - if order.amount_untaxed < minimum_tax: + if order.amount_total < minimum_tax: _logger.info(">>> Total di bawah minimum → apply No Tax") for line in order.order_line: line.taxes_id = [(6, 0, [no_tax.id])] @@ -162,7 +162,6 @@ class PurchaseOrder(models.Model): message="Total belanja PO belum mencapai minimum pajak vendor. " "Pajak diganti menjadi 'No Tax'.", title="Pajak Diperbarui", - sticky=True ) else: _logger.info(">>> Total memenuhi minimum → apply Pajak 11%") @@ -174,27 +173,27 @@ class PurchaseOrder(models.Model): message="Total belanja sebelum pajak telah memenuhi minimum. " "Pajak 11%% diterapkan", title="Pajak Diperbarui", - sticky=True ) - @api.onchange('order_line') - def _onchange_order_line_tax_default(self): - _logger.info("Onchange Order Line Tax Default Terpanggil") + # set default no_tax pada order line + # @api.onchange('order_line') + # def _onchange_order_line_tax_default(self): + # _logger.info("Onchange Order Line Tax Default Terpanggil") - no_tax = self.env['account.tax'].search([ - ('type_tax_use', '=', 'purchase'), - ('name', 'ilike', 'no tax') - ], limit=1) + # no_tax = self.env['account.tax'].search([ + # ('type_tax_use', '=', 'purchase'), + # ('name', 'ilike', 'no tax') + # ], limit=1) - if not no_tax: - _logger.info("No Tax tidak ditemukan") - return + # if not no_tax: + # _logger.info("No Tax tidak ditemukan") + # return - for order in self: - for line in order.order_line: - if not line.taxes_id: - line.taxes_id = [(6, 0, [no_tax.id])] - _logger.info("Auto-set No tax ke baris product: %s", line.product_id.name) + # for order in self: + # for line in order.order_line: + # if not line.taxes_id: + # line.taxes_id = [(6, 0, [no_tax.id])] + # _logger.info("Auto-set No tax ke baris product: %s", line.product_id.name) @api.onchange('total_cost_service') def _onchange_total_cost_service(self): @@ -1190,8 +1189,8 @@ class PurchaseOrder(models.Model): @api.model #override custom create & write for check payment term def create(self, vals): order = super().create(vals) - order.with_context(skip_check_payment=True)._check_payment_term() - order.with_context(notify_tax=True)._check_tax_rule() + # order.with_context(skip_check_payment=True)._check_payment_term() + # order.with_context(notify_tax=True)._check_tax_rule() return order def write(self, vals): -- cgit v1.2.3 From ad3eb842d881ad89b0239075695dc2ccf424031f Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 10 May 2025 15:36:55 +0700 Subject: (andri) add log note PO Confirm di purchase Pricelist --- indoteknik_custom/models/purchase_order.py | 122 ++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models/purchase_order.py') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 98b367d0..a3e2c388 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -580,10 +580,11 @@ class PurchaseOrder(models.Model): purchase_pricelist = self.env['purchase.pricelist'].search([ ('product_id', '=', line.product_id.id), ('vendor_id', '=', line.order_id.partner_id.id) - ]) - purchase_pricelist = purchase_pricelist.with_context(update_by='system') + ]) + if not purchase_pricelist: - purchase_pricelist.create([{ + # Buat pricelist baru dengan context + new_pricelist = self.env['purchase.pricelist'].with_context(update_by='system').create([{ 'vendor_id': line.order_id.partner_id.id, 'product_id': line.product_id.id, 'product_price': 0, @@ -591,12 +592,52 @@ class PurchaseOrder(models.Model): 'system_price': price_unit, 'system_last_update': current_time }]) + + # Buat lognote untuk pricelist baru + message = f""" + New Purchase Pricelist Created from PO
+ PO: {line.order_id.name}
+ System Price: {price_unit:,.2f}
+ System Tax: {taxes.name if taxes else 'No Tax'}
+ System Update: {current_time}
+ """ + new_pricelist.message_post(body=message, subtype_id=self.env.ref("mail.mt_note").id) else: + # Simpan nilai lama untuk logging + old_values = { + 'system_price': purchase_pricelist.system_price, + 'taxes_system_id': purchase_pricelist.taxes_system_id, + } + + # Update dengan context + purchase_pricelist = purchase_pricelist.with_context(update_by='system') purchase_pricelist.write({ 'system_last_update': current_time, 'taxes_system_id': taxes.id, 'system_price': price_unit }) + + # Buat lognote jika ada perubahan + changes = [] + if old_values['system_price'] != price_unit: + changes.append(f"
  • System Price: {old_values['system_price']:,.2f} → {price_unit:,.2f}
  • ") + if old_values['taxes_system_id'] != taxes: + old_tax_name = old_values['taxes_system_id'].name if old_values['taxes_system_id'] else 'No Tax' + new_tax_name = taxes.name if taxes else 'No Tax' + changes.append(f"
  • System Tax: {old_tax_name} → {new_tax_name}
  • ") + + if changes: + message = f""" + System Fields Updated from PO
    + PO: {line.order_id.name}
    + Changes: +
      + {"".join(changes)} +
    • System Update: {current_time}
    • +
    + Updated By: {self.env.user.name} + """ + purchase_pricelist.message_post(body=message, subtype_id=self.env.ref("mail.mt_note").id) def _compute_date_planned(self): for order in self: @@ -1198,8 +1239,83 @@ class PurchaseOrder(models.Model): if not self.env.context.get('skip_check_payment'): self.with_context(skip_check_payment=True)._check_payment_term() self.with_context(notify_tax=True)._check_tax_rule() + # Tambahkan pemanggilan method untuk handle pricelist system update + self._handle_pricelist_system_update(vals) return res + def _handle_pricelist_system_update(self, vals): + if 'order_line' in vals or any(key in vals for key in ['state', 'approval_status']): + for order in self: + # Hanya proses jika PO sudah approved + if order.state in ['purchase', 'done'] and order.approval_status == 'approved': + self._process_pricelist_update(order) + + def _process_pricelist_update(self, order): + for line in order.order_line: + pricelist = self._get_related_pricelist(line.product_id, order.partner_id) + + if pricelist: + # Simpan nilai lama + old_values = self._get_pricelist_old_values(pricelist) + + # Update dan cek perubahan + self._update_and_log_pricelist(pricelist, line, old_values) + + def _get_related_pricelist(self, product_id, vendor_id): + return self.env['purchase.pricelist'].search([ + ('product_id', '=', product_id.id), + ('vendor_id', '=', vendor_id.id) + ], limit=1) + + def _get_pricelist_old_values(self, pricelist): + return { + 'system_price': pricelist.system_price, + 'taxes_system_id': pricelist.taxes_system_id, + 'system_last_update': pricelist.system_last_update + } + + def _update_and_log_pricelist(self, pricelist, po_line, old_values): + changes = [] + current_time = fields.Datetime.now() + + # Cek perubahan System Price + if pricelist.system_price != po_line.price_unit: + if old_values['system_price'] != po_line.price_unit: + changes.append(f"
  • System Price: {old_values['system_price']:,.2f} → {po_line.price_unit:,.2f}
  • ") + + # Cek perubahan System Tax + if pricelist.taxes_system_id != po_line.taxes_id: + old_tax = old_values['taxes_system_id'] + old_tax_name = old_tax.name if old_tax else 'No Tax' + new_tax_name = po_line.taxes_id.name if po_line.taxes_id else 'No Tax' + if old_tax != po_line.taxes_id: + changes.append(f"
  • System Tax: {old_tax_name} → {new_tax_name}
  • ") + + # Update fields jika ada perubahan + if changes: + pricelist.with_context(update_by='system').write({ + 'system_price': po_line.price_unit, + 'taxes_system_id': po_line.taxes_id.id if po_line.taxes_id else False, + 'system_last_update': current_time + }) + + # Buat lognote + self._create_pricelist_lognote(pricelist, po_line, changes, current_time) + + def _create_pricelist_lognote(self, pricelist, po_line, changes, timestamp): + message = f""" + System Fields Updated from PO
    + PO: {po_line.order_id.name}
    + Changes: +
      + {"".join(changes)} +
    • System Update: {timestamp}
    • +
    + Updated By: {self.env.user.name} + """ + + pricelist.message_post(body=message, subtype_id=self.env.ref("mail.mt_note").id) + class PurchaseOrderUnlockWizard(models.TransientModel): _name = 'purchase.order.unlock.wizard' _description = 'Wizard untuk memberikan alasan unlock PO' -- cgit v1.2.3 From 360c47384d876d17725be8ff3ca6b83a9078615b Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 13 May 2025 10:10:34 +0700 Subject: (andri) revisi current date pada log note purchase pricelist --- indoteknik_custom/models/purchase_order.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/purchase_order.py') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index a3e2c388..c5ba5792 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -562,7 +562,9 @@ class PurchaseOrder(models.Model): i = 0 for line in self.order_line: i += 1 - current_time = datetime.utcnow() + + utc_time = fields.Datetime.now() + current_time = utc_time.astimezone(timezone('Asia/Jakarta')).strftime('%Y-%m-%d %H:%M:%S') # print(i, len(self.order_line)) price_unit = line.price_unit @@ -635,7 +637,6 @@ class PurchaseOrder(models.Model): {"".join(changes)}
  • System Update: {current_time}
  • - Updated By: {self.env.user.name} """ purchase_pricelist.message_post(body=message, subtype_id=self.env.ref("mail.mt_note").id) -- cgit v1.2.3 From 8ac8c2f8b89e6069bf13e879d486fa9cf4814cb4 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 27 May 2025 09:02:47 +0700 Subject: push cr shipment group, vendor approval and purchase order --- indoteknik_custom/models/purchase_order.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models/purchase_order.py') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index c5ba5792..cbfd4acd 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -827,11 +827,23 @@ class PurchaseOrder(models.Model): self.check_different_vendor_so_po() # self.check_data_vendor() - if self.amount_untaxed >= 50000000 and not self.env.user.has_group('indoteknik_custom.group_role_merchandiser'): - raise UserError("Hanya Merchandiser yang bisa approve") + if self.amount_untaxed >= 50000000 and not self.env.user.id == 21: + raise UserError("Hanya Rafly Hanggara yang bisa approve") if self.total_percent_margin < self.total_so_percent_margin and not self.env.user.has_group('indoteknik_custom.group_role_merchandiser') and not self.env.user.is_leader: - raise UserError("Beda Margin dengan Sales, harus approval Merchandise") + self.env.user.notify_danger( + title='WARNING!!!', + message='Beda Margin dengan Sale Order', + sticky=True + ) + + if len(self.order_sales_match_line) == 0 and not self.env.user.has_group('indoteknik_custom.group_role_merchandiser') and not self.env.user.is_leader: + self.env.user.notify_danger( + title='WARNING!!!', + message='Tidak ada matches SO', + sticky=True + ) + if not self.from_apo: if not self.matches_so and not self.env.user.has_group('indoteknik_custom.group_role_merchandiser') and not self.env.user.is_leader: raise UserError("Tidak ada link dengan SO, harus approval Merchandise") -- cgit v1.2.3