From 85caa56671d90cde807c44179680ef790d1a58c5 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 15 Jul 2025 11:59:36 +0700 Subject: tukar guling po vals rev --- indoteknik_custom/models/tukar_guling_po.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 6d7d7335..3292eb7d 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -311,7 +311,7 @@ class TukarGulingPO(models.Model): self._validate_product_lines() self._check_not_allow_tukar_guling_on_bu_input() - if self.operations and self.operations.picking_type_id.id == 28 and self.return_type == 'tukar_guling': + if self.operations.picking_type_id.id == 28: group = self.operations.group_id if group: # Cari BU/PUT dalam group yang sama @@ -325,11 +325,12 @@ class TukarGulingPO(models.Model): raise UserError("❌ Tidak bisa retur BU/INPUT karena BU/PUT sudah Done!") picking = self.operations - if picking.picking_type_id.id == 75: + pick_id = self.operations.picking_type_id.id + if pick_id == 75: if picking.state != 'done': raise UserError("BU/PUT belum Done!") - if picking.picking_type_id.id != 75 or picking.picking_type_id.id != 28: + if pick_id not in [75, 28]: raise UserError("❌ Tidak bisa retur bukan BU/INPUT atau BU/PUT!") if self._is_already_returned(self.operations): -- cgit v1.2.3 From 77d3bd2541a52270f03b84e38dd691630bcd82af Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 16 Jul 2025 08:47:04 +0700 Subject: mapping koli tukar guling --- indoteknik_custom/models/stock_picking.py | 2 + indoteknik_custom/models/stock_picking_return.py | 20 +++ indoteknik_custom/models/tukar_guling.py | 77 ++++++++--- indoteknik_custom/models/tukar_guling_po.py | 37 +++--- indoteknik_custom/security/ir.model.access.csv | 3 +- indoteknik_custom/views/tukar_guling.xml | 155 ++++++++++++----------- 6 files changed, 179 insertions(+), 115 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 7d3d963a..dc5b8ebd 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -2518,6 +2518,8 @@ class KonfirmKoli(models.Model): copy=False, ) pick_id = fields.Many2one('stock.picking', string='Pick') + product_id = fields.Many2one('product.product', string='Product') + qty_done = fields.Float(string='Qty Done') @api.constrains('pick_id') def _check_duplicate_pick_id(self): diff --git a/indoteknik_custom/models/stock_picking_return.py b/indoteknik_custom/models/stock_picking_return.py index a9781d3c..fa557ce8 100644 --- a/indoteknik_custom/models/stock_picking_return.py +++ b/indoteknik_custom/models/stock_picking_return.py @@ -89,6 +89,26 @@ class StockReturnPicking(models.TransientModel): if line_vals: context['default_line_ids'] = line_vals + if picking.picking_type_id.id == 29: + mapping_koli_vals = [] + sequence = 10 + returned_product_ids = set() + + # Ambil move lines dari BU/PICK + for move_line in picking.move_line_ids_without_package: + # Cek apakah produk ini ada di daftar retur dan qty_done > 0 + if move_line.product_id.id in returned_product_ids and move_line.qty_done > 0: + mapping_koli_vals.append((0, 0, { + 'sequence': sequence, + 'pick_id': picking.id, # ID BU/PICK itu sendiri + 'product_id': move_line.product_id.id, + 'qty_done': move_line.qty_done, + })) + sequence += 10 + + if mapping_koli_vals: + context['default_mapping_koli_ids'] = mapping_koli_vals + if picking.purchase_id or 'PO' in picking.origin: _logger.info("Redirect ke Tukar Guling PO via purchase_id / origin") return { diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 7e857d02..f0fe13f6 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -55,6 +55,7 @@ class TukarGuling(models.Model): ], default='draft', tracking=True, required=True) line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') + mapping_koli_ids = fields.One2many('tukar.guling.mapping.koli', 'tukar_guling_id', string='Mapping Koli') @api.onchange('operations') def _onchange_operations(self): @@ -71,10 +72,10 @@ class TukarGuling(models.Model): # Hanya update origin, jangan ubah lines if self.operations.origin: self.origin = self.operations.origin - return # Clear existing lines hanya jika tidak dari return picking self.line_ids = [(5, 0, 0)] + self.mapping_koli_ids = [(5, 0, 0)] # Clear existing mapping koli juga # Set origin dari operations if self.operations.origin: @@ -94,26 +95,59 @@ class TukarGuling(models.Model): elif hasattr(self.operations, 'move_lines') and self.operations.move_lines: moves_to_check = self.operations.move_lines + # Collect product data for both lines and mapping koli + product_data = {} for move in moves_to_check: - _logger.info( - f"Move: {move.name}, Product: {move.product_id.name if move.product_id else 'No Product'}, Qty: {move.product_uom_qty}, State: {move.state}") - - # Ambil semua move yang ada quantity if move.product_id and move.product_uom_qty > 0: - lines_data.append((0, 0, { - 'sequence': sequence, - 'product_id': move.product_id.id, - 'product_uom_qty': move.product_uom_qty, - 'product_uom': move.product_uom.id, - 'name': move.name or move.product_id.display_name, - })) - sequence += 10 + product_id = move.product_id.id + if product_id not in product_data: + product_data[product_id] = { + 'product': move.product_id, + 'qty': move.product_uom_qty, + 'uom': move.product_uom.id, + 'name': move.name or move.product_id.display_name + } + + # Create lines_data for product lines + for product_id, data in product_data.items(): + lines_data.append((0, 0, { + 'sequence': sequence, + 'product_id': product_id, + 'product_uom_qty': data['qty'], + 'product_uom': data['uom'], + 'name': data['name'], + })) + sequence += 10 if lines_data: self.line_ids = lines_data - _logger.info(f"Created {len(lines_data)} lines") + _logger.info(f"Created {len(lines_data)} product lines") + + # Prepare mapping koli based on picking type + mapping_koli_data = [] + sequence = 10 + + # Case 1: BU/OUT (picking_type_id.id == 29) + if self.operations.picking_type_id.id == 29: + # Ambil dari konfirm_koli_lines BU/OUT + for koli_line in self.operations.konfirm_koli_lines: + if koli_line.pick_id.move_line_ids_without_package.product_id.id in product_data: + mapping_koli_data.append((0, 0, { + 'sequence': sequence, + 'pick_id': koli_line.pick_id.move_line_ids_without_package.picking_id.id, + 'product_id': koli_line.pick_id.move_line_ids_without_package.product_id.id, + 'qty_done': koli_line.pick_id.move_line_ids_without_package.qty_done + })) + sequence += 10 + + + if mapping_koli_data: + self.mapping_koli_ids = mapping_koli_data + _logger.info(f"Created {len(mapping_koli_data)} mapping koli lines") + else: + _logger.info("No mapping koli lines created") else: - _logger.info("No lines created - no valid moves found") + _logger.info("No product lines created - no valid moves found") else: # Clear lines jika operations dikosongkan, kecuali dari return picking from_return_picking = self.env.context.get('from_return_picking', False) or \ @@ -121,6 +155,7 @@ class TukarGuling(models.Model): if not from_return_picking: self.line_ids = [(5, 0, 0)] + self.mapping_koli_ids = [(5, 0, 0)] self.origin = False @@ -470,7 +505,6 @@ class TukarGuling(models.Model): }) return_lines = [] - # 🔥 Hanya pakai qty dari tukar guling line for line in record.line_ids: move = picking.move_lines.filtered(lambda m: m.product_id == line.product_id) if move: @@ -568,3 +602,14 @@ class StockPicking(models.Model): _inherit = 'stock.picking' tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref') + + +class TukarGulingMappingKoli(models.Model): + _name = 'tukar.guling.mapping.koli' + _description = 'Mapping Koli di Tukar Guling' + + tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling') + pick_id = fields.Many2one('stock.picking', string='BU PICK') + product_id = fields.Many2one('product.product', string='Product') + qty_done = fields.Float(string='Qty Done di BU PICK') + sequence = fields.Integer(string='Sequence', default=10) diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 3292eb7d..88c4722a 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -389,7 +389,7 @@ class TukarGulingPO(models.Model): if not record.operations: raise UserError("BU Operations belum dipilih.") - created_returns = [] + created_returns = self.env['stock.picking'] group = record.operations.group_id bu_inputs = bu_puts = self.env['stock.picking'] @@ -404,17 +404,17 @@ class TukarGulingPO(models.Model): else: raise UserError("Group ID tidak ditemukan pada BU Operations.") - PARTNER_LOCATION_ID = 4 - BU_INPUT_LOCATION_ID = 58 - BU_STOCK_LOCATION_ID = 57 - def _create_return_from_picking(picking): if not picking: - return None + return self.env['stock.picking'] grup = record.operations.group_id - # Mapping lokasi sesuai picking type + # Tentukan location + PARTNER_LOCATION_ID = 4 + BU_INPUT_LOCATION_ID = 58 + BU_STOCK_LOCATION_ID = 57 + if picking.picking_type_id.id == 28: default_location_id = BU_INPUT_LOCATION_ID default_location_dest_id = PARTNER_LOCATION_ID @@ -428,7 +428,7 @@ class TukarGulingPO(models.Model): default_location_id = PARTNER_LOCATION_ID default_location_dest_id = BU_INPUT_LOCATION_ID else: - return None + return self.env['stock.picking'] return_context = dict(self.env.context) return_context.update({ @@ -444,7 +444,6 @@ class TukarGulingPO(models.Model): 'original_location_id': default_location_id }) - # Sesuai line tukar guling return_lines = [] for line in record.line_ids: move = picking.move_lines.filtered(lambda m: m.product_id == line.product_id) @@ -456,20 +455,17 @@ class TukarGulingPO(models.Model): })) else: raise UserError( - _("Tidak ditemukan move line di picking %s untuk produk %s") - % (picking.name, line.product_id.display_name) + _("Tidak ditemukan move line di picking %s untuk produk %s") % + (picking.name, line.product_id.display_name) ) if not return_lines: - return None + raise UserError(_("Tidak ada product line valid untuk retur picking %s") % picking.name) return_wizard.product_return_moves = return_lines return_vals = return_wizard.create_returns() return_picking = self.env['stock.picking'].browse(return_vals.get('res_id')) - if not return_picking: - raise UserError("Retur gagal dibuat. Hasil create_returns: %s" % str(return_vals)) - return_picking.write({ 'location_id': default_location_id, 'location_dest_id': default_location_dest_id, @@ -477,7 +473,6 @@ class TukarGulingPO(models.Model): 'tukar_guling_po_id': record.id, }) - # Paksa lokasi di move lines juga for move in return_picking.move_lines: move.write({ 'location_id': default_location_id, @@ -491,31 +486,31 @@ class TukarGulingPO(models.Model): # Kalau dari BU INPUT → hanya PRT prt = _create_return_from_picking(record.operations) if prt: - created_returns.append(prt) + created_returns |= prt else: # 1. Dari BU PUT buat VRT for bu_put in bu_puts: vrt = _create_return_from_picking(bu_put) if vrt: - created_returns.append(vrt) + created_returns |= vrt # 2. Dari BU INPUT buat PRT for bu_input in bu_inputs: prt = _create_return_from_picking(bu_input) if prt: - created_returns.append(prt) + created_returns |= prt # 3. Kalau tukar guling buat lanjut INPUT & PUT if record.return_type == 'tukar_guling': for prt in created_returns.filtered(lambda p: p.picking_type_id.id == 76): bu_input = _create_return_from_picking(prt) if bu_input: - created_returns.append(bu_input) + created_returns |= bu_input for vrt in created_returns.filtered(lambda p: p.picking_type_id.id == 77): bu_put = _create_return_from_picking(vrt) if bu_put: - created_returns.append(bu_put) + created_returns |= bu_put if not created_returns: raise UserError("Tidak ada dokumen retur yang berhasil dibuat.") diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 85781524..eb646dfb 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -187,4 +187,5 @@ access_approval_payment_term,access.approval.payment.term,model_approval_payment access_tukar_guling_all_users,tukar.guling.all.users,model_tukar_guling,base.group_user,1,1,1,1 access_tukar_guling_line_all_users,tukar.guling.line.all.users,model_tukar_guling_line,base.group_user,1,1,1,1 access_tukar_guling_po_all_users,tukar.guling.po.all.users,model_tukar_guling_po,base.group_user,1,1,1,1 -access_tukar_guling_line_po_all_users,tukar.guling.line.po.all.users,model_tukar_guling_line_po,base.group_user,1,1,1,1 \ No newline at end of file +access_tukar_guling_line_po_all_users,tukar.guling.line.po.all.users,model_tukar_guling_line_po,base.group_user,1,1,1,1 +access_tukar_guling_mapping_koli_all_users,tukar.guling.mapping.koli.all.users,model_tukar_guling_mapping_koli,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml index 41e9a18d..903a47fd 100644 --- a/indoteknik_custom/views/tukar_guling.xml +++ b/indoteknik_custom/views/tukar_guling.xml @@ -36,81 +36,82 @@ - - pengajuan.tukar.guling.form - tukar.guling - -
-
-
- -
- -
-
-

