diff --git a/rma/README.rst b/rma/README.rst
index 4fe6cf07..19617464 100644
--- a/rma/README.rst
+++ b/rma/README.rst
@@ -86,6 +86,14 @@ Create an RMA:
Order".
#. Go back to the RMA. Set the RMA to done if not further action is required.
+Known issues and Roadmap
+========================
+
+* Picking operations report in customer RMA dropshipping case is showing
+ "Vendor Address" while it should be "Customer Address".
+* Dropshipping always counted as a delivery on the smart buttons.
+* Uninstall hook.
+* Constraints instead of required fields on rma.order.line.
Bug Tracker
===========
diff --git a/rma/__manifest__.py b/rma/__manifest__.py
index 66256b0e..7335d0d6 100644
--- a/rma/__manifest__.py
+++ b/rma/__manifest__.py
@@ -3,7 +3,7 @@
{
'name': 'RMA (Return Merchandise Authorization)',
- 'version': '11.0.1.0.0',
+ 'version': '11.0.2.0.0',
'license': 'LGPL-3',
'category': 'RMA',
'summary': 'Introduces the return merchandise authorization (RMA) process '
diff --git a/rma/data/rma_operation.xml b/rma/data/rma_operation.xml
index 27e9be66..9d1fc02f 100755
--- a/rma/data/rma_operation.xml
+++ b/rma/data/rma_operation.xml
@@ -1,8 +1,8 @@
-
+
Replace After Receive
- RPLC
+ RPL-C
ordered
received
customer
@@ -12,7 +12,7 @@
Replace
- RPLS
+ RPL-S
ordered
ordered
supplier
@@ -21,25 +21,25 @@
- Drop Ship - Replace deliver to vendor
- DSRPLB
+ Dropship - Deliver to vendor
+ DS-RPL-C
ordered
no
customer
- True
-
+
+
- Drop Ship - Replace deliver to customer
- DSRPC
- ordered
- no
+ Dropship - Deliver to customer
+ DS-RPL-S
+ no
+ ordered
supplier
- True
-
+
+
diff --git a/rma/data/stock_data.xml b/rma/data/stock_data.xml
index ee943875..b9f100bd 100755
--- a/rma/data/stock_data.xml
+++ b/rma/data/stock_data.xml
@@ -188,7 +188,6 @@
Customer → Supplier
move
-
make_to_stock
@@ -200,7 +199,6 @@
Supplier → Customer
move
-
make_to_stock
diff --git a/rma/models/rma_operation.py b/rma/models/rma_operation.py
index 9e9c12d6..bee15958 100644
--- a/rma/models/rma_operation.py
+++ b/rma/models/rma_operation.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 Eficent Business and IT Consulting Services S.L.
+# Copyright 2017-18 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from odoo import api, fields, models
@@ -23,6 +23,14 @@ class RmaOperation(models.Model):
def _default_supplier_location_id(self):
return self.env.ref('stock.stock_location_suppliers') or False
+ @api.model
+ def _default_routes(self):
+ op_type = self.env.context.get('default_type')
+ if op_type == 'customer':
+ return self.env.ref('rma.route_rma_customer')
+ elif op_type == 'supplier':
+ return self.env.ref('rma.route_rma_supplier')
+
name = fields.Char('Description', required=True)
code = fields.Char('Code', required=True)
active = fields.Boolean(string='Active', default=True)
@@ -35,15 +43,21 @@ class RmaOperation(models.Model):
('received', 'Based on Received Quantities')],
string="Delivery Policy", default='no')
in_route_id = fields.Many2one(
- 'stock.location.route', string='Inbound Route',
- domain=[('rma_selectable', '=', True)])
+ comodel_name='stock.location.route', string='Inbound Route',
+ domain=[('rma_selectable', '=', True)],
+ default=_default_routes,
+ )
out_route_id = fields.Many2one(
- 'stock.location.route', string='Outbound Route',
- domain=[('rma_selectable', '=', True)])
+ comodel_name='stock.location.route', string='Outbound Route',
+ domain=[('rma_selectable', '=', True)],
+ default=_default_routes,
+ )
customer_to_supplier = fields.Boolean(
- 'The customer will send to the supplier', default=False)
+ string='The customer will send to the supplier',
+ )
supplier_to_customer = fields.Boolean(
- 'The supplier will send to the customer', default=False)
+ string='The supplier will send to the customer',
+ )
in_warehouse_id = fields.Many2one(
comodel_name='stock.warehouse', string='Inbound Warehouse',
default=_default_warehouse_id)
@@ -55,5 +69,7 @@ class RmaOperation(models.Model):
type = fields.Selection([
('customer', 'Customer'), ('supplier', 'Supplier')],
string="Used in RMA of this type", required=True)
- rma_line_ids = fields.One2many('rma.order.line', 'operation_id',
- 'RMA lines')
+ rma_line_ids = fields.One2many(
+ comodel_name='rma.order.line', inverse_name='operation_id',
+ string='RMA lines',
+ )
diff --git a/rma/models/rma_order_line.py b/rma/models/rma_order_line.py
index 58287ba7..fceeaa94 100644
--- a/rma/models/rma_order_line.py
+++ b/rma/models/rma_order_line.py
@@ -46,18 +46,26 @@ class RmaOrderLine(models.Model):
@api.multi
def _compute_in_shipment_count(self):
for line in self:
- moves = self.env['stock.move'].search([
- ('rma_line_id', '=', line.id)])
- line.in_shipment_count = len(moves.mapped('picking_id').filtered(
- lambda p: p.picking_type_code == 'incoming').ids)
+ picking_ids = []
+ for move in line.move_ids:
+ if move.location_dest_id.usage == 'internal':
+ picking_ids.append(move.picking_id.id)
+ else:
+ if line.customer_to_supplier:
+ picking_ids.append(move.picking_id.id)
+ shipments = list(set(picking_ids))
+ line.in_shipment_count = len(shipments)
@api.multi
def _compute_out_shipment_count(self):
+ picking_ids = []
for line in self:
- moves = self.env['stock.move'].search([
- ('rma_line_id', '=', line.id)])
- line.out_shipment_count = len(moves.mapped('picking_id').filtered(
- lambda p: p.picking_type_code == 'outgoing').ids)
+ for move in line.move_ids:
+ if move.location_dest_id.usage in ('supplier', 'customer'):
+ if not line.customer_to_supplier:
+ picking_ids.append(move.picking_id.id)
+ shipments = list(set(picking_ids))
+ line.out_shipment_count = len(shipments)
@api.multi
def _get_rma_move_qty(self, states, direction='in'):
@@ -82,9 +90,11 @@ class RmaOrderLine(models.Model):
for rec in self:
rec.qty_to_receive = 0.0
if rec.receipt_policy == 'ordered':
- rec.qty_to_receive = rec.product_qty - rec.qty_received
+ rec.qty_to_receive = \
+ rec.product_qty - rec.qty_incoming - rec.qty_received
elif rec.receipt_policy == 'delivered':
- rec.qty_to_receive = rec.qty_delivered - rec.qty_received
+ rec.qty_to_receive = \
+ rec.qty_delivered - rec.qty_incoming - rec.qty_received
@api.multi
@api.depends('move_ids', 'move_ids.state',
@@ -110,7 +120,10 @@ class RmaOrderLine(models.Model):
@api.depends('move_ids', 'move_ids.state', 'type')
def _compute_qty_received(self):
for rec in self:
- qty = rec._get_rma_move_qty('done', direction='in')
+ if rec.supplier_to_customer:
+ qty = rec._get_rma_move_qty('done', direction='out')
+ else:
+ qty = rec._get_rma_move_qty('done', direction='in')
rec.qty_received = qty
@api.multi
@@ -125,7 +138,10 @@ class RmaOrderLine(models.Model):
@api.depends('move_ids', 'move_ids.state', 'type')
def _compute_qty_delivered(self):
for rec in self:
- qty = rec._get_rma_move_qty('done', direction='out')
+ if rec.supplier_to_customer:
+ qty = rec._get_rma_move_qty('done', direction='in')
+ else:
+ qty = rec._get_rma_move_qty('done', direction='out')
rec.qty_delivered = qty
@api.model
@@ -140,9 +156,26 @@ class RmaOrderLine(models.Model):
'receipt_policy', 'product_qty', 'type')
def _compute_qty_supplier_rma(self):
for rec in self:
- qty = rec._get_supplier_rma_qty()
- rec.qty_to_supplier_rma = rec.product_qty - qty
- rec.qty_in_supplier_rma = qty
+ if rec.customer_to_supplier:
+ supplier_rma_qty = rec._get_supplier_rma_qty()
+ rec.qty_to_supplier_rma = rec.product_qty - supplier_rma_qty
+ rec.qty_in_supplier_rma = supplier_rma_qty
+ else:
+ rec.qty_to_supplier_rma = 0.0
+ rec.qty_in_supplier_rma = 0.0
+
+ @api.multi
+ def _compute_procurement_count(self):
+ for rec in self:
+ rec.procurement_count = len(rec.procurement_ids.filtered(
+ lambda p: p.state == 'exception'))
+
+ @api.multi
+ def _compute_rma_line_count(self):
+ for rec in self.filtered(lambda r: r.type == 'customer'):
+ rec.rma_line_count = len(rec.supplier_rma_line_ids)
+ for rec in self.filtered(lambda r: r.type == 'supplier'):
+ rec.rma_line_count = len(rec.customer_rma_id)
delivery_address_id = fields.Many2one(
comodel_name='res.partner', string='Partner delivery address',
@@ -180,9 +213,11 @@ class RmaOrderLine(models.Model):
)
assigned_to = fields.Many2one(
comodel_name='res.users', track_visibility='onchange',
+ default=lambda self: self.env.uid,
)
requested_by = fields.Many2one(
comodel_name='res.users', track_visibility='onchange',
+ default=lambda self: self.env.uid,
)
partner_id = fields.Many2one(
comodel_name='res.partner', required=True, store=True,
@@ -217,10 +252,12 @@ class RmaOrderLine(models.Model):
string='Price Unit',
readonly=True, states={'draft': [('readonly', False)]},
)
+ procurement_count = fields.Integer(compute=_compute_procurement_count,
+ string='# of Procurements', copy=False)
in_shipment_count = fields.Integer(compute=_compute_in_shipment_count,
- string='# of Shipments', default=0)
+ string='# of Shipments')
out_shipment_count = fields.Integer(compute=_compute_out_shipment_count,
- string='# of Deliveries', default=0)
+ string='# of Deliveries')
move_ids = fields.One2many('stock.move', 'rma_line_id',
string='Stock Moves', readonly=True,
copy=False)
@@ -293,11 +330,21 @@ class RmaOrderLine(models.Model):
'rma.order.line', string='Customer RMA line', ondelete='cascade')
supplier_rma_line_ids = fields.One2many(
'rma.order.line', 'customer_rma_id')
+ rma_line_count = fields.Integer(
+ compute='_compute_rma_line_count',
+ string='# of RMA lines associated',
+ )
supplier_address_id = fields.Many2one(
- 'res.partner', readonly=True,
+ comodel_name='res.partner', readonly=True,
states={'draft': [('readonly', False)]},
string='Supplier Address',
- help="This address of the supplier in case of Customer RMA operation "
+ help="Address of the supplier in case of Customer RMA operation "
+ "dropship.")
+ customer_address_id = fields.Many2one(
+ comodel_name='res.partner', readonly=True,
+ states={'draft': [('readonly', False)]},
+ string='Customer Address',
+ help="Address of the customer in case of Supplier RMA operation "
"dropship.")
qty_to_receive = fields.Float(
string='Qty To Receive',
@@ -370,8 +417,9 @@ class RmaOrderLine(models.Model):
[('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."))
+ raise ValidationError(
+ _("Please define a warehouse with a default RMA "
+ "location."))
data = {
'product_id': sm.product_id.id,
@@ -397,26 +445,32 @@ class RmaOrderLine(models.Model):
@api.onchange('reference_move_id')
def _onchange_reference_move_id(self):
self.ensure_one()
- for move in self.reference_move_id:
- data = self._prepare_rma_line_from_stock_move(move, lot=False)
+ 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')
- lot_ids = [x.lot_id.id for x in move.move_line_ids if x.lot_id]
- return {'domain': {'lot_id': [('id', 'in', lot_ids)]}}
+ self._remove_other_data_origin('reference_move_id')
@api.multi
@api.constrains('reference_move_id', 'partner_id')
def _check_move_partner(self):
for rec in self:
if (rec.reference_move_id and
- (rec.reference_move_id.partner_id != rec.partner_id) and
- (rec.reference_move_id.picking_id.partner_id !=
- rec.partner_id)):
- raise ValidationError(_(
- "RMA customer (%s) and originating stock move customer"
- " (%s) doesn't match." % (
- rec.reference_move_id.partner_id.name,
- rec.partner_id.name)))
+ rec.reference_move_id.picking_id.partner_id !=
+ rec.partner_id):
+ raise ValidationError(_(
+ "RMA customer and originating stock move customer "
+ "doesn't match."))
@api.multi
def _remove_other_data_origin(self, exception):
@@ -429,7 +483,6 @@ class RmaOrderLine(models.Model):
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
@@ -467,16 +520,20 @@ class RmaOrderLine(models.Model):
result = {}
if not self.product_id:
return result
- self.product_qty = 1
self.uom_id = self.product_id.uom_id.id
self.price_unit = self.product_id.standard_price
+ if not self.type:
+ self.type = self._get_default_type()
if self.type == 'customer':
self.operation_id = self.product_id.rma_customer_operation_id or \
self.product_id.categ_id.rma_customer_operation_id
else:
self.operation_id = self.product_id.rma_supplier_operation_id or \
self.product_id.categ_id.rma_supplier_operation_id
- return result
+ if self.lot_id.product_id != self.product_id:
+ self.lot_id = False
+ return {'domain': {
+ 'lot_id': [('product_id', '=', self.product_id.id)]}}
@api.onchange('operation_id')
def _onchange_operation_id(self):
@@ -491,10 +548,8 @@ class RmaOrderLine(models.Model):
self.in_warehouse_id.lot_rma_id
self.customer_to_supplier = self.operation_id.customer_to_supplier
self.supplier_to_customer = self.operation_id.supplier_to_customer
- if self.operation_id.in_route_id:
- self.in_route_id = self.operation_id.in_route_id
- if self.operation_id.out_route_id:
- self.out_route_id = self.operation_id.out_route_id
+ self.in_route_id = self.operation_id.in_route_id
+ self.out_route_id = self.operation_id.out_route_id
return result
@api.onchange('customer_to_supplier', 'type')
@@ -506,43 +561,92 @@ class RmaOrderLine(models.Model):
@api.onchange("lot_id")
def _onchange_lot_id(self):
- if self.lot_id and self.reference_move_id:
- data = self._prepare_rma_line_from_stock_move(
- self.reference_move_id, lot=self.lot_id)
- self.update(data)
+ product = self.lot_id.product_id
+ if product:
+ self.product_id = product
+ self.uom_id = product.uom_id
@api.multi
def action_view_in_shipments(self):
action = self.env.ref('stock.action_picking_tree_all')
result = action.read()[0]
- moves = self.env['stock.move'].search([
- ('rma_line_id', 'in', self.ids)])
- picking_ids = moves.mapped('picking_id').filtered(
- lambda p: p.picking_type_code == 'incoming').ids
- if picking_ids:
- # choose the view_mode accordingly
- if len(picking_ids) > 1:
- result['domain'] = [('id', 'in', picking_ids)]
- else:
- res = self.env.ref('stock.view_picking_form', False)
- result['views'] = [(res and res.id or False, 'form')]
- result['res_id'] = picking_ids and picking_ids[0]
+ picking_ids = []
+ for line in self:
+ for move in line.move_ids:
+ if move.location_dest_id.usage == 'internal':
+ picking_ids.append(move.picking_id.id)
+ else:
+ if line.customer_to_supplier:
+ picking_ids.append(move.picking_id.id)
+
+ shipments = list(set(picking_ids))
+ # choose the view_mode accordingly
+ if len(shipments) != 1:
+ result['domain'] = "[('id', 'in', " + \
+ str(shipments) + ")]"
+ elif len(shipments) == 1:
+ res = self.env.ref('stock.view_picking_form', False)
+ result['views'] = [(res and res.id or False, 'form')]
+ result['res_id'] = shipments[0]
return result
@api.multi
def action_view_out_shipments(self):
action = self.env.ref('stock.action_picking_tree_all')
result = action.read()[0]
- moves = self.env['stock.move'].search([
- ('rma_line_id', 'in', self.ids)])
- picking_ids = moves.mapped('picking_id').filtered(
- lambda p: p.picking_type_code == 'outgoing').ids
- if picking_ids:
- # choose the view_mode accordingly
- if len(picking_ids) > 1:
- result['domain'] = [('id', 'in', picking_ids)]
- else:
- res = self.env.ref('stock.view_picking_form', False)
- result['views'] = [(res and res.id or False, 'form')]
- result['res_id'] = picking_ids and picking_ids[0]
+ picking_ids = []
+ for line in self:
+ for move in line.move_ids:
+ if move.location_dest_id.usage in ('supplier', 'customer'):
+ if not line.customer_to_supplier:
+ picking_ids.append(move.picking_id.id)
+ shipments = list(set(picking_ids))
+ # choose the view_mode accordingly
+ if len(shipments) != 1:
+ result['domain'] = "[('id', 'in', " + \
+ str(shipments) + ")]"
+ elif len(shipments) == 1:
+ res = self.env.ref('stock.view_picking_form', False)
+ result['views'] = [(res and res.id or False, 'form')]
+ result['res_id'] = shipments[0]
+ return result
+
+ @api.multi
+ def action_view_procurements(self):
+ action = self.env.ref(
+ 'procurement.procurement_order_action_exceptions')
+ result = action.read()[0]
+ procurements = self.procurement_ids.filtered(
+ lambda p: p.state == 'exception').ids
+ # choose the view_mode accordingly
+ if len(procurements) != 1:
+ result['domain'] = "[('id', 'in', " + \
+ str(procurements) + ")]"
+ elif len(procurements) == 1:
+ res = self.env.ref('procurement.procurement_form_view', False)
+ result['views'] = [(res and res.id or False, 'form')]
+ result['res_id'] = procurements[0]
+ return result
+
+ @api.multi
+ def action_view_rma_lines(self):
+ if self.type == 'customer':
+ # from customer we link to supplier rma
+ action = self.env.ref(
+ 'rma.action_rma_supplier_lines')
+ rma_lines = self.supplier_rma_line_ids.ids
+ res = self.env.ref('rma.view_rma_line_supplier_form', False)
+ else:
+ # from supplier we link to customer rma
+ action = self.env.ref(
+ 'rma.action_rma_customer_lines')
+ rma_lines = self.customer_rma_id.ids
+ res = self.env.ref('rma.view_rma_line_form', False)
+ result = action.read()[0]
+ # choose the view_mode accordingly
+ if rma_lines and len(rma_lines) != 1:
+ result['domain'] = rma_lines.ids
+ elif len(rma_lines) == 1:
+ result['views'] = [(res and res.id or False, 'form')]
+ result['res_id'] = rma_lines[0]
return result
diff --git a/rma/models/stock.py b/rma/models/stock.py
index 1ccf52cc..aecb0cff 100644
--- a/rma/models/stock.py
+++ b/rma/models/stock.py
@@ -34,3 +34,10 @@ class StockMove(models.Model):
if group.rma_line_id:
vals['rma_line_id'] = group.rma_line_id.id
return super(StockMove, self).create(vals)
+
+ def _action_assign(self):
+ res = super(StockMove, self)._action_assign()
+ for move in self:
+ if move.rma_line_id:
+ move.partner_id = move.rma_line_id.partner_id.id or False
+ return res
diff --git a/rma/models/stock_warehouse.py b/rma/models/stock_warehouse.py
index 3111e137..50ba21d8 100644
--- a/rma/models/stock_warehouse.py
+++ b/rma/models/stock_warehouse.py
@@ -1,21 +1,255 @@
# Copyright (C) 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
-from odoo import fields, models
+from odoo import _, api, fields, models
class StockWarehouse(models.Model):
_inherit = "stock.warehouse"
- lot_rma_id = fields.Many2one('stock.location', 'RMA Location')
- rma_cust_out_type_id = fields.Many2one('stock.picking.type',
- 'RMA Customer out Type')
- rma_sup_out_type_id = fields.Many2one('stock.picking.type',
- 'RMA Supplier out Type')
- rma_cust_in_type_id = fields.Many2one('stock.picking.type',
- 'RMA Customer in Type')
- rma_sup_in_type_id = fields.Many2one('stock.picking.type',
- 'RMA Supplier in Type')
+ lot_rma_id = fields.Many2one(
+ comodel_name='stock.location', string='RMA Location',
+ ) # not readonly to have the possibility to edit location and
+ # propagate to rma rules (add a auto-update when writing this field?)
+ rma_cust_out_type_id = fields.Many2one(
+ comodel_name='stock.picking.type', string='RMA Customer out Type',
+ readonly=True,
+ )
+ rma_sup_out_type_id = fields.Many2one(
+ comodel_name='stock.picking.type', string='RMA Supplier out Type',
+ readonly=True,
+ )
+ rma_cust_in_type_id = fields.Many2one(
+ comodel_name='stock.picking.type', string='RMA Customer in Type',
+ readonly=True,
+ )
+ rma_sup_in_type_id = fields.Many2one(
+ comodel_name='stock.picking.type', string='RMA Supplier in Type',
+ readonly=True,
+ )
+ rma_in_this_wh = fields.Boolean(
+ string='RMA in this Warehouse',
+ help="If set, it will create RMA location, picking types and routes "
+ "for this warehouse.",
+ )
+ rma_customer_in_pull_id = fields.Many2one(
+ comodel_name='procurement.rule', string="RMA Customer In Rule",
+ )
+ rma_customer_out_pull_id = fields.Many2one(
+ comodel_name='procurement.rule', string="RMA Customer Out Rule",
+ )
+ rma_supplier_in_pull_id = fields.Many2one(
+ comodel_name='procurement.rule', string="RMA Supplier In Rule",
+ )
+ rma_supplier_out_pull_id = fields.Many2one(
+ comodel_name='procurement.rule', string="RMA Supplier Out Rule",
+ )
+
+ @api.multi
+ def _get_rma_types(self):
+ return [
+ self.rma_cust_out_type_id,
+ self.rma_sup_out_type_id,
+ self.rma_cust_in_type_id,
+ self.rma_sup_in_type_id]
+
+ @api.multi
+ def _rma_types_available(self):
+ self.ensure_one()
+ rma_types = self._get_rma_types()
+ for type in rma_types:
+ if not type:
+ return False
+ return True
+
+ @api.multi
+ def write(self, vals):
+ if 'rma_in_this_wh' in vals:
+ if vals.get("rma_in_this_wh"):
+ for wh in self:
+ # RMA location:
+ if not wh.lot_rma_id:
+ wh.lot_rma_id = self.env['stock.location'].create({
+ 'name': 'RMA',
+ 'usage': 'internal',
+ 'location_id': wh.lot_stock_id.id,
+ })
+ # RMA types
+ if not wh._rma_types_available():
+ wh._create_rma_picking_types()
+ else:
+ for type in wh._get_rma_types():
+ if type:
+ type.active = True
+ # RMA rules:
+ wh._create_or_update_rma_pull()
+ else:
+ for wh in self:
+ for type in wh._get_rma_types():
+ if type:
+ type.active = False
+ # Unlink rules:
+ self.mapped('rma_customer_in_pull_id').unlink()
+ self.mapped('rma_customer_out_pull_id').unlink()
+ self.mapped('rma_supplier_in_pull_id').unlink()
+ self.mapped('rma_supplier_out_pull_id').unlink()
+ return super(StockWarehouse, self).write(vals)
+
+ def _create_rma_picking_types(self):
+ picking_type_obj = self.env['stock.picking.type']
+ customer_loc, supplier_loc = self._get_partner_locations()
+ for wh in self:
+ other_pick_type = picking_type_obj.search(
+ [('warehouse_id', '=', wh.id)], order='sequence desc',
+ limit=1)
+ color = other_pick_type.color if other_pick_type else 0
+ max_sequence = other_pick_type and other_pick_type.sequence or 0
+ # create rma_cust_out_type_id:
+ rma_cust_out_type_id = picking_type_obj.create({
+ 'name': _('Customer RMA Deliveries'),
+ 'warehouse_id': wh.id,
+ 'code': 'outgoing',
+ 'use_create_lots': True,
+ 'use_existing_lots': False,
+ 'sequence_id': self.env.ref(
+ 'rma.seq_picking_type_rma_cust_out').id,
+ 'default_location_src_id': wh.lot_rma_id.id,
+ 'default_location_dest_id': customer_loc.id,
+ 'sequence': max_sequence,
+ 'color': color,
+ })
+ # create rma_sup_out_type_id:
+ rma_sup_out_type_id = picking_type_obj.create({
+ 'name': _('Supplier RMA Deliveries'),
+ 'warehouse_id': wh.id,
+ 'code': 'outgoing',
+ 'use_create_lots': True,
+ 'use_existing_lots': False,
+ 'sequence_id': self.env.ref(
+ 'rma.seq_picking_type_rma_sup_out').id,
+ 'default_location_src_id': wh.lot_rma_id.id,
+ 'default_location_dest_id': supplier_loc.id,
+ 'sequence': max_sequence,
+ 'color': color,
+ })
+ # create rma_cust_in_type_id:
+ rma_cust_in_type_id = picking_type_obj.create({
+ 'name': _('Customer RMA Receipts'),
+ 'warehouse_id': wh.id,
+ 'code': 'incoming',
+ 'use_create_lots': True,
+ 'use_existing_lots': False,
+ 'sequence_id': self.env.ref(
+ 'rma.seq_picking_type_rma_cust_in').id,
+ 'default_location_src_id': customer_loc.id,
+ 'default_location_dest_id': wh.lot_rma_id.id,
+ 'sequence': max_sequence,
+ 'color': color,
+ })
+ # create rma_sup_in_type_id:
+ rma_sup_in_type_id = picking_type_obj.create({
+ 'name': _('Supplier RMA Receipts'),
+ 'warehouse_id': wh.id,
+ 'code': 'incoming',
+ 'use_create_lots': True,
+ 'use_existing_lots': False,
+ 'sequence_id': self.env.ref(
+ 'rma.seq_picking_type_rma_sup_in').id,
+ 'default_location_src_id': supplier_loc.id,
+ 'default_location_dest_id': wh.lot_rma_id.id,
+ 'sequence': max_sequence,
+ 'color': color,
+ })
+ wh.write({
+ 'rma_cust_out_type_id': rma_cust_out_type_id.id,
+ 'rma_sup_out_type_id': rma_sup_out_type_id.id,
+ 'rma_cust_in_type_id': rma_cust_in_type_id.id,
+ 'rma_sup_in_type_id': rma_sup_in_type_id.id,
+ })
+ return True
+
+ @api.multi
+ def get_rma_rules_dict(self):
+ self.ensure_one()
+ rma_rules = dict()
+ customer_loc, supplier_loc = self._get_partner_locations()
+ # TODO: company_id?
+ rma_rules['rma_customer_in'] = {
+ 'name': self._format_rulename(self, customer_loc, self.lot_rma_id),
+ 'action': 'move',
+ 'warehouse_id': self.id,
+ 'location_src_id': customer_loc.id,
+ 'location_id': self.lot_rma_id.id,
+ 'procure_method': 'make_to_stock',
+ 'route_id': self.env.ref('rma.route_rma_customer').id,
+ 'picking_type_id': self.rma_cust_in_type_id.id,
+ 'active': True,
+ }
+ rma_rules['rma_customer_out'] = {
+ 'name': self._format_rulename(self, self.lot_rma_id, customer_loc),
+ 'action': 'move',
+ 'warehouse_id': self.id,
+ 'location_src_id': self.lot_rma_id.id,
+ 'location_id': customer_loc.id,
+ 'procure_method': 'make_to_stock',
+ 'route_id': self.env.ref('rma.route_rma_customer').id,
+ 'picking_type_id': self.rma_cust_out_type_id.id,
+ 'active': True,
+ }
+ rma_rules['rma_supplier_in'] = {
+ 'name': self._format_rulename(self, supplier_loc, self.lot_rma_id),
+ 'action': 'move',
+ 'warehouse_id': self.id,
+ 'location_src_id': supplier_loc.id,
+ 'location_id': self.lot_rma_id.id,
+ 'procure_method': 'make_to_stock',
+ 'route_id': self.env.ref('rma.route_rma_supplier').id,
+ 'picking_type_id': self.rma_sup_in_type_id.id,
+ 'active': True,
+ }
+ rma_rules['rma_supplier_out'] = {
+ 'name': self._format_rulename(self, self.lot_rma_id, supplier_loc),
+ 'action': 'move',
+ 'warehouse_id': self.id,
+ 'location_src_id': self.lot_rma_id.id,
+ 'location_id': supplier_loc.id,
+ 'procure_method': 'make_to_stock',
+ 'route_id': self.env.ref('rma.route_rma_supplier').id,
+ 'picking_type_id': self.rma_sup_out_type_id.id,
+ 'active': True,
+ }
+ return rma_rules
+
+ def _create_or_update_rma_pull(self):
+ rule_obj = self.env['procurement.rule']
+ for wh in self:
+ rules_dict = wh.get_rma_rules_dict()
+ if wh.rma_customer_in_pull_id:
+ wh.rma_customer_in_pull_id.write(rules_dict['rma_customer_in'])
+ else:
+ wh.rma_customer_in_pull_id = rule_obj.create(
+ rules_dict['rma_customer_in'])
+
+ if wh.rma_customer_out_pull_id:
+ wh.rma_customer_out_pull_id.write(
+ rules_dict['rma_customer_out'])
+ else:
+ wh.rma_customer_out_pull_id = rule_obj.create(
+ rules_dict['rma_customer_out'])
+
+ if wh.rma_supplier_in_pull_id:
+ wh.rma_supplier_in_pull_id.write(rules_dict['rma_supplier_in'])
+ else:
+ wh.rma_supplier_in_pull_id = rule_obj.create(
+ rules_dict['rma_supplier_in'])
+
+ if wh.rma_supplier_out_pull_id:
+ wh.rma_supplier_out_pull_id.write(
+ rules_dict['rma_supplier_out'])
+ else:
+ wh.rma_supplier_out_pull_id = rule_obj.create(
+ rules_dict['rma_supplier_out'])
+ return True
class StockLocationRoute(models.Model):
diff --git a/rma/views/product_view.xml b/rma/views/product_view.xml
index a2810600..ffb37dcd 100755
--- a/rma/views/product_view.xml
+++ b/rma/views/product_view.xml
@@ -5,16 +5,17 @@
product.category
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -22,16 +23,18 @@
product.template.stock.property.form.inherit
product.template
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
diff --git a/rma/views/rma_operation_view.xml b/rma/views/rma_operation_view.xml
index e4fd4824..5de6a550 100755
--- a/rma/views/rma_operation_view.xml
+++ b/rma/views/rma_operation_view.xml
@@ -1,87 +1,90 @@
-
+
-
- rma.operation.tree
- rma.operation
-
-
-
-
-
-
-
-
-
-
+
+
+ rma.operation.tree
+ rma.operation
+
+
+
+
+
+
+
+
+
+
-
- rma.operation.form
- rma.operation
-
-
-
-
+
+
+
-
- Customer Operations
- rma.operation
- form
- tree,form
- {'default_type': "customer"}
- [('type','=', 'customer')]
-
-
+
+ Customer Operations
+ rma.operation
+ form
+ tree,form
+ {'default_type': "customer"}
+ [('type','=', 'customer')]
+
+
-
- Supplier Operations
- rma.operation
- form
- tree,form
- {'default_type': "supplier"}
- [('type','=', 'supplier')]
-
-
+
+ Supplier Operations
+ rma.operation
+ form
+ tree,form
+ {'default_type': "supplier"}
+ [('type','=', 'supplier')]
+
+
-
+
-
+
+
+
diff --git a/rma/views/rma_order_line_view.xml b/rma/views/rma_order_line_view.xml
index 38093034..08b20fb0 100755
--- a/rma/views/rma_order_line_view.xml
+++ b/rma/views/rma_order_line_view.xml
@@ -83,6 +83,13 @@
+
@@ -109,7 +116,8 @@
+ attrs="{'required': [('product_tracking', 'in', ('serial', 'lot'))]}"
+ domain="[('product_id', '=', product_id)]"/>
@@ -125,32 +133,36 @@
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -198,7 +210,7 @@
rma.order.line.form
rma.order.line
-
@@ -284,37 +303,39 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -366,6 +387,14 @@
+
+
+
+
+
+
+
+
diff --git a/rma/wizards/rma_make_picking.py b/rma/wizards/rma_make_picking.py
index dfbac614..54fae91d 100644
--- a/rma/wizards/rma_make_picking.py
+++ b/rma/wizards/rma_make_picking.py
@@ -78,10 +78,12 @@ class RmaMakePicking(models.TransientModel):
@api.model
def _get_address(self, item):
- if item.line_id.delivery_address_id:
- delivery_address = item.line_id.delivery_address_id
- elif item.line_id.customer_to_supplier:
+ if item.line_id.customer_to_supplier:
delivery_address = item.line_id.supplier_address_id
+ elif item.line_id.supplier_to_customer:
+ delivery_address = item.line_id.customer_address_id
+ elif item.line_id.delivery_address_id:
+ delivery_address = item.line_id.delivery_address_id
elif item.line_id.partner_id:
delivery_address = item.line_id.partner_id
else:
diff --git a/rma/wizards/rma_order_line_make_supplier_rma.py b/rma/wizards/rma_order_line_make_supplier_rma.py
index f6def3b6..8a33ca7b 100644
--- a/rma/wizards/rma_order_line_make_supplier_rma.py
+++ b/rma/wizards/rma_order_line_make_supplier_rma.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 Eficent Business and IT Consulting Services S.L.
+# Copyright 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
import odoo.addons.decimal_precision as dp
@@ -22,14 +22,28 @@ class RmaLineMakeSupplierRma(models.TransientModel):
comodel_name='rma.order', string='Supplier RMA Order Group',
)
+ @api.model
+ def _get_default_operation(self):
+ """Dropshipping is the most common use case of this wizard, thus
+ trying to default to a dropshipping operation first."""
+ operation = self.env['rma.operation'].search([
+ ('type', '=', 'supplier'),
+ ('supplier_to_customer', '=', True)], limit=1)
+ if not operation:
+ operation = self.env['rma.operation'].search(
+ [('type', '=', 'supplier')], limit=1)
+ return operation
+
@api.model
def _prepare_item(self, line):
+ operation = self._get_default_operation()
return {
'line_id': line.id,
'product_id': line.product_id.id,
'name': line.name,
'product_qty': line.qty_to_supplier_rma,
'uom_id': line.uom_id.id,
+ 'operation_id': operation.id if operation else False,
}
@api.model
@@ -48,15 +62,13 @@ class RmaLineMakeSupplierRma(models.TransientModel):
lines = rma_line_obj.browse(rma_line_ids)
for line in lines:
items.append([0, 0, self._prepare_item(line)])
- suppliers = lines.mapped('supplier_address_id')
- if len(suppliers) == 0:
- pass
- elif len(suppliers) == 1:
- res['partner_id'] = suppliers.id
- else:
+ suppliers = lines.mapped(
+ lambda r: r.supplier_address_id.parent_id or r.supplier_address_id)
+ if len(suppliers) > 1:
raise ValidationError(
- _('Only RMA lines from the same supplier address can be '
+ _('Only RMA lines from the same supplier can be '
'processed at the same time'))
+ res['partner_id'] = suppliers.id
res['item_ids'] = items
return res
@@ -66,16 +78,17 @@ class RmaLineMakeSupplierRma(models.TransientModel):
raise ValidationError(_('Enter a supplier.'))
return {
'partner_id': self.partner_id.id,
+ 'delivery_address_id': self.partner_id.id,
'type': 'supplier',
'company_id': company.id,
}
@api.model
def _prepare_supplier_rma_line(self, rma, item):
- operation = item.line_id.product_id.rma_supplier_operation_id
- if not operation:
- operation = self.env['rma.operation'].search(
- [('type', '=', 'supplier')], limit=1)
+ if item.operation_id:
+ operation = item.operation_id
+ else:
+ operation = self._get_default_operation()
if not operation.in_route_id or not operation.out_route_id:
route = self.env['stock.location.route'].search(
[('rma_selectable', '=', True)], limit=1)
@@ -92,12 +105,13 @@ class RmaLineMakeSupplierRma(models.TransientModel):
'partner_id': self.partner_id.id,
'type': 'supplier',
'origin': item.line_id.rma_id.name,
- 'delivery_address_id':
- item.line_id.delivery_address_id.id,
+ 'customer_address_id':
+ item.line_id.delivery_address_id.id or
+ item.line_id.partner_id.id,
'product_id': item.line_id.product_id.id,
'customer_rma_id': item.line_id.id,
'product_qty': item.product_qty,
- 'rma_id': rma.id,
+ 'rma_id': rma.id if rma else False,
'uom_id': item.line_id.uom_id.id,
'operation_id': operation.id,
'receipt_policy': operation.receipt_policy,
@@ -128,23 +142,34 @@ class RmaLineMakeSupplierRma(models.TransientModel):
if self.supplier_rma_id:
rma = self.supplier_rma_id
- if not rma:
+ if not rma and len(self.item_ids) > 1:
rma_data = self._prepare_supplier_rma(line.company_id)
rma = rma_obj.create(rma_data)
rma_line_data = self._prepare_supplier_rma_line(rma, item)
- rma_line_obj.create(rma_line_data)
-
- return {
- 'name': _('Supplier RMA'),
- 'view_type': 'form',
- 'view_mode': 'form',
- 'res_model': 'rma.order',
- 'view_id': False,
- 'res_id': rma.id,
- 'context': {'supplier': True, 'customer': False},
- 'type': 'ir.actions.act_window'
- }
+ rma_line = rma_line_obj.create(rma_line_data)
+ if rma:
+ return {
+ 'name': _('Supplier RMA'),
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': 'rma.order',
+ 'view_id': False,
+ 'res_id': rma.id,
+ 'context': {'supplier': True, 'customer': False},
+ 'type': 'ir.actions.act_window'
+ }
+ else:
+ return {
+ 'name': _('Supplier RMA Line'),
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': 'rma.order.line',
+ 'view_id': False,
+ 'res_id': rma_line.id,
+ 'context': {'supplier': True, 'customer': False},
+ 'type': 'ir.actions.act_window'
+ }
class RmaLineMakeRmaOrderItem(models.TransientModel):
@@ -167,3 +192,7 @@ class RmaLineMakeRmaOrderItem(models.TransientModel):
uom_id = fields.Many2one('product.uom', string='UoM', readonly=True)
product_qty = fields.Float(string='Quantity',
digits=dp.get_precision('Product UoS'))
+ operation_id = fields.Many2one(
+ comodel_name="rma.operation", string="Operation",
+ domain=[('type', '=', 'supplier')],
+ )
diff --git a/rma/wizards/rma_order_line_make_supplier_rma_view.xml b/rma/wizards/rma_order_line_make_supplier_rma_view.xml
index 1ba47921..25ad8e38 100755
--- a/rma/wizards/rma_order_line_make_supplier_rma_view.xml
+++ b/rma/wizards/rma_order_line_make_supplier_rma_view.xml
@@ -29,6 +29,7 @@
options="{'no_open': true}"/>
+