Pricing Before/After
This commit is contained in:
@@ -48,11 +48,12 @@ WEEKDAY_MAP = {
|
||||
'sunday': 6
|
||||
}
|
||||
|
||||
DAYS = [
|
||||
(None,''),
|
||||
('monday', 'Monday'),
|
||||
('tuesday', 'Tuesday'),
|
||||
('wednesday', 'Wednesday'),
|
||||
DAYS = [
|
||||
(None,''),
|
||||
('before_after', 'Before/After'),
|
||||
('monday', 'Monday'),
|
||||
('tuesday', 'Tuesday'),
|
||||
('wednesday', 'Wednesday'),
|
||||
('thursday', 'Thursday'),
|
||||
('friday', 'Friday'),
|
||||
('saturday', 'Saturday'),
|
||||
@@ -1695,16 +1696,46 @@ class Period(ModelSQL,ModelView):
|
||||
result = cal.IsQuote(date)
|
||||
return True if result is None else bool(result)
|
||||
|
||||
@staticmethod
|
||||
def _as_datetime(date):
|
||||
return datetime.datetime(date.year, date.month, date.day)
|
||||
|
||||
def _available_quotation_dates(self, trigger_date, count, step, cal=None):
|
||||
dates = []
|
||||
current = trigger_date + datetime.timedelta(days=step)
|
||||
while len(dates) < count:
|
||||
date = self._as_datetime(current)
|
||||
if self._calendar_is_quote(cal, date):
|
||||
dates.append(date)
|
||||
current += datetime.timedelta(days=step)
|
||||
if step < 0:
|
||||
dates.reverse()
|
||||
return dates
|
||||
|
||||
def getDates(self,t, cal=None):
|
||||
date_from = None
|
||||
date_to = None
|
||||
dates = []
|
||||
if t:
|
||||
if self.every:
|
||||
if t:
|
||||
j = self.every
|
||||
if j not in WEEKDAY_MAP:
|
||||
raise ValueError(f"Invalid day : '{j}'")
|
||||
if t:
|
||||
j = self.every
|
||||
if j == 'before_after' and self.nb_quotation:
|
||||
count = abs(self.nb_quotation)
|
||||
dates.extend(
|
||||
self._available_quotation_dates(
|
||||
t, count, -1, cal))
|
||||
trigger_date = self._as_datetime(t)
|
||||
if (self.include
|
||||
and self._calendar_is_quote(
|
||||
cal, trigger_date)):
|
||||
dates.append(trigger_date)
|
||||
dates.extend(
|
||||
self._available_quotation_dates(
|
||||
t, count, 1, cal))
|
||||
return date_from, date_to, dates
|
||||
if j not in WEEKDAY_MAP:
|
||||
raise ValueError(f"Invalid day : '{j}'")
|
||||
|
||||
weekday_target = WEEKDAY_MAP[j]
|
||||
|
||||
@@ -1730,15 +1761,16 @@ class Period(ModelSQL,ModelView):
|
||||
|
||||
elif self.nb_quotation:
|
||||
step = 1 if self.nb_quotation > 0 else -1
|
||||
current = t + datetime.timedelta(days=step)
|
||||
while len(dates) < abs(self.nb_quotation):
|
||||
date = datetime.datetime(
|
||||
current.year, current.month, current.day)
|
||||
if self._calendar_is_quote(cal, date):
|
||||
dates.append(date)
|
||||
current += datetime.timedelta(days=step)
|
||||
if self.nb_quotation < 0:
|
||||
dates.reverse()
|
||||
dates.extend(
|
||||
self._available_quotation_dates(
|
||||
t, abs(self.nb_quotation), step, cal))
|
||||
trigger_date = self._as_datetime(t)
|
||||
if (self.include
|
||||
and self._calendar_is_quote(cal, trigger_date)):
|
||||
if self.nb_quotation > 0:
|
||||
dates.insert(0, trigger_date)
|
||||
else:
|
||||
dates.append(trigger_date)
|
||||
else:
|
||||
if self.startday == 'before':
|
||||
date_from = t - datetime.timedelta(days=(self.nbds if self.nbds else 0))
|
||||
|
||||
@@ -3385,6 +3385,27 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
datetime.date(2026, 5, 18),
|
||||
])
|
||||
|
||||
def test_pricing_period_blank_every_include_counts_trigger_plus_after_dates(self):
|
||||
'Blank Every with Inc. returns trigger date plus quotations after'
|
||||
Period = Pool().get('pricing.period')
|
||||
period = Period()
|
||||
period.trigger = 'bldate'
|
||||
period.include = True
|
||||
period.every = None
|
||||
period.nb_quotation = 4
|
||||
|
||||
_, _, dates = period.getDates(datetime.date(2026, 5, 12))
|
||||
|
||||
self.assertEqual(
|
||||
[date.date() for date in dates],
|
||||
[
|
||||
datetime.date(2026, 5, 12),
|
||||
datetime.date(2026, 5, 13),
|
||||
datetime.date(2026, 5, 14),
|
||||
datetime.date(2026, 5, 15),
|
||||
datetime.date(2026, 5, 16),
|
||||
])
|
||||
|
||||
def test_pricing_period_blank_every_negative_uses_available_dates_before_trigger(self):
|
||||
'Blank Every and negative Nb quotation also returns dates before trigger'
|
||||
Period = Pool().get('pricing.period')
|
||||
@@ -3404,6 +3425,75 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
datetime.date(2026, 5, 11),
|
||||
])
|
||||
|
||||
def test_pricing_period_before_after_returns_symmetric_dates(self):
|
||||
'Before/After returns quotations before and after trigger'
|
||||
Period = Pool().get('pricing.period')
|
||||
period = Period()
|
||||
period.trigger = 'bldate'
|
||||
period.include = False
|
||||
period.every = 'before_after'
|
||||
period.nb_quotation = 2
|
||||
|
||||
_, _, dates = period.getDates(datetime.date(2026, 5, 12))
|
||||
|
||||
self.assertEqual(
|
||||
[date.date() for date in dates],
|
||||
[
|
||||
datetime.date(2026, 5, 10),
|
||||
datetime.date(2026, 5, 11),
|
||||
datetime.date(2026, 5, 13),
|
||||
datetime.date(2026, 5, 14),
|
||||
])
|
||||
|
||||
def test_pricing_period_before_after_include_adds_trigger_date(self):
|
||||
'Before/After with Inc. adds trigger date between both sides'
|
||||
Period = Pool().get('pricing.period')
|
||||
period = Period()
|
||||
period.trigger = 'bldate'
|
||||
period.include = True
|
||||
period.every = 'before_after'
|
||||
period.nb_quotation = 2
|
||||
|
||||
_, _, dates = period.getDates(datetime.date(2026, 5, 12))
|
||||
|
||||
self.assertEqual(
|
||||
[date.date() for date in dates],
|
||||
[
|
||||
datetime.date(2026, 5, 10),
|
||||
datetime.date(2026, 5, 11),
|
||||
datetime.date(2026, 5, 12),
|
||||
datetime.date(2026, 5, 13),
|
||||
datetime.date(2026, 5, 14),
|
||||
])
|
||||
|
||||
def test_pricing_period_before_after_skips_calendar_exclusions(self):
|
||||
'Before/After uses available quotations around trigger date'
|
||||
Period = Pool().get('pricing.period')
|
||||
calendar = Mock()
|
||||
excluded = {
|
||||
datetime.date(2026, 5, 11),
|
||||
datetime.date(2026, 5, 13),
|
||||
}
|
||||
calendar.IsQuote = Mock(
|
||||
side_effect=lambda date: date.date() not in excluded)
|
||||
period = Period()
|
||||
period.trigger = 'bldate'
|
||||
period.include = True
|
||||
period.every = 'before_after'
|
||||
period.nb_quotation = 2
|
||||
|
||||
_, _, dates = period.getDates(datetime.date(2026, 5, 12), calendar)
|
||||
|
||||
self.assertEqual(
|
||||
[date.date() for date in dates],
|
||||
[
|
||||
datetime.date(2026, 5, 9),
|
||||
datetime.date(2026, 5, 10),
|
||||
datetime.date(2026, 5, 12),
|
||||
datetime.date(2026, 5, 14),
|
||||
datetime.date(2026, 5, 15),
|
||||
])
|
||||
|
||||
def test_pricing_trigger_application_period_defaults_to_pricing_period(self):
|
||||
'application period defaults to pricing period without being cleared'
|
||||
Trigger = Pool().get('pricing.trigger')
|
||||
|
||||
Reference in New Issue
Block a user