diff options
| author | Miqdad <ahmadmiqdad27@gmail.com> | 2025-06-21 09:07:05 +0700 |
|---|---|---|
| committer | Miqdad <ahmadmiqdad27@gmail.com> | 2025-06-21 09:07:05 +0700 |
| commit | 41b26b7fca60533fe30240d19b972cbe7022f333 (patch) | |
| tree | 6d8624279cbbfa99dafb56790b77184874852889 | |
| parent | bce4d940dc90bf50e045a8fde3fd1c7bb53e8562 (diff) | |
<miqdad> return oke
| -rw-r--r-- | indoteknik_custom/models/stock_picking_return.py | 127 | ||||
| -rw-r--r-- | indoteknik_custom/models/tukar_guling.py | 242 | ||||
| -rw-r--r-- | indoteknik_custom/views/tukar_guling.xml | 1 |
3 files changed, 221 insertions, 149 deletions
diff --git a/indoteknik_custom/models/stock_picking_return.py b/indoteknik_custom/models/stock_picking_return.py index d6225f1a..74bf6407 100644 --- a/indoteknik_custom/models/stock_picking_return.py +++ b/indoteknik_custom/models/stock_picking_return.py @@ -1,10 +1,6 @@ from odoo.exceptions import UserError from odoo.tools.float_utils import float_round from odoo import models, fields, api, _ -from odoo.exceptions import UserError - -from odoo import models, fields, api, _ -from odoo.exceptions import UserError class StockReturnPicking(models.TransientModel): @@ -16,8 +12,7 @@ class StockReturnPicking(models.TransientModel): ], string='Jenis Retur', default='revisi_so') def create_returns(self): - """Override method to handle Tukar Guling redirection""" - if self.return_type == 'tukar_guling': + if self._context.get('from_ui', True) and self.return_type == 'tukar_guling': return self._redirect_to_tukar_guling() return super(StockReturnPicking, self).create_returns() @@ -26,53 +21,100 @@ class StockReturnPicking(models.TransientModel): self.ensure_one() picking = self.picking_id - # Gunakan pendekatan yang lebih kompatibel untuk Odoo 14 - # Cari hanya baris yang masih ada dan memiliki quantity > 0 + # Get valid return lines with better error handling valid_lines = [] - for line in self.product_return_moves: - # Periksa apakah baris masih ada di database atau baru dibuat - if line.id: - # Untuk baris yang sudah ada di database, pastikan masih ada - if not self.env['stock.return.picking.line'].browse(line.id).exists(): - continue - # Baris baru yang belum disimpan tidak memiliki id - if line.quantity > 0: - valid_lines.append(line) + try: + # Refresh the recordset to ensure we have the latest data + self.env.cr.execute("SELECT id FROM stock_return_picking_line WHERE wizard_id = %s", (self.id,)) + line_ids = [row[0] for row in self.env.cr.fetchall()] + + if line_ids: + # Use sudo to avoid access rights issues and browse existing lines + existing_lines = self.env['stock.return.picking.line'].sudo().browse(line_ids) + for line in existing_lines: + if line.exists() and line.quantity > 0: + valid_lines.append(line) + + # If no lines found via direct query, try the original approach + if not valid_lines: + for line in self.product_return_moves: + if hasattr(line, 'quantity') and line.quantity > 0: + # Additional check to ensure the line is valid + if line.product_id and line.move_id: + valid_lines.append(line) + + except Exception as e: + # Fallback: create lines based on picking moves + valid_lines = [] + for move in picking.move_ids_without_package: + if move.product_uom_qty > 0 and move.state == 'done': + # Create a temporary line object for data extraction + temp_line = type('TempLine', (), { + 'product_id': move.product_id, + 'quantity': move.quantity_done or move.product_uom_qty, + 'move_id': move + })() + valid_lines.append(temp_line) if not valid_lines: - raise UserError(_("Please specify at least one product to return with positive quantity.")) + raise UserError(_("Tidak ada produk yang bisa diretur. Pastikan ada produk dengan quantity > 0.")) # Prepare context for Tukar Guling form context = { 'default_operations': picking.id, - 'default_partner_id': picking.partner_id.id, - 'default_origin': picking.origin or picking.name, 'default_return_type': 'tukar_guling', 'default_date': fields.Datetime.now(), 'default_state': 'draft', 'default_ba_num': _('Retur dari %s') % picking.name, + 'from_return_picking': True, # Flag to prevent onchange from overriding lines } + # Set origin + if picking.origin: + context['default_origin'] = picking.origin + + # Set partner + if picking.partner_id: + context['default_partner_id'] = picking.partner_id.id + + # Set shipping address + if hasattr(picking, 'real_shipping_id') and picking.real_shipping_id: + context['default_real_shipping_id'] = picking.real_shipping_id.id + elif picking.partner_id: + context['default_real_shipping_id'] = picking.partner_id.id + # Prepare product lines line_vals = [] + sequence = 10 + for line in valid_lines: - line_vals.append((0, 0, { - 'product_id': line.product_id.id, - 'product_uom_qty': line.quantity, - 'product_uom': line.product_id.uom_id.id, - 'name': line.product_id.display_name, - })) + try: + # Get quantity - handle both real lines and temp objects + quantity = getattr(line, 'quantity', 0) + if quantity <= 0: + continue - context['default_line_ids'] = line_vals + # Get product + product = getattr(line, 'product_id', None) + if not product: + continue - # Set SO if available - if picking.sale_id: - context['default_so_id'] = picking.sale_id.id + line_vals.append((0, 0, { + 'sequence': sequence, + 'product_id': product.id, + 'product_uom_qty': quantity, + 'product_uom': product.uom_id.id, + 'name': product.display_name, + })) + sequence += 10 - # Set shipping address - if picking.partner_id: - context['default_real_shipping_id'] = picking.partner_id.id + except Exception as e: + # Skip problematic lines + continue + + if line_vals: + context['default_line_ids'] = line_vals return { 'name': _('Tukar Guling'), @@ -83,13 +125,24 @@ class StockReturnPicking(models.TransientModel): 'context': context, } + class ReturnPickingLine(models.TransientModel): _inherit = 'stock.return.picking.line' @api.onchange('quantity') def _onchange_quantity(self): + """Validate quantity against done quantity""" for rec in self: - qty_done = rec.move_id.quantity_done - - if rec.quantity > qty_done: - raise UserError(f"Quantity yang Anda masukkan tidak boleh melebihi quantity done yaitu: {qty_done}")
\ No newline at end of file + if rec.move_id and rec.quantity > 0: + # Get quantity done from the move + qty_done = rec.move_id.quantity_done + + # If quantity_done is 0, use product_uom_qty as fallback + if qty_done == 0: + qty_done = rec.move_id.product_uom_qty + + if rec.quantity > qty_done: + raise UserError( + _("Quantity yang Anda masukkan (%.2f) tidak boleh melebihi quantity done yaitu: %.2f untuk produk %s") + % (rec.quantity, qty_done, rec.product_id.name) + )
\ No newline at end of file diff --git a/indoteknik_custom/models/tukar_guling.py b/indoteknik_custom/models/tukar_guling.py index 7bcf5e80..bdd2a2f5 100644 --- a/indoteknik_custom/models/tukar_guling.py +++ b/indoteknik_custom/models/tukar_guling.py @@ -11,6 +11,11 @@ class TukarGuling(models.Model): _order = 'date desc, id desc' _rec_name = 'name' + picking_ids = fields.One2many( + 'stock.picking', + 'tukar_guling_id', + string='Transfers') + origin = fields.Char(string='Origin SO') real_shipping_id = fields.Many2one('res.partner', string='Shipping Address') @@ -44,7 +49,16 @@ class TukarGuling(models.Model): def _onchange_operations(self): """Auto-populate lines ketika operations dipilih""" if self.operations: - # Clear existing lines + from_return_picking = self.env.context.get('from_return_picking', False) or \ + self.env.context.get('default_line_ids', False) + + if self.line_ids and from_return_picking: + # 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)] # Set origin dari operations @@ -91,10 +105,14 @@ class TukarGuling(models.Model): else: _logger.info("No lines created - no valid moves found") else: - # Clear lines jika operations dikosongkan - self.line_ids = [(5, 0, 0)] - self.origin = False + # 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) + if not from_return_picking: + self.line_ids = [(5, 0, 0)] + + self.origin = False def action_populate_lines(self): """Manual button untuk populate lines - sebagai alternatif""" self.ensure_one() @@ -272,113 +290,115 @@ class TukarGuling(models.Model): self.state = 'cancel' def _create_pickings(self): - if not self.operations: - raise UserError("BU/Out harus diisi terlebih dahulu.") - - origin_so = self.operations.origin - if not origin_so: - raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") - - # Cari DO dari SO - get_group_id = self.env['stock.picking'].search([ - ('origin', '=', origin_so), - ], limit=1) - - if not get_group_id: - raise UserError(f"Delivery Order dari SO {origin_so} tidak ditemukan.") - - group_id = get_group_id.group_id.id if get_group_id.group_id else False - - Picking = self.env['stock.picking'] - srt_type = self.env['stock.picking.type'].search([ - ('sequence_code', '=', 'SRT') - ], limit=1) - - ort_type = self.env['stock.picking.type'].search([ - ('sequence_code', '=', 'ORT') - ], limit=1) - - # Lokasi - location_dest_id = srt_type.default_location_dest_id.id - location_dest_id_ort = ort_type.default_location_dest_id.id - location_customer = self.operations.location_dest_id - - # 1. BU/SRT: retur dari operations - srt_picking = Picking.create({ - 'partner_id': self.operations.partner_id.id, - 'real_shipping_id': self.operations.real_shipping_id.id, - 'picking_type_id': srt_type.id, - 'location_id': location_customer.id, - 'location_dest_id': location_dest_id, - 'origin': f"Retur {self.operations.name}", - 'tukar_guling_id': self.id, - 'group_id': group_id, - 'move_ids_without_package': [ - (0, 0, { - 'name': line.name or line.product_id.name, - 'product_id': line.product_id.id, - 'product_uom_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'location_id': location_customer.id, - 'location_dest_id': location_dest_id, - 'group_id': group_id, - }) for line in self.line_ids - ] - }) - srt_picking.action_confirm() - - # 2. Cari BU/PICK dari SO yang sama - origin_so = self.operations.origin - if not origin_so: - raise UserError("BU/OUT tidak memiliki origin (SO), tidak bisa cari BU/PICK.") - - pick = Picking.search([ - ('origin', '=', origin_so), - ('picking_type_id.code', '=', 'internal') - ], limit=1) - - if not pick: - raise UserError(f"BU/PICK dengan origin {origin_so} tidak ditemukan.") - - # 3. BU/ORT: retur dari BU/PICK - ort_picking = Picking.create({ - 'partner_id': self.operations.partner_id.id, - 'real_shipping_id': self.operations.real_shipping_id.id, - 'picking_type_id': ort_type.id, - 'location_id': location_dest_id, - 'location_dest_id': location_dest_id_ort, - 'origin': f"Retur {pick.name}", - 'tukar_guling_id': self.id, - 'group_id': group_id, - 'move_ids_without_package': [ - (0, 0, { - 'name': line.name or line.product_id.name, - 'product_id': line.product_id.id, - 'product_uom_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'location_id': location_dest_id, - 'location_dest_id': location_dest_id_ort, - 'group_id': group_id, - }) for line in self.line_ids - ] - }) - for line in self.line_ids: - move = ort_picking.move_ids_without_package.filtered( - lambda m: m.product_id == line.product_id - )[:1] - - if move: - self.env['stock.move.line'].create({ - 'move_id': move.id, - 'picking_id': ort_picking.id, - 'product_id': line.product_id.id, - 'product_uom_id': line.product_uom.id, - 'qty_done': line.product_uom_qty, # Ambil dari return.picking.line.quantity - 'location_id': location_customer.id, - 'location_dest_id': location_dest_id, + for record in self: + if not record.operations: + raise UserError("BU/OUT dari field operations tidak ditemukan.") + + operation_picking = record.operations + + # 1. Cari semua picking DONE berdasarkan origin SO + related_pickings = self.env['stock.picking'].search([ + ('origin', '=', record.origin), + ('state', '=', 'done'), + ]) + if not related_pickings: + raise UserError("Tidak ditemukan BU/PICK atau BU/OUT dari SO: %s" % record.origin + "Atau masih belum Done") + + # 2. Filter berdasarkan tipe picking + bu_pick_to_return = related_pickings.filtered(lambda p: p.picking_type_id.id == 30) # BU/PICK + bu_out_to_return = related_pickings.filtered(lambda p: p.picking_type_id.id == 29) # BU/OUT + + 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 = [] + + # Lokasi default untuk retur + bu_out_type = self.env['stock.picking.type'].browse(73) + bu_stock_type = self.env['stock.picking.type'].browse(74) + + bu_out = bu_out_type.default_location_src_id.id + bu_stock = bu_out_type.default_location_dest_id.id + + if not bu_out or not bu_stock: + raise UserError("salahwoi") + + partner_location = self.env['stock.location'].browse(2) + if not partner_location: + raise UserError("Lokasi partner (real_shipping_id) tidak ditemukan pada BU/OUT utama.") + + # Fungsi membuat retur dari picking tertentu + def _create_return_from_picking(picking): + grup = self.env['stock.picking'].search([('origin', '=', self.operations.origin)]) + # Tentukan lokasi berdasarkan jenis picking + if picking.picking_type_id.id == 29: # BU/OUT → BU/SRT + default_location_id = partner_location.id + default_location_dest_id = bu_out + elif picking.picking_type_id.id == 30: # BU/PICK → BU/ORT + default_location_id = bu_out + default_location_dest_id = bu_stock + else: + return None + + return_context = dict(self.env.context) + return_context.update({ + 'active_id': picking.id, + 'default_location_id': default_location_id, + 'default_location_dest_id': default_location_dest_id, + 'from_ui': False, }) - ort_picking.action_confirm() - ort_picking.action_assign() + + return_wizard = self.env['stock.return.picking'].with_context(return_context).create({ + 'picking_id': picking.id, + 'location_id': default_location_id, + }) + + # Buat return lines + return_lines = [] + for move in picking.move_lines: + if move.quantity_done > 0: + return_lines.append((0, 0, { + 'product_id': move.product_id.id, + 'quantity': move.quantity_done, + 'move_id': move.id, + })) + if not return_lines: + return None + + return_wizard.product_return_moves = return_lines + + _logger.info("Creating return for picking %s", picking.name) + _logger.info("Default location src: %s", default_location_id) + _logger.info("Default location dest: %s", default_location_dest_id) + _logger.info("Move lines: %s", picking.move_lines) + return_vals = return_wizard.create_returns() + return_id = return_vals.get('res_id') + + if not return_id: + raise UserError("Retur gagal dibuat. Hasil create_returns: %s" % str(return_vals)) + + picking_obj = self.env['stock.picking'].browse(return_id) + for p in picking_obj: + p.group_id = self.operations.group_id.id + p.origin_tukar_guling_id = record.id + + return picking_obj.name + + # Buat return dari BU/PICK + for picking in bu_pick_to_return: + name = _create_return_from_picking(picking) + if name: + created_returns.append(name) + + # Buat return dari BU/OUT + for picking in bu_out_to_return: + name = _create_return_from_picking(picking) + if name: + created_returns.append(name) + + if not created_returns: + raise UserError("wkwkwk") + class TukarGulingLine(models.Model): _name = 'tukar.guling.line' @@ -418,4 +438,4 @@ class TukarGulingLine(models.Model): class StockPicking(models.Model): _inherit = 'stock.picking' - origin_tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref')
\ No newline at end of file + tukar_guling_id = fields.Many2one('tukar.guling', string='Tukar Guling Ref')
\ No newline at end of file diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml index 765c913d..942f085d 100644 --- a/indoteknik_custom/views/tukar_guling.xml +++ b/indoteknik_custom/views/tukar_guling.xml @@ -73,7 +73,6 @@ <h1> <field name="name" readonly="1" class="oe_inline"/> </h1> - <hr/> </div> <group> <group> |
