# Copyright 2020 Tecnativa - Ernesto Tejeda # Copyright 2023 Michael Tietz (MT Software) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import _, api, fields, models from odoo.exceptions import ValidationError from odoo.tools import float_compare class StockMove(models.Model): _inherit = "stock.move" # RMAs that were created from the delivery move rma_ids = fields.One2many( comodel_name="rma", inverse_name="move_id", string="RMAs", copy=False, ) # RMAs linked to the incoming movement from client rma_receiver_ids = fields.One2many( comodel_name="rma", inverse_name="reception_move_id", string="RMA receivers", copy=False, ) # RMA that creates the out move rma_id = fields.Many2one( comodel_name="rma", string="RMA return", copy=False, index=True ) def unlink(self): # A stock user could have no RMA permissions, so the ids wouldn't # be accessible due to record rules. rma_receiver = self.sudo().rma_receiver_ids rma = self.sudo().rma_id res = super().unlink() rma_receiver.filtered(lambda x: x.state != "cancelled").write( {"state": "draft"} ) rma.update_received_state() rma.update_replaced_state() return res def _action_cancel(self): res = super()._action_cancel() # A stock user could have no RMA permissions, so the ids wouldn't # be accessible due to record rules. cancelled_moves = self.filtered(lambda r: r.state == "cancel").sudo() cancelled_moves.mapped("rma_receiver_ids").write({"state": "draft"}) cancelled_moves.mapped("rma_id").update_received_state() cancelled_moves.mapped("rma_id").update_replaced_state() return res def _action_done(self, cancel_backorder=False): """Avoids to validate stock.move with less quantity than the quantity in the linked receiver RMA. It also set the appropriated linked RMA to 'received' or 'delivered'. """ for move in self.filtered(lambda r: r.state not in ("done", "cancel")): rma_receiver = move.sudo().rma_receiver_ids qty_prec = self.env["decimal.precision"].precision_get( "Product Unit of Measure" ) if ( rma_receiver and float_compare( move.quantity_done, rma_receiver.product_uom_qty, precision_digits=qty_prec, ) != 0 ): raise ValidationError( _( "The quantity done for the product '%(id)s' must " "be equal to its initial demand because the " "stock move is linked to an RMA (%(name)s)." ) % ( { "id": move.product_id.name, "name": move.rma_receiver_ids.name, } ) ) res = super()._action_done(cancel_backorder=cancel_backorder) move_done = self.filtered(lambda r: r.state == "done").sudo() # Set RMAs as received. We sudo so we can grant the operation even # if the stock user has no RMA permissions. to_be_received = ( move_done.sudo() .mapped("rma_receiver_ids") .filtered(lambda r: r.state == "confirmed") ) to_be_received.update_received_state_on_reception() # Set RMAs as delivered move_done.mapped("rma_id").update_replaced_state() move_done.mapped("rma_id").update_returned_state() return res @api.model def _prepare_merge_moves_distinct_fields(self): """The main use is that launched delivery RMAs doesn't merge two moves if they are linked to a different RMAs. """ return super()._prepare_merge_moves_distinct_fields() + [ "rma_id", "rma_receiver_ids", ] def _prepare_move_split_vals(self, qty): """Intended to the backport of picking linked to RMAs propagates the RMA link id. """ res = super()._prepare_move_split_vals(qty) res["rma_id"] = self.sudo().rma_id.id return res def _prepare_procurement_values(self): res = super()._prepare_procurement_values() if self.rma_id: res["rma_id"] = self.rma_id.id return res class StockRule(models.Model): _inherit = "stock.rule" def _get_custom_move_fields(self): move_fields = super()._get_custom_move_fields() move_fields += [ "rma_id", "origin_returned_move_id", "move_orig_ids", "rma_receiver_ids", ] return move_fields