- -

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- + + pengajuan.tukar.guling.form + tukar.guling + +
+
+
+ +
+ +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ \ No newline at end of file -- cgit v1.2.3 From 3b1bc7642b6a5d0ac19dd74563c5b353a7f3b8ba Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 16 Jul 2025 10:27:02 +0700 Subject: vals qty mapping koli tukar guling --- indoteknik_custom/models/tukar_guling.py | 20 ++++++++++++++++++++ indoteknik_custom/views/tukar_guling.xml | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index f0fe13f6..333e2c8e 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -57,6 +57,26 @@ class TukarGuling(models.Model): line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') mapping_koli_ids = fields.One2many('tukar.guling.mapping.koli', 'tukar_guling_id', string='Mapping Koli') + @api.constrains('mapping_koli_ids') + def _check_mapping_koli(self): + for record in self: + if record.operations.picking_type_id.id == 29: # Only for BU/OUT + if not record.mapping_koli_ids: + raise UserError("❌ Mapping Koli belum diisi") + + # Calculate totals as integers + total_mapping_qty = sum(int(mapping.qty_done) for mapping in record.mapping_koli_ids) + total_line_qty = sum(int(line.product_uom_qty) for line in record.line_ids) + + # Strict integer comparison + if total_mapping_qty != total_line_qty: + raise UserError( + "❌ Total quantity mapping koli (%d) tidak sama dengan quantity retur (%d)" % + (total_mapping_qty, total_line_qty) + ) + else: + _logger.info("qty koli sesuai") + @api.onchange('operations') def _onchange_operations(self): """Auto-populate lines ketika operations dipilih""" diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml index 903a47fd..c36089ad 100644 --- a/indoteknik_custom/views/tukar_guling.xml +++ b/indoteknik_custom/views/tukar_guling.xml @@ -99,8 +99,8 @@ - - + + -- cgit v1.2.3 From 0e330178d087b2b4cb519c4e078f0fe25a76dfe5 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 16 Jul 2025 13:37:08 +0700 Subject: fix qty prod line and add qty return --- indoteknik_custom/models/stock_picking_return.py | 1 + indoteknik_custom/models/tukar_guling.py | 51 ++++++++++++++++-------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/indoteknik_custom/models/stock_picking_return.py b/indoteknik_custom/models/stock_picking_return.py index fa557ce8..e274a147 100644 --- a/indoteknik_custom/models/stock_picking_return.py +++ b/indoteknik_custom/models/stock_picking_return.py @@ -103,6 +103,7 @@ class StockReturnPicking(models.TransientModel): 'pick_id': picking.id, # ID BU/PICK itu sendiri 'product_id': move_line.product_id.id, 'qty_done': move_line.qty_done, + 'qty_return': move_line.qty_done, })) sequence += 10 diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 333e2c8e..a2168f5b 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -65,13 +65,13 @@ class TukarGuling(models.Model): raise UserError("❌ Mapping Koli belum diisi") # Calculate totals as integers - total_mapping_qty = sum(int(mapping.qty_done) for mapping in record.mapping_koli_ids) + total_mapping_qty = sum(int(mapping.qty_done) for mapping in record.mapping_koli_ids.qty_return) total_line_qty = sum(int(line.product_uom_qty) for line in record.line_ids) # Strict integer comparison if total_mapping_qty != total_line_qty: raise UserError( - "❌ Total quantity mapping koli (%d) tidak sama dengan quantity retur (%d)" % + "❌ Total quantity return di mapping koli (%d) tidak sama dengan quantity retur product lines (%d)" % (total_mapping_qty, total_line_qty) ) else: @@ -92,6 +92,28 @@ class TukarGuling(models.Model): # Hanya update origin, jangan ubah lines if self.operations.origin: self.origin = self.operations.origin + _logger.info("📌 Menggunakan product lines dari return wizard, tidak populate ulang.") + + # 🚀 Tapi tetap populate mapping koli jika BU/OUT + if self.operations.picking_type_id.id == 29: + mapping_koli_data = [] + sequence = 10 + tg_product_ids = self.line_ids.mapped('product_id.id') + + for koli_line in self.operations.konfirm_koli_lines: + pick_move = koli_line.pick_id.move_line_ids_without_package + if pick_move.product_id.id in tg_product_ids: + mapping_koli_data.append((0, 0, { + 'sequence': sequence, + 'pick_id': koli_line.pick_id.id, + 'product_id': pick_move.product_id.id, + 'qty_done': pick_move.qty_done + })) + sequence += 10 + + self.mapping_koli_ids = mapping_koli_data + _logger.info(f"✅ Created {len(mapping_koli_data)} mapping koli lines (from return wizard)") + return # keluar supaya tidak populate ulang lines # Clear existing lines hanya jika tidak dari return picking self.line_ids = [(5, 0, 0)] @@ -108,14 +130,12 @@ class TukarGuling(models.Model): # Untuk Odoo 14, gunakan move_ids_without_package atau move_lines moves_to_check = [] - # 1. move_ids_without_package (standard di Odoo 14) if hasattr(self.operations, 'move_ids_without_package') and self.operations.move_ids_without_package: moves_to_check = self.operations.move_ids_without_package - # 2. move_lines (backup untuk versi lama) elif hasattr(self.operations, 'move_lines') and self.operations.move_lines: moves_to_check = self.operations.move_lines - # Collect product data for both lines and mapping koli + # Collect product data product_data = {} for move in moves_to_check: if move.product_id and move.product_uom_qty > 0: @@ -128,7 +148,7 @@ class TukarGuling(models.Model): 'name': move.name or move.product_id.display_name } - # Create lines_data for product lines + # Buat lines_data for product_id, data in product_data.items(): lines_data.append((0, 0, { 'sequence': sequence, @@ -143,24 +163,23 @@ class TukarGuling(models.Model): self.line_ids = lines_data _logger.info(f"Created {len(lines_data)} product lines") - # Prepare mapping koli based on picking type + # Prepare mapping koli jika BU/OUT mapping_koli_data = [] sequence = 10 - # Case 1: BU/OUT (picking_type_id.id == 29) if self.operations.picking_type_id.id == 29: - # Ambil dari konfirm_koli_lines BU/OUT + tg_product_ids = [p for p in product_data] for koli_line in self.operations.konfirm_koli_lines: - if koli_line.pick_id.move_line_ids_without_package.product_id.id in product_data: + pick_move = koli_line.pick_id.move_line_ids_without_package + if pick_move.product_id.id in tg_product_ids: mapping_koli_data.append((0, 0, { 'sequence': sequence, - 'pick_id': koli_line.pick_id.move_line_ids_without_package.picking_id.id, - 'product_id': koli_line.pick_id.move_line_ids_without_package.product_id.id, - 'qty_done': koli_line.pick_id.move_line_ids_without_package.qty_done + 'pick_id': koli_line.pick_id.id, + 'product_id': pick_move.product_id.id, + 'qty_done': pick_move.qty_done })) sequence += 10 - if mapping_koli_data: self.mapping_koli_ids = mapping_koli_data _logger.info(f"Created {len(mapping_koli_data)} mapping koli lines") @@ -169,7 +188,6 @@ class TukarGuling(models.Model): else: _logger.info("No product lines created - no valid moves found") else: - # Clear lines jika operations dikosongkan, kecuali dari return picking from_return_picking = self.env.context.get('from_return_picking', False) or \ self.env.context.get('default_line_ids', False) @@ -631,5 +649,6 @@ class TukarGulingMappingKoli(models.Model): tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling') pick_id = fields.Many2one('stock.picking', string='BU PICK') product_id = fields.Many2one('product.product', string='Product') - qty_done = fields.Float(string='Qty Done di BU PICK') + qty_done = fields.Float(string='Qty Done BU PICK') + qty_return = fields.Float(string='Qty yang mau diretur') sequence = fields.Integer(string='Sequence', default=10) -- cgit v1.2.3 From efa1650aae2bc2dca99624092adcc21f87dab648 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 16 Jul 2025 17:40:22 +0700 Subject: retur blm sesuai --- indoteknik_custom/models/tukar_guling.py | 99 +++++++++++++++++--------------- indoteknik_custom/views/tukar_guling.xml | 21 +++---- 2 files changed, 65 insertions(+), 55 deletions(-) diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index a2168f5b..2c39b547 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -57,25 +57,23 @@ class TukarGuling(models.Model): line_ids = fields.One2many('tukar.guling.line', 'tukar_guling_id', string='Product Lines') mapping_koli_ids = fields.One2many('tukar.guling.mapping.koli', 'tukar_guling_id', string='Mapping Koli') - @api.constrains('mapping_koli_ids') def _check_mapping_koli(self): for record in self: if record.operations.picking_type_id.id == 29: # Only for BU/OUT if not record.mapping_koli_ids: raise UserError("❌ Mapping Koli belum diisi") - # Calculate totals as integers - total_mapping_qty = sum(int(mapping.qty_done) for mapping in record.mapping_koli_ids.qty_return) + # Calculate totals + total_mapping_qty = sum(int(mapping.qty_return) for mapping in record.mapping_koli_ids) total_line_qty = sum(int(line.product_uom_qty) for line in record.line_ids) - # Strict integer comparison if total_mapping_qty != total_line_qty: raise UserError( "❌ Total quantity return di mapping koli (%d) tidak sama dengan quantity retur product lines (%d)" % (total_mapping_qty, total_line_qty) ) else: - _logger.info("qty koli sesuai") + _logger.info("✅ Qty mapping koli sesuai dengan product lines") @api.onchange('operations') def _onchange_operations(self): @@ -107,7 +105,8 @@ class TukarGuling(models.Model): 'sequence': sequence, 'pick_id': koli_line.pick_id.id, 'product_id': pick_move.product_id.id, - 'qty_done': pick_move.qty_done + 'qty_done': pick_move.qty_done, + 'qty_return': 0 })) sequence += 10 @@ -482,47 +481,39 @@ class TukarGuling(models.Model): if not record.operations: raise UserError("BU/OUT dari field operations tidak ditemukan.") + created_returns = [] + bu_pick_to_return = record.operations.konfirm_koli_lines.pick_id bu_out_to_return = record.operations if not bu_pick_to_return and not bu_out_to_return: raise UserError("Tidak ada BU/PICK atau BU/OUT yang selesai untuk diretur.") - created_returns = [] - - # Picking types & locations srt_type = self.env['stock.picking.type'].browse(73) ort_type = self.env['stock.picking.type'].browse(74) bu_pick_type = self.env['stock.picking.type'].browse(30) bu_out_type = self.env['stock.picking.type'].browse(29) - def _create_return_from_picking(picking): + PARTNER_LOCATION_ID = 5 + BU_OUTPUT_LOCATION_ID = 60 + BU_STOCK_LOCATION_ID = 57 + + def _create_return_from_picking_grouped(picking): if not picking: return None - grup = record.operations.group_id - PARTNER_LOCATION_ID = 5 - BU_OUTPUT_LOCATION_ID = 60 - BU_STOCK_LOCATION_ID = 57 + grup = record.operations.group_id if picking.picking_type_id.id == 30: - # BU/PICK → ORT - return_type = ort_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = BU_STOCK_LOCATION_ID elif picking.picking_type_id.id == 74: - # ORT → BU/PICK - return_type = bu_pick_type default_location_id = BU_STOCK_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID elif picking.picking_type_id.id == 29: - # BU/OUT → SRT - return_type = srt_type default_location_id = PARTNER_LOCATION_ID default_location_dest_id = BU_OUTPUT_LOCATION_ID elif picking.picking_type_id.id == 73: - # SRT → BU/OUT - return_type = bu_out_type default_location_id = BU_OUTPUT_LOCATION_ID default_location_dest_id = PARTNER_LOCATION_ID else: @@ -542,58 +533,75 @@ class TukarGuling(models.Model): 'original_location_id': default_location_id }) + # 🔥 If BU/OUT, ambil qty dari mapping koli (group by product) return_lines = [] - for line in record.line_ids: - move = picking.move_lines.filtered(lambda m: m.product_id == line.product_id) - if move: + if picking.picking_type_id.id == 29 and record.mapping_koli_ids: + grouped_qty = {} + for map_line in record.mapping_koli_ids: + pid = map_line.product_id.id + grouped_qty[pid] = grouped_qty.get(pid, 0) + map_line.qty_return + + for pid, qty in grouped_qty.items(): + move = picking.move_lines.filtered(lambda mv: mv.product_id.id == pid) + if move: + return_lines.append((0, 0, { + 'product_id': pid, + 'quantity': qty, + 'move_id': move[0].id, + })) + else: + raise UserError(_("Tidak ditemukan move line di picking %s untuk produk %s") + % (picking.name, map_line.product_id.display_name)) + else: + # Default kalau bukan BU/OUT (misalnya ORT), retur full qty done + for move in picking.move_lines: return_lines.append((0, 0, { - 'product_id': line.product_id.id, - 'quantity': line.product_uom_qty, - 'move_id': move[0].id, + 'product_id': move.product_id.id, + 'quantity': move.quantity_done or move.product_uom_qty, + 'move_id': move.id, })) - else: - raise UserError( - _("Tidak ditemukan move line di picking %s untuk produk %s") - % (picking.name, line.product_id.display_name) - ) if not return_lines: raise UserError(_("Tidak ada product line valid untuk retur picking %s") % picking.name) return_wizard.product_return_moves = return_lines return_vals = return_wizard.create_returns() - return_id = return_vals.get('res_id') - return_picking = self.env['stock.picking'].browse(return_id) + return_picking = self.env['stock.picking'].browse(return_vals.get('res_id')) if not return_picking: raise UserError("Retur gagal dibuat. Hasil create_returns: %s" % str(return_vals)) return_picking.write({ - 'location_dest_id': default_location_dest_id, 'location_id': default_location_id, + 'location_dest_id': default_location_dest_id, 'group_id': grup.id, 'tukar_guling_id': record.id, }) + for move in return_picking.move_lines: + move.write({ + 'location_id': default_location_id, + 'location_dest_id': default_location_dest_id, + }) + return return_picking - # === PERBAIKI URUTAN === - srt = _create_return_from_picking(bu_out_to_return) + # === FLOW === + srt = _create_return_from_picking_grouped(bu_out_to_return) if srt: created_returns.append(srt) - picks = record.operations.konfirm_koli_lines.pick_id - for picking in picks: - ort = _create_return_from_picking(picking) + for picking in bu_pick_to_return: + ort = _create_return_from_picking_grouped(picking) if ort: created_returns.append(ort) if record.return_type == 'tukar_guling': - bu_pick = _create_return_from_picking(ort) + bu_pick = _create_return_from_picking_grouped(ort) if bu_pick: created_returns.append(bu_pick) if record.return_type == 'tukar_guling' and srt: - bu_out = _create_return_from_picking(srt) + bu_out = _create_return_from_picking_grouped(srt) if bu_out: created_returns.append(bu_out) @@ -601,6 +609,7 @@ class TukarGuling(models.Model): raise UserError("Tidak ada dokumen retur yang berhasil dibuat.") + class TukarGulingLine(models.Model): _name = 'tukar.guling.line' _description = 'Tukar Guling Line' @@ -650,5 +659,5 @@ class TukarGulingMappingKoli(models.Model): pick_id = fields.Many2one('stock.picking', string='BU PICK') product_id = fields.Many2one('product.product', string='Product') qty_done = fields.Float(string='Qty Done BU PICK') - qty_return = fields.Float(string='Qty yang mau diretur') - sequence = fields.Integer(string='Sequence', default=10) + qty_return = fields.Float(string='Qty diretur') + sequence = fields.Integer(string='Sequence', default=10) \ No newline at end of file diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml index c36089ad..acc48d3f 100644 --- a/indoteknik_custom/views/tukar_guling.xml +++ b/indoteknik_custom/views/tukar_guling.xml @@ -86,13 +86,13 @@ - - - + + + - - + + @@ -101,10 +101,11 @@ - - - - + + + + + -- cgit v1.2.3