diff --git a/modules/purchase_trade/pricing.py b/modules/purchase_trade/pricing.py
index 067f408..4f0fc9b 100755
--- a/modules/purchase_trade/pricing.py
+++ b/modules/purchase_trade/pricing.py
@@ -50,7 +50,6 @@ WEEKDAY_MAP = {
DAYS = [
(None,''),
- ('before_after', 'Before/After'),
('monday', 'Monday'),
('tuesday', 'Tuesday'),
('wednesday', 'Wednesday'),
@@ -1666,9 +1665,11 @@ class Period(ModelSQL,ModelView):
endday = fields.Selection(DAYTYPES,"End day")
nbde = fields.Integer("Nb")
nbms = fields.Integer("Starting month")
- nbme = fields.Integer("Ending month")
- every = fields.Selection(DAYS,"Every")
- nb_quotation = fields.Integer("Nb quotation")
+ nbme = fields.Integer("Ending month")
+ every = fields.Selection(DAYS,"Every")
+ nb_quotation_before = fields.Integer("Nb quotes before")
+ nb_quotation_after = fields.Integer("Nb quotes after")
+ nb_quotation = fields.Integer("Nb quotation")
@classmethod
def default_nbds(cls):
@@ -1683,8 +1684,16 @@ class Period(ModelSQL,ModelView):
return 0
@classmethod
- def default_nbme(cls):
- return 0
+ def default_nbme(cls):
+ return 0
+
+ @classmethod
+ def default_nb_quotation_before(cls):
+ return 0
+
+ @classmethod
+ def default_nb_quotation_after(cls):
+ return 0
@staticmethod
def _calendar_is_quote(cal, date):
@@ -1712,19 +1721,44 @@ class Period(ModelSQL,ModelView):
dates.reverse()
return dates
+ def _quotation_counts(self):
+ before = self.nb_quotation_before or 0
+ after = self.nb_quotation_after or 0
+ if before or after or not self.nb_quotation:
+ return max(before, 0), max(after, 0)
+ if self.every == 'before_after':
+ count = abs(self.nb_quotation)
+ return count, count
+ if self.nb_quotation < 0:
+ return abs(self.nb_quotation), 0
+ return 0, self.nb_quotation
+
+ @staticmethod
+ def _weekday_dates(trigger_date, weekday_target, count, step):
+ dates = []
+ current = trigger_date
+ while len(dates) < count:
+ current += datetime.timedelta(days=step)
+ if current.weekday() == weekday_target:
+ dates.append(datetime.datetime(
+ current.year, current.month, current.day))
+ if step < 0:
+ dates.reverse()
+ return dates
+
def getDates(self,t, cal=None):
date_from = None
date_to = None
dates = []
if t:
+ before_count, after_count = self._quotation_counts()
if self.every:
if t:
j = self.every
- if j == 'before_after' and self.nb_quotation:
- count = abs(self.nb_quotation)
+ if j == 'before_after':
dates.extend(
self._available_quotation_dates(
- t, count, -1, cal))
+ t, before_count, -1, cal))
trigger_date = self._as_datetime(t)
if (self.include
and self._calendar_is_quote(
@@ -1732,7 +1766,7 @@ class Period(ModelSQL,ModelView):
dates.append(trigger_date)
dates.extend(
self._available_quotation_dates(
- t, count, 1, cal))
+ t, after_count, 1, cal))
return date_from, date_to, dates
if j not in WEEKDAY_MAP:
raise ValueError(f"Invalid day : '{j}'")
@@ -1743,34 +1777,30 @@ class Period(ModelSQL,ModelView):
first_day = t.replace(day=1)
days_to_add = (weekday_target - first_day.weekday()) % 7
current = first_day + datetime.timedelta(days=days_to_add)
- while current.month == t.month:
- dates.append(datetime.datetime(current.year, current.month, current.day))
- current += datetime.timedelta(days=7)
- elif self.nb_quotation > 0:
- days_to_add = (weekday_target - t.weekday()) % 7
- current = t + datetime.timedelta(days=days_to_add)
- while len(dates) < self.nb_quotation:
- dates.append(datetime.datetime(current.year, current.month, current.day))
- current += datetime.timedelta(days=7)
- elif self.nb_quotation < 0:
- days_to_sub = (t.weekday() - weekday_target) % 7
- current = t - datetime.timedelta(days=days_to_sub)
- while len(dates) < -self.nb_quotation:
+ while current.month == t.month:
dates.append(datetime.datetime(current.year, current.month, current.day))
- current -= datetime.timedelta(days=7)
+ current += datetime.timedelta(days=7)
+ elif before_count or after_count:
+ dates.extend(self._weekday_dates(
+ t, weekday_target, before_count, -1))
+ trigger_date = self._as_datetime(t)
+ if (self.include
+ and trigger_date.weekday() == weekday_target):
+ dates.append(trigger_date)
+ dates.extend(self._weekday_dates(
+ t, weekday_target, after_count, 1))
- elif self.nb_quotation:
- step = 1 if self.nb_quotation > 0 else -1
+ elif before_count or after_count:
dates.extend(
self._available_quotation_dates(
- t, abs(self.nb_quotation), step, cal))
+ t, before_count, -1, 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)
+ dates.append(trigger_date)
+ dates.extend(
+ self._available_quotation_dates(
+ t, after_count, 1, cal))
else:
if self.startday == 'before':
date_from = t - datetime.timedelta(days=(self.nbds if self.nbds else 0))
diff --git a/modules/purchase_trade/tests/test_module.py b/modules/purchase_trade/tests/test_module.py
index c2f8379..d201210 100644
--- a/modules/purchase_trade/tests/test_module.py
+++ b/modules/purchase_trade/tests/test_module.py
@@ -3346,7 +3346,7 @@ class PurchaseTradeTestCase(ModuleTestCase):
period = Period()
period.trigger = 'bldate'
period.every = 'friday'
- period.nb_quotation = 4
+ period.nb_quotation_after = 4
_, _, dates = period.getDates(datetime.date(2026, 4, 1))
@@ -3359,27 +3359,27 @@ class PurchaseTradeTestCase(ModuleTestCase):
datetime.date(2026, 4, 24),
])
- def test_pricing_period_every_negative_returns_weekdays_before_trigger(self):
- 'Every Friday and -4 returns the four Fridays before trigger date'
+ def test_pricing_period_every_before_returns_weekdays_before_trigger(self):
+ 'Every Friday with before returns the four Fridays before trigger date'
Period = Pool().get('pricing.period')
period = Period()
period.trigger = 'bldate'
period.every = 'friday'
- period.nb_quotation = -4
+ period.nb_quotation_before = 4
_, _, dates = period.getDates(datetime.date(2026, 4, 30))
self.assertEqual(
[date.date() for date in dates],
[
- datetime.date(2026, 4, 24),
- datetime.date(2026, 4, 17),
- datetime.date(2026, 4, 10),
datetime.date(2026, 4, 3),
+ datetime.date(2026, 4, 10),
+ datetime.date(2026, 4, 17),
+ datetime.date(2026, 4, 24),
])
- def test_pricing_period_blank_every_positive_uses_available_dates_after_trigger(self):
- 'Blank Every and positive Nb quotation returns dates after trigger'
+ def test_pricing_period_blank_every_after_uses_available_dates_after_trigger(self):
+ 'Blank Every and Nb quotes after returns dates after trigger'
Period = Pool().get('pricing.period')
calendar = Mock()
excluded = {
@@ -3391,7 +3391,7 @@ class PurchaseTradeTestCase(ModuleTestCase):
period = Period()
period.trigger = 'bldate'
period.every = None
- period.nb_quotation = 4
+ period.nb_quotation_after = 4
_, _, dates = period.getDates(datetime.date(2026, 5, 12), calendar)
@@ -3405,13 +3405,13 @@ class PurchaseTradeTestCase(ModuleTestCase):
])
def test_pricing_period_blank_every_include_counts_trigger_plus_after_dates(self):
- 'Blank Every with Inc. returns trigger date plus quotations after'
+ 'Blank Every with Inc. returns trigger date plus quotes after'
Period = Pool().get('pricing.period')
period = Period()
period.trigger = 'bldate'
period.include = True
period.every = None
- period.nb_quotation = 4
+ period.nb_quotation_after = 4
_, _, dates = period.getDates(datetime.date(2026, 5, 12))
@@ -3425,13 +3425,13 @@ class PurchaseTradeTestCase(ModuleTestCase):
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'
+ def test_pricing_period_blank_every_before_uses_available_dates_before_trigger(self):
+ 'Blank Every and Nb quotes before returns dates before trigger'
Period = Pool().get('pricing.period')
period = Period()
period.trigger = 'bldate'
period.every = None
- period.nb_quotation = -4
+ period.nb_quotation_before = 4
_, _, dates = period.getDates(datetime.date(2026, 5, 12))
@@ -3444,34 +3444,36 @@ 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'
+ def test_pricing_period_blank_every_returns_asymmetric_dates(self):
+ 'Blank Every returns independent 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
+ period.every = None
+ period.nb_quotation_before = 1
+ period.nb_quotation_after = 3
_, _, 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),
+ datetime.date(2026, 5, 15),
])
- def test_pricing_period_before_after_include_adds_trigger_date(self):
- 'Before/After with Inc. adds trigger date between both sides'
+ def test_pricing_period_blank_every_before_and_after_include_adds_trigger_date(self):
+ 'Blank Every 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
+ period.every = None
+ period.nb_quotation_before = 2
+ period.nb_quotation_after = 2
_, _, dates = period.getDates(datetime.date(2026, 5, 12))
@@ -3485,8 +3487,8 @@ class PurchaseTradeTestCase(ModuleTestCase):
datetime.date(2026, 5, 14),
])
- def test_pricing_period_before_after_skips_calendar_exclusions(self):
- 'Before/After uses available quotations around trigger date'
+ def test_pricing_period_blank_every_before_and_after_skips_calendar_exclusions(self):
+ 'Blank Every uses available quotations around trigger date'
Period = Pool().get('pricing.period')
calendar = Mock()
excluded = {
@@ -3498,15 +3500,15 @@ class PurchaseTradeTestCase(ModuleTestCase):
period = Period()
period.trigger = 'bldate'
period.include = True
- period.every = 'before_after'
- period.nb_quotation = 2
+ period.every = None
+ period.nb_quotation_before = 1
+ period.nb_quotation_after = 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),
diff --git a/modules/purchase_trade/view/period_form.xml b/modules/purchase_trade/view/period_form.xml
index d863323..c084571 100755
--- a/modules/purchase_trade/view/period_form.xml
+++ b/modules/purchase_trade/view/period_form.xml
@@ -6,11 +6,13 @@
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/modules/purchase_trade/view/period_tree.xml b/modules/purchase_trade/view/period_tree.xml
index 0043240..bd9fecf 100755
--- a/modules/purchase_trade/view/period_tree.xml
+++ b/modules/purchase_trade/view/period_tree.xml
@@ -1,8 +1,11 @@
-
-
-
+
+
+
+
+
+