Commit b8fdde98 authored by Jérome Perrin's avatar Jérome Perrin

accounting: suggest date based on trade conditions in create related payment

This introduce a new utility script Invoice_getPaymentDueDate and removes
the previous attempts, SaleInvoiceTransaction_getDueDate and
TradeCondition_getDueDate that were never used.
parent 0f18a204
Pipeline #14149 failed with stage
in 0 seconds
# TODO: this script is not well tested and not fully implemented
# TODO: this is actually PaymentCondition_getDueDate
from DateTime import DateTime
from datetime import timedelta
import calendar
source = False
if context.getPortalType() in ('Purchase Invoice', 'Purchase Invoice Transaction',):
source = False
elif context.getPortalType() in ('Sale Invoice', 'Sale Invoice Transaction',):
source = True
else: # internal invoices
source = context.AccountingTransaction_isSourceView()
if context.getPortalType() == 'Payment Condition':
delivery = context.getParentValue()
payment_condition = context
else:
delivery = context
payment_condition = context.getDefaultPaymentConditionValue()
delivery = context
trade_condition = delivery.getSpecialiseValue(
portal_type=(
'Purchase Trade Condition',
'Sale Trade Condition',
'Internal Trade Condition',
)
)
if trade_condition is None:
return None
payment_condition = trade_condition.getDefaultPaymentConditionValue()
if payment_condition is None:
return None
# Absolute payment date has priority
if payment_condition.getPaymentDate():
......@@ -18,45 +32,38 @@ def OrderDateGetter(invoice):
def getter():
packing_list = invoice.getCausalityValue(
portal_type=context.getPortalDeliveryTypeList())
if packing_list:
if packing_list is not None:
order = packing_list.getCausalityValue(
portal_type=context.getPortalOrderTypeList())
return order.getStartDate() # TODO start or stop ? -> based on source/destination
return order.getStartDate()
return getter
def PackingListDateGetter(invoice):
def getter():
packing_list = invoice.getCausalityValue(
portal_type=context.getPortalDeliveryTypeList())
if packing_list:
return packing_list.getStartDate() # TODO start or stop ? -> based on source/destination
if packing_list is not None:
return packing_list.getStopDate()
return getter
case = {
'invoice': delivery.getStartDate,
'order': OrderDateGetter(delivery),
'packing list': PackingListDateGetter(delivery),
date_getter = {
'invoice': delivery.getStartDate if source else delivery.getStopDate,
'order': OrderDateGetter(delivery),
'packing_list': PackingListDateGetter(delivery),
}
due_date = case.get(payment_condition.getTradeDate(), delivery.getStartDate)()
due_date += payment_condition.getPaymentTerm(0)
due_date = date_getter.get(payment_condition.getTradeDate(), lambda: None)()
if not due_date:
return None
due_date = due_date.asdatetime()
pat = payment_condition.getPaymentAdditionalTerm()
due_date += timedelta(days=payment_condition.getPaymentTerm(0))
if payment_condition.getPaymentEndOfMonth():
i = 0
month = due_date.month()
while (month == (due_date + i).month()):
i += 1
due_date = (due_date + i - 1)
if pat:
due_date += pat
else:
if pat:
i = 0
month = due_date.month()
while (month == (due_date + i).month()):
i -= 1
due_date = (due_date + i + pat)
return due_date
last_day_of_month = calendar.monthrange(due_date.year, due_date.month)[1]
due_date = due_date.replace(day=last_day_of_month)
due_date += timedelta(days=payment_condition.getPaymentAdditionalTerm(0))
return DateTime(due_date)
......@@ -54,11 +54,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>TradeCondition_getDueDate</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
<value> <string>Invoice_getPaymentDueDate</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -9,7 +9,9 @@
<item>
<key> <string>delegated_list</string> </key>
<value>
<list/>
<list>
<string>default</string>
</list>
</value>
</item>
<item>
......@@ -50,6 +52,12 @@
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
......@@ -69,6 +77,12 @@
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>default</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>your_date</string> </value>
......@@ -87,4 +101,17 @@
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>context/Invoice_getPaymentDueDate</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
packing_list_list = context.getCausalityValueList(portal_type='Sale Packing List')
if len(packing_list_list) > 0:
packing_list = packing_list_list[0]
order = packing_list.getCausalityValue(portal_type='Sale Order')
from DateTime import DateTime
due_date = order.getPaymentConditionPaymentDate( DateTime() )
pat = None #order.getPaymentAdditionalTerm()
else:
due_date = context.getStartDate()
pat = None
due_date += context.getPaymentConditionPaymentTerm(30)
peom = context.getPaymentEndOfMonth()
if peom:
i = 0
month = due_date.month()
while (month == (due_date + i).month()):
i += 1
due_date = (due_date + i - 1)
if pat != None:
due_date += pat
else:
if pat != None:
i = 0
month = due_date.month()
while (month == (due_date + i).month()):
i -= 1
due_date = (due_date + i + pat)
return due_date
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SaleInvoiceTransaction_getDueDate</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -6171,3 +6171,146 @@ class TestAccountingPeriod(AccountingTestCase):
start_date=DateTime('2023/01/01'),
stop_date=DateTime('2023/12/31'),)
self.portal.portal_workflow.doActionFor(third_accounting_period, 'start_action')
class TestInvoice_getPaymentTransactionDueDate(AccountingTestCase):
"""Test Invoice_getPaymentTransactionDueDate.
"""
def test_due_date_calculation(self):
"""Test the rule to calculate the due date from payment condition properties.
"""
sale_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type='Sale Trade Condition',
)
invoice = self._makeOne(
portal_type='Sale Invoice Transaction',
stop_date=DateTime(1970, 1, 1)
)
self.assertEqual(invoice.Invoice_getPaymentDueDate(), None)
invoice.setSpecialiseValue(sale_trade_condition)
self.assertEqual(invoice.Invoice_getPaymentDueDate(), None)
sale_trade_condition.setPaymentConditionTradeDate('invoice')
for invoice_date, payment_term, payment_end_of_month, payment_additional_term, expected_date in (
(DateTime('2001/01/01'), 10, False, 0, DateTime('2001/01/11')),
(DateTime('2001/01/01'), 10, True, 0, DateTime('2001/01/31')),
(DateTime('2001/01/01'), 10, True, 10, DateTime('2001/02/10')),
(DateTime('2001/01/31'), 10, False, 0, DateTime('2001/02/10')),
(DateTime('2001/01/31'), 10, True, 0, DateTime('2001/02/28')),
(DateTime('2001/01/31'), 10, True, 15, DateTime('2001/03/15')),
# leap year
(DateTime('2004/01/31'), 10, True, 0, DateTime('2004/02/29')),
# this keeps hours/minutes and timezones
(DateTime('2001/01/01 01:02:03 GMT+2'), 10, False, 0, DateTime('2001/01/11 01:02:03 GMT+2')),
# and works well across daylight time switchs
(DateTime('2001/03/31 00:00:00 Europe/Paris'), 10, False, 0, DateTime('2001/04/10 00:00:00 Europe/Paris')),
(DateTime('2001/03/31 00:00:00 Europe/Paris'), 0, False, 10, DateTime('2001/04/10 00:00:00 Europe/Paris')),
(DateTime('2001/10/27 00:00:00 Europe/Paris'), 10, False, 0, DateTime('2001/11/06 00:00:00 Europe/Paris')),
(DateTime('2001/10/27 00:00:00 Europe/Paris'), 0, False, 10, DateTime('2001/11/06 00:00:00 Europe/Paris')),
):
invoice.setStartDate(invoice_date)
sale_trade_condition.setPaymentConditionPaymentTerm(payment_term)
sale_trade_condition.setPaymentConditionPaymentEndOfMonth(payment_end_of_month)
sale_trade_condition.setPaymentConditionPaymentAdditionalTerm(payment_additional_term)
self.assertEqual(
invoice.Invoice_getPaymentDueDate(),
expected_date,
"{actual} != {expected_date} for case invoice_date:{invoice_date}, "
"payment_term:{payment_term}, payment_end_of_month:{payment_end_of_month}, "
"payment_additional_term:{payment_additional_term}".format(
actual=invoice.Invoice_getPaymentDueDate(),
**locals()
)
)
def test_sale_invoice_packing_list_or_order(self):
"""Test how the date is selected from invoice, packing list or order, for sales case
"""
sale_trade_condition = self.portal.sale_trade_condition_module.newContent(
portal_type='Sale Trade Condition',
payment_condition_payment_term=10,
)
order = self.portal.sale_order_module.newContent(
portal_type='Sale Order',
start_date=DateTime(2001, 1, 1),
stop_date=DateTime(1970, 1, 1),
)
delivery = self.portal.sale_packing_list_module.newContent(
portal_type='Sale Packing List',
start_date=DateTime(1970, 1, 1),
stop_date=DateTime(2002, 1, 1),
causality_value=order,
)
invoice = self._makeOne(
portal_type='Sale Invoice Transaction',
start_date=DateTime(2003, 1, 1),
stop_date=DateTime(1970, 1, 1),
specialise_value=sale_trade_condition,
)
trade_date = self.portal.portal_categories.trade_date
sale_trade_condition.setPaymentConditionTradeDate(trade_date.invoice.getRelativeUrl())
self.assertEqual(
invoice.Invoice_getPaymentDueDate(),
DateTime(2003, 1, 11))
sale_trade_condition.setPaymentConditionTradeDate(trade_date.packing_list.getRelativeUrl())
# no related delivery
self.assertEqual(
invoice.Invoice_getPaymentDueDate(),
None)
invoice.setCausalityValue(delivery)
self.assertEqual(
invoice.Invoice_getPaymentDueDate(),
DateTime(2002, 1, 11))
sale_trade_condition.setPaymentConditionTradeDate(trade_date.order.getRelativeUrl())
self.assertEqual(
invoice.Invoice_getPaymentDueDate(),
DateTime(2001, 1, 11))
def test_purchase_invoice_packing_list_or_order(self):
"""Test how the date is selected from invoice, packing list or order, for purchase case
"""
purchase_trade_condition = self.portal.purchase_trade_condition_module.newContent(
portal_type='Purchase Trade Condition',
payment_condition_payment_term=10,
)
order = self.portal.purchase_order_module.newContent(
portal_type='Purchase Order',
start_date=DateTime(2001, 1, 1),
stop_date=DateTime(1970, 1, 1),
)
delivery = self.portal.purchase_packing_list_module.newContent(
portal_type='Purchase Packing List',
start_date=DateTime(1970, 1, 1),
stop_date=DateTime(2002, 1, 1),
causality_value=order,
)
invoice = self._makeOne(
portal_type='Purchase Invoice Transaction',
start_date=DateTime(2003, 1, 1),
specialise_value=purchase_trade_condition,
)
trade_date = self.portal.portal_categories.trade_date
purchase_trade_condition.setPaymentConditionTradeDate(trade_date.invoice.getRelativeUrl())
self.assertEqual(
invoice.Invoice_getPaymentDueDate(),
DateTime(2003, 1, 11))
purchase_trade_condition.setPaymentConditionTradeDate(trade_date.packing_list.getRelativeUrl())
# no related delivery
self.assertEqual(
invoice.Invoice_getPaymentDueDate(),
None)
invoice.setCausalityValue(delivery)
self.assertEqual(
invoice.Invoice_getPaymentDueDate(),
DateTime(2002, 1, 11))
purchase_trade_condition.setPaymentConditionTradeDate(trade_date.order.getRelativeUrl())
self.assertEqual(
invoice.Invoice_getPaymentDueDate(),
DateTime(2001, 1, 11))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment