From e3856970da63328c820833893c894f18dc6700bd Mon Sep 17 00:00:00 2001 From: Miqdad Date: Fri, 30 May 2025 15:11:42 +0700 Subject: fix bug total percent margin --- indoteknik_custom/models/sale_order.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index fa570819..d1dc3324 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -403,14 +403,14 @@ class SaleOrder(models.Model): if len(tax_sets) > 1: raise ValidationError("Semua produk dalam Sales Order harus memiliki kombinasi pajak yang sama.") - # @api.constrains('fee_third_party', 'delivery_amt', 'biaya_lain_lain') + # @api.constrains('fee_third_party', 'delivery_amt', 'biaya_lain_lain', 'ongkir_ke_xpdc') # def _check_total_margin_excl_third_party(self): # for rec in self: # if rec.fee_third_party == 0 and rec.total_margin_excl_third_party != rec.total_percent_margin: # # Gunakan direct SQL atau flag context untuk menghindari rekursi # self.env.cr.execute(""" - # UPDATE sale_order - # SET total_margin_excl_third_party = %s + # UPDATE sale_order + # SET total_margin_excl_third_party = %s # WHERE id = %s # """, (rec.total_percent_margin, rec.id)) # self.invalidate_cache() @@ -1703,9 +1703,14 @@ class SaleOrder(models.Model): else: delivery_amt = 0 - # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed-delivery_amt-order.fee_third_party)) * 100, 2) + net_margin = order.total_margin - order.biaya_lain_lain + order.total_percent_margin = round( - (order.total_margin / (order.amount_untaxed - order.fee_third_party - order.biaya_lain_lain)) * 100, 2) + (net_margin / (order.amount_untaxed - order.fee_third_party)) * 100, 2) + + # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed-delivery_amt-order.fee_third_party)) * 100, 2) + # order.total_percent_margin = round( + # (order.total_margin / (order.amount_untaxed - order.fee_third_party - order.biaya_lain_lain)) * 100, 2) # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed)) * 100, 2) @api.onchange('sales_tax_id') -- cgit v1.2.3 From b6962acb39ad373f2aded4bebfa1e7a2dbbb0a8a Mon Sep 17 00:00:00 2001 From: Miqdad Date: Sat, 31 May 2025 09:17:01 +0700 Subject: before margin --- indoteknik_custom/models/sale_order.py | 228 ++++++++++++++++++++++++++------- 1 file changed, 182 insertions(+), 46 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index d1dc3324..3c69c3d1 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -148,8 +148,8 @@ class SaleOrder(models.Model): help="Total Margin in Sales Order Header") total_percent_margin = fields.Float('Total Percent Margin', compute='_compute_total_percent_margin', help="Total % Margin in Sales Order Header") - total_margin_excl_third_party = fields.Float('Before Margin', help="Before Margin in Sales Order Header", - compute='_compute_total_margin_excl_third_party') + total_margin_excl_third_party = fields.Float('Before Margin', help="Before Margin in Sales Order Header") + approval_status = fields.Selection([ ('pengajuan1', 'Approval Manager'), ('pengajuan2', 'Approval Pimpinan'), @@ -340,16 +340,16 @@ class SaleOrder(models.Model): date_unhold = fields.Datetime(string='Date Unhold', tracking=True, readonly=True, help='Waktu ketika SO di Unhold' ) - def _compute_total_margin_excl_third_party(self): - for order in self: - if order.amount_untaxed == 0: - order.total_margin_excl_third_party = 0 - continue - - # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed-delivery_amt-order.fee_third_party)) * 100, 2) - order.total_margin_excl_third_party = round((order.total_before_margin / (order.amount_untaxed)) * 100, 2) - # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed)) * 100, 2) - + # def _compute_total_margin_excl_third_party(self): + # for order in self: + # if order.amount_untaxed == 0: + # order.total_margin_excl_third_party = 0 + # continue + # + # # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed-delivery_amt-order.fee_third_party)) * 100, 2) + # order.total_margin_excl_third_party = round((order.total_before_margin / (order.amount_untaxed)) * 100, 2) + # # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed)) * 100, 2) + # def ask_retur_cancel_purchasing(self): for rec in self: if self.env.user.has_group('indoteknik_custom.group_role_purchasing'): @@ -1035,11 +1035,11 @@ class SaleOrder(models.Model): line_no += 1 line.line_no = line_no - def write(self, vals): - if 'carrier_id' in vals: - for picking in self.picking_ids: - if picking.state == 'assigned': - picking.carrier_id = self.carrier_id + # def write(self, vals): + # if 'carrier_id' in vals: + # for picking in self.picking_ids: + # if picking.state == 'assigned': + # picking.carrier_id = self.carrier_id def calculate_so_status(self): so_state = ['sale'] @@ -1157,12 +1157,12 @@ class SaleOrder(models.Model): helper_ids_str = self.env['ir.config_parameter'].sudo().get_param('sale.order.user_helper_ids') return helper_ids_str.split(', ') - def write(self, values): - helper_ids = self._get_helper_ids() - if str(self.env.user.id) in helper_ids: - values['helper_by_id'] = self.env.user.id - - return super(SaleOrder, self).write(values) + # def write(self, values): + # helper_ids = self._get_helper_ids() + # if str(self.env.user.id) in helper_ids: + # values['helper_by_id'] = self.env.user.id + # + # return super(SaleOrder, self).write(values) def check_due(self): """To show the due amount and warning stage""" @@ -1693,25 +1693,161 @@ class SaleOrder(models.Model): total_before_margin = sum(line.item_before_margin for line in order.order_line if line.product_id) order.total_before_margin = total_before_margin + # def _compute_total_percent_margin(self): + # for order in self: + # if order.amount_untaxed == 0: + # order.total_percent_margin = 0 + # continue + # if order.shipping_cost_covered == 'indoteknik': + # delivery_amt = order.delivery_amt + # else: + # delivery_amt = 0 + # + # net_margin = order.total_margin - order.biaya_lain_lain + # + # order.total_percent_margin = round( + # (net_margin / (order.amount_untaxed - order.fee_third_party)) * 100, 2) + + # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed-delivery_amt-order.fee_third_party)) * 100, 2) + # order.total_percent_margin = round( + # (order.total_margin / (order.amount_untaxed - order.fee_third_party - order.biaya_lain_lain)) * 100, 2) + # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed)) * 100, 2) + def _compute_total_percent_margin(self): for order in self: if order.amount_untaxed == 0: order.total_percent_margin = 0 continue + if order.shipping_cost_covered == 'indoteknik': delivery_amt = order.delivery_amt else: delivery_amt = 0 + # Net margin = total margin - biaya tambahan net_margin = order.total_margin - order.biaya_lain_lain + denominator = order.amount_untaxed - order.fee_third_party - order.total_percent_margin = round( - (net_margin / (order.amount_untaxed - order.fee_third_party)) * 100, 2) + if denominator > 0: + order.total_percent_margin = round((net_margin / denominator) * 100, 2) + else: + order.total_percent_margin = 0 - # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed-delivery_amt-order.fee_third_party)) * 100, 2) - # order.total_percent_margin = round( - # (order.total_margin / (order.amount_untaxed - order.fee_third_party - order.biaya_lain_lain)) * 100, 2) - # order.total_percent_margin = round((order.total_margin / (order.amount_untaxed)) * 100, 2) + # @api.onchange('biaya_lain_lain') + # def _onchange_biaya_lain_lain(self): + # """Ketika biaya_lain_lain berubah, simpan nilai margin sebelumnya""" + # if hasattr(self, '_origin') and self._origin.id: + # # Hitung margin sebelum biaya_lain_lain ditambahkan + # if self.amount_untaxed > 0: + # original_net_margin = self.total_margin # tanpa biaya_lain_lain + # self.total_margin_excl_third_party = round( + # (original_net_margin / (self.amount_untaxed - self.fee_third_party)) * 100, 2) + + def write(self, vals): + import logging + _logger = logging.getLogger(__name__) + + # Simpan nilai margin sebelumnya untuk field yang mempengaruhi perhitungan + margin_sebelumnya = {} + + margin_affecting_fields = [ + 'biaya_lain_lain', 'fee_third_party', 'delivery_amt', + 'ongkir_ke_xpdc', 'shipping_cost_covered', 'order_line' + ] + + if any(field in vals for field in margin_affecting_fields): + for order in self: + if order.amount_untaxed > 0: + # LOGIC PENTING: Kapan Before Margin harus diupdate? + + current_before = order.total_margin_excl_third_party or 0 + + # CASE 1: Before margin masih kosong, simpan margin saat ini + if current_before == 0: + margin_sebelumnya[order.id] = order.total_percent_margin + _logger.info( + f"CASE 1 - SO {order.name}: Before margin kosong, simpan {order.total_percent_margin}%") + + # CASE 2: Ada perubahan biaya_lain_lain + elif 'biaya_lain_lain' in vals: + old_biaya = order.biaya_lain_lain or 0 + new_biaya = vals['biaya_lain_lain'] or 0 + + _logger.info(f"SO {order.name}: Biaya lain-lain berubah dari {old_biaya} ke {new_biaya}") + + # Jika sebelumnya tidak ada biaya_lain_lain, dan sekarang ada + if old_biaya == 0 and new_biaya > 0: + # Simpan margin saat ini sebagai "before margin" + margin_sebelumnya[order.id] = order.total_percent_margin + _logger.info(f"Menyimpan before margin: {order.total_percent_margin}%") + + # Jika biaya_lain_lain dihapus (dari ada jadi 0) + elif old_biaya > 0 and new_biaya == 0: + # Before margin tetap tidak berubah (sudah tersimpan sebelumnya) + _logger.info(f"Biaya dihapus, before margin tetap: {current_before}%") + # TIDAK mengubah before margin + + # CASE 3: Perubahan field lain (fee_third_party, dll) + elif any(field in vals for field in + ['fee_third_party', 'delivery_amt', 'ongkir_ke_xpdc', 'order_line']): + # Simpan margin saat ini sebelum perubahan + margin_sebelumnya[order.id] = order.total_percent_margin + _logger.info(f"CASE 3 - Field lain berubah, simpan {order.total_percent_margin}%") + + # Validasi dan proses lainnya... + for order in self: + if order.state in ['sale', 'cancel']: + if 'order_line' in vals: + new_lines = vals.get('order_line', []) + for command in new_lines: + if command[0] == 0: + raise UserError( + "SO tidak dapat ditambahkan produk baru karena SO sudah menjadi sale order.") + + if 'carrier_id' in vals: + for order in self: + for picking in order.picking_ids: + if picking.state == 'assigned': + picking.carrier_id = vals['carrier_id'] + + try: + helper_ids = self._get_helper_ids() + if str(self.env.user.id) in helper_ids: + vals['helper_by_id'] = self.env.user.id + except: + pass + + # Jalankan super write + res = super(SaleOrder, self).write(vals) + + # Update before margin jika diperlukan + if margin_sebelumnya: + for order_id, margin_value in margin_sebelumnya.items(): + _logger.info(f"Updating before margin untuk SO {order_id}: {margin_value}%") + + self.env.cr.execute(""" + UPDATE sale_order + SET total_margin_excl_third_party = %s + WHERE id = %s + """, (margin_value, order_id)) + + self.env.cr.commit() + self.invalidate_cache(['total_margin_excl_third_party']) + + # Validasi lainnya + if any(field in vals for field in ['delivery_amt', 'carrier_id', 'shipping_cost_covered']): + try: + self._validate_delivery_amt() + except: + pass + + if any(field in vals for field in ["order_line", "client_order_ref"]): + try: + self._calculate_etrts_date() + except: + pass + + return res @api.onchange('sales_tax_id') def onchange_sales_tax_id(self): @@ -2002,23 +2138,23 @@ class SaleOrder(models.Model): 'customer_type': partner.customer_type, }) - def write(self, vals): - for order in self: - if order.state in ['sale', 'cancel']: - if 'order_line' in vals: - new_lines = vals.get('order_line', []) - for command in new_lines: - if command[0] == 0: # A new line is being added - raise UserError( - "SO tidak dapat ditambahkan produk baru karena SO sudah menjadi sale order.") - - res = super(SaleOrder, self).write(vals) - # self._check_total_margin_excl_third_party() - if any(fields in vals for fields in ['delivery_amt', 'carrier_id', 'shipping_cost_covered']): - self._validate_delivery_amt() - if any(field in vals for field in ["order_line", "client_order_ref"]): - self._calculate_etrts_date() - return res + # def write(self, vals): + # for order in self: + # if order.state in ['sale', 'cancel']: + # if 'order_line' in vals: + # new_lines = vals.get('order_line', []) + # for command in new_lines: + # if command[0] == 0: # A new line is being added + # raise UserError( + # "SO tidak dapat ditambahkan produk baru karena SO sudah menjadi sale order.") + # + # res = super(SaleOrder, self).write(vals) + # # self._check_total_margin_excl_third_party() + # if any(fields in vals for fields in ['delivery_amt', 'carrier_id', 'shipping_cost_covered']): + # self._validate_delivery_amt() + # if any(field in vals for field in ["order_line", "client_order_ref"]): + # self._calculate_etrts_date() + # return res # @api.depends('commitment_date') def _compute_ready_to_ship_status_detail(self): -- cgit v1.2.3 From 04e6162ba44784eb60b1f7122856f5f1a578d16f Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 2 Jun 2025 08:26:20 +0700 Subject: fix margin calculation formula --- indoteknik_custom/models/sale_order.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 3c69c3d1..096ffe3a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1724,12 +1724,11 @@ class SaleOrder(models.Model): else: delivery_amt = 0 - # Net margin = total margin - biaya tambahan - net_margin = order.total_margin - order.biaya_lain_lain - denominator = order.amount_untaxed - order.fee_third_party + net_margin = order.total_margin - order.fee_third_party - order.biaya_lain_lain - if denominator > 0: - order.total_percent_margin = round((net_margin / denominator) * 100, 2) + + if order.amount_untaxed > 0: + order.total_percent_margin = round((net_margin / order.amount_untaxed) * 100, 2) else: order.total_percent_margin = 0 -- cgit v1.2.3 From 570555a44d521ebc593490c35e5dfdb30790c201 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 2 Jun 2025 09:10:37 +0700 Subject: fix margin calculation formula --- indoteknik_custom/models/sale_order.py | 1 + 1 file changed, 1 insertion(+) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 096ffe3a..07e9a97e 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1693,6 +1693,7 @@ class SaleOrder(models.Model): total_before_margin = sum(line.item_before_margin for line in order.order_line if line.product_id) order.total_before_margin = total_before_margin + # Perhitungan Lama # def _compute_total_percent_margin(self): # for order in self: # if order.amount_untaxed == 0: -- cgit v1.2.3 From e846e6fef3274839e9f9b21984a753205d31970e Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 4 Jun 2025 15:21:27 +0700 Subject: unify write method --- indoteknik_custom/models/sale_order.py | 174 +++++++++++++++------------------ 1 file changed, 78 insertions(+), 96 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 07e9a97e..705d16ef 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -866,7 +866,6 @@ class SaleOrder(models.Model): def _validate_delivery_amt(self): is_indoteknik = self.carrier_id.id == 1 or self.shipping_cost_covered == 'indoteknik' is_active_id = not self.env.context.get('active_id', []) - if is_indoteknik and is_active_id: if self.delivery_amt == 0: if self.carrier_id.id == 1: @@ -1743,11 +1742,7 @@ class SaleOrder(models.Model): # self.total_margin_excl_third_party = round( # (original_net_margin / (self.amount_untaxed - self.fee_third_party)) * 100, 2) - def write(self, vals): - import logging - _logger = logging.getLogger(__name__) - - # Simpan nilai margin sebelumnya untuk field yang mempengaruhi perhitungan + def _prepare_before_margin_values(self, vals): margin_sebelumnya = {} margin_affecting_fields = [ @@ -1755,99 +1750,37 @@ class SaleOrder(models.Model): 'ongkir_ke_xpdc', 'shipping_cost_covered', 'order_line' ] - if any(field in vals for field in margin_affecting_fields): - for order in self: - if order.amount_untaxed > 0: - # LOGIC PENTING: Kapan Before Margin harus diupdate? - - current_before = order.total_margin_excl_third_party or 0 - - # CASE 1: Before margin masih kosong, simpan margin saat ini - if current_before == 0: - margin_sebelumnya[order.id] = order.total_percent_margin - _logger.info( - f"CASE 1 - SO {order.name}: Before margin kosong, simpan {order.total_percent_margin}%") + if not any(field in vals for field in margin_affecting_fields): + return {} - # CASE 2: Ada perubahan biaya_lain_lain - elif 'biaya_lain_lain' in vals: - old_biaya = order.biaya_lain_lain or 0 - new_biaya = vals['biaya_lain_lain'] or 0 - - _logger.info(f"SO {order.name}: Biaya lain-lain berubah dari {old_biaya} ke {new_biaya}") - - # Jika sebelumnya tidak ada biaya_lain_lain, dan sekarang ada - if old_biaya == 0 and new_biaya > 0: - # Simpan margin saat ini sebagai "before margin" - margin_sebelumnya[order.id] = order.total_percent_margin - _logger.info(f"Menyimpan before margin: {order.total_percent_margin}%") - - # Jika biaya_lain_lain dihapus (dari ada jadi 0) - elif old_biaya > 0 and new_biaya == 0: - # Before margin tetap tidak berubah (sudah tersimpan sebelumnya) - _logger.info(f"Biaya dihapus, before margin tetap: {current_before}%") - # TIDAK mengubah before margin - - # CASE 3: Perubahan field lain (fee_third_party, dll) - elif any(field in vals for field in - ['fee_third_party', 'delivery_amt', 'ongkir_ke_xpdc', 'order_line']): - # Simpan margin saat ini sebelum perubahan - margin_sebelumnya[order.id] = order.total_percent_margin - _logger.info(f"CASE 3 - Field lain berubah, simpan {order.total_percent_margin}%") - - # Validasi dan proses lainnya... for order in self: - if order.state in ['sale', 'cancel']: - if 'order_line' in vals: - new_lines = vals.get('order_line', []) - for command in new_lines: - if command[0] == 0: - raise UserError( - "SO tidak dapat ditambahkan produk baru karena SO sudah menjadi sale order.") - - if 'carrier_id' in vals: - for order in self: - for picking in order.picking_ids: - if picking.state == 'assigned': - picking.carrier_id = vals['carrier_id'] - - try: - helper_ids = self._get_helper_ids() - if str(self.env.user.id) in helper_ids: - vals['helper_by_id'] = self.env.user.id - except: - pass - - # Jalankan super write - res = super(SaleOrder, self).write(vals) - - # Update before margin jika diperlukan - if margin_sebelumnya: - for order_id, margin_value in margin_sebelumnya.items(): - _logger.info(f"Updating before margin untuk SO {order_id}: {margin_value}%") - - self.env.cr.execute(""" - UPDATE sale_order - SET total_margin_excl_third_party = %s - WHERE id = %s - """, (margin_value, order_id)) - - self.env.cr.commit() - self.invalidate_cache(['total_margin_excl_third_party']) - - # Validasi lainnya - if any(field in vals for field in ['delivery_amt', 'carrier_id', 'shipping_cost_covered']): - try: - self._validate_delivery_amt() - except: - pass + if order.amount_untaxed <= 0: + continue - if any(field in vals for field in ["order_line", "client_order_ref"]): - try: - self._calculate_etrts_date() - except: - pass + current_before = order.total_margin_excl_third_party or 0 + + # CASE 1: Before margin masih kosong → ambil dari item_percent_margin + if current_before == 0: + line_margin = 0 + for line in order.order_line: + if line.item_percent_margin is not None: + line_margin = line.item_percent_margin + break + margin_sebelumnya[order.id] = line_margin + _logger.info(f"[BEFORE] SO {order.name}: Before margin kosong, ambil dari order line: {line_margin}%") + else: + # CASE 2: Ada perubahan field yang mempengaruhi margin + for field in margin_affecting_fields: + if field in vals: + old_val = getattr(order, field, 0) or 0 + new_val = vals[field] or 0 + if old_val != new_val: + margin_sebelumnya[order.id] = order.total_percent_margin + _logger.info( + f"[BEFORE] SO {order.name}: {field} berubah dari {old_val} ke {new_val}, simpan {order.total_percent_margin}%") + break - return res + return margin_sebelumnya @api.onchange('sales_tax_id') def onchange_sales_tax_id(self): @@ -2184,4 +2117,53 @@ class SaleOrder(models.Model): else: order.ready_to_ship_status_detail = "On Track" else: - order.ready_to_ship_status_detail = 'On Track' \ No newline at end of file + order.ready_to_ship_status_detail = 'On Track' + + def write(self, vals): + + margin_sebelumnya = self._prepare_before_margin_values(vals) + + for order in self: + if order.state in ['sale', 'cancel']: + if 'order_line' in vals: + for command in vals.get('order_line', []): + if command[0] == 0: + raise UserError( + "SO tidak dapat ditambahkan produk baru karena SO sudah menjadi sale order.") + + if 'carrier_id' in vals: + for order in self: + for picking in order.picking_ids: + if picking.state == 'assigned': + picking.carrier_id = vals['carrier_id'] + + try: + helper_ids = self._get_helper_ids() + if str(self.env.user.id) in helper_ids: + vals['helper_by_id'] = self.env.user.id + except: + pass + + res = super(SaleOrder, self).write(vals) + + # Update before margin setelah write + if margin_sebelumnya: + for order_id, margin_value in margin_sebelumnya.items(): + _logger.info(f"[UPDATE] SO ID {order_id}: Set before margin ke {margin_value}%") + self.env.cr.execute(""" + UPDATE sale_order + SET total_margin_excl_third_party = %s + WHERE id = %s + """, (margin_value, order_id)) + + self.env.cr.commit() + self.invalidate_cache(['total_margin_excl_third_party']) + + # Validasi setelah write + if any(field in vals for field in ['delivery_amt', 'carrier_id', 'shipping_cost_covered']): + self._validate_delivery_amt() + + if any(field in vals for field in ["order_line", "client_order_ref"]): + self._calculate_etrts_date() + + return res \ No newline at end of file -- cgit v1.2.3