[IMP] account: payment term - Days end of month
This commit add a new delay_type that will add to the date the nb_days then go the end of the month and finally add a new field called days_next_month. This field is a Char because we want the field to be of size 2. Also, we added a constraint that this field must be numeric and between 0 and 31. Ex of use with Invoice date the 25/11/2023, if we have a payment term with 90 for the nb_days and 10 for the days_next_month: +90 days = 23/02/2024 End of month = 29/02/2024 +10 days = 10/03/2024 (Also, there is a special case handling when the day of the month is 29, 30 or 31 to avoid exceeding the next month's end. For instance, with a payment term of 30 days end of month and using the 31st of a month, prevent calculation from moving beyond the end of the next month (e.g., early March instead of end February) closes odoo/odoo#143758 Task: 3609320 Signed-off-by: Florian Gilbert (flg) <flg@odoo.com>
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
@@ -0,0 +1,21 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Payment Term - Days end of month on the',
|
||||
'version': '1.0',
|
||||
'category': 'Accounting/Accounting',
|
||||
'description': """
|
||||
Bridge module to add a new payment term - Days end of month on the
|
||||
""",
|
||||
'website': 'https://www.odoo.com/app/invoicing',
|
||||
'depends': ['account'],
|
||||
'data': [
|
||||
'views/account_payment_term_views.xml',
|
||||
],
|
||||
'demo': [
|
||||
'demo/account_payment_term_demo.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
'license': 'OEEL-1',
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="account_payment_term_advance" model="account.payment.term">
|
||||
<field name="name">90 days, on the 10th</field>
|
||||
<field name="note">Payment terms: 90 days, on the 10th</field>
|
||||
<field name="line_ids" eval="[
|
||||
Command.clear(),
|
||||
Command.create({'value': 'percent', 'value_amount': 100.0, 'delay_type': 'days_end_of_month_on_the','nb_days': 90, 'days_next_month': 10})]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,65 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * account_payment_term
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 17.0+e\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-12-13 08:17+0000\n"
|
||||
"PO-Revision-Date: 2023-12-13 08:17+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: account_payment_term
|
||||
#: model:account.payment.term,name:account_payment_term.account_payment_term_advance
|
||||
msgid "90 days, on the 10th"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_payment_term
|
||||
#: model:ir.model.fields.selection,name:account_payment_term.selection__account_payment_term_line__delay_type__days_end_of_month_on_the
|
||||
msgid "Days end of month on the"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_payment_term
|
||||
#: model:ir.model.fields,field_description:account_payment_term.field_account_payment_term_line__days_next_month
|
||||
msgid "Days on the next month"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_payment_term
|
||||
#: model:ir.model.fields,field_description:account_payment_term.field_account_payment_term_line__delay_type
|
||||
msgid "Delay Type"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_payment_term
|
||||
#: model:ir.model.fields,field_description:account_payment_term.field_account_payment_term_line__display_days_next_month
|
||||
msgid "Display Days Next Month"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_payment_term
|
||||
#: model:ir.model,name:account_payment_term.model_account_payment_term_line
|
||||
msgid "Payment Terms Line"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_payment_term
|
||||
#: model_terms:account.payment.term,note:account_payment_term.account_payment_term_advance
|
||||
msgid "Payment terms: 90 days, on the 10th"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_payment_term
|
||||
#. odoo-python
|
||||
#: code:addons/account_payment_term/models/account_payment_term.py:0
|
||||
#, python-format
|
||||
msgid "The days added must be a number and has to be between 0 and 31."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_payment_term
|
||||
#. odoo-python
|
||||
#: code:addons/account_payment_term/models/account_payment_term.py:0
|
||||
#, python-format
|
||||
msgid "The days added must be between 0 and 31."
|
||||
msgstr ""
|
||||
@@ -0,0 +1,3 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_payment_term
|
||||
@@ -0,0 +1,54 @@
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools import date_utils
|
||||
|
||||
|
||||
class AccountPaymentTermLine(models.Model):
|
||||
_inherit = "account.payment.term.line"
|
||||
|
||||
delay_type = fields.Selection(
|
||||
selection_add=[('days_end_of_month_on_the', 'Days end of month on the')],
|
||||
ondelete={'days_end_of_month_on_the': 'set default'}
|
||||
)
|
||||
display_days_next_month = fields.Boolean(compute='_compute_display_days_next_month')
|
||||
days_next_month = fields.Char(
|
||||
string='Days on the next month',
|
||||
readonly=False,
|
||||
store=True,
|
||||
default='10',
|
||||
size=2,
|
||||
)
|
||||
|
||||
@api.constrains('days_next_month')
|
||||
def _check_valid_char_value(self):
|
||||
for record in self:
|
||||
if record.days_next_month.isnumeric():
|
||||
if not (0 <= int(record.days_next_month) <= 31):
|
||||
raise ValidationError(_('The days added must be between 0 and 31.'))
|
||||
else:
|
||||
raise ValidationError(_('The days added must be a number and has to be between 0 and 31.'))
|
||||
|
||||
@api.depends('delay_type')
|
||||
def _compute_display_days_next_month(self):
|
||||
for record in self:
|
||||
record.display_days_next_month = record.delay_type == 'days_end_of_month_on_the'
|
||||
|
||||
def _get_due_date(self, date_ref):
|
||||
res = super()._get_due_date(date_ref)
|
||||
|
||||
due_date = fields.Date.from_string(date_ref) or fields.Date.today()
|
||||
if self.delay_type == 'days_end_of_month_on_the':
|
||||
date_end_of_month = date_utils.end_of(due_date + relativedelta(days=self.nb_days), 'month')
|
||||
|
||||
# Special case handling when the day of the month is 29, 30 or 31 to avoid exceeding the next month's end
|
||||
# For instance, with a payment term of 30 days end of month and using the 31st of a month,
|
||||
# prevent calculation from moving beyond the end of the next month (e.g., early March instead of end February)
|
||||
if self.days_next_month in {'29', '30', '31'}:
|
||||
# We get the min of the days the user enter in the payment term and the last day of the next month
|
||||
days_next_month = relativedelta(days=min(int(self.days_next_month), (date_end_of_month + relativedelta(month=2)).day))
|
||||
else:
|
||||
days_next_month = relativedelta(days=int(self.days_next_month))
|
||||
return date_end_of_month + days_next_month
|
||||
return res
|
||||
@@ -0,0 +1,3 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_payment_term
|
||||
@@ -0,0 +1,58 @@
|
||||
from odoo import Command, fields
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import Form
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestAccountPaymentTerms(AccountTestInvoicingCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls, chart_template_ref=None):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
|
||||
cls.invoice = cls.init_invoice('out_refund', products=cls.product_a + cls.product_b)
|
||||
|
||||
cls.pay_term_days_end_of_month_10 = cls.env['account.payment.term'].create({
|
||||
'name': "basic case",
|
||||
'line_ids': [
|
||||
Command.create({
|
||||
'value': 'percent',
|
||||
'value_amount': 100,
|
||||
'nb_days': 30,
|
||||
'delay_type': 'days_end_of_month_on_the',
|
||||
'days_next_month': 10,
|
||||
}),
|
||||
],
|
||||
})
|
||||
cls.pay_term_days_end_of_month_31 = cls.env['account.payment.term'].create({
|
||||
'name': "special case",
|
||||
'line_ids': [
|
||||
Command.create({
|
||||
'value': 'percent',
|
||||
'value_amount': 100,
|
||||
'nb_days': 30,
|
||||
'delay_type': 'days_end_of_month_on_the',
|
||||
'days_next_month': 31,
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
def test_payment_term_days_end_of_month_on_the(self):
|
||||
"""
|
||||
This test will check that payment terms with a delay_type 'days_end_of_month_on_the' works as expected.
|
||||
It will check if the date of the date maturity is correctly calculated depending on the invoice date and payment
|
||||
term selected.
|
||||
"""
|
||||
with Form(self.invoice) as basic_case:
|
||||
basic_case.invoice_payment_term_id = self.pay_term_days_end_of_month_10
|
||||
basic_case.invoice_date = '2023-12-12'
|
||||
|
||||
expected_date_basic_case = self.invoice.line_ids.filtered(lambda l: l.account_id == self.company_data['default_account_receivable']).mapped('date_maturity'),
|
||||
self.assertEqual(expected_date_basic_case[0], [fields.Date.from_string('2024-02-10')])
|
||||
|
||||
with Form(self.invoice) as special_case:
|
||||
special_case.invoice_payment_term_id = self.pay_term_days_end_of_month_31
|
||||
special_case.invoice_date = '2023-12-12'
|
||||
|
||||
expected_date_special_case = self.invoice.line_ids.filtered(lambda l: l.account_id == self.company_data['default_account_receivable']).mapped('date_maturity'),
|
||||
self.assertEqual(expected_date_special_case[0], [fields.Date.from_string('2024-02-29')])
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="account_payment_term_form" model="ir.ui.view">
|
||||
<field name="model">account.payment.term</field>
|
||||
<field name="name">account.payment.term.form</field>
|
||||
<field name="inherit_id" ref="account.view_payment_term_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='line_ids']/tree['delay_type']" position="inside">
|
||||
<field name="display_days_next_month" column_invisible="1"/>
|
||||
<field name="days_next_month" width="30px" nolabel="1" invisible="not display_days_next_month"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user