diff options
| author | Miqdad <ahmadmiqdad27@gmail.com> | 2025-08-08 09:16:15 +0700 |
|---|---|---|
| committer | Miqdad <ahmadmiqdad27@gmail.com> | 2025-08-08 09:16:15 +0700 |
| commit | 176dd85c3d809c035128847378bf78d96aa0896a (patch) | |
| tree | db560e64f6d2ca124849666662e90460f30c8488 | |
| parent | 26b1df8d150a46297d84f24283687c56b81e4e65 (diff) | |
<miqdad> handle duplicate prod in stock move line
| -rw-r--r-- | indoteknik_custom/models/tukar_guling.py | 87 |
1 files changed, 67 insertions, 20 deletions
diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 4c7722d5..eadb164c 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -2,6 +2,7 @@ from odoo import models, fields, api, _ from odoo.exceptions import UserError, ValidationError import logging from datetime import datetime +from collections import defaultdict _logger = logging.getLogger(__name__) @@ -540,7 +541,7 @@ class TukarGuling(models.Model): ]) if self.state == 'approved' and done_ort: self.state = 'done' - else: + elif self.operations.picking_type_id.id == 30 and self.return_type == 'revisi_so' and not has_bu_pick: raise UserError("Tidak bisa menentukan jenis retur.") def action_approve(self): @@ -676,30 +677,47 @@ class TukarGuling(models.Model): ### ======== SRT dari BU/OUT ========= srt_return_lines = [] if mapping_koli: - for prod in mapping_koli.mapped('product_id'): - qty_total = sum(mk.qty_return for mk in mapping_koli.filtered(lambda m: m.product_id == prod)) - move = bu_out.move_lines.filtered(lambda m: m.product_id == prod) - if not move: - raise UserError(f"Move BU/OUT tidak ditemukan untuk produk {prod.display_name}") + move_per_product = defaultdict(list) + for move in bu_out.move_lines: + move_per_product[move.product_id.id].append(move) + + mapped_product_ids = set(mapping_koli.mapped('product_id').ids) + + for product_id in mapped_product_ids: + qty_ret = sum( + line.qty_return for line in mapping_koli.filtered(lambda l: l.product_id.id == product_id)) + if qty_ret <= 0: + continue + + product_moves = move_per_product[product_id] + if not product_moves: + raise UserError(f"❌ Move BU/OUT tidak ditemukan untuk product ID {product_id}") + + if len(product_moves) > 1: + _logger.warning(f"🟠 Detected duplicate moves for product {product_id}, picking {bu_out.name}") + chosen_move = product_moves[0] + else: + chosen_move = product_moves[0] + srt_return_lines.append((0, 0, { - 'product_id': prod.id, - 'quantity': qty_total, - 'move_id': move.id, + 'product_id': product_id, + 'quantity': qty_ret, + 'move_id': chosen_move.id, })) - _logger.info(f"📟 SRT line: {prod.display_name} | qty={qty_total}") + _logger.info(f"📟 SRT line: {chosen_move.product_id.display_name} | qty={qty_ret}") - elif not mapping_koli: + else: + # --- Fallback ke line_ids jika tidak ada mapping_koli --- for line in record.line_ids: move = bu_out.move_lines.filtered(lambda m: m.product_id == line.product_id) if not move: - raise UserError(f"Move BU/OUT tidak ditemukan untuk produk {line.product_id.display_name}") + raise UserError(f"❌ Move BU/OUT tidak ditemukan untuk produk {line.product_id.display_name}") srt_return_lines.append((0, 0, { 'product_id': line.product_id.id, 'quantity': line.product_uom_qty, 'move_id': move.id, })) - _logger.info( - f"📟 SRT line (fallback line_ids): {line.product_id.display_name} | qty={line.product_uom_qty}") + _logger.info(f"📟 SRT line (fallback): {line.product_id.display_name} | qty={line.product_uom_qty}") srt_picking = None if srt_return_lines: @@ -738,33 +756,62 @@ class TukarGuling(models.Model): for pick in picks_to_return: ort_return_lines = [] + if is_retur_from_bu_pick: + # Build map produk -> move list + move_map = defaultdict(list) + for move in pick.move_lines: + move_map[move.product_id.id].append(move) + for line in record.line_ids: - move = pick.move_lines.filtered(lambda m: m.product_id == line.product_id) - if not move: + moves = move_map.get(line.product_id.id) + if not moves: raise UserError( f"Move tidak ditemukan di BU/PICK {pick.name} untuk {line.product_id.display_name}") + + chosen_move = moves[0] + if len(moves) > 1: + _logger.warning( + f"🟠 Duplicate move detected for {line.product_id.display_name} in {pick.name}. Using the first move only.") + pick.message_post( + body=f"🟠 Duplicate move ditemukan untuk produk <b>{line.product_id.display_name}</b>. Hanya 1 move yang dipakai.") + ort_return_lines.append((0, 0, { 'product_id': line.product_id.id, 'quantity': line.product_uom_qty, - 'move_id': move.id, + 'move_id': chosen_move.id, })) _logger.info( f"📟 ORT (BU/PICK langsung) | {pick.name} | {line.product_id.display_name} | qty={line.product_uom_qty}") + else: + # Mapping koli case + move_map = defaultdict(list) + for move in pick.move_lines: + move_map[move.product_id.id].append(move) + for mk in mapping_koli.filtered(lambda m: m.pick_id == pick): - move = pick.move_lines.filtered(lambda m: m.product_id == mk.product_id) - if not move: + moves = move_map.get(mk.product_id.id) + if not moves: raise UserError( f"Move tidak ditemukan di BU/PICK {pick.name} untuk {mk.product_id.display_name}") + + chosen_move = moves[0] + if len(moves) > 1: + _logger.warning( + f"🟠 Duplicate move detected for {mk.product_id.display_name} in {pick.name}. Using the first move only.") + pick.message_post( + body=f"🟠 Duplicate move ditemukan untuk produk <b>{mk.product_id.display_name}</b>. Hanya 1 move yang dipakai.") + ort_return_lines.append((0, 0, { 'product_id': mk.product_id.id, 'quantity': mk.qty_return, - 'move_id': move.id, + 'move_id': chosen_move.id, })) _logger.info( f"📟 ORT (mapping koli) | {pick.name} | {mk.product_id.display_name} | qty={mk.qty_return}") + # Buat retur jika ada return line if ort_return_lines: ort_wizard = self.env['stock.return.picking'].with_context({ 'active_id': pick.id, |
