Replace dates checks of fiscal year and period by overlap SQL constraint

This commit is contained in:
Cédric Krier committed 2024-10-09 17:29:27 +02:00
1 parent f64e38f799
commit 6f4fbb008a
5 files changed
+46 -91

No files matched your search

-4
View File
@@ -33,10 +33,6 @@ class FiscalYearValidationError(ValidationError):
pass
class FiscalYearDatesError(FiscalYearValidationError):
pass
class FiscalYearSequenceError(FiscalYearValidationError):
pass
+26 -46
View File
@@ -2,21 +2,26 @@
# this repository contains the full copyright notices and license terms.
from dateutil.relativedelta import relativedelta
from sql import Null
from sql.conditionals import Case
from sql.operators import Equal, NotEqual
from trytond.cache import Cache
from trytond.i18n import gettext
from trytond.model import ModelSQL, ModelView, Workflow, fields
from trytond.model import Exclude, ModelSQL, ModelView, Workflow, fields
from trytond.model.exceptions import AccessError
from trytond.pool import Pool
from trytond.pyson import Eval, Id
from trytond.rpc import RPC
from trytond.sql.functions import DateRange
from trytond.sql.operators import RangeOverlap
from trytond.transaction import Transaction
from trytond.wizard import (
Button, StateAction, StateTransition, StateView, Wizard)
from .exceptions import (
FiscalYearCloseError, FiscalYearDatesError, FiscalYearNotFoundError,
FiscalYearReOpenError, FiscalYearSequenceError)
FiscalYearCloseError, FiscalYearNotFoundError, FiscalYearReOpenError,
FiscalYearSequenceError)
STATES = {
'readonly': Eval('state') != 'open',
@@ -57,6 +62,24 @@ class FiscalYear(Workflow, ModelSQL, ModelView):
@classmethod
def __setup__(cls):
super(FiscalYear, cls).__setup__()
t = cls.__table__()
cls._sql_constraints += [
('dates_overlap',
Exclude(t,
(t.company, Equal),
(DateRange(t.start_date, t.end_date, '[]'), RangeOverlap),
),
'account.msg_fiscalyear_overlap'),
('open_earlier',
Exclude(t,
(t.company, Equal),
(DateRange(
Case(
(t.state == 'open', t.start_date), else_=Null),
t.end_date), RangeOverlap),
(Case((t.state == 'open', t.id), else_=-1), NotEqual)),
'account.msg_open_fiscalyear_earlier'),
]
cls._order.insert(0, ('start_date', 'DESC'))
cls._transitions |= set((
('open', 'closed'),
@@ -113,52 +136,9 @@ class FiscalYear(Workflow, ModelSQL, ModelView):
@classmethod
def validate_fields(cls, fiscalyears, field_names):
super().validate_fields(fiscalyears, field_names)
cls.check_dates(fiscalyears, field_names)
cls.check_post_move_sequence(fiscalyears, field_names)
cls.check_period_dates(fiscalyears, field_names)
@classmethod
def check_dates(cls, fiscalyears, field_names=None):
if field_names and not (field_names & {
'start_date', 'end_date', 'state', 'company'}):
return
transaction = Transaction()
connection = transaction.connection
cls.lock()
cursor = connection.cursor()
table = cls.__table__()
for year in fiscalyears:
cursor.execute(*table.select(table.id,
where=(((table.start_date <= year.start_date)
& (table.end_date >= year.start_date))
| ((table.start_date <= year.end_date)
& (table.end_date >= year.end_date))
| ((table.start_date >= year.start_date)
& (table.end_date <= year.end_date)))
& (table.company == year.company.id)
& (table.id != year.id)))
second_id = cursor.fetchone()
if second_id:
second = cls(second_id[0])
raise FiscalYearDatesError(
gettext('account.msg_fiscalyear_overlap',
first=year.rec_name,
second=second.rec_name))
if year.state == 'open':
fiscalyears = cls.search([
('start_date', '>=', year.end_date),
('state', 'in', ['closed', 'locked']),
('company', '=', year.company.id),
],
limit=1,
order=[('start_date', 'ASC')])
if fiscalyears:
fiscalyear, = fiscalyears
raise FiscalYearDatesError(
gettext('account.msg_open_fiscalyear_earlier',
open=year.rec_name,
closed=fiscalyear.rec_name))
@classmethod
def check_post_move_sequence(cls, fiscalyears, field_names=None):
if field_names and 'post_move_sequence' not in field_names:
+4 -4
View File
@@ -21,8 +21,8 @@ this repository contains the full copyright notices and license terms. -->
<record model="ir.message" id="msg_close_period_non_posted_moves">
<field name="text">To close period "%(period)s" you must post the moves "%(moves)s".</field>
</record>
<record model="ir.message" id="msg_period_overlap">
<field name="text">The periods "%(first)s" and "%(second)s" overlap, you must use different dates.</field>
<record model="ir.message" id="msg_period_standard_overlap">
<field name="text">The dates of standard period of the same fiscal year can not overlap.</field>
</record>
<record model="ir.message" id="msg_period_same_sequence">
<field name="text">You cannot use the same post move sequence for the periods "%(first)s" and "%(second)s" because they are from different fiscal years.</field>
@@ -64,10 +64,10 @@ this repository contains the full copyright notices and license terms. -->
<field name="text">To continue, you must reopen the fiscal year "%(fiscalyear)s" of company "%(company)s" for the date "%(date)s".</field>
</record>
<record model="ir.message" id="msg_fiscalyear_overlap">
<field name="text">The fiscal years "%(first)s" and "%(second)s" overlap, you must use different dates.</field>
<field name="text">The dates of fiscal years of the same company can not overlap.</field>
</record>
<record model="ir.message" id="msg_open_fiscalyear_earlier">
<field name="text">The open fiscal year "%(open)s" can not be before the close fiscal year "%(closed)s".</field>
<field name="text">Open fiscal years can not be before closed fiscal years.</field>
</record>
<record model="ir.message" id="msg_fiscalyear_different_post_move_sequence">
<field name="text">The fiscal years "%(first)s" and "%(second)s" cannot have the same post move sequence, you must use different sequences.</field>
+13 -34
View File
@@ -1,13 +1,17 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from sql.operators import Equal
from trytond.cache import Cache
from trytond.const import OPERATORS
from trytond.i18n import gettext
from trytond.model import Index, ModelSQL, ModelView, Workflow, fields
from trytond.model import Exclude, Index, ModelSQL, ModelView, Workflow, fields
from trytond.model.exceptions import AccessError
from trytond.pool import Pool
from trytond.pyson import Eval, Id
from trytond.sql.functions import DateRange
from trytond.sql.operators import RangeOverlap
from trytond.tools import grouped_slice
from trytond.transaction import Transaction
@@ -59,6 +63,14 @@ class Period(Workflow, ModelSQL, ModelView):
super(Period, cls).__setup__()
t = cls.__table__()
cls.__access__.add('fiscalyear')
cls._sql_constraints += [
('standard_dates_overlap',
Exclude(t,
(t.fiscalyear, Equal),
(DateRange(t.start_date, t.end_date, '[]'), RangeOverlap),
where=t.type == 'standard'),
'account.msg_period_standard_overlap'),
]
cls._sql_indexes.add(
Index(
t,
@@ -121,43 +133,10 @@ class Period(Workflow, ModelSQL, ModelView):
@classmethod
def validate_fields(cls, periods, field_names):
super().validate_fields(periods, field_names)
cls.check_dates(periods, field_names)
cls.check_fiscalyear_dates(periods, field_names)
cls.check_move_dates(periods, field_names)
cls.check_post_move_sequence(periods, field_names)
@classmethod
def check_dates(cls, periods, field_names=None):
if field_names and not (
field_names & {
'start_date', 'end_date', 'fiscalyear', 'type'}):
return
transaction = Transaction()
connection = transaction.connection
cls.lock()
table = cls.__table__()
cursor = connection.cursor()
for period in periods:
if period.type != 'standard':
continue
cursor.execute(*table.select(table.id,
where=(((table.start_date <= period.start_date)
& (table.end_date >= period.start_date))
| ((table.start_date <= period.end_date)
& (table.end_date >= period.end_date))
| ((table.start_date >= period.start_date)
& (table.end_date <= period.end_date)))
& (table.fiscalyear == period.fiscalyear.id)
& (table.type == 'standard')
& (table.id != period.id)))
period_id = cursor.fetchone()
if period_id:
overlapping_period = cls(period_id[0])
raise PeriodDatesError(
gettext('account.msg_period_overlap',
first=period.rec_name,
second=overlapping_period.rec_name))
@classmethod
def check_fiscalyear_dates(cls, periods, field_names=None):
if field_names and not (
+3 -3
View File
@@ -7,8 +7,8 @@ from decimal import Decimal
from dateutil.relativedelta import relativedelta
from trytond.exceptions import UserError
from trytond.modules.account.exceptions import (
FiscalYearDatesError, PeriodDatesError)
from trytond.model.exceptions import SQLConstraintError
from trytond.modules.account.exceptions import PeriodDatesError
from trytond.modules.account.tax import TaxableMixin
from trytond.modules.company.tests import (
CompanyTestMixin, PartyCompanyCheckEraseMixin, create_company, set_company)
@@ -309,7 +309,7 @@ class AccountTestCase(
company,
start_date=datetime.date(2021, 1, 1),
end_date=datetime.date(2021, 12, 31))
with self.assertRaises(FiscalYearDatesError):
with self.assertRaises(SQLConstraintError):
earlier_fiscalyear.save()
@with_transaction()