diff options
| author | Miqdad <ahmadmiqdad27@gmail.com> | 2025-07-18 14:05:48 +0700 |
|---|---|---|
| committer | Miqdad <ahmadmiqdad27@gmail.com> | 2025-07-18 14:05:48 +0700 |
| commit | c6f3edf6eaf705511b926b961b7ae4fcf017e17f (patch) | |
| tree | e30d52f5c0b0858f549756f4ae4b63d577794e95 | |
| parent | 82c1a95f447d191018bed2f3a3c93831f6d398cc (diff) | |
<miqdad> don
| -rw-r--r-- | indoteknik_custom/models/stock_picking_return.py | 4 | ||||
| -rw-r--r-- | indoteknik_custom/models/tukar_guling_po.py | 116 |
2 files changed, 84 insertions, 36 deletions
diff --git a/indoteknik_custom/models/stock_picking_return.py b/indoteknik_custom/models/stock_picking_return.py index e274a147..1fc8d088 100644 --- a/indoteknik_custom/models/stock_picking_return.py +++ b/indoteknik_custom/models/stock_picking_return.py @@ -5,7 +5,7 @@ import logging _logger = logging.getLogger(__name__) -class StockReturnPicking(models.TransientModel): +class ReturnPicking(models.TransientModel): _inherit = 'stock.return.picking' # return_type = fields.Selection([ @@ -26,7 +26,7 @@ class StockReturnPicking(models.TransientModel): if self._context.get('from_ui', True): return self._redirect_to_tukar_guling() - return super(StockReturnPicking, self).create_returns() + return super(ReturnPicking, self).create_returns() def _redirect_to_tukar_guling(self): """Redirect ke Tukar Guling SO atau PO form dengan pre-filled data""" diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index 997e1963..72417a72 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -238,7 +238,8 @@ class TukarGulingPO(models.Model): def _is_already_returned(self, picking): return self.env['stock.picking'].search_count([ ('origin', '=', 'Return of %s' % picking.name), - ('state', '!=', 'cancel') + # ('returned_from_id', '=', picking.id), + ('state', 'not in', ['cancel', 'draft']), ]) > 0 def copy(self, default=None): @@ -286,9 +287,9 @@ class TukarGulingPO(models.Model): if self.operations.picking_type_id.id == 28 and tipe == 'tukar_guling': raise UserError("❌ BU/INPUT tidak boleh di retur tukar guling") - if self.operations.picking_type_id.id != 28: - if self._is_already_returned(self.operations): - raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") + # if self.operations.picking_type_id.id != 28: + # if self._is_already_returned(self.operations): + # raise UserError("BU ini sudah pernah diretur oleh dokumen lain.") if 'operations' in vals and not vals.get('origin'): picking = self.env['stock.picking'].browse(vals['operations']) if picking.origin: @@ -413,6 +414,14 @@ class TukarGulingPO(models.Model): group = record.operations.group_id bu_inputs = bu_puts = self.env['stock.picking'] + # Buat qty map awal dari line_ids + bu_input_qty_map = { + line.product_id.id: line.product_uom_qty + for line in record.line_ids + if line.product_id and line.product_uom_qty > 0 + } + bu_put_qty_map = bu_input_qty_map.copy() + if group: po_pickings = self.env['stock.picking'].search([ ('group_id', '=', group.id), @@ -423,27 +432,28 @@ class TukarGulingPO(models.Model): else: raise UserError("Group ID tidak ditemukan pada BU Operations.") - def _create_return_from_picking(picking): + def _create_return_from_picking(picking, qty_map): if not picking: return self.env['stock.picking'] grup = record.operations.group_id - # Tentukan location + # Tentukan lokasi PARTNER_LOCATION_ID = 4 BU_INPUT_LOCATION_ID = 58 BU_STOCK_LOCATION_ID = 57 - if picking.picking_type_id.id == 28: + picking_type = picking.picking_type_id.id + if picking_type == 28: default_location_id = BU_INPUT_LOCATION_ID default_location_dest_id = PARTNER_LOCATION_ID - elif picking.picking_type_id.id == 75: + elif picking_type == 75: default_location_id = BU_STOCK_LOCATION_ID default_location_dest_id = BU_INPUT_LOCATION_ID - elif picking.picking_type_id.id == 77: + elif picking_type == 77: default_location_id = BU_INPUT_LOCATION_ID default_location_dest_id = BU_STOCK_LOCATION_ID - elif picking.picking_type_id.id == 76: + elif picking_type == 76: default_location_id = PARTNER_LOCATION_ID default_location_dest_id = BU_INPUT_LOCATION_ID else: @@ -464,18 +474,46 @@ class TukarGulingPO(models.Model): }) return_lines = [] + moves = getattr(picking, 'move_ids_without_package', False) or picking.move_lines + + for move in moves: + product = move.product_id + if not product: + continue + + pid = product.id + available_qty = qty_map.get(pid, 0.0) + move_qty = move.product_uom_qty + allocate_qty = min(available_qty, move_qty) + + if allocate_qty <= 0: + continue - for move in picking.move_lines: - line = record.line_ids.filtered(lambda l: l.product_id == move.product_id) - qty = line.product_uom_qty if line else 0.0 return_lines.append((0, 0, { - 'product_id': move.product_id.id, - 'quantity': qty, + 'product_id': pid, + 'quantity': allocate_qty, 'move_id': move.id, })) + qty_map[pid] -= allocate_qty + + _logger.info(f"📦 Alokasi {allocate_qty} untuk {product.display_name} | Sisa: {qty_map[pid]}") if not return_lines: - raise UserError(_("Tidak ada product line valid untuk retur picking %s") % picking.name) + # Tukar Guling lanjut dari PRT/VRT + if picking.picking_type_id.id in [76, 77]: + for move in moves: + if move.product_uom_qty > 0: + return_lines.append((0, 0, { + 'product_id': move.product_id.id, + 'quantity': move.product_uom_qty, + 'move_id': move.id, + })) + _logger.info( + f"🔁 TG lanjutan: Alokasi {move.product_uom_qty} untuk {move.product_id.display_name}") + else: + _logger.warning( + f"⏭️ Skipped return picking {picking.name}, tidak ada qty yang bisa dialokasikan.") + return self.env['stock.picking'] return_wizard.product_return_moves = return_lines return_vals = return_wizard.create_returns() @@ -488,42 +526,52 @@ class TukarGulingPO(models.Model): 'tukar_guling_po_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 - # === Eksekusi pembuatan picking === + # ============================ + # Eksekusi utama return logic + # ============================ + if record.operations.picking_type_id.id == 28: - # Kalau dari BU INPUT → hanya PRT - prt = _create_return_from_picking(record.operations) + # Dari BU INPUT langsung buat PRT + prt = _create_return_from_picking(record.operations, bu_input_qty_map) if prt: created_returns |= prt else: - # 1. Dari BU PUT buat VRT - for bu_put in bu_puts: - vrt = _create_return_from_picking(bu_put) + # ✅ Pairing BU PUT ↔ BU INPUT + # Temukan index dari BU PUT yang dipilih user + try: + bu_put_index = sorted(bu_puts, key=lambda p: p.name).index(record.operations) + except ValueError: + raise UserError("Dokumen BU PUT yang dipilih tidak ditemukan dalam daftar BU PUT.") + + # Ambil pasangannya di BU INPUT (asumsi urutan sejajar) + sorted_bu_puts = sorted(bu_puts, key=lambda p: p.name) + sorted_bu_inputs = sorted(bu_inputs, key=lambda p: p.name) + + if bu_put_index >= len(sorted_bu_inputs): + raise UserError("Tidak ditemukan pasangan BU INPUT untuk BU PUT yang dipilih.") + + paired = [(sorted_bu_puts[bu_put_index], sorted_bu_inputs[bu_put_index])] + + for bu_put, bu_input in paired: + vrt = _create_return_from_picking(bu_put, bu_put_qty_map) if vrt: created_returns |= vrt - # 2. Dari BU INPUT buat PRT - for bu_input in bu_inputs: - prt = _create_return_from_picking(bu_input) + prt = _create_return_from_picking(bu_input, bu_input_qty_map) if prt: created_returns |= prt - # 3. Kalau tukar guling buat lanjut INPUT & PUT + # 🌀 Tukar Guling: buat dokumen baru dari PRT & VRT 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) + bu_input = _create_return_from_picking(prt, bu_input_qty_map) if 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) + bu_put = _create_return_from_picking(vrt, bu_put_qty_map) if bu_put: created_returns |= bu_put |
