diff --git a/sale_stock_available_to_promise_release_block/tests/test_sale_block_release.py b/sale_stock_available_to_promise_release_block/tests/test_sale_block_release.py index 0c7885d400..06f89ad9e7 100644 --- a/sale_stock_available_to_promise_release_block/tests/test_sale_block_release.py +++ b/sale_stock_available_to_promise_release_block/tests/test_sale_block_release.py @@ -1,9 +1,7 @@ # Copyright 2024 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -import psycopg2 - -from odoo import fields +from odoo import exceptions, fields from odoo.tests.common import Form from odoo.addons.sale_stock_available_to_promise_release.tests import common @@ -29,13 +27,13 @@ def test_sale_release_blocked(self): self.assertTrue(self.sale.picking_ids.release_blocked) def _create_unblock_release_wizard( - self, order_lines, date_deadline=None, from_order=None, option="free" + self, records=None, date_deadline=None, from_order=None, option="free" ): wiz_form = Form( self.env["unblock.release"].with_context( from_sale_order_id=from_order and from_order.id, - active_model=order_lines._name, - active_ids=order_lines.ids, + active_model=records._name, + active_ids=records.ids, default_option=option, ) ) @@ -43,7 +41,7 @@ def _create_unblock_release_wizard( wiz_form.date_deadline = date_deadline return wiz_form.save() - def test_sale_order_line_unblock_release_contextual(self): + def test_unblock_release_contextual(self): self._set_stock(self.line.product_id, self.line.product_uom_qty) self.sale.block_release = True self.sale.action_confirm() @@ -65,7 +63,33 @@ def test_sale_order_line_unblock_release_contextual(self): self.assertNotEqual(old_picking, new_picking) self.assertFalse(old_picking.exists()) - def test_sale_order_line_unblock_release_free(self): + def test_unblock_release_contextual_update_date(self): + self._set_stock(self.line.product_id, self.line.product_uom_qty) + self.sale.block_release = True + self.sale.action_confirm() + # Unblock deliveries through the wizard, opened from another SO + # to define default values + update the proposed date + new_sale = self._create_sale_order() + new_sale.commitment_date = fields.Datetime.add(fields.Datetime.now(), days=1) + wiz = self._create_unblock_release_wizard( + self.sale.order_line, from_order=new_sale + ) + self.assertEqual(wiz.option, "contextual") + self.assertEqual(wiz.date_deadline, new_sale.commitment_date) + self.assertNotEqual(wiz.order_line_ids.move_ids.date, new_sale.commitment_date) + old_picking = wiz.order_line_ids.move_ids.picking_id + new_date_deadline = fields.Datetime.add(fields.Datetime.now(), days=2) + wiz.date_deadline = new_date_deadline + wiz.validate() + # Deliveries have been scheduled to the new date deadline + new_picking = wiz.order_line_ids.move_ids.picking_id + self.assertEqual(wiz.order_line_ids.move_ids.date, new_sale.commitment_date) + self.assertNotEqual(old_picking, new_picking) + self.assertFalse(old_picking.exists()) + # Commitment date on contextual order has been updated too + self.assertEqual(new_sale.commitment_date, new_date_deadline) + + def test_unblock_release_free(self): self._set_stock(self.line.product_id, self.line.product_uom_qty) self.sale.block_release = True self.sale.action_confirm() @@ -84,7 +108,7 @@ def test_sale_order_line_unblock_release_free(self): self.assertNotEqual(old_picking, new_picking) self.assertFalse(old_picking.exists()) - def test_sale_order_line_unblock_release_asap(self): + def test_unblock_release_asap(self): # Start with a blocked SO having a commitment date in the past self._set_stock(self.line.product_id, self.line.product_uom_qty) self.sale.block_release = True @@ -92,8 +116,8 @@ def test_sale_order_line_unblock_release_asap(self): self.sale.commitment_date = yesterday self.sale.action_confirm() # Unblock deliveries through the wizard - today = fields.Datetime.now() wiz = self._create_unblock_release_wizard(self.sale.order_line, option="asap") + today = wiz.date_deadline self.assertEqual(wiz.date_deadline, today) self.assertNotEqual(wiz.order_line_ids.move_ids.date, today) old_picking = wiz.order_line_ids.move_ids.picking_id @@ -104,7 +128,30 @@ def test_sale_order_line_unblock_release_asap(self): self.assertNotEqual(old_picking, new_picking) self.assertFalse(old_picking.exists()) - def test_sale_order_line_unblock_release_past_date_deadline(self): + def test_unblock_release_asap_from_moves(self): + # Same test than above but running the wizard from moves. + # Start with a blocked SO having a commitment date in the past + self._set_stock(self.line.product_id, self.line.product_uom_qty) + self.sale.block_release = True + yesterday = fields.Datetime.subtract(fields.Datetime.now(), days=1) + self.sale.commitment_date = yesterday + self.sale.action_confirm() + # Unblock deliveries through the wizard + today = fields.Datetime.now() + wiz = self._create_unblock_release_wizard( + self.sale.order_line.move_ids, option="asap" + ) + self.assertEqual(wiz.date_deadline, today) + self.assertNotEqual(wiz.move_ids.date, today) + old_picking = wiz.move_ids.picking_id + wiz.validate() + # Deliveries have been scheduled for today + new_picking = wiz.move_ids.picking_id + self.assertEqual(wiz.move_ids.date, today) + self.assertNotEqual(old_picking, new_picking) + self.assertFalse(old_picking.exists()) + + def test_unblock_release_past_date_deadline(self): self._set_stock(self.line.product_id, self.line.product_uom_qty) self.sale.block_release = True self.sale.action_confirm() @@ -112,7 +159,7 @@ def test_sale_order_line_unblock_release_past_date_deadline(self): # in the past new_sale = self._create_sale_order() yesterday = fields.Datetime.subtract(fields.Datetime.now(), days=1) - with self.assertRaises(psycopg2.errors.CheckViolation): + with self.assertRaises(exceptions.ValidationError): self._create_unblock_release_wizard( self.sale.order_line, date_deadline=yesterday, from_order=new_sale ) diff --git a/sale_stock_available_to_promise_release_block/wizards/unblock_release.py b/sale_stock_available_to_promise_release_block/wizards/unblock_release.py index 6428fe456e..667f0bd1ea 100644 --- a/sale_stock_available_to_promise_release_block/wizards/unblock_release.py +++ b/sale_stock_available_to_promise_release_block/wizards/unblock_release.py @@ -1,7 +1,7 @@ # Copyright 2024 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import api, fields, models +from odoo import _, api, exceptions, fields, models class UnblockRelease(models.TransientModel): @@ -25,13 +25,14 @@ class UnblockRelease(models.TransientModel): compute="_compute_date_deadline", store=True, readonly=False, required=True ) - _sql_constraints = [ - ( - "check_scheduled_date", - "CHECK (date_deadline::date >= now()::date)", - "You cannot reschedule deliveries in the past.", - ), - ] + @api.constrains("date_deadline") + def _constrains_date_deadline(self): + today = fields.Date.today() + for rec in self: + if rec.date_deadline.date() < today: + raise exceptions.ValidationError( + _("You cannot reschedule deliveries in the past.") + ) def _selection_option(self): options = [ @@ -97,3 +98,8 @@ def validate(self): moves.action_unblock_release() # Clean up empty deliveries pickings.filtered(lambda o: not o.move_ids and not o.printed).unlink() + # Update commitment date of contextual sale order if any + from_sale_order_id = self.env.context.get("from_sale_order_id") + from_sale_order = self.env["sale.order"].browse(from_sale_order_id).exists() + if from_sale_order.state in ("draft", "sent"): + from_sale_order.commitment_date = self.date_deadline