From 6adb1cb5fe8dbe85b971dc1cad476718f24c38cb Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Wed, 1 Oct 2025 13:49:26 +0700 Subject: (andri) add validasi approval pimpinan jika PO biasa tidak ada matches SO dan PO PJ terdapat matches SO line dengan SO margin <=15% --- indoteknik_custom/models/purchase_order.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index b34ec926..a76cdab1 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1069,6 +1069,19 @@ class PurchaseOrder(models.Model): ) % order.name) def button_confirm(self): + if self.env.user.id != 7: # Pimpinan + if '/PJ/' in self.name: + low_margin_lines = self.order_sales_match_line.filtered( + lambda match: match.sale_line_id.item_percent_margin <= 34.0 + ) + if low_margin_lines: + raise UserError("Matches SO terdapat item dengan margin SO <= 15%. Approval Pimpinan diperlukan.") + else: + is_po_manual = '/A/' not in self.name and '/MO/' not in self.name + if is_po_manual: + if not self.order_sales_match_line: + raise UserError("Tidak ada matches SO, Approval Pimpinan diperlukan.") + self._check_assets_note() # self._check_payment_term() # check payment term res = super(PurchaseOrder, self).button_confirm() -- cgit v1.2.3 From c04dd5a6a38ba35b30a39ed6596514679067e541 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Wed, 1 Oct 2025 15:10:10 +0700 Subject: (andri) add so header margin pada matches SO + fix --- indoteknik_custom/models/purchase_order.py | 4 ++-- indoteknik_custom/models/purchase_order_sales_match.py | 5 +++++ indoteknik_custom/views/purchase_order.xml | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index a76cdab1..1a88d036 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1072,10 +1072,10 @@ class PurchaseOrder(models.Model): if self.env.user.id != 7: # Pimpinan if '/PJ/' in self.name: low_margin_lines = self.order_sales_match_line.filtered( - lambda match: match.sale_line_id.item_percent_margin <= 34.0 + lambda match: match.so_header_margin <= 34.0 ) if low_margin_lines: - raise UserError("Matches SO terdapat item dengan margin SO <= 15%. Approval Pimpinan diperlukan.") + raise UserError("Matches SO terdapat item dengan header margin SO <= 15%. Approval Pimpinan diperlukan.") else: is_po_manual = '/A/' not in self.name and '/MO/' not in self.name if is_po_manual: diff --git a/indoteknik_custom/models/purchase_order_sales_match.py b/indoteknik_custom/models/purchase_order_sales_match.py index 084b93f7..ea25a3b1 100644 --- a/indoteknik_custom/models/purchase_order_sales_match.py +++ b/indoteknik_custom/models/purchase_order_sales_match.py @@ -29,6 +29,11 @@ class PurchaseOrderSalesMatch(models.Model): purchase_line_id = fields.Many2one('purchase.order.line', string='Purchase Line', compute='_compute_purchase_line_id') hold_outgoing_so = fields.Boolean(string='Hold Outgoing SO', related='sale_id.hold_outgoing') bu_pick = fields.Many2one('stock.picking', string='BU Pick', compute='compute_bu_pick') + so_header_margin = fields.Float( + related='sale_id.total_percent_margin', + string='SO Header Margin %', + readonly=True + ) def compute_bu_pick(self): for rec in self: diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index 7feec934..437b86b8 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -390,6 +390,7 @@ + -- cgit v1.2.3 From 40c66acf47f900ecd776358758ac053347c078c7 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 13 Oct 2025 11:21:04 +0700 Subject: set back to pengajuan1 when CO from website and change SO line behavior onchange vendor_id --- indoteknik_api/controllers/api_v1/sale_order.py | 12 ++++++++---- indoteknik_custom/models/sale_order.py | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 56b49f9a..741dc0b1 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -231,10 +231,11 @@ class SaleOrder(controller.Controller): for so in filtered_orders_paginated: item = request.env['sale.order'].api_v1_single_response(so) + approval_ok = (so.approval_status in ('pengajuan1', 'pengajuan2')) source_ok = _is_website_order(so) term_ok = bool(so.payment_term_id and so.payment_term_id.id == CBD_PAYMENT_TERM_ID) pay_status = (getattr(so, 'payment_status', '') or '').strip().lower() - eligible = bool(source_ok and term_ok and pay_status in ALLOWED_CONTINUE) + eligible = bool(approval_ok and source_ok and term_ok and pay_status in ALLOWED_CONTINUE) redirect_url = getattr(so, 'payment_link_midtrans', '') or '' @@ -242,6 +243,7 @@ class SaleOrder(controller.Controller): 'eligibleContinue': eligible, 'paymentSummary': { 'eligible': eligible, + 'approvalStatus': so.approval_status or '', 'paymentStatus': pay_status, 'paymentTermId': so.payment_term_id.id if so.payment_term_id else None, 'sourceId': so.source_id.id if so.source_id else None, @@ -283,6 +285,7 @@ class SaleOrder(controller.Controller): pay_status = (getattr(sale_order, 'payment_status', '') or '').strip().lower() eligible = ( + sale_order.approval_status in ('pengajuan1', 'pengajuan2') and _is_website_order(sale_order) and sale_order.payment_term_id and sale_order.payment_term_id.id == CBD_PAYMENT_TERM_ID and pay_status in ALLOWED_CONTINUE @@ -314,6 +317,7 @@ class SaleOrder(controller.Controller): 'eligible_continue': eligible, 'payment_summary': { 'eligible': eligible, + 'approval_status': sale_order.approval_status or '', 'payment_status': pay_status, 'payment_term_id': sale_order.payment_term_id.id if sale_order.payment_term_id else None, 'source_id': sale_order.source_id.id if sale_order.source_id else None, @@ -723,9 +727,9 @@ class SaleOrder(controller.Controller): _logger.info(f"Updated user_id from partner: {parameters['user_id']}") if params['value']['type'] == 'sale_order': - # parameters['approval_status'] = 'pengajuan1' - parameters['approval_status'] = False - _logger.info("Setting approval_status to 'false'") + parameters['approval_status'] = 'pengajuan1' + # parameters['approval_status'] = False + _logger.info("Setting approval_status to 'pengajuan1'") sale_order = request.env['sale.order'].with_context(from_website_checkout=True).create([parameters]) sale_order.onchange_partner_contact() diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 4a7203a1..2d2d71b3 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -3230,6 +3230,7 @@ class SaleOrder(models.Model): # order._auto_set_shipping_from_website() order._compute_etrts_date() order._validate_expected_ready_ship_date() + order.onchange_vendor_id() # order._validate_delivery_amt() # order._check_total_margin_excl_third_party() # order._update_partner_details() -- cgit v1.2.3 From dab2cdebb698b817b979efd2087175060f9b8f03 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 13 Oct 2025 13:01:35 +0700 Subject: change SO line behavior onchange vendor_id --- indoteknik_custom/models/sale_order.py | 11 ++++- indoteknik_custom/models/sale_order_line.py | 62 ++++++++++++++++++----------- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 2d2d71b3..3bd1ca59 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -3230,7 +3230,10 @@ class SaleOrder(models.Model): # order._auto_set_shipping_from_website() order._compute_etrts_date() order._validate_expected_ready_ship_date() - order.onchange_vendor_id() + for line in order.order_line: + updated_vals = line._update_purchase_info() + if updated_vals: + line.write(updated_vals) # order._validate_delivery_amt() # order._check_total_margin_excl_third_party() # order._update_partner_details() @@ -3362,6 +3365,12 @@ class SaleOrder(models.Model): if any(field in vals for field in ["order_line", "client_order_ref"]): self._calculate_etrts_date() + for order in self: + for line in order.order_line: + updated_vals = line._update_purchase_info() + if updated_vals: + line.write(updated_vals) + return res def button_refund(self): diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index 1f2ea1fb..f8fb898c 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -247,29 +247,29 @@ class SaleOrderLine(models.Model): margin_per_item = sales_price - purchase_price line.item_before_margin = margin_per_item - @api.onchange('vendor_id') - def onchange_vendor_id(self): - # TODO : need to change this logic @stephan - if not self.product_id or self.product_id.type == 'service': - return - elif self.product_id.categ_id.id == 34: # finish good / manufacturing only - cost = self.product_id.standard_price - self.purchase_price = cost - elif self.product_id.x_manufacture.override_vendor_id: - # purchase_price = self.env['purchase.pricelist'].search( - # [('vendor_id', '=', self.product_id.x_manufacture.override_vendor_id.id), - # ('product_id', '=', self.product_id.id)], - # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') - price, taxes, vendor_id = self._get_purchase_price_by_vendor(self.product_id, self.vendor_id) - self.purchase_price = price - self.purchase_tax_id = taxes - # else: - # purchase_price = self.env['purchase.pricelist'].search( - # [('vendor_id', '=', self.vendor_id.id), ('product_id', '=', self.product_id.id)], - # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') - # price, taxes = self._get_valid_purchase_price(purchase_price) - # self.purchase_price = price - # self.purchase_tax_id = taxes + # @api.onchange('vendor_id') + # def onchange_vendor_id(self): + # # TODO : need to change this logic @stephan + # if not self.product_id or self.product_id.type == 'service': + # return + # elif self.product_id.categ_id.id == 34: # finish good / manufacturing only + # cost = self.product_id.standard_price + # self.purchase_price = cost + # elif self.product_id.x_manufacture.override_vendor_id: + # # purchase_price = self.env['purchase.pricelist'].search( + # # [('vendor_id', '=', self.product_id.x_manufacture.override_vendor_id.id), + # # ('product_id', '=', self.product_id.id)], + # # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') + # price, taxes, vendor_id = self._get_purchase_price_by_vendor(self.product_id, self.vendor_id) + # self.purchase_price = price + # self.purchase_tax_id = taxes + # # else: + # # purchase_price = self.env['purchase.pricelist'].search( + # # [('vendor_id', '=', self.vendor_id.id), ('product_id', '=', self.product_id.id)], + # # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') + # # price, taxes = self._get_valid_purchase_price(purchase_price) + # # self.purchase_price = price + # # self.purchase_tax_id = taxes # def _calculate_selling_price(self): # rec_purchase_price, rec_taxes, rec_vendor_id = self._get_purchase_price(self.product_id) @@ -512,3 +512,19 @@ class SaleOrderLine(models.Model): else: line.product_updatable = False # line.desc_updatable = False + + @api.onchange('vendor_id') + def _onchange_vendor_id_custom(self): + self._update_purchase_info() + + def _update_purchase_info(self): + if not self.product_id or self.product_id.type == 'service': + return + + if self.product_id.categ_id.id == 34: + self.purchase_price = self.product_id.standard_price + self.purchase_tax_id = [(5, 0, 0)] # reset + elif self.product_id.x_manufacture.override_vendor_id: + price, taxes, vendor_id = self._get_purchase_price_by_vendor(self.product_id, self.vendor_id) + self.purchase_price = price + self.purchase_tax_id = taxes -- cgit v1.2.3 From 724d7ed6d85ecc3acadbcf56a98aead0512af01d Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 14 Oct 2025 14:11:18 +0700 Subject: push --- indoteknik_custom/models/partial_delivery.py | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/indoteknik_custom/models/partial_delivery.py b/indoteknik_custom/models/partial_delivery.py index 83fe9981..977cceed 100644 --- a/indoteknik_custom/models/partial_delivery.py +++ b/indoteknik_custom/models/partial_delivery.py @@ -46,7 +46,6 @@ class PartialDeliveryWizard(models.TransientModel): raise UserError(_("Tidak ada produk yang dipilih.")) for line in self.line_ids: line.selected = True - # return action supaya wizard gak nutup return { 'type': 'ir.actions.act_window', 'res_model': self._name, @@ -60,7 +59,6 @@ class PartialDeliveryWizard(models.TransientModel): raise UserError(_("Tidak ada produk yang dipilih.")) for line in self.line_ids: line.selected = False - # juga reload biar tetap di wizard return { 'type': 'ir.actions.act_window', 'res_model': self._name, @@ -76,7 +74,6 @@ class PartialDeliveryWizard(models.TransientModel): self.line_ids = [(5, 0, 0)] return - # ๐Ÿงน hapus line lama dulu if self.line_ids: self.line_ids.unlink() @@ -95,7 +92,7 @@ class PartialDeliveryWizard(models.TransientModel): 'wizard_id': self.id, 'product_id': move.product_id.id, 'reserved_qty': reserved_qty, - 'selected_qty': reserved_qty, # biar langsung keisi default + # 'selected_qty': reserved_qty, 'move_id': move.id, 'sale_line_id': move.sale_line_id.id if move.sale_line_id else False, }) @@ -119,12 +116,11 @@ class PartialDeliveryWizard(models.TransientModel): lines_by_qty = self.line_ids.filtered(lambda l: l.selected_qty > 0) lines_by_selected = self.line_ids.filtered(lambda l: l.selected and not l.selected_qty) - selected_lines = lines_by_qty | lines_by_selected # gabung dua domain hasil filter + selected_lines = lines_by_qty | lines_by_selected if not selected_lines: raise UserError(_("Tidak ada produk yang dipilih atau diisi jumlahnya.")) - # ๐Ÿง  Tambahan: kalau semua line dipilih (full delivery) all_selected = len(selected_lines) == len(self.line_ids) full_selected = all_selected and all( (line.selected_qty or line.reserved_qty) >= line.reserved_qty @@ -132,10 +128,8 @@ class PartialDeliveryWizard(models.TransientModel): ) if full_selected: - # ๐Ÿ’ก Gak perlu bikin picking baru, langsung ubah state_reserve picking.write({'state_reserve': 'partial'}) - # Tambahin log aja biar ada jejak picking.message_post( body=f"Full Picking Confirmed dari wizard partial delivery oleh {self.env.user.name}", message_type="comment", @@ -171,11 +165,9 @@ class PartialDeliveryWizard(models.TransientModel): move = line.move_id move._do_unreserve() - # kalau cuma selected tanpa isi qty, otomatis set selected_qty = reserved_qty if line.selected and not line.selected_qty: line.selected_qty = line.reserved_qty - # MODE 1 โ†’ Prioritas kalau ada selected_qty if line.selected_qty > 0: if line.selected_qty > move.product_uom_qty: raise UserError(_( @@ -184,7 +176,6 @@ class PartialDeliveryWizard(models.TransientModel): if line.selected_qty < move.product_uom_qty: qty_to_keep = move.product_uom_qty - line.selected_qty - # split move new_move = move.copy(default={ 'product_uom_qty': line.selected_qty, 'picking_id': new_picking.id, @@ -192,19 +183,13 @@ class PartialDeliveryWizard(models.TransientModel): }) move.write({'product_uom_qty': qty_to_keep}) else: - # full pindah move.write({'picking_id': new_picking.id, 'partial': True}) - - - # Confirm & assign DO baru new_picking.action_confirm() new_picking.action_assign() - # Reassign DO lama biar sisa qty ke-update picking.action_assign() - # --- ๐Ÿ”ข Rename picking baru dengan format "/(Nomor urut)" --- existing_partials = self.env['stock.picking'].search([ ('origin', '=', picking.origin), ('state_reserve', '=', 'partial'), @@ -220,7 +205,6 @@ class PartialDeliveryWizard(models.TransientModel): new_name = f"{picking.name}/{suffix_number}" new_picking.name = new_name - # --- ๐Ÿ’ฌ Post message ke SO --- if picking.origin: sale_order = self.env['sale.order'].search([('name', '=', picking.origin)], limit=1) if sale_order: @@ -231,7 +215,6 @@ class PartialDeliveryWizard(models.TransientModel): subtype_xmlid="mail.mt_note", ) - # --- ๐Ÿ“ Log di DO baru --- new_picking.message_post( body=f"Partial Picking created dari {picking.name} oleh {self.env.user.name}", message_type="comment", -- cgit v1.2.3 From b41c2d160e5e114bf805baed573d791fbac3feac Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 14 Oct 2025 14:12:23 +0700 Subject: push --- indoteknik_custom/models/partial_delivery.py | 37 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/indoteknik_custom/models/partial_delivery.py b/indoteknik_custom/models/partial_delivery.py index 977cceed..4df7da1e 100644 --- a/indoteknik_custom/models/partial_delivery.py +++ b/indoteknik_custom/models/partial_delivery.py @@ -116,22 +116,31 @@ class PartialDeliveryWizard(models.TransientModel): lines_by_qty = self.line_ids.filtered(lambda l: l.selected_qty > 0) lines_by_selected = self.line_ids.filtered(lambda l: l.selected and not l.selected_qty) - selected_lines = lines_by_qty | lines_by_selected + selected_lines = lines_by_qty | lines_by_selected # gabung dua domain hasil filter if not selected_lines: raise UserError(_("Tidak ada produk yang dipilih atau diisi jumlahnya.")) - all_selected = len(selected_lines) == len(self.line_ids) - full_selected = all_selected and all( - (line.selected_qty or line.reserved_qty) >= line.reserved_qty - for line in selected_lines + # ๐Ÿง  Cek apakah semua move di DO sudah muncul di wizard dan semua dipilih + picking_move_ids = picking.move_ids_without_package.ids + wizard_move_ids = self.line_ids.mapped('move_id').ids + + # Semua move DO muncul di wizard, dan semua baris dipilih + full_selected = ( + set(picking_move_ids) == set(wizard_move_ids) + and len(selected_lines) == len(self.line_ids) + and all( + (line.selected_qty or line.reserved_qty) >= line.reserved_qty + for line in selected_lines + ) ) if full_selected: + # ๐Ÿ’ก Gak perlu bikin picking baru, langsung ubah state_reserve picking.write({'state_reserve': 'partial'}) picking.message_post( - body=f"Full Picking Confirmed dari wizard partial delivery oleh {self.env.user.name}", + body=f"Full Picking Confirmed via wizard partial delivery oleh {self.env.user.name} (tanpa DO baru)", message_type="comment", subtype_xmlid="mail.mt_note", ) @@ -144,11 +153,12 @@ class PartialDeliveryWizard(models.TransientModel): "target": "current", "effect": { "fadeout": "slow", - "message": f"โœ… Semua produk dikirim penuh โ€” tidak dibuat DO baru.", + "message": f"โœ… Semua produk dari DO ini dikirim penuh โ€” tidak dibuat DO baru.", "type": "rainbow_man", }, } + # ๐Ÿงฉ Kalau bukan full selected, lanjut bikin DO baru new_picking = StockPicking.create({ 'origin': picking.origin, 'partner_id': picking.partner_id.id, @@ -187,7 +197,6 @@ class PartialDeliveryWizard(models.TransientModel): new_picking.action_confirm() new_picking.action_assign() - picking.action_assign() existing_partials = self.env['stock.picking'].search([ @@ -196,14 +205,8 @@ class PartialDeliveryWizard(models.TransientModel): ('id', '!=', new_picking.id), ], order='name asc') - suffix_number = len(existing_partials) - if suffix_number == 0: - suffix_number = 1 - else: - suffix_number += 1 - - new_name = f"{picking.name}/{suffix_number}" - new_picking.name = new_name + suffix_number = len(existing_partials) + 1 + new_picking.name = f"{picking.name}/{suffix_number}" if picking.origin: sale_order = self.env['sale.order'].search([('name', '=', picking.origin)], limit=1) @@ -234,8 +237,6 @@ class PartialDeliveryWizard(models.TransientModel): }, } - - class PartialDeliveryWizardLine(models.TransientModel): _name = 'partial.delivery.wizard.line' _description = 'Partial Delivery Wizard Line' -- cgit v1.2.3 From 5a181ec0100dcb7ad5742f757195599deffa2b29 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 14 Oct 2025 21:20:57 +0700 Subject: make move line to get uom from so line --- indoteknik_custom/models/stock_move.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index b7db8775..1da2befe 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -21,6 +21,14 @@ class StockMove(models.Model): product_image = fields.Binary(related="product_id.image_128", string="Product Image", readonly=True) partial = fields.Boolean('Partial?', default=False) + # Ambil product uom dari SO line + @api.model + def create(self, vals): + if vals.get('sale_line_id'): + sale_line = self.env['sale.order.line'].browse(vals['sale_line_id']) + vals['product_uom'] = sale_line.product_uom.id + return super().create(vals) + # @api.model_create_multi # def create(self, vals_list): # moves = super(StockMove, self).create(vals_list) @@ -178,3 +186,12 @@ class StockMoveLine(models.Model): line_no = fields.Integer('No', default=0) note = fields.Char('Note') manufacture = fields.Many2one('x_manufactures', string="Brands", related="product_id.x_manufacture", store=True) + + # Ambil uom dari stock move + @api.model + def create(self, vals): + if 'move_id' in vals and 'product_uom_id' not in vals: + move = self.env['stock.move'].browse(vals['move_id']) + if move.product_uom: + vals['product_uom_id'] = move.product_uom.id + return super().create(vals) \ No newline at end of file -- cgit v1.2.3 From 4502ebb5452b150b5e36175bb7900bace760e9f4 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 15 Oct 2025 09:38:46 +0700 Subject: push fix bug --- indoteknik_custom/models/sale_order_line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index f8fb898c..12e7e6f9 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -523,7 +523,7 @@ class SaleOrderLine(models.Model): if self.product_id.categ_id.id == 34: self.purchase_price = self.product_id.standard_price - self.purchase_tax_id = [(5, 0, 0)] # reset + self.purchase_tax_id = False # reset elif self.product_id.x_manufacture.override_vendor_id: price, taxes, vendor_id = self._get_purchase_price_by_vendor(self.product_id, self.vendor_id) self.purchase_price = price -- cgit v1.2.3 From b6bf4c8b58b9311c6a6cf7f745cb42b025159759 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 15 Oct 2025 12:08:34 +0700 Subject: SO line onchange vendor id --- indoteknik_custom/models/sale_order.py | 18 +++++------ indoteknik_custom/models/sale_order_line.py | 46 ++++++++++++++--------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 3bd1ca59..a5e2f7c4 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -3230,10 +3230,10 @@ class SaleOrder(models.Model): # order._auto_set_shipping_from_website() order._compute_etrts_date() order._validate_expected_ready_ship_date() - for line in order.order_line: - updated_vals = line._update_purchase_info() - if updated_vals: - line.write(updated_vals) + # for line in order.order_line: + # updated_vals = line._update_purchase_info() + # if updated_vals: + # line.write(updated_vals) # order._validate_delivery_amt() # order._check_total_margin_excl_third_party() # order._update_partner_details() @@ -3365,11 +3365,11 @@ class SaleOrder(models.Model): if any(field in vals for field in ["order_line", "client_order_ref"]): self._calculate_etrts_date() - for order in self: - for line in order.order_line: - updated_vals = line._update_purchase_info() - if updated_vals: - line.write(updated_vals) + # for order in self: + # for line in order.order_line: + # updated_vals = line._update_purchase_info() + # if updated_vals: + # line.write(updated_vals) return res diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index 12e7e6f9..bd2600ae 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -247,29 +247,29 @@ class SaleOrderLine(models.Model): margin_per_item = sales_price - purchase_price line.item_before_margin = margin_per_item - # @api.onchange('vendor_id') - # def onchange_vendor_id(self): - # # TODO : need to change this logic @stephan - # if not self.product_id or self.product_id.type == 'service': - # return - # elif self.product_id.categ_id.id == 34: # finish good / manufacturing only - # cost = self.product_id.standard_price - # self.purchase_price = cost - # elif self.product_id.x_manufacture.override_vendor_id: - # # purchase_price = self.env['purchase.pricelist'].search( - # # [('vendor_id', '=', self.product_id.x_manufacture.override_vendor_id.id), - # # ('product_id', '=', self.product_id.id)], - # # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') - # price, taxes, vendor_id = self._get_purchase_price_by_vendor(self.product_id, self.vendor_id) - # self.purchase_price = price - # self.purchase_tax_id = taxes - # # else: - # # purchase_price = self.env['purchase.pricelist'].search( - # # [('vendor_id', '=', self.vendor_id.id), ('product_id', '=', self.product_id.id)], - # # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') - # # price, taxes = self._get_valid_purchase_price(purchase_price) - # # self.purchase_price = price - # # self.purchase_tax_id = taxes + @api.onchange('vendor_id') + def onchange_vendor_id(self): + # TODO : need to change this logic @stephan + if not self.product_id or self.product_id.type == 'service': + return + elif self.product_id.categ_id.id == 34: # finish good / manufacturing only + cost = self.product_id.standard_price + self.purchase_price = cost + elif self.product_id.x_manufacture.override_vendor_id: + # purchase_price = self.env['purchase.pricelist'].search( + # [('vendor_id', '=', self.product_id.x_manufacture.override_vendor_id.id), + # ('product_id', '=', self.product_id.id)], + # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') + price, taxes, vendor_id = self._get_purchase_price_by_vendor(self.product_id, self.vendor_id) + self.purchase_price = price + self.purchase_tax_id = taxes + # else: + # purchase_price = self.env['purchase.pricelist'].search( + # [('vendor_id', '=', self.vendor_id.id), ('product_id', '=', self.product_id.id)], + # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') + # price, taxes = self._get_valid_purchase_price(purchase_price) + # self.purchase_price = price + # self.purchase_tax_id = taxes # def _calculate_selling_price(self): # rec_purchase_price, rec_taxes, rec_vendor_id = self._get_purchase_price(self.product_id) -- cgit v1.2.3 From ee6d5d4433a397ca5cdf3474f29bcbd0b2553736 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 15 Oct 2025 12:18:34 +0700 Subject: SO line onchange vendor id --- indoteknik_custom/models/sale_order_line.py | 48 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index bd2600ae..1df1a058 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -247,29 +247,29 @@ class SaleOrderLine(models.Model): margin_per_item = sales_price - purchase_price line.item_before_margin = margin_per_item - @api.onchange('vendor_id') - def onchange_vendor_id(self): - # TODO : need to change this logic @stephan - if not self.product_id or self.product_id.type == 'service': - return - elif self.product_id.categ_id.id == 34: # finish good / manufacturing only - cost = self.product_id.standard_price - self.purchase_price = cost - elif self.product_id.x_manufacture.override_vendor_id: - # purchase_price = self.env['purchase.pricelist'].search( - # [('vendor_id', '=', self.product_id.x_manufacture.override_vendor_id.id), - # ('product_id', '=', self.product_id.id)], - # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') - price, taxes, vendor_id = self._get_purchase_price_by_vendor(self.product_id, self.vendor_id) - self.purchase_price = price - self.purchase_tax_id = taxes - # else: - # purchase_price = self.env['purchase.pricelist'].search( - # [('vendor_id', '=', self.vendor_id.id), ('product_id', '=', self.product_id.id)], - # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') - # price, taxes = self._get_valid_purchase_price(purchase_price) - # self.purchase_price = price - # self.purchase_tax_id = taxes + # @api.onchange('vendor_id') + # def onchange_vendor_id(self): + # # TODO : need to change this logic @stephan + # if not self.product_id or self.product_id.type == 'service': + # return + # elif self.product_id.categ_id.id == 34: # finish good / manufacturing only + # cost = self.product_id.standard_price + # self.purchase_price = cost + # elif self.product_id.x_manufacture.override_vendor_id: + # # purchase_price = self.env['purchase.pricelist'].search( + # # [('vendor_id', '=', self.product_id.x_manufacture.override_vendor_id.id), + # # ('product_id', '=', self.product_id.id)], + # # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') + # price, taxes, vendor_id = self._get_purchase_price_by_vendor(self.product_id, self.vendor_id) + # self.purchase_price = price + # self.purchase_tax_id = taxes + # # else: + # # purchase_price = self.env['purchase.pricelist'].search( + # # [('vendor_id', '=', self.vendor_id.id), ('product_id', '=', self.product_id.id)], + # # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') + # # price, taxes = self._get_valid_purchase_price(purchase_price) + # # self.purchase_price = price + # # self.purchase_tax_id = taxes # def _calculate_selling_price(self): # rec_purchase_price, rec_taxes, rec_vendor_id = self._get_purchase_price(self.product_id) @@ -523,7 +523,7 @@ class SaleOrderLine(models.Model): if self.product_id.categ_id.id == 34: self.purchase_price = self.product_id.standard_price - self.purchase_tax_id = False # reset + self.purchase_tax_id = False elif self.product_id.x_manufacture.override_vendor_id: price, taxes, vendor_id = self._get_purchase_price_by_vendor(self.product_id, self.vendor_id) self.purchase_price = price -- cgit v1.2.3 From b3eab8f1694e5fa1b8229784d247f437f68c2b4c Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 16 Oct 2025 08:46:23 +0700 Subject: balikin --- indoteknik_custom/models/stock_move.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 1da2befe..163c7505 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -22,12 +22,12 @@ class StockMove(models.Model): partial = fields.Boolean('Partial?', default=False) # Ambil product uom dari SO line - @api.model - def create(self, vals): - if vals.get('sale_line_id'): - sale_line = self.env['sale.order.line'].browse(vals['sale_line_id']) - vals['product_uom'] = sale_line.product_uom.id - return super().create(vals) + # @api.model + # def create(self, vals): + # if vals.get('sale_line_id'): + # sale_line = self.env['sale.order.line'].browse(vals['sale_line_id']) + # vals['product_uom'] = sale_line.product_uom.id + # return super().create(vals) # @api.model_create_multi # def create(self, vals_list): @@ -188,10 +188,10 @@ class StockMoveLine(models.Model): manufacture = fields.Many2one('x_manufactures', string="Brands", related="product_id.x_manufacture", store=True) # Ambil uom dari stock move - @api.model - def create(self, vals): - if 'move_id' in vals and 'product_uom_id' not in vals: - move = self.env['stock.move'].browse(vals['move_id']) - if move.product_uom: - vals['product_uom_id'] = move.product_uom.id - return super().create(vals) \ No newline at end of file + # @api.model + # def create(self, vals): + # if 'move_id' in vals and 'product_uom_id' not in vals: + # move = self.env['stock.move'].browse(vals['move_id']) + # if move.product_uom: + # vals['product_uom_id'] = move.product_uom.id + # return super().create(vals) \ No newline at end of file -- cgit v1.2.3 From efaeb9e53727f6ccfa0c42bbf8a7c7cc0f25da4b Mon Sep 17 00:00:00 2001 From: Miqdad Date: Thu, 16 Oct 2025 08:50:57 +0700 Subject: fix api --- indoteknik_api/controllers/api_v1/sale_order.py | 2 +- indoteknik_custom/models/stock_move.py | 26 ++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 741dc0b1..cff1921d 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -789,7 +789,7 @@ class SaleOrder(controller.Controller): order_line.product_id_change() order_line.weight = order_line.product_id.weight - order_line.onchange_vendor_id() + order_line._onchange_vendor_id_custom() _logger.info(f"After onchanges - Price: {order_line.price_unit}, Disc: {order_line.discount}") elif cart['cart_type'] == 'promotion': diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 163c7505..1da2befe 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -22,12 +22,12 @@ class StockMove(models.Model): partial = fields.Boolean('Partial?', default=False) # Ambil product uom dari SO line - # @api.model - # def create(self, vals): - # if vals.get('sale_line_id'): - # sale_line = self.env['sale.order.line'].browse(vals['sale_line_id']) - # vals['product_uom'] = sale_line.product_uom.id - # return super().create(vals) + @api.model + def create(self, vals): + if vals.get('sale_line_id'): + sale_line = self.env['sale.order.line'].browse(vals['sale_line_id']) + vals['product_uom'] = sale_line.product_uom.id + return super().create(vals) # @api.model_create_multi # def create(self, vals_list): @@ -188,10 +188,10 @@ class StockMoveLine(models.Model): manufacture = fields.Many2one('x_manufactures', string="Brands", related="product_id.x_manufacture", store=True) # Ambil uom dari stock move - # @api.model - # def create(self, vals): - # if 'move_id' in vals and 'product_uom_id' not in vals: - # move = self.env['stock.move'].browse(vals['move_id']) - # if move.product_uom: - # vals['product_uom_id'] = move.product_uom.id - # return super().create(vals) \ No newline at end of file + @api.model + def create(self, vals): + if 'move_id' in vals and 'product_uom_id' not in vals: + move = self.env['stock.move'].browse(vals['move_id']) + if move.product_uom: + vals['product_uom_id'] = move.product_uom.id + return super().create(vals) \ No newline at end of file -- cgit v1.2.3 From 7abcc2bd055eecea8caacae8187b7dacff671c46 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 16 Oct 2025 10:46:15 +0700 Subject: push --- indoteknik_custom/models/purchase_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index b34ec926..672db7cf 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1354,7 +1354,7 @@ class PurchaseOrder(models.Model): for rec in self: if rec.from_apo: rec.compute_total_margin_from_apo() - return + continue sum_so_margin = sum_sales_price = sum_margin = 0 for line in self.order_line: -- cgit v1.2.3 From 8ff0270ee40b34d8eb85cd8d5e9572211a0e36a9 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 16 Oct 2025 11:52:52 +0700 Subject: (andri) off check po manual confirm --- indoteknik_custom/models/purchase_order.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 1a88d036..f8718a0d 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1072,15 +1072,15 @@ class PurchaseOrder(models.Model): if self.env.user.id != 7: # Pimpinan if '/PJ/' in self.name: low_margin_lines = self.order_sales_match_line.filtered( - lambda match: match.so_header_margin <= 34.0 + lambda match: match.so_header_margin <= 15.0 ) if low_margin_lines: raise UserError("Matches SO terdapat item dengan header margin SO <= 15%. Approval Pimpinan diperlukan.") - else: - is_po_manual = '/A/' not in self.name and '/MO/' not in self.name - if is_po_manual: - if not self.order_sales_match_line: - raise UserError("Tidak ada matches SO, Approval Pimpinan diperlukan.") + # else: + # is_po_manual = '/A/' not in self.name and '/MO/' not in self.name + # if is_po_manual: + # if not self.order_sales_match_line: + # raise UserError("Tidak ada matches SO, Approval Pimpinan diperlukan.") self._check_assets_note() # self._check_payment_term() # check payment term -- cgit v1.2.3 From e210b6fa5ec6603920f550e5f8cc64e32e832e44 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 16 Oct 2025 12:01:20 +0700 Subject: (andri) fix --- indoteknik_custom/models/purchase_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index f8718a0d..7badff95 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1069,7 +1069,7 @@ class PurchaseOrder(models.Model): ) % order.name) def button_confirm(self): - if self.env.user.id != 7: # Pimpinan + if self.env.user.id != 7 and not self.env.user.is_leader: # Pimpinan if '/PJ/' in self.name: low_margin_lines = self.order_sales_match_line.filtered( lambda match: match.so_header_margin <= 15.0 -- cgit v1.2.3 From bd0cc0fa2518e735cbe766bd0ea3726c63d3ebbd Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Fri, 17 Oct 2025 11:08:26 +0700 Subject: (andri) off validasi approval matches SO --- indoteknik_custom/models/purchase_order.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index b596f365..a1c3c0c4 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1069,13 +1069,13 @@ class PurchaseOrder(models.Model): ) % order.name) def button_confirm(self): - if self.env.user.id != 7 and not self.env.user.is_leader: # Pimpinan - if '/PJ/' in self.name: - low_margin_lines = self.order_sales_match_line.filtered( - lambda match: match.so_header_margin <= 15.0 - ) - if low_margin_lines: - raise UserError("Matches SO terdapat item dengan header margin SO <= 15%. Approval Pimpinan diperlukan.") + # if self.env.user.id != 7 and not self.env.user.is_leader: # Pimpinan + # if '/PJ/' in self.name: + # low_margin_lines = self.order_sales_match_line.filtered( + # lambda match: match.so_header_margin <= 15.0 + # ) + # if low_margin_lines: + # raise UserError("Matches SO terdapat item dengan header margin SO <= 15%. Approval Pimpinan diperlukan.") # else: # is_po_manual = '/A/' not in self.name and '/MO/' not in self.name # if is_po_manual: -- cgit v1.2.3 From 7ba88682ecb9d1445f5565723adaf93106e056e2 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Fri, 17 Oct 2025 11:10:33 +0700 Subject: (andri) on --- indoteknik_custom/models/purchase_order.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index a1c3c0c4..b596f365 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1069,13 +1069,13 @@ class PurchaseOrder(models.Model): ) % order.name) def button_confirm(self): - # if self.env.user.id != 7 and not self.env.user.is_leader: # Pimpinan - # if '/PJ/' in self.name: - # low_margin_lines = self.order_sales_match_line.filtered( - # lambda match: match.so_header_margin <= 15.0 - # ) - # if low_margin_lines: - # raise UserError("Matches SO terdapat item dengan header margin SO <= 15%. Approval Pimpinan diperlukan.") + if self.env.user.id != 7 and not self.env.user.is_leader: # Pimpinan + if '/PJ/' in self.name: + low_margin_lines = self.order_sales_match_line.filtered( + lambda match: match.so_header_margin <= 15.0 + ) + if low_margin_lines: + raise UserError("Matches SO terdapat item dengan header margin SO <= 15%. Approval Pimpinan diperlukan.") # else: # is_po_manual = '/A/' not in self.name and '/MO/' not in self.name # if is_po_manual: -- cgit v1.2.3 From 66983246c83db072c62abba4a40a70e514b081d0 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Fri, 17 Oct 2025 13:40:39 +0700 Subject: (andri) fix --- indoteknik_custom/models/purchase_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index b596f365..701c0a3f 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1090,7 +1090,7 @@ class PurchaseOrder(models.Model): self.check_different_vendor_so_po() # self.check_data_vendor() - if self.amount_untaxed >= 50000000 and not self.env.user.id == 21: + if self.amount_untaxed >= 50000000 and not self.env.user.id in (21, 7): raise UserError("Hanya Rafly Hanggara yang bisa approve") if not self.date_planned: -- cgit v1.2.3 From a850dd693d4342181cfe4ca15fcd5ff4a528dcc4 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 17 Oct 2025 14:26:06 +0700 Subject: push --- indoteknik_custom/models/commision.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 6d538b83..a937e2d0 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -428,22 +428,22 @@ class CustomerCommision(models.Model): if not self.status or self.status == 'draft': self.status = 'pengajuan1' - elif self.status == 'pengajuan1' and self.env.user.id == 19: + elif self.status == 'pengajuan1' and (self.env.user.id == 19 or self.env.user.has_group('indoteknik_custom.group_role_it')): self.status = 'pengajuan2' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name self.date_approved_sales = now_naive self.position_sales = 'Sales Manager' - elif self.status == 'pengajuan2' and self.env.user.id == 216: + elif self.status == 'pengajuan2' and (self.env.user.id == 216 or self.env.user.has_group('indoteknik_custom.group_role_it')): self.status = 'pengajuan3' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name self.date_approved_marketing = now_naive self.position_marketing = 'Marketing Manager' - elif self.status == 'pengajuan3' and self.env.user.is_leader: + elif self.status == 'pengajuan3' and (self.env.user.is_leader or self.env.user.has_group('indoteknik_custom.group_role_it')): self.status = 'pengajuan4' self.approved_by = (self.approved_by + ', ' if self.approved_by else '') + self.env.user.name self.date_approved_pimpinan = now_naive self.position_pimpinan = 'Pimpinan' - elif self.status == 'pengajuan4' and self.env.user.id == 1272: + elif self.status == 'pengajuan4' and (self.env.user.id == 1272 or self.env.user.has_group('indoteknik_custom.group_role_it')): for line in self.commision_lines: line.invoice_id.is_customer_commision = True if self.commision_type == 'fee': -- cgit v1.2.3 From 2cd0d5ede34e2c46b3bcd1f1907cd0526ef42b9d Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Fri, 17 Oct 2025 15:45:48 +0700 Subject: (andri) butuh approval pimpinan bila ada perubahan unit price pada PO PJ + matches SO margin header <=15% --- indoteknik_custom/models/purchase_order.py | 6 ++++-- indoteknik_custom/models/purchase_order_line.py | 6 ++++++ indoteknik_custom/views/purchase_order.xml | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 701c0a3f..51517437 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1074,8 +1074,10 @@ class PurchaseOrder(models.Model): low_margin_lines = self.order_sales_match_line.filtered( lambda match: match.so_header_margin <= 15.0 ) - if low_margin_lines: - raise UserError("Matches SO terdapat item dengan header margin SO <= 15%. Approval Pimpinan diperlukan.") + price_change_detected = any(line.price_unit_before for line in self.order_line) + if low_margin_lines and price_change_detected: + # raise UserError("Matches SO terdapat item dengan header margin SO <= 15%. Approval Pimpinan diperlukan.") + raise UserError("Approval Pimpinan diperlukan jika terdapat perubahan Unit Price pada PO Line yang Matches SO item memiliki header margin SO <= 15%") # else: # is_po_manual = '/A/' not in self.name and '/MO/' not in self.name # if is_po_manual: diff --git a/indoteknik_custom/models/purchase_order_line.py b/indoteknik_custom/models/purchase_order_line.py index a3c3a33b..8c72887d 100755 --- a/indoteknik_custom/models/purchase_order_line.py +++ b/indoteknik_custom/models/purchase_order_line.py @@ -51,6 +51,12 @@ class PurchaseOrderLine(models.Model): contribution_cost_service = fields.Float(string='Contribution Cost Service', compute='_compute_doc_delivery_amt') ending_price = fields.Float(string='Ending Price', compute='_compute_doc_delivery_amt') show_description = fields.Boolean(string='Show Description', help="Show Description when print po", default=True) + price_unit_before = fields.Float(string='Unit Price Before', help="Harga awal yang sebelumnya telah diinputkan") + + @api.onchange('price_unit') + def _onchange_price_unit_before(self): + if self._origin: + self.price_unit_before = self._origin.price_unit def _compute_doc_delivery_amt(self): for line in self: diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index 437b86b8..09d901b9 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -144,6 +144,7 @@ + -- cgit v1.2.3 From 2a61388ec5938888710d7c15f660f19ed55def51 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Sat, 18 Oct 2025 11:27:24 +0700 Subject: push --- indoteknik_custom/models/purchase_order.py | 51 +++++++++++------------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 51517437..7fc038a0 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1420,63 +1420,50 @@ class PurchaseOrder(models.Model): ('product_id', '=', line.product_id.id), ('order_id', '=', line.purchase_order_id.id) ], limit=1) - sale_order_line = line.sale_line_id - if not sale_order_line: - sale_order_line = self.env['sale.order.line'].search([ - ('product_id', '=', line.product_id.id), - ('order_id', '=', line.sale_id.id) - ], limit=1, order='price_reduce_taxexcl') + sale_order_line = line.sale_line_id or self.env['sale.order.line'].search([ + ('product_id', '=', line.product_id.id), + ('order_id', '=', line.sale_id.id) + ], limit=1, order='price_reduce_taxexcl') if sale_order_line and po_line: - so_margin = (line.qty_po / line.qty_so) * sale_order_line.item_margin + qty_so = line.qty_so or 0 + qty_po = line.qty_po or 0 + + # Hindari division by zero + so_margin = (qty_po / qty_so) * sale_order_line.item_margin if qty_so > 0 else 0 sum_so_margin += so_margin - sales_price = sale_order_line.price_reduce_taxexcl * line.qty_po + sales_price = sale_order_line.price_reduce_taxexcl * qty_po if sale_order_line.order_id.shipping_cost_covered == 'indoteknik': - sales_price -= (sale_order_line.delivery_amt_line / sale_order_line.product_uom_qty) * line.qty_po + sales_price -= (sale_order_line.delivery_amt_line / sale_order_line.product_uom_qty) * qty_po if sale_order_line.order_id.fee_third_party > 0: - sales_price -= (sale_order_line.fee_third_party_line / sale_order_line.product_uom_qty) * line.qty_po + sales_price -= (sale_order_line.fee_third_party_line / sale_order_line.product_uom_qty) * qty_po sum_sales_price += sales_price - purchase_price = po_line.price_subtotal if po_line.ending_price > 0: if po_line.taxes_id.id == 22: - ending_price = po_line.ending_price / 1.11 - purchase_price = ending_price + purchase_price = po_line.ending_price / 1.11 else: purchase_price = po_line.ending_price if line.purchase_order_id.delivery_amount > 0: - purchase_price += (po_line.delivery_amt_line / po_line.product_qty) * line.qty_po + purchase_price += (po_line.delivery_amt_line / po_line.product_qty) * qty_po if line.purchase_order_id.delivery_amt > 0: purchase_price += line.purchase_order_id.delivery_amt + real_item_margin = sales_price - purchase_price sum_margin += real_item_margin - if sum_so_margin != 0 and sum_sales_price != 0 and sum_margin != 0: + # Akumulasi hasil akhir + if sum_sales_price != 0: self.total_so_margin = sum_so_margin self.total_so_percent_margin = round((sum_so_margin / sum_sales_price), 2) * 100 self.total_margin = sum_margin self.total_percent_margin = round((sum_margin / sum_sales_price), 2) * 100 - else: - self.total_margin = 0 - self.total_percent_margin = 0 - self.total_so_margin = 0 - self.total_so_percent_margin = 0 - + self.total_margin = self.total_percent_margin = 0 + self.total_so_margin = self.total_so_percent_margin = 0 - if sum_so_margin != 0 and sum_sales_price != 0 and sum_margin != 0: - self.total_so_margin = sum_so_margin - self.total_so_percent_margin = round((sum_so_margin / sum_sales_price), 2) * 100 - self.total_margin = sum_margin - self.total_percent_margin = round((sum_margin / sum_sales_price), 2) * 100 - - else: - self.total_margin = 0 - self.total_percent_margin = 0 - self.total_so_margin = 0 - self.total_so_percent_margin = 0 def compute_amt_total_without_service(self): for order in self: -- cgit v1.2.3 From 29b3ec084ed1791330551c5de34f045565eef6f1 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 20 Oct 2025 13:18:14 +0700 Subject: push --- indoteknik_custom/models/purchase_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 7fc038a0..e79417aa 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -1369,7 +1369,7 @@ class PurchaseOrder(models.Model): for rec in self: if rec.from_apo: rec.compute_total_margin_from_apo() - continue + return sum_so_margin = sum_sales_price = sum_margin = 0 for line in self.order_line: -- cgit v1.2.3 From da01b745094082f7efd837bef2a796d8dcede50b Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 20 Oct 2025 15:37:57 +0700 Subject: hilangin validasi sudah pernah diretur ccm --- indoteknik_custom/models/tukar_guling.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index cb630a04..cfa0ac77 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -492,14 +492,14 @@ class TukarGuling(models.Model): self.ensure_one() self._check_not_allow_tukar_guling_on_bu_pick() - existing_tukar_guling = self.env['tukar.guling'].search([ - ('operations', '=', self.operations.id), - ('id', '!=', self.id), - ('state', '!=', 'cancel'), - ], limit=1) - - if existing_tukar_guling: - raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) + # existing_tukar_guling = self.env['tukar.guling'].search([ + # ('operations', '=', self.operations.id), + # ('id', '!=', self.id), + # ('state', '!=', 'cancel'), + # ], limit=1) + # + # if existing_tukar_guling: + # raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) picking = self.operations if picking.picking_type_id.id == 30 and self.return_type == 'tukar_guling': raise UserError("โŒ BU/PICK tidak boleh di retur tukar guling") -- cgit v1.2.3 From fdf9425e60b8c91ef797f7c7b88baf98f2bd4619 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Mon, 20 Oct 2025 19:44:18 +0700 Subject: balikin validasi CCM --- indoteknik_custom/models/tukar_guling.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index cfa0ac77..cb630a04 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -492,14 +492,14 @@ class TukarGuling(models.Model): self.ensure_one() self._check_not_allow_tukar_guling_on_bu_pick() - # existing_tukar_guling = self.env['tukar.guling'].search([ - # ('operations', '=', self.operations.id), - # ('id', '!=', self.id), - # ('state', '!=', 'cancel'), - # ], limit=1) - # - # if existing_tukar_guling: - # raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) + existing_tukar_guling = self.env['tukar.guling'].search([ + ('operations', '=', self.operations.id), + ('id', '!=', self.id), + ('state', '!=', 'cancel'), + ], limit=1) + + if existing_tukar_guling: + raise UserError("BU ini sudah pernah diretur oleh dokumen %s." % existing_tukar_guling.name) picking = self.operations if picking.picking_type_id.id == 30 and self.return_type == 'tukar_guling': raise UserError("โŒ BU/PICK tidak boleh di retur tukar guling") -- cgit v1.2.3 From 00f94ec61fad94ed49f646396292228187b50173 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 21 Oct 2025 11:46:47 +0700 Subject: add asset type in assets --- indoteknik_custom/models/account_asset.py | 4 ++++ indoteknik_custom/views/account_asset_views.xml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/indoteknik_custom/models/account_asset.py b/indoteknik_custom/models/account_asset.py index bd5f9adb..211ab229 100644 --- a/indoteknik_custom/models/account_asset.py +++ b/indoteknik_custom/models/account_asset.py @@ -4,6 +4,10 @@ from odoo.exceptions import AccessError, UserError, ValidationError class AccountAsset(models.Model): _inherit = 'account.asset.asset' + asset_type = fields.Selection(string='Tipe Aset', selection=[ + ('aset_gudang', ' Aset Gudang'), + ('aset_kantor', 'Aset Kantor'), + ], tracking=True ) def action_close_asset(self): for asset in self: diff --git a/indoteknik_custom/views/account_asset_views.xml b/indoteknik_custom/views/account_asset_views.xml index 90c53623..776ab51f 100644 --- a/indoteknik_custom/views/account_asset_views.xml +++ b/indoteknik_custom/views/account_asset_views.xml @@ -12,6 +12,9 @@ type="object" /> + + + -- cgit v1.2.3 From e19f7637f648fb37fc9049ce7898dfc22568bf63 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 09:42:13 +0700 Subject: fix driver arrival date api for indoteknik delivery --- indoteknik_api/controllers/api_v1/stock_picking.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 2ec1ec2a..9affb492 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -1,5 +1,7 @@ +from adodbapi.apibase import DateTime + from .. import controller -from odoo import http +from odoo import http, fields from odoo.http import request, Response from pytz import timezone from datetime import datetime @@ -140,19 +142,19 @@ class StockPicking(controller.Controller): if not picking_data: return self.response(code=403, description='picking not found') - params = { - 'driver_arrival_date': datetime.utcnow(), - } + params = {} if sj_document: params['sj_documentation'] = sj_document if paket_document: params['paket_documentation'] = paket_document + params['driver_arrival_date'] = datetime.utcnow() if dispatch_document: params['dispatch_documentation'] = dispatch_document picking_data.write(params) return self.response({'name': picking_data.name}) + @http.route(prefix + 'webhook/biteship', type='json', auth='public', methods=['POST'], csrf=False) def update_status_from_biteship(self, **kw): _logger.info("Biteship Webhook: Request received at controller start (type='json').") -- cgit v1.2.3 From a1862d78377710020c40361401af79766ea9ca70 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 11:48:36 +0700 Subject: fix sj tele message sent multiple chat bubble with same message --- indoteknik_custom/models/sj_tele.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sj_tele.py b/indoteknik_custom/models/sj_tele.py index 3ef4b877..d3d7dfce 100644 --- a/indoteknik_custom/models/sj_tele.py +++ b/indoteknik_custom/models/sj_tele.py @@ -18,6 +18,7 @@ class SjTele(models.Model): sale_name = fields.Char(string='Sale Name') create_date = fields.Datetime(string='Create Date') date_doc_kirim = fields.Datetime(string='Tanggal Kirim SJ') + is_sent = fields.Boolean(default=False) def woi(self): bot_mqdd = '8203414501:AAHy_XwiUAVrgRM2EJzW7sZx9npRLITZpb8' @@ -27,7 +28,9 @@ class SjTele(models.Model): # chat_id_testing = '-4920864331' # api_testing = f'https://api.telegram.org/bot{bot_testing}' - data = self.search([], order='create_date asc') + data = self.search([('is_sent', '=', False)], order='create_date asc') + # Old + # data = self.search([], order='create_date asc') if not data: text = "โœ… tidak ada data (semua sudah tercatat)." @@ -83,6 +86,9 @@ class SjTele(models.Model): _logger.exception("Gagal kirim Telegram (batch %s-%s): %s", i + 1, min(i + BUB, len(lines)), e) time.sleep(5) # jeda kecil biar rapi & aman rate limit + # Set sent = true ketika sudah terkirim + data.write({'is_sent': True}) + return True # header = "Berikut merupakan nomor BU/OUT yang belum ada di Logbook SJ report:\n" -- cgit v1.2.3 From 9f916d880c5d061ee2ba66a80d26fbca76355054 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 11:49:37 +0700 Subject: fix sj tele message sent multiple chat bubble with same message --- indoteknik_custom/models/sj_tele.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indoteknik_custom/models/sj_tele.py b/indoteknik_custom/models/sj_tele.py index d3d7dfce..53ba26fc 100644 --- a/indoteknik_custom/models/sj_tele.py +++ b/indoteknik_custom/models/sj_tele.py @@ -28,7 +28,9 @@ class SjTele(models.Model): # chat_id_testing = '-4920864331' # api_testing = f'https://api.telegram.org/bot{bot_testing}' + # Select Data data = self.search([('is_sent', '=', False)], order='create_date asc') + # Old # data = self.search([], order='create_date asc') -- cgit v1.2.3 From d3e1c12c5745f95551116723efd64e7a15dd5fc1 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 22 Oct 2025 13:49:38 +0700 Subject: push --- indoteknik_custom/models/mrp_production.py | 3 +++ indoteknik_custom/models/stock_picking.py | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py index 30956082..02679458 100644 --- a/indoteknik_custom/models/mrp_production.py +++ b/indoteknik_custom/models/mrp_production.py @@ -308,6 +308,9 @@ class CheckBomProduct(models.Model): if not self.code_product: return + if self.production_id.qty_producing == 0: + raise UserError("Isi dan Save dahulu Quantity To Produce yang diinginkan!") + # Cari product berdasarkan default_code, barcode, atau barcode_box product = self.env['product.product'].search([ '|', diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 4772c433..d6096cc0 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -464,15 +464,15 @@ class StockPicking(models.Model): rec.last_update_date_doc_kirim = datetime.datetime.utcnow() - @api.constrains('scan_koli_lines') - def _constrains_scan_koli_lines(self): - now = datetime.datetime.utcnow() - for picking in self: - if len(picking.scan_koli_lines) > 0: - if len(picking.scan_koli_lines) != picking.total_mapping_koli: - raise UserError("Scan Koli Tidak Sesuai Dengan Total Mapping Koli") + # @api.constrains('scan_koli_lines') + # def _constrains_scan_koli_lines(self): + # now = datetime.datetime.utcnow() + # for picking in self: + # if len(picking.scan_koli_lines) > 0: + # if len(picking.scan_koli_lines) != picking.total_mapping_koli: + # raise UserError("Scan Koli Tidak Sesuai Dengan Total Mapping Koli") - picking.driver_departure_date = now + # picking.driver_departure_date = now @api.depends('total_so_koli') def _compute_total_so_koli(self): @@ -1303,6 +1303,9 @@ class StockPicking(models.Model): and self.create_date > threshold_datetime and not self.so_lama): raise UserError(_("Tidak ada scan koli! Harap periksa kembali.")) + + if 'BU/OUT/' in self.name: + self.driver_departure_date = datetime.datetime.utcnow() # if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing': # raise UserError(_("Isi Driver Departure Date dulu sebelum validate")) -- cgit v1.2.3 From 6e746afb7fcd7a3762dc7beb27ac3f77a21c7a98 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 13:51:30 +0700 Subject: fix --- indoteknik_api/controllers/api_v1/stock_picking.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 9affb492..310554c2 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -1,5 +1,3 @@ -from adodbapi.apibase import DateTime - from .. import controller from odoo import http, fields from odoo.http import request, Response -- cgit v1.2.3 From e65db92c25f8e54773cd1113736005f20799ea2b Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 13:51:45 +0700 Subject: fix --- indoteknik_api/controllers/api_v1/stock_picking.py | 1 - 1 file changed, 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 310554c2..20a48886 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -8,7 +8,6 @@ import logging _logger = logging.getLogger(__name__) -_logger = logging.getLogger(__name__) class StockPicking(controller.Controller): prefix = '/api/v1/' -- cgit v1.2.3 From 7c7519d2031a474901702fdd1c7921389eb44a15 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 16:10:38 +0700 Subject: get self pickup params from website --- indoteknik_api/controllers/api_v1/stock_picking.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 20a48886..b7ff5690 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -127,6 +127,7 @@ class StockPicking(controller.Controller): sj_document = kw.get('sj_document') if 'sj_document' in kw else None paket_document = kw.get('paket_document') if 'paket_document' in kw else None dispatch_document = kw.get('dispatch_document') if 'dispatch_document' in kw else None + self_pu= kw.get('self_pu') if 'self_pu' in kw else None # ===== Cari picking by id / picking_code ===== picking_data = False @@ -142,6 +143,8 @@ class StockPicking(controller.Controller): params = {} if sj_document: params['sj_documentation'] = sj_document + if params['self_pu']: + params['driver_arrival_date'] = datetime.utcnow() if paket_document: params['paket_documentation'] = paket_document params['driver_arrival_date'] = datetime.utcnow() -- cgit v1.2.3 From 8d649f97dade329859b5770d1f3972cdd7233f97 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 22 Oct 2025 16:22:17 +0700 Subject: get self pickup params from website --- indoteknik_api/controllers/api_v1/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index b7ff5690..fe82e665 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -143,7 +143,7 @@ class StockPicking(controller.Controller): params = {} if sj_document: params['sj_documentation'] = sj_document - if params['self_pu']: + if self_pu: params['driver_arrival_date'] = datetime.utcnow() if paket_document: params['paket_documentation'] = paket_document -- cgit v1.2.3