[FIX] stock: avoid recomputing the schedule_date too early
Steps to reproduce: - Inventory -> Delivery -> New Planned transfer - Add two lines with two different products and save - Set different scheduled dates for each move - Set the earliest move quantity_done to the demand - Validate and create a backorder Issue: The done picking scheduled date will be changed to the latest move (that is moved to the backorder). This is due to a recompute of the picking's scheduled_date *before* the remaining moves are assigned to the backorder. Note: The order on the `stock.move.line` had to be removed, as it forced a recompute of the original picking *before* the assignation of the remaining moves to the backorder. What happens is : - Updating the move will check its move lines - To follow the SML order, it will need their pickings - To follow the picking order, it will need their scheduled date - Their scheduled dates can be out of date (since we already put some moves to `done`) - This will lead to a recompute of the original picking's scheduled date Instead, what we do is adapt the changes from odoo/odoo#79069, moving the order from the model to the reports where it's used. opw-3346598 closes odoo/odoo#147330 X-original-commit: 92092420548cb68db03ac139c23608fc1aa3a7cd Related: odoo/enterprise#53320 Signed-off-by: Arnold Moyaux (arm) <arm@odoo.com> Signed-off-by: Quentin Wolfs (quwo) <quwo@odoo.com>
This commit is contained in:
@@ -14,7 +14,7 @@ class StockMoveLine(models.Model):
|
||||
_name = "stock.move.line"
|
||||
_description = "Product Moves (Stock Move Line)"
|
||||
_rec_name = "product_id"
|
||||
_order = "result_package_id desc, location_id asc, location_dest_id asc, picking_id asc, id"
|
||||
_order = "result_package_id desc, id"
|
||||
|
||||
picking_id = fields.Many2one(
|
||||
'stock.picking', 'Transfer', auto_join=True,
|
||||
|
||||
@@ -1261,14 +1261,13 @@ class Picking(models.Model):
|
||||
'move_line_ids': [],
|
||||
'backorder_id': picking.id
|
||||
})
|
||||
moves_to_backorder.write({'picking_id': backorder_picking.id, 'picked': False})
|
||||
moves_to_backorder.move_line_ids.package_level_id.write({'picking_id': backorder_picking.id})
|
||||
moves_to_backorder.mapped('move_line_ids').write({'picking_id': backorder_picking.id})
|
||||
backorders |= backorder_picking
|
||||
picking.message_post(
|
||||
body=_('The backorder %s has been created.', backorder_picking._get_html_link())
|
||||
)
|
||||
moves_to_backorder.write({'picking_id': backorder_picking.id, 'picked': False})
|
||||
moves_to_backorder.move_line_ids.package_level_id.write({'picking_id': backorder_picking.id})
|
||||
# moves_to_backorder._do_unreserve()
|
||||
moves_to_backorder.mapped('move_line_ids').write({'picking_id': backorder_picking.id})
|
||||
backorders |= backorder_picking
|
||||
if backorder_picking.picking_type_id.reservation_method == 'at_confirm':
|
||||
bo_to_assign |= backorder_picking
|
||||
if bo_to_assign:
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- In case you come across duplicated lines, ask NIM or LAP -->
|
||||
<tr t-foreach="o.move_line_ids_without_package" t-as="ml">
|
||||
<tr t-foreach="o.move_line_ids_without_package.sorted(lambda ml: (ml.location_id.complete_name, ml.location_dest_id.complete_name))" t-as="ml">
|
||||
<td>
|
||||
<span t-field="ml.product_id.display_name">Customizable Desk</span><br/>
|
||||
<span t-field="ml.product_id.description_picking">Description on transfer</span>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import Command
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import Command, fields
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests import Form, new_test_user
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.addons.mail.tests.common import mail_new_test_user
|
||||
|
||||
|
||||
class StockMove(TransactionCase):
|
||||
@@ -60,6 +63,14 @@ class StockMove(TransactionCase):
|
||||
'type': 'consu',
|
||||
'categ_id': cls.env.ref('product.product_category_all').id,
|
||||
})
|
||||
cls.user_stock_user = mail_new_test_user(
|
||||
cls.env,
|
||||
name='Stock user',
|
||||
login='stock_user',
|
||||
email='s.u@example.com',
|
||||
notification_type='inbox',
|
||||
groups='stock.group_stock_user',
|
||||
)
|
||||
|
||||
def gather_relevant(self, product_id, location_id, lot_id=None, package_id=None, owner_id=None, strict=False):
|
||||
quants = self.env['stock.quant']._gather(product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=strict)
|
||||
@@ -6221,3 +6232,36 @@ class StockMove(TransactionCase):
|
||||
self.assertRecordValues(receipt.move_ids.move_line_ids[-1], [
|
||||
{'location_dest_id': child_location.id, 'product_id': self.product.id, 'quantity': 2},
|
||||
])
|
||||
|
||||
def test_scheduled_date_after_backorder(self):
|
||||
today = fields.Datetime.today()
|
||||
with Form(self.env['stock.picking']) as picking_form:
|
||||
picking_form.picking_type_id = self.env.ref('stock.picking_type_out')
|
||||
with picking_form.move_ids_without_package.new() as move:
|
||||
move.product_id = self.product
|
||||
move.product_uom_qty = 1
|
||||
move.date = today + relativedelta(day=5)
|
||||
with picking_form.move_ids_without_package.new() as move:
|
||||
move.product_id = self.product_consu
|
||||
move.product_uom_qty = 1
|
||||
move.date = today + relativedelta(day=10)
|
||||
picking = picking_form.save()
|
||||
|
||||
# Set different scheduled dates for each move
|
||||
move_product = picking.move_ids.filtered(lambda m: m.product_id == self.product)
|
||||
move_product.date = today + relativedelta(day=5)
|
||||
move_consu = picking.move_ids.filtered(lambda m: m.product_id == self.product_consu)
|
||||
move_consu.date = today + relativedelta(day=10)
|
||||
self.assertEqual(picking.scheduled_date, today + relativedelta(day=5))
|
||||
picking.action_confirm()
|
||||
|
||||
# Complete one move and create a backorder with the remaining move
|
||||
move_product.quantity = 1
|
||||
move_consu.quantity = 0
|
||||
backorder_wizard_dict = picking.button_validate()
|
||||
backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
|
||||
backorder_wizard.with_user(self.user_stock_user).process()
|
||||
backorder = self.env['stock.picking'].search([('backorder_id', '=', picking.id)])
|
||||
|
||||
self.assertEqual(picking.scheduled_date, today + relativedelta(day=5))
|
||||
self.assertEqual(backorder.scheduled_date, today + relativedelta(day=10))
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
<field name="model">stock.move.line</field>
|
||||
<field name="priority">1000</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom" decoration-muted="state == 'done' and is_locked == True">
|
||||
<tree editable="bottom" decoration-muted="state == 'done' and is_locked == True" default_order="result_package_id desc, location_id asc, location_dest_id asc, picking_id asc, id">
|
||||
<field name="company_id" column_invisible="True" force_save="1"/>
|
||||
<field name="picking_id" column_invisible="True" force_save="1"/>
|
||||
<field name="move_id" column_invisible="True" force_save="1" />
|
||||
@@ -241,7 +241,7 @@
|
||||
<field name="model">stock.move.line</field>
|
||||
<field name="priority">1000</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom" decoration-muted="(state == 'done' and is_locked == True)" multi_edit="1">
|
||||
<tree editable="bottom" decoration-muted="(state == 'done' and is_locked == True)" multi_edit="1" default_order="result_package_id desc, location_id asc, location_dest_id asc, picking_id asc, id">
|
||||
<header>
|
||||
<button class="btn-primary" name="action_put_in_pack" type="object" string="Put in Pack" groups="stock.group_tracking_lot"/>
|
||||
</header>
|
||||
|
||||
Reference in New Issue
Block a user