diff --git a/account_invoice_constraint_chronology/model/account_move.py b/account_invoice_constraint_chronology/model/account_move.py index c95dfdcb..8829254c 100644 --- a/account_invoice_constraint_chronology/model/account_move.py +++ b/account_invoice_constraint_chronology/model/account_move.py @@ -91,6 +91,64 @@ class AccountMove(models.Model): ) ) + def _conflicting_inv_after_sequence_before_inv_date_domain(self): + return expression.AND( + [ + ( + ("name", ">", self.name), + ("invoice_date", "<", self.invoice_date), + ) + ] + ) + + def _conflicting_inv_before_sequence_after_inv_date_domain(self): + return expression.AND( + [ + ( + ("name", "<", self.name), + ("invoice_date", ">", self.invoice_date), + ) + ] + ) + + def _get_sequence_order_conflicting_previously_validated(self): + self.ensure_one() + return expression.AND( + [ + self._get_conflicting_invoices_domain(), + expression.OR( + [ + self._conflicting_inv_after_sequence_before_inv_date_domain(), + self._conflicting_inv_before_sequence_after_inv_date_domain(), + ] + ), + ] + ) + + def _raise_sequence_order_conflicting_previously_validated(self): + self.ensure_one() + before_inv = self.search( + self._conflicting_inv_after_sequence_before_inv_date_domain(), limit=1 + ) + after_inv = self.search( + self._conflicting_inv_before_sequence_after_inv_date_domain(), limit=1 + ) + if after_inv: + time = "before" + else: + time = "after" + raise UserError( + _( + "Chronology conflict: Invoice {name} cannot be {time} " + "invoice {inv_name}." + ).format( + name=self.name, + time=time, + inv_name=after_inv.name if after_inv else before_inv.name, + date_invoice=format_date(self.env, self.invoice_date), + ) + ) + def write(self, vals): if vals.get("state") != "posted": return super().write(vals) @@ -105,6 +163,10 @@ class AccountMove(models.Model): if self.search(move._get_older_conflicting_invoices_domain(), limit=1): move._raise_older_conflicting_invoices() if move in previously_validated: + if self.search( + move._get_sequence_order_conflicting_previously_validated(), limit=1 + ): + move._raise_sequence_order_conflicting_previously_validated() continue if self.search(move._get_newer_conflicting_invoices_domain(), limit=1): move._raise_newer_conflicting_invoices() diff --git a/account_invoice_constraint_chronology/tests/test_account_invoice_constraint_chronology.py b/account_invoice_constraint_chronology/tests/test_account_invoice_constraint_chronology.py index e55bfbea..18a73d52 100644 --- a/account_invoice_constraint_chronology/tests/test_account_invoice_constraint_chronology.py +++ b/account_invoice_constraint_chronology/tests/test_account_invoice_constraint_chronology.py @@ -159,3 +159,69 @@ class TestAccountInvoiceConstraintChronology(common.TransactionCase): self.invoice_1_5.invoice_date = self.yesterday with self.assertRaises(UserError): self.invoice_1_5.action_post() + + def test_modify_invoice_date_validated_past_invoice(self): + # INV5 YYYYMM20 posted + # INV4 YYYYMM15 posted + # INV3 YYYYMM10 posted + # INV2 YYYYMM05 posted + # INV1 YYYYMM01 posted + # if we pass INV3 to draft and change the date to YYYYYMM15 or YYYYYMM05 + # it should be able to validate, but if we change the date + # higher than YYYYYMM15 or lower than YYYYYMM05 + # it should not be able to validate. + after_5_days = self.today + timedelta(days=5) + after_10_days = self.today + timedelta(days=10) + after_15_days = self.today + timedelta(days=15) + after_20_days = self.today + timedelta(days=20) + after_25_days = self.today + timedelta(days=25) + + self.invoice_1.action_post() + + self.invoice_1_a_5 = self.invoice_1.copy() + self.invoice_1_a_5.invoice_date = after_5_days + self.invoice_1_a_5.action_post() + self.invoice_1_a_10 = self.invoice_1.copy() + self.invoice_1_a_10.invoice_date = after_10_days + self.invoice_1_a_10.action_post() + self.invoice_1_a_15 = self.invoice_1.copy() + self.invoice_1_a_15.invoice_date = after_15_days + self.invoice_1_a_15.action_post() + self.invoice_1_a_20 = self.invoice_1.copy() + self.invoice_1_a_20.invoice_date = after_20_days + self.invoice_1_a_20.action_post() + self.invoice_1_a_25 = self.invoice_1.copy() + self.invoice_1_a_25.invoice_date = after_25_days + self.invoice_1_a_25.action_post() + + self.invoice_1_a_15.button_cancel() + self.invoice_1_a_15.button_draft() + self.invoice_1_a_15.invoice_date = after_10_days + self.invoice_1_a_15.action_post() + + self.invoice_1_a_15.button_cancel() + self.invoice_1_a_15.button_draft() + self.invoice_1_a_15.invoice_date = after_10_days - timedelta(days=1) + with self.assertRaisesRegex( + UserError, + "Chronology conflict: Invoice {} cannot be before invoice {}.".format( + self.invoice_1_a_15.name, self.invoice_1_a_10.name + ), + ): + self.invoice_1_a_15.action_post() + + self.invoice_1_a_15.button_cancel() + self.invoice_1_a_15.button_draft() + self.invoice_1_a_15.invoice_date = after_20_days + self.invoice_1_a_15.action_post() + + self.invoice_1_a_15.button_cancel() + self.invoice_1_a_15.button_draft() + self.invoice_1_a_15.invoice_date = after_20_days + timedelta(days=1) + with self.assertRaisesRegex( + UserError, + "Chronology conflict: Invoice {} cannot be after invoice {}.".format( + self.invoice_1_a_15.name, self.invoice_1_a_20.name + ), + ): + self.invoice_1_a_15.action_post()