mirror of
https://github.com/ForgeFlow/stock-rma.git
synced 2025-01-21 12:57:49 +02:00
[9.0][REW] rma: workflow centralized on rma.order.line and the use of rma.order is optional.
This commit is contained in:
committed by
Carlos Vallés Fuster
parent
2b15523f2c
commit
3c0e629f57
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user