Replace dates checks of fiscal year and period by overlap SQL constraint
This commit is contained in:
1 parent
f64e38f799
commit
6f4fbb008a
5 files changed
+46
-91
No files matched your search
@@ -33,10 +33,6 @@ class FiscalYearValidationError(ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class FiscalYearDatesError(FiscalYearValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class FiscalYearSequenceError(FiscalYearValidationError):
|
||||
pass
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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 (
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in new issue
Block a user