Replace dates checks of stock forecast by overlap SQL constraint

This commit is contained in:
Cédric Krier
2024-10-11 16:01:55 +02:00
parent fce7629ecb
commit 5853d86d25
3 changed files with 17 additions and 53 deletions
-8
View File
@@ -1,8 +0,0 @@
# 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 trytond.model.exceptions import ValidationError
class ForecastValidationError(ValidationError):
pass
+15 -43
View File
@@ -8,19 +8,21 @@ from dateutil.relativedelta import relativedelta
from sql import Null
from sql.aggregate import Sum
from sql.conditionals import Coalesce
from sql.operators import Equal
from trytond import backend
from trytond.i18n import gettext
from trytond.model import Index, ModelSQL, ModelView, Unique, Workflow, fields
from trytond.model import (
Exclude, Index, ModelSQL, ModelView, Unique, Workflow, fields)
from trytond.model.exceptions import AccessError
from trytond.pool import Pool
from trytond.pyson import Bool, Eval, If
from trytond.sql.functions import DateRange
from trytond.sql.operators import RangeOverlap
from trytond.tools import grouped_slice, reduce_ids
from trytond.transaction import Transaction
from trytond.wizard import Button, StateTransition, StateView, Wizard
from .exceptions import ForecastValidationError
class Forecast(Workflow, ModelSQL, ModelView):
"Stock Forecast"
@@ -66,6 +68,16 @@ class Forecast(Workflow, ModelSQL, ModelView):
def __setup__(cls):
super(Forecast, cls).__setup__()
t = cls.__table__()
cls._sql_constraints += [
('dates_done_overlap',
Exclude(t,
(t.company, Equal),
(t.warehouse, Equal),
(t.destination, Equal),
(DateRange(t.from_date, t.to_date, '[]'), RangeOverlap),
where=t.state == 'done'),
'stock_forecast.msg_forecast_done_dates_overlap'),
]
cls._sql_indexes.add(
Index(
t,
@@ -188,46 +200,6 @@ class Forecast(Workflow, ModelSQL, ModelView):
('destination.rec_name', *clause[1:]),
]
@classmethod
def validate_fields(cls, forecasts, field_names):
super().validate_fields(forecasts, field_names)
cls.check_date_overlap(forecasts, field_names)
@classmethod
def check_date_overlap(cls, forecasts, field_names=None):
if field_names and not (field_names & {
'from_date', 'to_date',
'warehouse', 'destination',
'company', 'state'}):
return
transaction = Transaction()
connection = transaction.connection
cls.lock()
table = cls.__table__()
cursor = connection.cursor()
for forecast in forecasts:
if forecast.state != 'done':
continue
cursor.execute(*table.select(table.id,
where=(((table.from_date <= forecast.from_date)
& (table.to_date >= forecast.from_date))
| ((table.from_date <= forecast.to_date)
& (table.to_date >= forecast.to_date))
| ((table.from_date >= forecast.from_date)
& (table.to_date <= forecast.to_date)))
& (table.warehouse == forecast.warehouse.id)
& (table.destination == forecast.destination.id)
& (table.company == forecast.company.id)
& (table.state == 'done')
& (table.id != forecast.id)))
forecast_id = cursor.fetchone()
if forecast_id:
second = cls(forecast_id[0])
raise ForecastValidationError(
gettext('stock_forecast.msg_forecast_date_overlap',
first=forecast.rec_name,
second=second.rec_name))
@classmethod
def delete(cls, forecasts):
# Cancel before delete
+2 -2
View File
@@ -3,8 +3,8 @@
this repository contains the full copyright notices and license terms. -->
<tryton>
<data grouped="1">
<record model="ir.message" id="msg_forecast_date_overlap">
<field name="text">The forecasts "%(first)s" and "%(second)s" overlap, you must use different dates.</field>
<record model="ir.message" id="msg_forecast_done_dates_overlap">
<field name="text">The dates of done forecasts for the same company, warehouse and destination can not overlap.</field>
</record>
<record model="ir.message" id="msg_forecast_delete_cancel">
<field name="text">To delete forecast "%(forecast)s" you must cancel it.</field>