Demurrage
This commit is contained in:
@@ -1622,13 +1622,12 @@ class ShipmentIn(metaclass=PoolMeta):
|
||||
for sh in shipments:
|
||||
if sh.sof:
|
||||
for s in sh.sof:
|
||||
if s.notice_of_readiness_time:
|
||||
s.laytime_commenced = (
|
||||
s.notice_of_readiness_time
|
||||
+ s._applied_turn_delta())
|
||||
laytime_start = s._applied_laytime_start()
|
||||
if laytime_start:
|
||||
s.laytime_commenced = laytime_start
|
||||
quantity = float(s.quantity or sh.get_quantity() or 0)
|
||||
s.laytime_allowed = s._applied_laytime_allowed(quantity)
|
||||
s.laytime_completed = s.hoses_disconnected
|
||||
s.laytime_completed = s._applied_laytime_end()
|
||||
#s.laytime_completed = s.laytime_commenced + datetime.timedelta(hours=s.laytime_allowed)
|
||||
if not s.laytime_completed or s.laytime_allowed is None:
|
||||
Sof.save([s])
|
||||
@@ -2227,6 +2226,10 @@ class StatementOfFacts(ModelSQL, ModelView):
|
||||
hours *= Decimal(24)
|
||||
return datetime.timedelta(hours=float(hours))
|
||||
|
||||
text_delta = self._laytime_start_text_delta(condition)
|
||||
if text_delta is not None:
|
||||
return text_delta
|
||||
|
||||
if self.laytime_clause_hour:
|
||||
t = self.laytime_clause_hour
|
||||
elif self.laytime_clause_hour_o:
|
||||
@@ -2236,6 +2239,48 @@ class StatementOfFacts(ModelSQL, ModelView):
|
||||
return datetime.timedelta(
|
||||
hours=t.hour, minutes=t.minute, seconds=t.second)
|
||||
|
||||
@staticmethod
|
||||
def _laytime_start_text_delta(condition):
|
||||
text = (getattr(condition, 'laytime_start', None) or '').lower()
|
||||
match = re.search(
|
||||
r'([+-]?\s*\d+(?:[.,]\d+)?)\s*(hours?|hrs?|h|days?|d)',
|
||||
text)
|
||||
if not match:
|
||||
return
|
||||
quantity = Decimal(match.group(1).replace(' ', '').replace(',', '.'))
|
||||
unit = match.group(2)
|
||||
if unit.startswith('d'):
|
||||
quantity *= Decimal(24)
|
||||
return datetime.timedelta(hours=float(quantity))
|
||||
|
||||
def _laytime_start_base(self):
|
||||
condition = getattr(self, 'applied_condition', None)
|
||||
text = (getattr(condition, 'laytime_start', None) or '').lower()
|
||||
if 'arrival' in text:
|
||||
return self.arrival_time
|
||||
if 'hose' in text and 'connect' in text:
|
||||
return self.hoses_connected
|
||||
if 'pump' in text or 'loading' in text:
|
||||
return self.start_pumping
|
||||
return self.notice_of_readiness_time
|
||||
|
||||
def _applied_laytime_start(self):
|
||||
base = self._laytime_start_base()
|
||||
if not base:
|
||||
return
|
||||
return base + self._applied_turn_delta()
|
||||
|
||||
def _applied_laytime_end(self):
|
||||
condition = getattr(self, 'applied_condition', None)
|
||||
text = (getattr(condition, 'laytime_end', None) or '').lower()
|
||||
if 'pump' in text or 'loading' in text:
|
||||
return self.end_pumping
|
||||
if 'sailing' in text or 'sailed' in text:
|
||||
return self.sailing_time
|
||||
if 'hose' in text and ('disconnect' in text or 'off' in text):
|
||||
return self.hoses_disconnected
|
||||
return self.hoses_disconnected or self.end_pumping or self.sailing_time
|
||||
|
||||
def _applied_compensation_amount(self):
|
||||
balance = Decimal(str(self.laytime_balance or 0))
|
||||
if balance < 0:
|
||||
|
||||
@@ -214,6 +214,33 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
sof._applied_compensation_amount(), Decimal('-12000.00'))
|
||||
self.assertEqual(sof.compensation_type, 'demurrage')
|
||||
|
||||
@with_transaction()
|
||||
def test_sof_laytime_start_reads_text_clause_turn_time(self):
|
||||
'sof laytime start reads NOR plus hours from the condition text'
|
||||
Sof = Pool().get('sof.statement')
|
||||
sof = Sof()
|
||||
sof.applied_condition = Mock(
|
||||
turn_time=None,
|
||||
laytime_start='NOR + 6 hours')
|
||||
sof.notice_of_readiness_time = datetime.datetime(2026, 6, 1, 2, 0)
|
||||
|
||||
self.assertEqual(
|
||||
sof._applied_laytime_start(),
|
||||
datetime.datetime(2026, 6, 1, 8, 0))
|
||||
|
||||
@with_transaction()
|
||||
def test_sof_laytime_end_reads_hoses_disconnected_clause(self):
|
||||
'sof laytime end can use hoses disconnected from the condition text'
|
||||
Sof = Pool().get('sof.statement')
|
||||
sof = Sof()
|
||||
sof.applied_condition = Mock(laytime_end='Hoses disconnected')
|
||||
sof.end_pumping = datetime.datetime(2026, 6, 1, 12, 0)
|
||||
sof.hoses_disconnected = datetime.datetime(2026, 6, 1, 13, 0)
|
||||
|
||||
self.assertEqual(
|
||||
sof._applied_laytime_end(),
|
||||
datetime.datetime(2026, 6, 1, 13, 0))
|
||||
|
||||
@with_transaction()
|
||||
def test_sof_header_date_time_values_sync_to_datetime(self):
|
||||
'sof header date and time fields build the technical datetime value'
|
||||
|
||||
Reference in New Issue
Block a user