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(-) (limited to 'indoteknik_custom/models') 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(-) (limited to 'indoteknik_custom/models') 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(-) (limited to 'indoteknik_custom/models') 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(+) (limited to 'indoteknik_custom/models') 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 c22cbb9f52be68baa089af59dc822cfe56554d70 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Mon, 2 Jun 2025 09:33:59 +0700 Subject: remove merchandiser from po --- indoteknik_custom/models/purchase_order.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 21ca55eb..240289bf 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -816,7 +816,7 @@ class PurchaseOrder(models.Model): for line in self.order_line: if not line.so_line_id: continue - if line.so_line_id.vendor_id.id != vendor_po and not self.env.user.has_group('indoteknik_custom.group_role_merchandiser'): + if line.so_line_id.vendor_id.id != vendor_po: self.env.user.notify_danger( title='WARNING!!!', message="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)+")", @@ -834,7 +834,7 @@ class PurchaseOrder(models.Model): 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: + if self.total_percent_margin < self.total_so_percent_margin: self.env.user.notify_danger( title='WARNING!!!', message='Beda Margin dengan Sale Order', @@ -1002,7 +1002,7 @@ class PurchaseOrder(models.Model): self.approval_status_unlock = 'approvedFinance' else: raise UserError("Bisa langsung Confirm, menunggu persetujuan Finance jika ingin unlock PO") - elif self.env.user.is_leader or self.env.user.has_group('indoteknik_custom.group_role_merchandiser'): + elif self.env.user.is_leader or self.env.user.has_group('indoteknik_custom.group_role_purchasing'): raise UserError("Bisa langsung Confirm") elif self.total_percent_margin == self.total_so_percent_margin and self.matches_so and not greater_than_plafon and not different_vendor_message: raise UserError("Bisa langsung Confirm") -- cgit v1.2.3 From 01029f94471df384decb606dbb3088f31a5a8108 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 3 Jun 2025 09:49:28 +0700 Subject: push --- indoteknik_custom/models/stock_picking.py | 1 + 1 file changed, 1 insertion(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index a215eb74..3921ed5a 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -584,6 +584,7 @@ class StockPicking(models.Model): self.lalamove_phone = phone self.lalamove_status = pod.get("status") self.lalamove_delivered_at = delivered_at_dt + self.driver_arrival_date = delivered_at_dt return data raise UserError("No delivered data found in Lalamove response.") -- 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(-) (limited to 'indoteknik_custom/models') 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 From fa2c8cc7b00e963e740307484c174961f61ffc84 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Thu, 5 Jun 2025 08:35:39 +0700 Subject: (andri) add field Journal Uang Muka di PO dan Purchase Order pada CAB --- indoteknik_custom/models/account_move.py | 11 +++++++++++ indoteknik_custom/models/purchase_order.py | 2 +- indoteknik_custom/models/uangmuka_pembelian.py | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 30de67be..24cedc34 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -68,6 +68,17 @@ class AccountMove(models.Model): purchase_order_id = fields.Many2one('purchase.order', string='Purchase Order') length_of_payment = fields.Integer(string="Length of Payment", compute='compute_length_of_payment') + def name_get(self): + result = [] + for move in self: + if move.move_type == 'entry': + # Tampilkan nomor CAB (name) + result.append((move.id, move.name)) + else: + # Gunakan default display + result.append((move.id, move.display_name)) + return result + def compute_length_of_payment(self): for rec in self: payment_term = rec.invoice_payment_term_id.line_ids[0].days diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 240289bf..0696dd9f 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -65,7 +65,7 @@ class PurchaseOrder(models.Model): sale_order = fields.Char(string='Sale Order') matches_so = fields.Many2many('sale.order', string='Matches SO', compute='_compute_matches_so') is_create_uangmuka = fields.Boolean(string='Uang Muka?') - move_id = fields.Many2one('account.move', string='Account Move') + move_id = fields.Many2one('account.move', string='Journal Entries Uang Muka', domain=[('move_type', '=', 'entry')]) logbook_bill_id = fields.Many2one('report.logbook.bill', string='Logbook Bill') status_printed = fields.Selection([ ('not_printed', 'Belum Print'), diff --git a/indoteknik_custom/models/uangmuka_pembelian.py b/indoteknik_custom/models/uangmuka_pembelian.py index ba41f814..13d51dcf 100644 --- a/indoteknik_custom/models/uangmuka_pembelian.py +++ b/indoteknik_custom/models/uangmuka_pembelian.py @@ -57,6 +57,8 @@ class UangmukaPembelian(models.TransientModel): account_move = request.env['account.move'].create([param_header]) _logger.info('Success Create Uang Muka Pembelian %s' % account_move.name) + account_move.purchase_order_id = order.id # isi field purchase_order_id + if order.partner_id.parent_id: partner_id = order.partner_id.parent_id.id else: -- cgit v1.2.3 From c43cba9dd205a3f08343f79233d3af6f5ab189ff Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Thu, 5 Jun 2025 08:47:31 +0700 Subject: (andri) penyesuaian value field CAB ketika masi dalam keadaan draft --- indoteknik_custom/models/account_move.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 24cedc34..29368089 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -72,10 +72,14 @@ class AccountMove(models.Model): result = [] for move in self: if move.move_type == 'entry': - # Tampilkan nomor CAB (name) - result.append((move.id, move.name)) + # Jika masih draft, tampilkan 'Draft CAB' + if move.state == 'draft': + label = 'Draft CAB' + else: + label = move.name + result.append((move.id, label)) else: - # Gunakan default display + # Untuk invoice dan lainnya, pakai default result.append((move.id, move.display_name)) return result -- cgit v1.2.3 From 2b4d72b6157a438f0a77be8aaae6875c2b415392 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 5 Jun 2025 09:53:57 +0700 Subject: change acccess approve rpo --- indoteknik_custom/models/requisition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/requisition.py b/indoteknik_custom/models/requisition.py index 25133e72..6d10f511 100644 --- a/indoteknik_custom/models/requisition.py +++ b/indoteknik_custom/models/requisition.py @@ -87,7 +87,7 @@ class Requisition(models.Model): if self.env.user.id == 19 or self.env.user.id == 28: self.sales_approve = True elif self.env.user.id == 21 or self.env.user.id == 28: - if not self.sales_approve: + if not self.sales_approve and not self.env.user.id == 21: raise UserError('Darren Belum Approve') self.merchandise_approve = True -- cgit v1.2.3 From 9b47b9b9c09e2a04bf3be0e81c0931e825500ea4 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 5 Jun 2025 10:07:27 +0700 Subject: cr change access approve rpo --- indoteknik_custom/models/requisition.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/requisition.py b/indoteknik_custom/models/requisition.py index 6d10f511..75529378 100644 --- a/indoteknik_custom/models/requisition.py +++ b/indoteknik_custom/models/requisition.py @@ -87,15 +87,11 @@ class Requisition(models.Model): if self.env.user.id == 19 or self.env.user.id == 28: self.sales_approve = True elif self.env.user.id == 21 or self.env.user.id == 28: - if not self.sales_approve and not self.env.user.id == 21: - raise UserError('Darren Belum Approve') self.merchandise_approve = True def create_po_from_requisition(self): - if not self.sales_approve: - raise UserError('Harus Di Approve oleh Darren') - if not self.merchandise_approve: - raise UserError('Harus Di Approve oleh Rafly') + if not self.sales_approve and not self.merchandise_approve: + raise UserError('Harus Di Approve oleh Darren atau Rafly') if not self.requisition_lines: raise UserError('Tidak ada Lines, belum bisa create PO') if self.is_po: -- cgit v1.2.3 From 9a3b6feba01b1d27524484f5e612e49412942414 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 5 Jun 2025 10:12:14 +0700 Subject: push --- indoteknik_custom/models/requisition.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/requisition.py b/indoteknik_custom/models/requisition.py index 75529378..bcdafb12 100644 --- a/indoteknik_custom/models/requisition.py +++ b/indoteknik_custom/models/requisition.py @@ -48,8 +48,8 @@ class Requisition(models.Model): is_po = fields.Boolean(string='Is PO') requisition_match = fields.One2many('requisition.purchase.match', 'requisition_id', string='Matches', auto_join=True) sale_order_id = fields.Many2one('sale.order', string='SO', help='harus diisi nomor SO yang ingin digenerate') - sales_approve = fields.Boolean(string='Sales Approve', tracking=3, copy=False) - merchandise_approve = fields.Boolean(string='Merchandise Approve', tracking=3, copy=False) + sales_approve = fields.Boolean(string='Approval Status', tracking=3, copy=False) + merchandise_approve = fields.Boolean(string='Approval Status', tracking=3, copy=False) def generate_requisition_from_so(self): state = ['done', 'sale'] -- cgit v1.2.3 From 55fb073416a7f8504c3819178aa7b6883304335f Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Thu, 5 Jun 2025 10:53:30 +0700 Subject: (andri) merubah peletakan CAB menjadi berada di atas sebelah tombol receipt --- indoteknik_custom/models/purchase_order.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 0696dd9f..27f1aebd 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -89,6 +89,20 @@ 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') + def action_view_journal_uangmuka(self): + self.ensure_one() + if not self.move_id: + raise UserError("Journal Uang Muka belum tersedia.") + + return { + 'type': 'ir.actions.act_window', + 'name': 'Journal Entry', + 'res_model': 'account.move', + 'res_id': self.move_id.id, + 'view_mode': 'form', + 'target': 'current', + } + # cek payment term def _check_payment_term(self): _logger.info("Check Payment Term Terpanggil") -- cgit v1.2.3 From add53c91a49d43b20438b14cf48aaececbabf2cd Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 5 Jun 2025 11:43:02 +0700 Subject: add before margin in order line --- indoteknik_custom/models/sale_order_line.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index c8066961..da66465e 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -10,6 +10,8 @@ class SaleOrderLine(models.Model): help="Total Margin in Sales Order Header") item_percent_margin = fields.Float('%Margin', compute='compute_item_margin', help="Total % Margin in Sales Order Header") + item_percent_margin_before = fields.Float('%Margin Before', compute='_compute_item_percent_margin_before', + help="Total % Margin excluding third party in Sales Order Header") initial_discount = fields.Float('Initial Discount') vendor_id = fields.Many2one( 'res.partner', string='Vendor', readonly=True, @@ -134,6 +136,29 @@ class SaleOrderLine(models.Model): else: line.item_percent_margin_without_deduction = 0 + def _compute_item_percent_margin_before(self): + for line in self: + if not line.product_id or line.product_id.type == 'service' \ + or line.price_unit <= 0 or line.product_uom_qty <= 0 \ + or not line.vendor_id: + line.item_percent_margin_before = 0 + continue + + sales_price = line.price_reduce_taxexcl * line.product_uom_qty + + purchase_price = line.purchase_price + if line.purchase_tax_id and line.purchase_tax_id.price_include: + purchase_price = line.purchase_price / 1.11 + + purchase_price = purchase_price * line.product_uom_qty + + margin_before = sales_price - purchase_price + + if sales_price > 0: + line.item_percent_margin_before = round((margin_before / sales_price), 4) * 100 + else: + line.item_percent_margin_before = 0 + def compute_item_margin(self): for line in self: if not line.product_id or line.product_id.type == 'service' \ -- cgit v1.2.3 From 32c2899ccd24071adcee829b569b9d29bb3a4fe9 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 5 Jun 2025 11:59:27 +0700 Subject: fix round --- indoteknik_custom/models/sale_order_line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index da66465e..291940ed 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -155,7 +155,7 @@ class SaleOrderLine(models.Model): margin_before = sales_price - purchase_price if sales_price > 0: - line.item_percent_margin_before = round((margin_before / sales_price), 4) * 100 + line.item_percent_margin_before = round((margin_before / sales_price), 2) * 100 else: line.item_percent_margin_before = 0 -- cgit v1.2.3 From d3538371691a43efbd5527b746c942bdef9fd1ba Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 7 Jun 2025 11:02:03 +0700 Subject: (andri) Pada PO, CAB tidak tampil jika statusnya bukan posted --- indoteknik_custom/models/purchase_order.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 27f1aebd..004a1fa4 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -89,6 +89,14 @@ 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') + is_cab_visible = fields.Boolean(string='Tampilkan Tombol CAB', compute='_compute_is_cab_visible') + + @api.depends('move_id.state') + def _compute_is_cab_visible(self): + for order in self: + move = order.move_id + order.is_cab_visible = bool(move and move.state == 'posted') + def action_view_journal_uangmuka(self): self.ensure_one() if not self.move_id: -- cgit v1.2.3 From 02511349a98e9488ed91795a062774f7d3ad26a6 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 7 Jun 2025 13:41:09 +0700 Subject: (andri) Nomor CAB serta nominal terisi otomatis ketika create reklas --- indoteknik_custom/models/invoice_reklas.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/invoice_reklas.py b/indoteknik_custom/models/invoice_reklas.py index d10d4c31..8641ca07 100644 --- a/indoteknik_custom/models/invoice_reklas.py +++ b/indoteknik_custom/models/invoice_reklas.py @@ -18,6 +18,17 @@ class InvoiceReklas(models.TransientModel): ('pembelian', 'Pembelian'), ], string='Reklas Tipe') + @api.model + def default_get(self, fields): + res = super().default_get(fields) + active_ids = self._context.get('active_ids', []) + if active_ids: + move = self.env['account.move'].browse(active_ids[0]) + if move.move_type == 'entry': + res['reklas_id'] = move.id + res['pay_amt'] = move.amount_total # atau amount_residual jika mau sisa + return res + @api.onchange('reklas_type') def _onchange_reklas_type(self): if self.reklas_type == 'penjualan': -- cgit v1.2.3 From 49dcd6e8111483cd8f64aa2401a41b3a8d57a1dc Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 7 Jun 2025 13:46:46 +0700 Subject: (andri) onchange Nomor CAB serta nominal terisi otomatis ketika create reklas --- indoteknik_custom/models/invoice_reklas.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/invoice_reklas.py b/indoteknik_custom/models/invoice_reklas.py index 8641ca07..59c78ce6 100644 --- a/indoteknik_custom/models/invoice_reklas.py +++ b/indoteknik_custom/models/invoice_reklas.py @@ -18,6 +18,15 @@ class InvoiceReklas(models.TransientModel): ('pembelian', 'Pembelian'), ], string='Reklas Tipe') + @api.onchange('reklas_type') + def _onchange_reklas_type(self): + if self.reklas_type == 'penjualan': + invoices = self.env['account.move'].browse(self._context.get('active_ids', [])) + self.pay_amt = invoices.amount_total + # Tambahan ini: + if len(invoices) == 1 and invoices.move_type == 'entry': + self.reklas_id = invoices.id + @api.model def default_get(self, fields): res = super().default_get(fields) -- cgit v1.2.3 From 984e128294f4c3d9d3d8eab8dacf03846f8336b2 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 7 Jun 2025 15:53:32 +0700 Subject: (andri) revisi peletakan CAB pada PO dan add field reklas misc --- indoteknik_custom/models/account_move.py | 1 + 1 file changed, 1 insertion(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 29368089..73dbefd6 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -67,6 +67,7 @@ class AccountMove(models.Model): is_hr = fields.Boolean(string="Is HR?", default=False) purchase_order_id = fields.Many2one('purchase.order', string='Purchase Order') length_of_payment = fields.Integer(string="Length of Payment", compute='compute_length_of_payment') + reklas_misc_id = fields.Many2one('account.move', string='No Jurnal Reklas (MISC)') def name_get(self): result = [] -- cgit v1.2.3 From 9200e74126c99410b79ef2c344915251bef6af19 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Sat, 7 Jun 2025 15:55:40 +0700 Subject: (andri) edit penamaan field Reklas --- indoteknik_custom/models/account_move.py | 2 +- indoteknik_custom/models/invoice_reklas.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 73dbefd6..3333af8f 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -67,7 +67,7 @@ class AccountMove(models.Model): is_hr = fields.Boolean(string="Is HR?", default=False) purchase_order_id = fields.Many2one('purchase.order', string='Purchase Order') length_of_payment = fields.Integer(string="Length of Payment", compute='compute_length_of_payment') - reklas_misc_id = fields.Many2one('account.move', string='No Jurnal Reklas (MISC)') + reklas_misc_id = fields.Many2one('account.move', string='Journal Entries Reklas') def name_get(self): result = [] diff --git a/indoteknik_custom/models/invoice_reklas.py b/indoteknik_custom/models/invoice_reklas.py index 59c78ce6..5e21a787 100644 --- a/indoteknik_custom/models/invoice_reklas.py +++ b/indoteknik_custom/models/invoice_reklas.py @@ -11,7 +11,7 @@ _logger = logging.getLogger(__name__) class InvoiceReklas(models.TransientModel): _name = 'invoice.reklas' _description = "digunakan untuk reklas Uang Muka Penjualan" - reklas_id = fields.Many2one('account.move', string='Nomor CAB') + reklas_id = fields.Many2one('account.move', string='Nomor CAB', domain="[('move_type','=','entry')]") pay_amt = fields.Float(string='Yang dibayarkan') reklas_type = fields.Selection([ ('penjualan', 'Penjualan'), -- cgit v1.2.3 From a7f3eb12ddc41a18df90a3d5519014c07c0e5d7b Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 9 Jun 2025 08:23:04 +0700 Subject: (andri) fix retry email marketing --- indoteknik_custom/models/mail_mail.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/mail_mail.py b/indoteknik_custom/models/mail_mail.py index 82b1fcca..7ebd9293 100644 --- a/indoteknik_custom/models/mail_mail.py +++ b/indoteknik_custom/models/mail_mail.py @@ -1,12 +1,21 @@ from odoo import fields, models, api, _ +from datetime import timedelta class MailMail(models.Model): _inherit = 'mail.mail' + @api.model def retry_send_mail(self): - mails = self.env['mail.mail'].search([ + now = fields.Datetime.now() + seven_days_ago = now - timedelta(days=7) + + # Filter hanya email gagal dalam 7 hari terakhir + mails = self.search([ ('state', 'in', ['exception', 'cancel']), + ('create_date', '>=', seven_days_ago), + ('create_date', '<=', now), ], limit=250) + for mail in mails: - mail.state = 'outgoing' + mail.state = 'outgoing' -- cgit v1.2.3 From 114d17b3d8e21801a4020956c8e8eb80d33b1be6 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Mon, 9 Jun 2025 09:28:58 +0700 Subject: (andri) fix retry --- indoteknik_custom/models/mail_mail.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/mail_mail.py b/indoteknik_custom/models/mail_mail.py index 7ebd9293..cbcd883a 100644 --- a/indoteknik_custom/models/mail_mail.py +++ b/indoteknik_custom/models/mail_mail.py @@ -1,6 +1,8 @@ from odoo import fields, models, api, _ from datetime import timedelta +import logging +_logger = logging.getLogger(__name__) class MailMail(models.Model): _inherit = 'mail.mail' @@ -12,10 +14,14 @@ class MailMail(models.Model): # Filter hanya email gagal dalam 7 hari terakhir mails = self.search([ - ('state', 'in', ['exception', 'cancel']), + ('state', 'in', 'exception'), ('create_date', '>=', seven_days_ago), ('create_date', '<=', now), ], limit=250) + _logger.info("Found %s failed emails in last 7 days to retry.", len(mails)) + for mail in mails: - mail.state = 'outgoing' + _logger.info("Retrying email ID %s - To: %s - Subject: %s", + mail.id, mail.email_to, mail.subject) + mail.state = 'outgoing' -- cgit v1.2.3 From 03bceb0f2641c1f7303dffcf0dcbc855c70cffb3 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 9 Jun 2025 12:59:54 +0700 Subject: fix commitment date kosong --- indoteknik_custom/models/sale_order.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 705d16ef..565e8a19 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2109,11 +2109,15 @@ class SaleOrder(models.Model): ], limit=1) picking_in = stock_move.picking_id result_date = picking_in.date_done if picking_in else None - if result_date: + + if result_date and eta and isinstance(eta, (datetime.date, datetime.datetime)): status = "Early" if result_date < eta else "Delay" result_date_str = result_date.strftime('%m/%d/%Y') eta_str = eta.strftime('%m/%d/%Y') order.ready_to_ship_status_detail = f"Expected: {eta_str} | Realtime: {result_date_str} | {status}" + elif not eta: + # If eta is missing or False, treat as 'On Track' or you may choose different logic + order.ready_to_ship_status_detail = "On Track" else: order.ready_to_ship_status_detail = "On Track" else: -- cgit v1.2.3 From e577c31c748d66b102362af35a33984b9b28edd4 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 9 Jun 2025 15:51:28 +0700 Subject: fix bug cant open so --- indoteknik_custom/models/sale_order.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 565e8a19..eab4f2f3 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2092,7 +2092,7 @@ class SaleOrder(models.Model): # @api.depends('commitment_date') def _compute_ready_to_ship_status_detail(self): for order in self: - eta = order.commitment_date + eta = order.commitment_date if order.commitment_date else None match_lines = self.env['purchase.order.sales.match'].search([ ('sale_id', '=', order.id) ]) @@ -2109,9 +2109,10 @@ class SaleOrder(models.Model): ], limit=1) picking_in = stock_move.picking_id result_date = picking_in.date_done if picking_in else None - - if result_date and eta and isinstance(eta, (datetime.date, datetime.datetime)): - status = "Early" if result_date < eta else "Delay" + if result_date: + status = "Delay" + if result_date and eta and result_date < eta: + status = "Early" result_date_str = result_date.strftime('%m/%d/%Y') eta_str = eta.strftime('%m/%d/%Y') order.ready_to_ship_status_detail = f"Expected: {eta_str} | Realtime: {result_date_str} | {status}" -- cgit v1.2.3 From 05dafe1ab837cac8992d1dc6c012a26bce88c15c Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 10 Jun 2025 10:36:45 +0700 Subject: push code apit --- indoteknik_custom/models/sale_order.py | 49 +++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 16 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index eab4f2f3..baa8207f 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2091,11 +2091,22 @@ class SaleOrder(models.Model): # @api.depends('commitment_date') def _compute_ready_to_ship_status_detail(self): + def is_empty(val): + """Helper untuk cek data kosong yang umum di Odoo.""" + return val is None or val == "" or val == [] or val == {} + for order in self: - eta = order.commitment_date if order.commitment_date else None + order.ready_to_ship_status_detail = 'On Track' # Default value + + # Skip if no commitment date + if is_empty(order.commitment_date): + continue + + eta = order.commitment_date match_lines = self.env['purchase.order.sales.match'].search([ ('sale_id', '=', order.id) ]) + if match_lines: for match in match_lines: po = match.purchase_order_id @@ -2104,25 +2115,31 @@ class SaleOrder(models.Model): ('order_id', '=', po.id), ('product_id', '=', product.id) ], limit=1) + + if is_empty(po_line): + continue + stock_move = self.env['stock.move'].search([ ('purchase_line_id', '=', po_line.id) ], limit=1) + + if is_empty(stock_move) or is_empty(stock_move.picking_id): + continue + picking_in = stock_move.picking_id - result_date = picking_in.date_done if picking_in else None - if result_date: - status = "Delay" - if result_date and eta and result_date < eta: - status = "Early" - result_date_str = result_date.strftime('%m/%d/%Y') - eta_str = eta.strftime('%m/%d/%Y') - order.ready_to_ship_status_detail = f"Expected: {eta_str} | Realtime: {result_date_str} | {status}" - elif not eta: - # If eta is missing or False, treat as 'On Track' or you may choose different logic - order.ready_to_ship_status_detail = "On Track" - else: - order.ready_to_ship_status_detail = "On Track" - else: - order.ready_to_ship_status_detail = 'On Track' + result_date = picking_in.date_done + + if is_empty(result_date): + continue + + try: + if result_date < eta: + order.ready_to_ship_status_detail = f"Early (Actual: {result_date.strftime('%m/%d/%Y')})" + else: + order.ready_to_ship_status_detail = f"Delay (Actual: {result_date.strftime('%m/%d/%Y')})" + except Exception as e: + _logger.error(f"Error computing ready to ship status: {str(e)}") + continue def write(self, vals): -- cgit v1.2.3 From c827294ee6dd613de089af846521cfdc76550e16 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 10 Jun 2025 13:10:35 +0700 Subject: (andri) revisi mengenai onchange create reklas dan yang lain --- indoteknik_custom/models/account_move.py | 3 + indoteknik_custom/models/invoice_reklas.py | 97 ++++++++++++++++------ .../models/invoice_reklas_penjualan.py | 60 +++++++++---- 3 files changed, 117 insertions(+), 43 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 3333af8f..4cd2b6b3 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -68,6 +68,9 @@ class AccountMove(models.Model): purchase_order_id = fields.Many2one('purchase.order', string='Purchase Order') length_of_payment = fields.Integer(string="Length of Payment", compute='compute_length_of_payment') reklas_misc_id = fields.Many2one('account.move', string='Journal Entries Reklas') + # Di model account.move + bill_id = fields.Many2one('account.move', string='Vendor Bill', domain=[('move_type', '=', 'in_invoice')], help='Bill asal dari proses reklas ini') + def name_get(self): result = [] diff --git a/indoteknik_custom/models/invoice_reklas.py b/indoteknik_custom/models/invoice_reklas.py index 5e21a787..b7d52371 100644 --- a/indoteknik_custom/models/invoice_reklas.py +++ b/indoteknik_custom/models/invoice_reklas.py @@ -20,12 +20,26 @@ class InvoiceReklas(models.TransientModel): @api.onchange('reklas_type') def _onchange_reklas_type(self): - if self.reklas_type == 'penjualan': - invoices = self.env['account.move'].browse(self._context.get('active_ids', [])) - self.pay_amt = invoices.amount_total - # Tambahan ini: - if len(invoices) == 1 and invoices.move_type == 'entry': - self.reklas_id = invoices.id + active_ids = self._context.get('active_ids', []) + if not active_ids: + return + + move = self.env['account.move'].browse(active_ids[0]) + cab = False + + if move.move_type == 'entry': + cab = move + elif move.move_type == 'in_invoice': + if move.reklas_misc_id: + cab = move.reklas_misc_id + elif move.purchase_order_id and move.purchase_order_id.move_id: + cab = move.purchase_order_id.move_id + + if cab: + self.reklas_id = cab.id + + # ✅ Selalu ambil nilai dari invoice yang direklas (bukan dari CAB) + self.pay_amt = move.amount_total @api.model def default_get(self, fields): @@ -33,11 +47,23 @@ class InvoiceReklas(models.TransientModel): active_ids = self._context.get('active_ids', []) if active_ids: move = self.env['account.move'].browse(active_ids[0]) + cab = False + if move.move_type == 'entry': - res['reklas_id'] = move.id - res['pay_amt'] = move.amount_total # atau amount_residual jika mau sisa + cab = move + elif move.move_type == 'in_invoice': + if move.reklas_misc_id: + cab = move.reklas_misc_id + elif move.purchase_order_id and move.purchase_order_id.move_id: + cab = move.purchase_order_id.move_id + + if cab: + res['reklas_id'] = cab.id + + res['pay_amt'] = move.amount_total return res + @api.onchange('reklas_type') def _onchange_reklas_type(self): if self.reklas_type == 'penjualan': @@ -49,33 +75,47 @@ class InvoiceReklas(models.TransientModel): raise UserError('Reklas Tipe harus diisi') if not self.reklas_id: raise UserError('Nomor CAB harus diisi') + invoices = self.env['account.move'].browse(self._context.get('active_ids', [])) current_time = datetime.now() + for invoice in invoices: - if self.reklas_type == 'penjualan': - ref_name = 'REKLAS '+self.reklas_id.name+" UANG MUKA PENJUALAN "+invoice.name+" "+invoice.partner_id.name - else: - ref_name = 'REKLAS '+self.reklas_id.name+" UANG MUKA PEMBELIAN "+invoice.name+" "+invoice.partner_id.name - if self.reklas_type == 'penjualan': - parameters_header = { - 'ref': ref_name, - 'date': current_time, - 'journal_id': 13 - } - else: - parameters_header = { - 'ref': ref_name, - 'date': current_time, - 'journal_id': 13 - } + # Ambil nama PO jika ada + po_name = invoice.purchase_order_id.name if invoice.purchase_order_id else '' + + # Susun nama referensi dengan aman + ref_name = 'REKLAS {} UANG MUKA {} {}{} {}'.format( + self.reklas_id.name or '', + 'PENJUALAN' if self.reklas_type == 'penjualan' else 'PEMBELIAN', + invoice.name or '', + f" - {po_name}" if po_name else '', + invoice.partner_id.name or '' + ) + + # Header jurnal reklas + parameters_header = { + 'ref': ref_name, + 'date': current_time, + 'journal_id': 13 + } account_move = request.env['account.move'].create([parameters_header]) _logger.info('Success Reklas with %s' % account_move.name) + # ✅ Set Bill asal sebagai source document + account_move.bill_id = invoice.id + + # Tambahkan info asal invoice ke jurnal (opsional) + account_move.invoice_origin = invoice.name + + # Simpan hubungan balik ke invoice + invoice.reklas_misc_id = account_move.id + + # Buat line debit dan kredit if self.reklas_type == 'penjualan': parameter_debit = { 'move_id': account_move.id, - 'account_id': 668, # penerimaan belum alokasi + 'account_id': 668, # penerimaan belum alokasi 'partner_id': invoice.partner_id.id, 'currency_id': 12, 'debit': self.pay_amt, @@ -91,7 +131,7 @@ class InvoiceReklas(models.TransientModel): 'credit': self.pay_amt, 'name': ref_name } - else: + else: # pembelian parameter_debit = { 'move_id': account_move.id, 'account_id': 438, @@ -110,7 +150,11 @@ class InvoiceReklas(models.TransientModel): 'credit': self.pay_amt, 'name': ref_name } + + # Simpan journal lines request.env['account.move.line'].create([parameter_debit, parameter_credit]) + + # Tampilkan hasil jurnal reklas return { 'name': _('Journal Entries'), 'view_mode': 'form', @@ -120,4 +164,3 @@ class InvoiceReklas(models.TransientModel): 'type': 'ir.actions.act_window', 'res_id': account_move.id } - \ No newline at end of file diff --git a/indoteknik_custom/models/invoice_reklas_penjualan.py b/indoteknik_custom/models/invoice_reklas_penjualan.py index 80c3ed43..2f5ee160 100644 --- a/indoteknik_custom/models/invoice_reklas_penjualan.py +++ b/indoteknik_custom/models/invoice_reklas_penjualan.py @@ -17,43 +17,70 @@ class InvoiceReklasPenjualan(models.TransientModel): def create_reklas_penjualan(self): invoices = self.invoice_reklas_line - current_time = datetime.now() account_move_ids = [] - for invoice in invoices: - ref_name = 'REKLAS ' + invoice.reklas_id.name + " UANG MUKA PENJUALAN " + invoice.name + " " + invoice.partner_id.name + + for line in invoices: + # Ambil nama SO jika ada + so_name = line.sale_id.name if line.sale_id else '' + + # Susun referensi nama jurnal + ref_name = 'REKLAS {} UANG MUKA PENJUALAN {}{} {}'.format( + line.reklas_id.name or '', + line.name or '', + f" - {so_name}" if so_name else '', + line.partner_id.name or '' + ) + + # Header jurnal parameters_header = { 'ref': ref_name, 'date': current_time, - 'journal_id': 13 + 'journal_id': 13, + # ⬇️ Tambahkan jika tahu invoice asal (name = ID Bill) + 'bill_id': int(line.name) if line.name and line.name.isdigit() else False, } account_move = self.env['account.move'].create([parameters_header]) _logger.info('Success Reklas with %s' % account_move.name) - parameter_debit = { + # Simpan info asal (optional) + account_move.invoice_origin = line.name + + # Simpan juga ke `reklas_misc_id` jika ditemukan invoice valid + if line.name and line.name.isdigit(): + invoice_id = self.env['account.move'].browse(int(line.name)) + if invoice_id.exists(): + invoice_id.reklas_misc_id = account_move.id + + # Buat debit kredit line + debit_line = { 'move_id': account_move.id, - 'account_id': 668, # uang muka penjualan - 'partner_id': invoice.partner_id.id, + 'account_id': 668, # akun penerimaan belum alokasi + 'partner_id': line.partner_id.id, 'currency_id': 12, - 'debit': invoice.pay_amt, + 'debit': line.pay_amt, 'credit': 0, 'name': ref_name } - parameter_credit = { + credit_line = { 'move_id': account_move.id, - 'account_id': 395, - 'partner_id': invoice.partner_id.id, + 'account_id': 395, # akun pengurang + 'partner_id': line.partner_id.id, 'currency_id': 12, 'debit': 0, - 'credit': invoice.pay_amt, + 'credit': line.pay_amt, 'name': ref_name } - self.env['account.move.line'].create([parameter_debit, parameter_credit]) + + self.env['account.move.line'].create([debit_line, credit_line]) account_move_ids.append(account_move.id) - invoice.unlink() - - self.unlink() + + line.unlink() # bersihkan line setelah selesai + + self.unlink() # hapus wizard utama setelah selesai + + # Tampilkan hasil jurnal reklas return { 'name': _('Journal Entries'), 'view_mode': 'tree,form', @@ -63,6 +90,7 @@ class InvoiceReklasPenjualan(models.TransientModel): 'domain': [('id', 'in', account_move_ids)], } + class InvoiceReklasPenjualanLine(models.TransientModel): _name = 'invoice.reklas.penjualan.line' _description = "digunakan untuk reklas Uang Muka Penjualan" -- cgit v1.2.3 From d89f6b21f0174d52b1efb7d54ec3443bbbe3fbcb Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Tue, 10 Jun 2025 14:00:00 +0700 Subject: (andri) penyesuaian munculnya field --- indoteknik_custom/models/invoice_reklas.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/invoice_reklas.py b/indoteknik_custom/models/invoice_reklas.py index b7d52371..5145e098 100644 --- a/indoteknik_custom/models/invoice_reklas.py +++ b/indoteknik_custom/models/invoice_reklas.py @@ -27,6 +27,7 @@ class InvoiceReklas(models.TransientModel): move = self.env['account.move'].browse(active_ids[0]) cab = False + # Deteksi dari mana asal CAB if move.move_type == 'entry': cab = move elif move.move_type == 'in_invoice': @@ -35,12 +36,14 @@ class InvoiceReklas(models.TransientModel): elif move.purchase_order_id and move.purchase_order_id.move_id: cab = move.purchase_order_id.move_id + # Isi field Nomor CAB jika ditemukan if cab: self.reklas_id = cab.id - # ✅ Selalu ambil nilai dari invoice yang direklas (bukan dari CAB) + # Nilai yang dibayarkan harus tetap ambil dari invoice/bill self.pay_amt = move.amount_total + @api.model def default_get(self, fields): res = super().default_get(fields) @@ -64,11 +67,11 @@ class InvoiceReklas(models.TransientModel): return res - @api.onchange('reklas_type') - def _onchange_reklas_type(self): - if self.reklas_type == 'penjualan': - invoices = self.env['account.move'].browse(self._context.get('active_ids', [])) - self.pay_amt = invoices.amount_total + # @api.onchange('reklas_type') + # def _onchange_reklas_type(self): + # if self.reklas_type == 'penjualan': + # invoices = self.env['account.move'].browse(self._context.get('active_ids', [])) + # self.pay_amt = invoices.amount_total def create_reklas(self): if not self.reklas_type: -- cgit v1.2.3 From acc284c5881675780faecd89ab5aaa85fd017914 Mon Sep 17 00:00:00 2001 From: "Indoteknik ." Date: Wed, 11 Jun 2025 13:07:51 +0700 Subject: (andri) fix bug name invoice yang berdampak pada dunningrun line --- indoteknik_custom/models/account_move.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 4cd2b6b3..54eaabcf 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -72,20 +72,20 @@ class AccountMove(models.Model): bill_id = fields.Many2one('account.move', string='Vendor Bill', domain=[('move_type', '=', 'in_invoice')], help='Bill asal dari proses reklas ini') - def name_get(self): - result = [] - for move in self: - if move.move_type == 'entry': - # Jika masih draft, tampilkan 'Draft CAB' - if move.state == 'draft': - label = 'Draft CAB' - else: - label = move.name - result.append((move.id, label)) - else: - # Untuk invoice dan lainnya, pakai default - result.append((move.id, move.display_name)) - return result + # def name_get(self): + # result = [] + # for move in self: + # if move.move_type == 'entry': + # # Jika masih draft, tampilkan 'Draft CAB' + # if move.state == 'draft': + # label = 'Draft CAB' + # else: + # label = move.name + # result.append((move.id, label)) + # else: + # # Untuk invoice dan lainnya, pakai default + # result.append((move.id, move.display_name)) + # return result def compute_length_of_payment(self): for rec in self: -- cgit v1.2.3 From b6f63fcf96355bc11f9843a9f661cea0a458475e Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 12 Jun 2025 13:17:22 +0700 Subject: fix tracking api lalamove --- indoteknik_custom/models/stock_picking.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 3921ed5a..9dfc8b78 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -529,13 +529,7 @@ class StockPicking(models.Model): record.kgx_pod_photo = "No image available." def action_fetch_lalamove_order(self): - pickings = self.env['stock.picking'].search([ - ('picking_type_code', '=', 'outgoing'), - ('state', '=', 'done'), - ('carrier_id', '=', 9), - ('lalamove_order_id', '!=', False) - ]) - for picking in pickings: + for picking in self: try: order_id = picking.lalamove_order_id apikey = self.env['ir.config_parameter'].sudo().get_param('lalamove.apikey') -- cgit v1.2.3