diff --git a/rma/data/rma_sequence.xml b/rma/data/rma_sequence.xml index 1162e9ad..e59ec85f 100644 --- a/rma/data/rma_sequence.xml +++ b/rma/data/rma_sequence.xml @@ -4,7 +4,7 @@ Customer RMA sequence rma.order.customer 5 - RMA/%(year)s/ + RMAG/%(year)s/ @@ -13,7 +13,7 @@ Supplier RMA sequence rma.order.supplier 5 - RTV/%(year)s/ + RTVG/%(year)s/ @@ -22,7 +22,7 @@ Customer RMA Line sequence rma.order.line.customer 5 - RMAL/%(year)s/ + RMA/%(year)s/ @@ -31,7 +31,7 @@ Supplier RMA Line sequence rma.order.line.supplier 5 - RTVL/%(year)s/ + RTV/%(year)s/ diff --git a/rma/models/procurement.py b/rma/models/procurement.py index c8cd39db..eb4a46b7 100644 --- a/rma/models/procurement.py +++ b/rma/models/procurement.py @@ -8,7 +8,10 @@ from openerp import api, fields, models class ProcurementOrder(models.Model): _inherit = 'procurement.order' - rma_line_id = fields.Many2one('rma.order.line', 'RMA', ondelete="set null") + rma_line_id = fields.Many2one( + comodel_name='rma.order.line', string='RMA line', + ondelete="set null", + ) @api.model def _run_move_create(self, procurement): @@ -26,4 +29,11 @@ class ProcurementOrder(models.Model): class ProcurementGroup(models.Model): _inherit = 'procurement.group' - rma_id = fields.Many2one('rma.order', 'RMA', ondelete="set null") + rma_id = fields.Many2one( + comodel_name='rma.order', string='RMA', + ondelete="set null", + ) + rma_line_id = fields.Many2one( + comodel_name='rma.order.line', string='RMA line', + ondelete="set null", + ) diff --git a/rma/models/rma_order.py b/rma/models/rma_order.py index ae1afc4d..39130e40 100644 --- a/rma/models/rma_order.py +++ b/rma/models/rma_order.py @@ -47,30 +47,17 @@ class RmaOrder(models.Model): return datetime.now() name = fields.Char( - string='Order Number', index=True, readonly=True, - states={'progress': [('readonly', False)]}, copy=False) + string='Group Number', index=True, copy=False) type = fields.Selection( [('customer', 'Customer'), ('supplier', 'Supplier')], string="Type", required=True, default=_get_default_type, readonly=True) reference = fields.Char(string='Partner Reference', help="The partner reference of this RMA order.") - comment = fields.Text('Additional Information', readonly=True, states={ - 'draft': [('readonly', False)]}) - - state = fields.Selection([('draft', 'Draft'), ('to_approve', 'To Approve'), - ('approved', 'Approved'), - ('done', 'Done')], string='State', index=True, - default='draft') + comment = fields.Text('Additional Information') date_rma = fields.Datetime(string='Order Date', index=True, default=_default_date_rma) - partner_id = fields.Many2one('res.partner', string='Partner', - required=True, readonly=True, - states={'draft': [('readonly', False)]}) - assigned_to = fields.Many2one('res.users', 'Assigned to', - track_visibility='onchange') - requested_by = fields.Many2one('res.users', 'Requested by', - track_visibility='onchange', - default=lambda self: self.env.user) + partner_id = fields.Many2one( + comodel_name='res.partner', string='Partner', required=True) rma_line_ids = fields.One2many('rma.order.line', 'rma_id', string='RMA lines') in_shipment_count = fields.Integer(compute=_compute_in_shipment_count, @@ -87,7 +74,8 @@ class RmaOrder(models.Model): @api.model def create(self, vals): - if self.env.context.get('supplier'): + if (self.env.context.get('supplier') or + vals.get('type') == 'supplier'): vals['name'] = self.env['ir.sequence'].next_by_code( 'rma.order.supplier') else: @@ -149,32 +137,6 @@ class RmaOrder(models.Model): result['res_id'] = shipments[0] return result - @api.multi - def action_rma_to_approve(self): - self.write({'state': 'to_approve'}) - for rec in self: - pols = rec.mapped('rma_line_ids.product_id.rma_approval_policy') - if not any(x != 'one_step' for x in pols): - rec.write({'assigned_to': self.env.uid}) - rec.action_rma_approve() - return True - - @api.multi - def action_rma_draft(self): - self.write({'state': 'draft'}) - return True - - @api.multi - def action_rma_approve(self): - # pass the supplier address in case this is a customer RMA - self.write({'state': 'approved'}) - return True - - @api.multi - def action_rma_done(self): - self.write({'state': 'done'}) - return True - @api.multi def _get_valid_lines(self): """:return: A recordset of rma lines. diff --git a/rma/models/rma_order_line.py b/rma/models/rma_order_line.py index 6fddca53..acecfb06 100644 --- a/rma/models/rma_order_line.py +++ b/rma/models/rma_order_line.py @@ -2,7 +2,8 @@ # © 2017 Eficent Business and IT Consulting Services S.L. # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) -from openerp import api, fields, models +from openerp import api, fields, models, _ +from openerp.exceptions import ValidationError, UserError from openerp.addons import decimal_precision as dp import operator ops = {'=': operator.eq, @@ -11,7 +12,13 @@ ops = {'=': operator.eq, class RmaOrderLine(models.Model): _name = "rma.order.line" - _rec_name = "rma_id" + _inherit = ['mail.thread'] + + @api.model + def _get_default_type(self): + if 'supplier' in self.env.context: + return "supplier" + return "customer" @api.model def _default_warehouse_id(self): @@ -41,7 +48,8 @@ class RmaOrderLine(models.Model): def _compute_in_shipment_count(self): for line in self: moves = line.procurement_ids.mapped('move_ids').filtered( - lambda m: m.location_dest_id.usage == 'internal') + lambda m: m.location_dest_id.usage == 'internal' and + m.state != 'cancel') pickings = moves.mapped('picking_id') line.in_shipment_count = len(pickings) @@ -49,7 +57,8 @@ class RmaOrderLine(models.Model): def _compute_out_shipment_count(self): for line in self: moves = line.procurement_ids.mapped('move_ids').filtered( - lambda m: m.location_dest_id.usage != 'internal') + lambda m: m.location_dest_id.usage != 'internal' and + m.state != 'cancel') pickings = moves.mapped('picking_id') line.out_shipment_count = len(pickings) @@ -131,7 +140,6 @@ class RmaOrderLine(models.Model): @api.multi @api.depends('customer_to_supplier', 'supplier_rma_line_ids', - 'supplier_rma_line_ids.rma_id.state', 'move_ids', 'move_ids.state', 'qty_received', 'receipt_policy', 'product_qty', 'type') def _compute_qty_supplier_rma(self): @@ -147,33 +155,58 @@ class RmaOrderLine(models.Model): lambda p: p.state == 'exception')) delivery_address_id = fields.Many2one( - 'res.partner', string='Partner delivery address', + comodel_name='res.partner', string='Partner delivery address', default=_default_delivery_address, - help="This address will be used to " - "deliver repaired or replacement products.") - - rma_id = fields.Many2one('rma.order', string='RMA', - ondelete='cascade', required=True) - name = fields.Char(string='Reference', required=True, default='/', - help='Add here the supplier RMA #. Otherwise an ' - 'internal code is assigned.') + readonly=True, states={'draft': [('readonly', False)]}, + help="This address will be used to deliver repaired or replacement " + "products.", + ) + rma_id = fields.Many2one( + comodel_name='rma.order', string='RMA Group', + track_visibility='onchange', readonly=True, + ) + name = fields.Char( + string='Reference', required=True, default='/', + readonly=True, states={'draft': [('readonly', False)]}, + help='Add here the supplier RMA #. Otherwise an internal code is' + ' assigned.', + ) description = fields.Text(string='Description') - origin = fields.Char(string='Source Document', - help="Reference of the document that produced " - "this rma.") - state = fields.Selection(related='rma_id.state') + origin = fields.Char( + string='Source Document', + readonly=True, states={'draft': [('readonly', False)]}, + help="Reference of the document that produced this rma.") + state = fields.Selection( + selection=[('draft', 'Draft'), + ('to_approve', 'To Approve'), + ('approved', 'Approved'), + ('done', 'Done')], + string='State', default='draft', + track_visibility='onchange', + ) operation_id = fields.Many2one( - comodel_name="rma.operation", string="Operation") - - assigned_to = fields.Many2one('res.users', related='rma_id.assigned_to') - requested_by = fields.Many2one('res.users', related='rma_id.requested_by') - partner_id = fields.Many2one('res.partner', related='rma_id.partner_id', - store=True) - sequence = fields.Integer(default=10, - help="Gives the sequence of this line " - "when displaying the rma.") - product_id = fields.Many2one('product.product', string='Product', - ondelete='restrict', required=-True) + comodel_name="rma.operation", string="Operation", + readonly=True, states={'draft': [('readonly', False)]}, + ) + assigned_to = fields.Many2one( + comodel_name='res.users', track_visibility='onchange', + ) + requested_by = fields.Many2one( + comodel_name='res.users', track_visibility='onchange', + ) + partner_id = fields.Many2one( + comodel_name='res.partner', required=True, store=True, + track_visibility='onchange', + readonly=True, states={'draft': [('readonly', False)]}, + ) + sequence = fields.Integer( + default=10, + help="Gives the sequence of this line when displaying the rma.") + product_id = fields.Many2one( + comodel_name='product.product', string='Product', + ondelete='restrict', required=True, + readonly=True, states={'draft': [('readonly', False)]}, + ) product_tracking = fields.Selection(related="product_id.tracking") lot_id = fields.Many2one( comodel_name="stock.production.lot", string="Lot/Serial Number", @@ -181,14 +214,18 @@ class RmaOrderLine(models.Model): ) product_qty = fields.Float( string='Ordered Qty', copy=False, default=1.0, - digits=dp.get_precision('Product Unit of Measure')) - uom_id = fields.Many2one('product.uom', string='Unit of Measure', - required=True) - price_unit = fields.Monetary(string='Price Unit', readonly=False, - states={'approved': [('readonly', True)], - 'done': [('readonly', True)], - 'to_approve': [('readonly', True)]}) - + digits=dp.get_precision('Product Unit of Measure'), + readonly=True, states={'draft': [('readonly', False)]}, + ) + uom_id = fields.Many2one( + comodel_name='product.uom', string='Unit of Measure', + required=True, + readonly=True, states={'draft': [('readonly', False)]}, + ) + price_unit = fields.Monetary( + string='Price Unit', + readonly=True, states={'draft': [('readonly', False)]}, + ) procurement_count = fields.Integer(compute=_compute_procurement_count, string='# of Procurements', copy=False, default=0) @@ -199,48 +236,75 @@ class RmaOrderLine(models.Model): move_ids = fields.One2many('stock.move', 'rma_line_id', string='Stock Moves', readonly=True, copy=False) - reference_move_id = fields.Many2one(comodel_name='stock.move', - string='Originating stock move', - readonly=True, copy=False) + reference_move_id = fields.Many2one( + comodel_name='stock.move', string='Originating Stock Move', + copy=False, + readonly=True, states={'draft': [('readonly', False)]}, + ) procurement_ids = fields.One2many('procurement.order', 'rma_line_id', string='Procurements', readonly=True, states={'draft': [('readonly', False)]}, copy=False) currency_id = fields.Many2one('res.currency', string="Currency") - company_id = fields.Many2one('res.company', string='Company', - related='rma_id.company_id', store=True) - type = fields.Selection(related='rma_id.type') + company_id = fields.Many2one( + comodel_name='res.company', string='Company', required=True, + default=lambda self: self.env.user.company_id) + type = fields.Selection( + selection=[('customer', 'Customer'), ('supplier', 'Supplier')], + string="Type", required=True, default=_get_default_type, + readonly=True, + ) customer_to_supplier = fields.Boolean( - 'The customer will send to the supplier') + 'The customer will send to the supplier', + readonly=True, states={'draft': [('readonly', False)]}, + ) supplier_to_customer = fields.Boolean( - 'The supplier will send to the customer') + 'The supplier will send to the customer', + readonly=True, states={'draft': [('readonly', False)]}, + ) receipt_policy = fields.Selection([ ('no', 'Not required'), ('ordered', 'Based on Ordered Quantities'), ('delivered', 'Based on Delivered Quantities')], - required=True, string="Receipts Policy") + required=True, string="Receipts Policy", + readonly=True, states={'draft': [('readonly', False)]}, + ) delivery_policy = fields.Selection([ ('no', 'Not required'), ('ordered', 'Based on Ordered Quantities'), ('received', 'Based on Received Quantities')], required=True, - string="Delivery Policy") + string="Delivery Policy", + readonly=True, states={'draft': [('readonly', False)]}, + ) in_route_id = fields.Many2one( 'stock.location.route', string='Inbound Route', required=True, - domain=[('rma_selectable', '=', True)]) + domain=[('rma_selectable', '=', True)], + readonly=True, states={'draft': [('readonly', False)]}, + ) out_route_id = fields.Many2one( 'stock.location.route', string='Outbound Route', required=True, - domain=[('rma_selectable', '=', True)]) - in_warehouse_id = fields.Many2one('stock.warehouse', - string='Inbound Warehouse', - required=True, - default=_default_warehouse_id) - out_warehouse_id = fields.Many2one('stock.warehouse', - string='Outbound Warehouse', - required=True, - default=_default_warehouse_id) + domain=[('rma_selectable', '=', True)], + readonly=True, states={'draft': [('readonly', False)]}, + ) + in_warehouse_id = fields.Many2one( + comodel_name='stock.warehouse', + string='Inbound Warehouse', + required=True, + readonly=True, states={'draft': [('readonly', False)]}, + default=_default_warehouse_id, + ) + out_warehouse_id = fields.Many2one( + comodel_name='stock.warehouse', string='Outbound Warehouse', + required=True, + readonly=True, states={'draft': [('readonly', False)]}, + default=_default_warehouse_id, + ) location_id = fields.Many2one( - 'stock.location', 'Send To This Company Location', required=True, - default=_default_location_id) + comodel_name='stock.location', string='Send To This Company Location', + required=True, + readonly=True, states={'draft': [('readonly', False)]}, + default=_default_location_id, + ) customer_rma_id = fields.Many2one( 'rma.order.line', string='Customer RMA line', ondelete='cascade') supplier_rma_line_ids = fields.One2many( @@ -290,14 +354,119 @@ class RmaOrderLine(models.Model): readonly=True, compute=_compute_qty_supplier_rma, store=True) + @api.multi + def _prepare_rma_line_from_stock_move(self, sm, lot=False): + if not self.type: + self.type = self._get_default_type() + if self.type == 'customer': + operation = sm.product_id.rma_customer_operation_id or \ + sm.product_id.categ_id.rma_customer_operation_id + else: + operation = sm.product_id.rma_supplier_operation_id or \ + sm.product_id.categ_id.rma_supplier_operation_id + + if not operation: + operation = self.env['rma.operation'].search( + [('type', '=', self.type)], limit=1) + if not operation: + raise ValidationError("Please define an operation first.") + + if not operation.in_route_id or not operation.out_route_id: + route = self.env['stock.location.route'].search( + [('rma_selectable', '=', True)], limit=1) + if not route: + raise ValidationError("Please define an RMA route.") + + if not operation.in_warehouse_id or not operation.out_warehouse_id: + warehouse = self.env['stock.warehouse'].search( + [('company_id', '=', self.company_id.id), + ('lot_rma_id', '!=', False)], limit=1) + if not warehouse: + raise ValidationError( + "Please define a warehouse with a default RMA location.") + + data = { + 'product_id': sm.product_id.id, + 'lot_id': lot and lot.id or False, + 'origin': sm.picking_id.name or sm.name, + 'uom_id': sm.product_uom.id, + 'product_qty': sm.product_uom_qty, + 'delivery_address_id': sm.picking_id.partner_id.id, + 'operation_id': operation.id, + 'receipt_policy': operation.receipt_policy, + 'delivery_policy': operation.delivery_policy, + 'in_warehouse_id': operation.in_warehouse_id.id or warehouse.id, + 'out_warehouse_id': operation.out_warehouse_id.id or warehouse.id, + 'in_route_id': operation.in_route_id.id or route.id, + 'out_route_id': operation.out_route_id.id or route.id, + 'location_id': (operation.location_id.id or + operation.in_warehouse_id.lot_rma_id.id or + warehouse.lot_rma_id.id) + } + return data + + @api.multi + @api.onchange('reference_move_id') + def _onchange_reference_move_id(self): + self.ensure_one() + sm = self.reference_move_id + if not sm: + return + if sm.lot_ids: + if len(sm.lot_ids) > 1: + raise UserError(_('To manage lots use RMA groups.')) + else: + data = self._prepare_rma_line_from_stock_move( + sm, lot=sm.lot_ids[0]) + self.update(data) + else: + data = self._prepare_rma_line_from_stock_move( + sm, lot=False) + self.update(data) + self._remove_other_data_origin('reference_move_id') + + @api.multi + def _remove_other_data_origin(self, exception): + if not exception == 'reference_move_id': + self.reference_move_id = False + return True + + @api.multi + def action_rma_to_approve(self): + self.write({'state': 'to_approve'}) + for rec in self: + if rec.product_id.rma_approval_policy == 'one_step': + rec.write({'assigned_to': self.env.uid}) + rec.action_rma_approve() + return True + + @api.multi + def action_rma_draft(self): + if self.in_shipment_count or self.out_shipment_count: + raise UserError(_( + "You cannot reset to draft a RMA with related pickings.")) + self.write({'state': 'draft'}) + return True + + @api.multi + def action_rma_approve(self): + self.write({'state': 'approved'}) + return True + + @api.multi + def action_rma_done(self): + self.write({'state': 'done'}) + return True + @api.model def create(self, vals): - if self.env.context.get('supplier'): - vals['name'] = self.env['ir.sequence'].next_by_code( - 'rma.order.line.supplier') - else: - vals['name'] = self.env['ir.sequence'].next_by_code( - 'rma.order.line.customer') + if not vals.get('name') or vals.get('name') == '/': + if self.env.context.get('supplier'): + vals['name'] = self.env['ir.sequence'].next_by_code( + 'rma.order.line.supplier') + else: + vals['name'] = self.env['ir.sequence'].next_by_code( + 'rma.order.line.customer') return super(RmaOrderLine, self).create(vals) @api.onchange('product_id') diff --git a/rma/views/rma_order_line_view.xml b/rma/views/rma_order_line_view.xml index edabd55f..002a3d91 100644 --- a/rma/views/rma_order_line_view.xml +++ b/rma/views/rma_order_line_view.xml @@ -5,20 +5,20 @@ rma.order.line.tree rma.order.line - - - + + - + + @@ -27,19 +27,19 @@ rma.order.line.supplier.tree rma.order.line - - - + + - + + @@ -48,8 +48,25 @@ rma.order.line.supplier.form rma.order.line - - + + + + + + + + - + + + + + + @@ -96,12 +124,16 @@ - + + + + + @@ -147,14 +179,9 @@ - + - - - - - - + @@ -163,6 +190,10 @@ + + + + @@ -172,7 +203,24 @@ rma.order.line - + + + + + + + - + + + + + + @@ -229,6 +288,10 @@ + + + + @@ -279,14 +342,8 @@ - + - - - - - - @@ -295,6 +352,10 @@ + + + + @@ -304,6 +365,7 @@ rma.order.line + @@ -313,6 +375,8 @@ + - Customer RMA Lines + Customer RMA rma.order.line - [('state','in', ['approved', 'done']), - ('type','=', 'customer') - ] + [('type','=', 'customer')] {"search_default_assigned_to":uid} form tree,form @@ -341,10 +403,9 @@ - Supplier RMA Lines + Supplier RMA rma.order.line - [('state','in', ['approved', 'done']), - ('type','=', 'supplier')] + [('type','=', 'supplier')] {"search_default_assigned_to":uid, "supplier":1} form tree,form diff --git a/rma/views/rma_order_view.xml b/rma/views/rma_order_view.xml index b5a89702..0cdf7adb 100644 --- a/rma/views/rma_order_view.xml +++ b/rma/views/rma_order_view.xml @@ -6,16 +6,11 @@ rma.order.tree rma.order - + - - - - @@ -24,17 +19,12 @@ rma.order.supplier.tree rma.order - + - - - - @@ -44,25 +34,7 @@ rma.order - - - - - - - - + - - - @@ -145,6 +110,7 @@ + @@ -162,26 +128,7 @@ rma.order - - - - - - - - + - - - @@ -258,6 +198,7 @@ + @@ -278,10 +219,7 @@ - - - @@ -293,23 +231,21 @@ - Customer RMA + Customer RMA Group rma.order form [('type','=', 'customer')] - {"search_default_assigned_to":uid, - 'customer':1} + {'customer':1} tree,form - Supplier RMA + Supplier RMA Group rma.order form [('type','=', 'supplier')] - {"search_default_assigned_to":uid, - 'supplier':1} + {'supplier':1} tree,form @@ -343,14 +279,14 @@ diff --git a/rma/wizards/rma_add_stock_move_view.xml b/rma/wizards/rma_add_stock_move_view.xml index 58870e2b..2bad25ed 100644 --- a/rma/wizards/rma_add_stock_move_view.xml +++ b/rma/wizards/rma_add_stock_move_view.xml @@ -61,12 +61,11 @@ rma.order - + - + type="action"/> + @@ -82,7 +81,7 @@ + domain="[('picking_id.partner_id', '=', partner_id), ('location_id.usage', '=', 'supplier')]"/>