mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
[IMP] rma,rma_sale: link inbound stock moves to sale order line
H9748
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Hibou RMAs',
|
'name': 'Hibou RMAs',
|
||||||
'version': '13.0.1.3.0',
|
'version': '13.0.1.4.0',
|
||||||
'category': 'Warehouse',
|
'category': 'Warehouse',
|
||||||
'author': 'Hibou Corp.',
|
'author': 'Hibou Corp.',
|
||||||
'license': 'OPL-1',
|
'license': 'OPL-1',
|
||||||
|
|||||||
@@ -557,6 +557,12 @@ class RMA(models.Model):
|
|||||||
'to_refund': self.template_id.in_to_refund,
|
'to_refund': self.template_id.in_to_refund,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _get_old_move(self, old_picking, line):
|
||||||
|
return old_picking.move_lines.filtered(
|
||||||
|
lambda ol: ol.state == 'done' and
|
||||||
|
ol.product_id == line.product_id
|
||||||
|
)[0]
|
||||||
|
|
||||||
def _new_in_moves(self, old_picking, new_picking, move_update):
|
def _new_in_moves(self, old_picking, new_picking, move_update):
|
||||||
lines = self.lines.filtered(lambda l: l.product_uom_qty >= 1)
|
lines = self.lines.filtered(lambda l: l.product_uom_qty >= 1)
|
||||||
if not lines:
|
if not lines:
|
||||||
@@ -564,7 +570,7 @@ class RMA(models.Model):
|
|||||||
|
|
||||||
moves = self.env['stock.move']
|
moves = self.env['stock.move']
|
||||||
for l in lines:
|
for l in lines:
|
||||||
return_move = old_picking.move_lines.filtered(lambda ol: ol.state == 'done' and ol.product_id.id == l.product_id.id)[0]
|
return_move = self._get_old_move(old_picking, l)
|
||||||
copy_vals = self._new_in_move_vals(l, new_picking, return_move)
|
copy_vals = self._new_in_move_vals(l, new_picking, return_move)
|
||||||
copy_vals.update(move_update)
|
copy_vals.update(move_update)
|
||||||
r = return_move.copy(copy_vals)
|
r = return_move.copy(copy_vals)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Hibou RMAs for Sale Orders',
|
'name': 'Hibou RMAs for Sale Orders',
|
||||||
'version': '13.0.1.2.0',
|
'version': '13.0.1.3.0',
|
||||||
'category': 'Sale',
|
'category': 'Sale',
|
||||||
'author': 'Hibou Corp.',
|
'author': 'Hibou Corp.',
|
||||||
'license': 'OPL-1',
|
'license': 'OPL-1',
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class RMATemplate(models.Model):
|
|||||||
raise ValidationError('Product is past the return period.')
|
raise ValidationError('Product is past the return period.')
|
||||||
lines.append((0, 0, {
|
lines.append((0, 0, {
|
||||||
'product_id': line.product_id.id,
|
'product_id': line.product_id.id,
|
||||||
|
'sale_line_id': line.id,
|
||||||
'product_uom_id': line.product_uom.id,
|
'product_uom_id': line.product_uom.id,
|
||||||
'product_uom_qty': qty,
|
'product_uom_qty': qty,
|
||||||
}))
|
}))
|
||||||
@@ -164,29 +165,18 @@ class RMA(models.Model):
|
|||||||
def _so_action_done(self):
|
def _so_action_done(self):
|
||||||
warnings = []
|
warnings = []
|
||||||
for rma in self:
|
for rma in self:
|
||||||
sale_orders = rma.sale_order_id
|
|
||||||
if rma.template_id.so_decrement_order_qty:
|
if rma.template_id.so_decrement_order_qty:
|
||||||
sale_orders = self.env['sale.order'].browse()
|
|
||||||
for rma_line in rma.lines:
|
for rma_line in rma.lines:
|
||||||
so_lines = rma.sale_order_id.order_line.filtered(lambda l: l.product_id == rma_line.product_id)
|
sale_line = rma_line.sale_line_id
|
||||||
qty_remaining = rma_line.product_uom_qty
|
if sale_line:
|
||||||
for sale_line in so_lines:
|
sale_line_qty = sale_line.product_uom_qty - rma_line.product_uom_qty
|
||||||
if qty_remaining == 0:
|
|
||||||
continue
|
|
||||||
sale_line_qty = sale_line.product_uom_qty
|
|
||||||
sale_line_qty = sale_line_qty - qty_remaining
|
|
||||||
if sale_line_qty < 0:
|
if sale_line_qty < 0:
|
||||||
qty_remaining = abs(sale_line_qty)
|
warnings.append((rma, rma.sale_order_id, rma_line, abs(sale_line_qty)))
|
||||||
sale_line_qty = 0
|
sale_line_qty = 0
|
||||||
else:
|
|
||||||
qty_remaining = 0
|
|
||||||
sale_line.with_context(rma_done=True).write({'product_uom_qty': sale_line_qty})
|
sale_line.with_context(rma_done=True).write({'product_uom_qty': sale_line_qty})
|
||||||
sale_orders |= sale_line.order_id
|
|
||||||
if qty_remaining:
|
|
||||||
warnings.append((rma, rma.sale_order_id, rma_line, qty_remaining))
|
|
||||||
# Try to invoice if we don't already have an invoice (e.g. from resetting to draft)
|
# Try to invoice if we don't already have an invoice (e.g. from resetting to draft)
|
||||||
if sale_orders and rma.template_id.invoice_done and not rma.invoice_ids:
|
if rma.sale_order_id and rma.template_id.invoice_done and not rma.invoice_ids:
|
||||||
rma.invoice_ids |= rma._sale_invoice_done(sale_orders)
|
rma.invoice_ids |= rma._sale_invoice_done(rma.sale_order_id)
|
||||||
if warnings:
|
if warnings:
|
||||||
return {'warning': _('Could not reduce all ordered qty:\n %s' % '\n'.join(
|
return {'warning': _('Could not reduce all ordered qty:\n %s' % '\n'.join(
|
||||||
['%s %s %s : %s' % (w[0].name, w[1].name, w[2].product_id.display_name, w[3]) for w in warnings]))}
|
['%s %s %s : %s' % (w[0].name, w[1].name, w[2].product_id.display_name, w[3]) for w in warnings]))}
|
||||||
@@ -263,3 +253,21 @@ class RMA(models.Model):
|
|||||||
new_picking = self._new_out_picking(old_picking)
|
new_picking = self._new_out_picking(old_picking)
|
||||||
self._new_out_moves(old_picking, new_picking, {})
|
self._new_out_moves(old_picking, new_picking, {})
|
||||||
return new_picking
|
return new_picking
|
||||||
|
|
||||||
|
def _get_old_move(self, old_picking, line):
|
||||||
|
if self.template_usage != 'sale_order':
|
||||||
|
return super(RMA, self)._get_old_move(old_picking, line)
|
||||||
|
return old_picking.move_lines.filtered(
|
||||||
|
lambda ol: ol.state == 'done' and
|
||||||
|
ol.product_id == line.product_id and
|
||||||
|
ol.sale_line_id == line.sale_line_id
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
|
||||||
|
class RMALine(models.Model):
|
||||||
|
_inherit = 'rma.line'
|
||||||
|
|
||||||
|
sale_line_id = fields.Many2one('sale.order.line', 'Sale Order Line')
|
||||||
|
sale_line_product_uom_qty = fields.Float('Ordered', related='sale_line_id.product_uom_qty')
|
||||||
|
sale_line_qty_delivered = fields.Float('Delivered', related='sale_line_id.qty_delivered')
|
||||||
|
sale_line_qty_invoiced = fields.Float('Invoiced', related='sale_line_id.qty_invoiced')
|
||||||
|
|||||||
@@ -304,3 +304,76 @@ class TestRMASale(TestRMA):
|
|||||||
wiz.create_batch()
|
wiz.create_batch()
|
||||||
self.assertTrue(rtv_rma.out_picking_id)
|
self.assertTrue(rtv_rma.out_picking_id)
|
||||||
self.assertEqual(rtv_rma.out_picking_id.partner_id, self.partner2)
|
self.assertEqual(rtv_rma.out_picking_id.partner_id, self.partner2)
|
||||||
|
|
||||||
|
def test_40_product_on_multiple_lines(self):
|
||||||
|
self.template_sale_return.write({
|
||||||
|
'usage': 'sale_order',
|
||||||
|
'so_decrement_order_qty': True,
|
||||||
|
'invoice_done': True,
|
||||||
|
})
|
||||||
|
self.assertTrue(self.template_sale_return.in_require_return, "Inbound Require return not set")
|
||||||
|
|
||||||
|
self.product1.write({
|
||||||
|
'type': 'product',
|
||||||
|
'invoice_policy': 'delivery',
|
||||||
|
})
|
||||||
|
order = self.env['sale.order'].create({
|
||||||
|
'partner_id': self.partner1.id,
|
||||||
|
'partner_invoice_id': self.partner1.id,
|
||||||
|
'partner_shipping_id': self.partner1.id,
|
||||||
|
'order_line': [
|
||||||
|
(0, 0, {
|
||||||
|
'product_id': self.product1.id,
|
||||||
|
'product_uom_qty': 3.0,
|
||||||
|
'product_uom': self.product1.uom_id.id,
|
||||||
|
'price_unit': 10.0,
|
||||||
|
}),
|
||||||
|
(0, 0, {
|
||||||
|
'product_id': self.product1.id,
|
||||||
|
'product_uom_qty': 2.0,
|
||||||
|
'product_uom': self.product1.uom_id.id,
|
||||||
|
'price_unit': 13.0,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
order.action_confirm()
|
||||||
|
self.assertTrue(order.state in ('sale', 'done'))
|
||||||
|
self.assertEqual(len(order.picking_ids), 1, 'Tests only run with single stage delivery.')
|
||||||
|
|
||||||
|
order.picking_ids.action_assign()
|
||||||
|
out_moves = order.picking_ids.move_ids_without_package
|
||||||
|
out_moves[0].quantity_done = 3.0
|
||||||
|
out_moves[1].quantity_done = 2.0
|
||||||
|
order.picking_ids.button_validate()
|
||||||
|
self.assertEqual(order.picking_ids.state, 'done')
|
||||||
|
|
||||||
|
rma = self.env['rma.rma'].create({
|
||||||
|
'template_id': self.template_sale_return.id,
|
||||||
|
'partner_id': self.partner1.id,
|
||||||
|
'partner_shipping_id': self.partner1.id,
|
||||||
|
'sale_order_id': order.id,
|
||||||
|
})
|
||||||
|
self.assertEqual(rma.state, 'draft')
|
||||||
|
|
||||||
|
wizard = self.env['rma.sale.make.lines'].with_user(self.user1).create({'rma_id': rma.id})
|
||||||
|
self.assertEqual(wizard.line_ids.mapped('qty_delivered'), [3.0, 2.0])
|
||||||
|
# Partially return line 1 to make sure delivered/order qty get decremented properly on second line
|
||||||
|
wizard.line_ids[0].product_uom_qty = 1.0
|
||||||
|
wizard.line_ids[1].product_uom_qty = 2.0
|
||||||
|
wizard.add_lines()
|
||||||
|
|
||||||
|
self.assertEqual(len(rma.lines), 2)
|
||||||
|
rma.action_confirm()
|
||||||
|
|
||||||
|
self.assertEqual(rma.in_picking_id.state, 'assigned')
|
||||||
|
in_moves = rma.in_picking_id.move_ids_without_package
|
||||||
|
in_moves[0].quantity_done = 1
|
||||||
|
in_moves[1].quantity_done = 2
|
||||||
|
rma.in_picking_id.button_validate()
|
||||||
|
self.assertEqual(in_moves.mapped('sale_line_id'), order.order_line, "Inbound stock moves not linked to SO")
|
||||||
|
self.assertEqual(rma.in_picking_id.state, 'done')
|
||||||
|
self.assertEqual(order.order_line.mapped('qty_delivered'), [2.0, 0.0])
|
||||||
|
|
||||||
|
rma.action_done()
|
||||||
|
self.assertEqual(order.order_line.mapped('product_uom_qty'), [2.0, 0.0])
|
||||||
|
self.assertEqual(order.order_line.mapped('qty_delivered'), [2.0, 0.0])
|
||||||
|
|||||||
@@ -31,6 +31,11 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<button string="Add lines" type="object" name="action_add_so_lines" attrs="{'invisible': ['|', ('sale_order_id', '=', False), ('state', '!=', 'draft')]}"/>
|
<button string="Add lines" type="object" name="action_add_so_lines" attrs="{'invisible': ['|', ('sale_order_id', '=', False), ('state', '!=', 'draft')]}"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<xpath expr="//page/field[@name='lines']/tree/field[@name='product_uom_qty']" position="before">
|
||||||
|
<field name="sale_line_product_uom_qty" attrs="{'column_invisible': [('parent.sale_order_id', '=', False)]}"/>
|
||||||
|
<field name="sale_line_qty_delivered" attrs="{'column_invisible': [('parent.sale_order_id', '=', False)]}"/>
|
||||||
|
<field name="sale_line_qty_invoiced" attrs="{'column_invisible': [('parent.sale_order_id', '=', False)]}"/>
|
||||||
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class RMASaleMakeLines(models.TransientModel):
|
|||||||
def _line_values(self, so_line):
|
def _line_values(self, so_line):
|
||||||
return {
|
return {
|
||||||
'rma_make_lines_id': self.id,
|
'rma_make_lines_id': self.id,
|
||||||
|
'sale_line_id': so_line.id,
|
||||||
'product_id': so_line.product_id.id,
|
'product_id': so_line.product_id.id,
|
||||||
'qty_ordered': so_line.product_uom_qty,
|
'qty_ordered': so_line.product_uom_qty,
|
||||||
'qty_delivered': so_line.qty_delivered,
|
'qty_delivered': so_line.qty_delivered,
|
||||||
@@ -49,6 +50,7 @@ class RMASaleMakeLines(models.TransientModel):
|
|||||||
for l in lines:
|
for l in lines:
|
||||||
rma_line_obj.create({
|
rma_line_obj.create({
|
||||||
'rma_id': o.rma_id.id,
|
'rma_id': o.rma_id.id,
|
||||||
|
'sale_line_id': l.sale_line_id.id,
|
||||||
'product_id': l.product_id.id,
|
'product_id': l.product_id.id,
|
||||||
'product_uom_id': l.product_uom_id.id,
|
'product_uom_id': l.product_uom_id.id,
|
||||||
'product_uom_qty': l.product_uom_qty,
|
'product_uom_qty': l.product_uom_qty,
|
||||||
@@ -60,6 +62,7 @@ class RMASOMakeLinesLine(models.TransientModel):
|
|||||||
_description = 'RMA Sale Make Lines Line'
|
_description = 'RMA Sale Make Lines Line'
|
||||||
|
|
||||||
rma_make_lines_id = fields.Many2one('rma.sale.make.lines')
|
rma_make_lines_id = fields.Many2one('rma.sale.make.lines')
|
||||||
|
sale_line_id = fields.Many2one('sale.order.line', string="Sale Order Line")
|
||||||
product_id = fields.Many2one('product.product', string="Product")
|
product_id = fields.Many2one('product.product', string="Product")
|
||||||
qty_ordered = fields.Float(string='Ordered')
|
qty_ordered = fields.Float(string='Ordered')
|
||||||
qty_invoiced = fields.Float(string='Invoiced')
|
qty_invoiced = fields.Float(string='Invoiced')
|
||||||
|
|||||||
Reference in New Issue
Block a user