[IMP] account: improve payment terms with new options

This commit improves the payment terms by:
- making the payment terms lines editable on-the-fly removing the need for a wizard
- refactoring the computation of the payment terms
- adds new demo data for payment terms
- adds an example section where the user can test and preview the due dates and amount of its payment terms
- adding a new parameter "Display terms on invoice". If set, the payment deadlines and respective due amounts will be detailed on invoices.

The refactoring:
- adds a field `months` (months to add after the invoice date)
- adds a field `end_month` (if True, switch to the end of the month after having added months or days)
- renames the field `day_of_the_month` to `days_after` (days to be added after the switching to the end of the month, only if `end_month` is set)
- deletes the field `option` which was too restrictive
- modifies the calculation of the payments terms taking into account these new fields

This allows some combinations that were not possible before.
Example: The French payment terms "30 jours fin de mois le 10" (which should be interpreted as "10 days after the end of the next month") can be encoded as
```
months = 1
days = 0
end_month = True
days_after = 10
```

Task id 2852814

closes odoo/odoo#91490

Related: odoo/enterprise#29661
Related: odoo/upgrade#3524
Signed-off-by: Laurent Smet <las@odoo.com>
This commit is contained in:
Ricardo Gomes Rodrigues (rigr)
2022-09-01 19:16:19 +02:00
parent 87b21cd711
commit 3dad4b3cb1
8 changed files with 270 additions and 152 deletions
+17 -8
View File
@@ -37,49 +37,58 @@
<record id="account_payment_term_immediate" model="account.payment.term">
<field name="name">Immediate Payment</field>
<field name="note">Payment terms: Immediate Payment</field>
<field name="line_ids" eval="[Command.clear(), Command.create({'value': 'balance', 'value_amount': 0.0})]"/>
</record>
<record id="account_payment_term_15days" model="account.payment.term">
<field name="name">15 Days</field>
<field name="note">Payment terms: 15 Days</field>
<field name="line_ids" eval="[(5, 0), (0, 0, {'value': 'balance', 'value_amount': 0.0, 'sequence': 500, 'days': 15, 'option': 'day_after_invoice_date'})]"/>
<field name="line_ids" eval="[Command.clear(), Command.create({'value': 'balance', 'value_amount': 0.0, 'days': 15})]"/>
</record>
<record id="account_payment_term_21days" model="account.payment.term">
<field name="name">21 Days</field>
<field name="note">Payment terms: 21 Days</field>
<field name="line_ids" eval="[(5, 0), (0, 0, {'value': 'balance', 'value_amount': 0.0, 'sequence': 500, 'days': 21, 'option': 'day_after_invoice_date'})]"/>
<field name="line_ids" eval="[Command.clear(), Command.create({'value': 'balance', 'value_amount': 0.0, 'days': 21})]"/>
</record>
<record id="account_payment_term_30days" model="account.payment.term">
<field name="name">30 Days</field>
<field name="note">Payment terms: 30 Days</field>
<field name="line_ids" eval="[(5, 0), (0, 0, {'value': 'balance', 'value_amount': 0.0, 'sequence': 500, 'days': 30, 'option': 'day_after_invoice_date'})]"/>
<field name="line_ids" eval="[Command.clear(), Command.create({'value': 'balance', 'value_amount': 0.0, 'days': 30})]"/>
</record>
<record id="account_payment_term_45days" model="account.payment.term">
<field name="name">45 Days</field>
<field name="note">Payment terms: 45 Days</field>
<field name="line_ids" eval="[(5, 0), (0, 0, {'value': 'balance', 'value_amount': 0.0, 'sequence': 500, 'days': 45, 'option': 'day_after_invoice_date'})]"/>
<field name="line_ids" eval="[Command.clear(), Command.create({'value': 'balance', 'value_amount': 0.0, 'days': 45})]"/>
</record>
<record id="account_payment_term_2months" model="account.payment.term">
<field name="name">2 Months</field>
<field name="note">Payment terms: 2 Months</field>
<field name="line_ids" eval="[(5, 0), (0, 0, {'value': 'balance', 'value_amount': 0.0, 'sequence': 500, 'days': 60, 'option': 'day_after_invoice_date'})]"/>
<field name="line_ids" eval="[Command.clear(), Command.create({'value': 'balance', 'value_amount': 0.0, 'months': 2})]"/>
</record>
<record id="account_payment_term_end_following_month" model="account.payment.term">
<field name="name">End of Following Month</field>
<field name="note">Payment terms: End of Following Month</field>
<field name="line_ids" eval="[(5, 0), (0, 0, {'value': 'balance', 'value_amount': 0.0, 'sequence': 500, 'days': 31, 'option': 'day_following_month'})]"/>
<field name="line_ids" eval="[Command.clear(), Command.create({'value': 'balance', 'value_amount': 0.0, 'months': 1, 'end_month': True})]"/>
</record>
<record id="account_payment_term_30_days_end_month_the_10" model="account.payment.term">
<field name="name">30 days End of Month on the 10th</field>
<field name="note">Payment terms: 30 days End of Month on the 10th</field>
<field name="line_ids" eval="[Command.clear(), Command.create({'value': 'balance', 'value_amount': 0.0, 'months': 1, 'end_month': True, 'days_after': 10})]"/>
</record>
<record id="account_payment_term_advance_60days" model="account.payment.term">
<field name="name">30% Now, Balance 60 Days</field>
<field name="note">Payment terms: 30% Now, Balance 60 Days</field>
<field name="line_ids" eval="[(5, 0), (0, 0, {'value': 'percent', 'value_amount': 30.0, 'sequence': 400, 'days': 0, 'option': 'day_after_invoice_date'}),
(0, 0, {'value': 'balance', 'value_amount': 0.0, 'sequence': 500, 'days': 60, 'option': 'day_after_invoice_date'})]"/>
<field name="line_ids" eval="[
Command.clear(),
Command.create({'value': 'percent', 'value_amount': 30.0, 'days': 0}),
Command.create({'value': 'balance', 'value_amount': 0.0, 'days': 60})]"/>
</record>
<!--
+4 -2
View File
@@ -24,8 +24,10 @@
<record id="account_payment_term_advance" model="account.payment.term">
<field name="name">30% Advance End of Following Month</field>
<field name="note">Payment terms: 30% Advance End of Following Month</field>
<field name="line_ids" eval="[(5, 0), (0, 0, {'value': 'percent', 'value_amount': 30.0, 'sequence': 400, 'days': 0, 'option': 'day_after_invoice_date'}),
(0, 0, {'value': 'balance', 'value_amount': 0.0, 'sequence': 500, 'days': 31, 'option': 'day_following_month'})]"/>
<field name="line_ids" eval="[
Command.clear(),
Command.create({'value': 'percent', 'value_amount': 30.0, 'days': 0}),
Command.create({'value': 'balance', 'value_amount': 0.0, 'months': 1, 'end_month': True})]"/>
</record>
<record id="base.user_demo" model="res.users">
+69 -47
View File
@@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
from odoo import api, exceptions, fields, models, _
from odoo import api, fields, models, _, Command
from odoo.exceptions import UserError, ValidationError
from odoo.tools import format_date, formatLang
from dateutil.relativedelta import relativedelta
@@ -12,7 +13,7 @@ class AccountPaymentTerm(models.Model):
_order = "sequence, id"
def _default_line_ids(self):
return [(0, 0, {'value': 'balance', 'value_amount': 0.0, 'sequence': 9, 'days': 0, 'option': 'day_after_invoice_date'})]
return [Command.create({'value': 'balance', 'value_amount': 0.0, 'days': 0, 'end_month': False})]
name = fields.Char(string='Payment Terms', translate=True, required=True)
active = fields.Boolean(default=True, help="If the active field is set to False, it will allow you to hide the payment terms without removing it.")
@@ -20,16 +21,55 @@ class AccountPaymentTerm(models.Model):
line_ids = fields.One2many('account.payment.term.line', 'payment_id', string='Terms', copy=True, default=_default_line_ids)
company_id = fields.Many2one('res.company', string='Company')
sequence = fields.Integer(required=True, default=10)
display_on_invoice = fields.Boolean(string='Display terms on invoice', help="If set, the payment deadlines and respective due amounts will be detailed on invoices.")
example_amount = fields.Float(default=100, store=False)
example_date = fields.Date(string='Date example', default=fields.Date.context_today, store=False)
example_invalid = fields.Boolean(compute='_compute_example_invalid')
example_preview = fields.Html(compute='_compute_example_preview')
@api.depends('line_ids')
def _compute_example_invalid(self):
for payment_term in self:
payment_term.example_invalid = len(payment_term.line_ids.filtered(lambda l: l.value == 'balance')) != 1
@api.depends('example_amount', 'example_date', 'line_ids.value', 'line_ids.value_amount',
'line_ids.months', 'line_ids.days', 'line_ids.end_month', 'line_ids.days_after')
def _compute_example_preview(self):
for record in self:
example_preview = ""
if not record.example_invalid:
currency = self.env.company.currency_id
terms = record.compute(record.example_amount, record.example_amount, record.example_date, currency)
for i, (date, amount) in enumerate(record._get_amount_by_date(terms, currency).items()):
example_preview += f"""
<div style='margin-left: 20px;'>
<b>{i+1}#</b>
Installment of
<b>{formatLang(self.env, amount, monetary=True, currency_obj=currency)}</b>
on
<b style='color: #704A66;'>{date}</b>
</div>
"""
record.example_preview = example_preview
@api.model
def _get_amount_by_date(self, terms, currency):
"""
Returns a dictionary with the amount for each date of the payment term (grouped by date, sorted by date and ignoring null amounts).
"""
terms = sorted(terms)
amount_by_date = {}
for date, amount in terms:
date = format_date(self.env, date)
if currency.compare_amounts(amount[0], 0) > 0:
amount_by_date[date] = amount_by_date.get(date, 0) + amount[0]
return amount_by_date
@api.constrains('line_ids')
def _check_lines(self):
for terms in self:
payment_term_lines = terms.line_ids.sorted()
if payment_term_lines and payment_term_lines[-1].value != 'balance':
raise ValidationError(_('The last line of a Payment Term should have the Balance type.'))
lines = terms.line_ids.filtered(lambda r: r.value == 'balance')
if len(lines) > 1:
raise ValidationError(_('A Payment Term should have only one line of type Balance.'))
if len(terms.line_ids.filtered(lambda r: r.value == 'balance')) != 1:
raise ValidationError(_('The Payment Term must have one Balance line.'))
def compute(self, company_value, foreign_value, date_ref, currency):
"""Get the distribution of this payment term.
@@ -48,7 +88,7 @@ class AccountPaymentTerm(models.Model):
sign = company_value < 0 and -1 or 1
result = []
company_currency = self.env.company.currency_id
for line in self.line_ids:
for line in self.line_ids.sorted(lambda line: line.value == 'balance'):
if line.value == 'fixed':
company_amt = sign * company_currency.round(line.value_amount)
foreign_amt = sign * currency.round(line.value_amount)
@@ -58,20 +98,7 @@ class AccountPaymentTerm(models.Model):
elif line.value == 'balance':
company_amt = company_currency.round(company_amount)
foreign_amt = currency.round(foreign_amount)
next_date = fields.Date.from_string(date_ref)
if line.option == 'day_after_invoice_date':
next_date += relativedelta(days=line.days)
if line.day_of_the_month > 0:
months_delta = (line.day_of_the_month < next_date.day) and 1 or 0
next_date += relativedelta(day=line.day_of_the_month, months=months_delta)
elif line.option == 'after_invoice_month':
next_first_date = next_date + relativedelta(day=1, months=1) # Getting 1st of next month
next_date = next_first_date + relativedelta(days=line.days - 1)
elif line.option == 'day_following_month':
next_date += relativedelta(day=line.days, months=1)
elif line.option == 'day_current_month':
next_date += relativedelta(day=line.days, months=0)
result.append((fields.Date.to_date(next_date), (company_amt, foreign_amt)))
result.append((line._get_due_date(date_ref), (company_amt, foreign_amt)))
company_amount -= company_amt
foreign_amt -= foreign_amount
company_amount = sum(company_amt for _, (company_amt, _) in result)
@@ -99,27 +126,29 @@ class AccountPaymentTerm(models.Model):
class AccountPaymentTermLine(models.Model):
_name = "account.payment.term.line"
_description = "Payment Terms Line"
_order = "sequence, id"
_order = "id"
value = fields.Selection([
('balance', 'Balance'),
('percent', 'Percent'),
('fixed', 'Fixed Amount')
], string='Type', required=True, default='balance',
], string='Type', required=True, default='percent',
help="Select here the kind of valuation related to this payment terms line.")
value_amount = fields.Float(string='Value', digits='Payment Terms', help="For percent enter a ratio between 0-100.")
days = fields.Integer(string='Number of Days', required=True, default=0)
day_of_the_month = fields.Integer(string='Day of the month', help="Day of the month on which the invoice must come to its term. If zero or negative, this value will be ignored, and no specific day will be set. If greater than the last day of a month, this number will instead select the last day of this month.")
option = fields.Selection([
('day_after_invoice_date', "days after the invoice date"),
('after_invoice_month', "days after the end of the invoice month"),
('day_following_month', "of the following month"),
('day_current_month', "of the current month"),
],
default='day_after_invoice_date', required=True, string='Options'
)
months = fields.Integer(string='Months', required=True, default=0)
days = fields.Integer(string='Days', required=True, default=0)
end_month = fields.Boolean(string='End of month', help="Switch to end of the month after having added months or days")
days_after = fields.Integer(string='Days after End of month', help="Days to add after the end of the month")
payment_id = fields.Many2one('account.payment.term', string='Payment Terms', required=True, index=True, ondelete='cascade')
sequence = fields.Integer(default=10, help="Gives the sequence order when displaying a list of payment terms lines.")
def _get_due_date(self, date_ref):
due_date = fields.Date.from_string(date_ref)
due_date += relativedelta(months=self.months)
due_date += relativedelta(days=self.days)
if self.end_month:
due_date += relativedelta(day=31)
due_date += relativedelta(days=self.days_after)
return due_date
@api.constrains('value', 'value_amount')
def _check_percent(self):
@@ -127,15 +156,8 @@ class AccountPaymentTermLine(models.Model):
if term_line.value == 'percent' and (term_line.value_amount < 0.0 or term_line.value_amount > 100.0):
raise ValidationError(_('Percentages on the Payment Terms lines must be between 0 and 100.'))
@api.constrains('days')
def _check_days(self):
@api.constrains('months', 'days', 'days_after')
def _check_positive(self):
for term_line in self:
if term_line.option in ('day_following_month', 'day_current_month') and term_line.days <= 0:
raise ValidationError(_("The day of the month used for this term must be strictly positive."))
elif term_line.days < 0:
raise ValidationError(_("The number of days used for a payment term cannot be negative."))
@api.onchange('option')
def _onchange_option(self):
if self.option in ('day_current_month', 'day_following_month'):
self.days = 0
if term_line.months < 0 or term_line.days < 0:
raise ValidationError(_('The Months and Days of the Payment Terms lines must be positive.'))
+2 -5
View File
@@ -129,16 +129,13 @@ class AccountTestInvoicingCommon(TransactionCase):
(0, 0, {
'value': 'percent',
'value_amount': 30.0,
'sequence': 400,
'days': 0,
'option': 'day_after_invoice_date',
}),
(0, 0, {
'value': 'balance',
'value_amount': 0.0,
'sequence': 500,
'days': 31,
'option': 'day_following_month',
'months': 1,
'end_month': True,
}),
],
})
@@ -2884,16 +2884,12 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
(0, 0, {
'value': 'percent',
'value_amount': 100.0,
'sequence': 10,
'days': 0,
'option': 'day_after_invoice_date',
}),
(0, 0, {
'value': 'balance',
'value_amount': 0.0,
'sequence': 20,
'days': 0,
'option': 'day_after_invoice_date',
}),
],
})
+141 -28
View File
@@ -1,13 +1,14 @@
# -*- coding: utf-8 -*-
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.exceptions import ValidationError
from odoo.tests import tagged
from odoo import fields
from odoo.tests.common import Form
@tagged('post_install', '-at_install')
class TestAccountInvoiceRounding(AccountTestInvoicingCommon):
class TestAccountPaymentTerms(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
@@ -17,30 +18,18 @@ class TestAccountInvoiceRounding(AccountTestInvoicingCommon):
(0, 0, {
'value': 'balance',
'days': 0,
'option': 'day_after_invoice_date',
}),
],
})
cls.pay_term_min_31days_15th = cls.env['account.payment.term'].create({
'name': 'the 15th of the month, min 31 days from now',
cls.pay_term_next_month_on_the_15 = cls.env['account.payment.term'].create({
'name': 'Next month on the 15th',
'line_ids': [
(0, 0, {
'value': 'balance',
'days': 31,
'day_of_the_month': 15,
'option': 'day_after_invoice_date',
}),
],
})
cls.pay_term_45_end_month = cls.env['account.payment.term'].create({
'name': '45 Days from End of Month',
'line_ids': [
(0, 0, {
'value': 'balance',
'days': 45,
'option': 'after_invoice_month',
'days': 0,
'end_month': True,
'days_after': 15,
}),
],
})
@@ -50,8 +39,8 @@ class TestAccountInvoiceRounding(AccountTestInvoicingCommon):
'line_ids': [
(0, 0, {
'value': 'balance',
'days': 31,
'option': 'day_current_month',
'days': 0,
'end_month': True
}),
],
})
@@ -61,8 +50,102 @@ class TestAccountInvoiceRounding(AccountTestInvoicingCommon):
'line_ids': [
(0, 0, {
'value': 'balance',
'days': 1,
'option': 'day_following_month',
'days': 0,
'end_month': True,
'days_after': 1,
}),
],
})
cls.pay_term_net_30_days = cls.env['account.payment.term'].create({
'name': 'Net 30 days',
'line_ids': [
(0, 0, {
'value': 'balance',
'days': 30,
}),
],
})
cls.pay_term_30_days_end_of_month = cls.env['account.payment.term'].create({
'name': '30 days end of month',
'line_ids': [
(0, 0, {
'value': 'balance',
'days': 30,
'end_month': True
}),
],
})
cls.pay_term_1_month_end_of_month = cls.env['account.payment.term'].create({
'name': '1 month, end of month',
'line_ids': [
(0, 0, {
'value': 'balance',
'months': 1,
'days': 0,
'end_month': True
}),
],
})
cls.pay_term_30_days_end_of_month_the_10 = cls.env['account.payment.term'].create({
'name': '30 days end of month the 10th',
'line_ids': [
(0, 0, {
'value': 'balance',
'days': 30,
'end_month': True,
'days_after': 10,
}),
],
})
cls.pay_term_90_days_end_of_month_the_10 = cls.env['account.payment.term'].create({
'name': '90 days end of month the 10',
'line_ids': [
(0, 0, {
'value': 'balance',
'days': 90,
'end_month': True,
'days_after': 10,
}),
],
})
cls.pay_term_3_months_end_of_month_the_10 = cls.env['account.payment.term'].create({
'name': '3 months end of month the 10',
'line_ids': [
(0, 0, {
'value': 'balance',
'months': 3,
'end_month': True,
'days_after': 10,
}),
],
})
cls.pay_term_end_month_on_the_30th = cls.env['account.payment.term'].create({
'name': 'End of month, the 30th',
'line_ids': [
(0, 0, {
'value': 'balance',
'end_month': True,
'days_after': 30,
}),
],
})
cls.pay_term_1_month_15_days_end_month_45_days = cls.env['account.payment.term'].create({
'name': '1 month, 15 days, end month, 45 days',
'line_ids': [
(0, 0, {
'value': 'balance',
'months': 1,
'days': 15,
'end_month': True,
'days_after': 45,
}),
],
})
@@ -84,15 +167,45 @@ class TestAccountInvoiceRounding(AccountTestInvoicingCommon):
self.assertPaymentTerm(self.pay_term_today, '2019-01-01', ['2019-01-01'])
self.assertPaymentTerm(self.pay_term_today, '2019-01-15', ['2019-01-15'])
self.assertPaymentTerm(self.pay_term_today, '2019-01-31', ['2019-01-31'])
self.assertPaymentTerm(self.pay_term_45_end_month, '2019-01-01', ['2019-03-17'])
self.assertPaymentTerm(self.pay_term_45_end_month, '2019-01-15', ['2019-03-17'])
self.assertPaymentTerm(self.pay_term_45_end_month, '2019-01-31', ['2019-03-17'])
self.assertPaymentTerm(self.pay_term_min_31days_15th, '2019-01-01', ['2019-02-15'])
self.assertPaymentTerm(self.pay_term_min_31days_15th, '2019-01-15', ['2019-02-15'])
self.assertPaymentTerm(self.pay_term_min_31days_15th, '2019-01-31', ['2019-03-15'])
self.assertPaymentTerm(self.pay_term_next_month_on_the_15, '2019-01-01', ['2019-02-15'])
self.assertPaymentTerm(self.pay_term_next_month_on_the_15, '2019-01-15', ['2019-02-15'])
self.assertPaymentTerm(self.pay_term_next_month_on_the_15, '2019-01-31', ['2019-02-15'])
self.assertPaymentTerm(self.pay_term_last_day_of_month, '2019-01-01', ['2019-01-31'])
self.assertPaymentTerm(self.pay_term_last_day_of_month, '2019-01-15', ['2019-01-31'])
self.assertPaymentTerm(self.pay_term_last_day_of_month, '2019-01-31', ['2019-01-31'])
self.assertPaymentTerm(self.pay_term_first_day_next_month, '2019-01-01', ['2019-02-01'])
self.assertPaymentTerm(self.pay_term_first_day_next_month, '2019-01-15', ['2019-02-01'])
self.assertPaymentTerm(self.pay_term_first_day_next_month, '2019-01-31', ['2019-02-01'])
self.assertPaymentTerm(self.pay_term_net_30_days, '2022-01-01', ['2022-01-31'])
self.assertPaymentTerm(self.pay_term_net_30_days, '2022-01-15', ['2022-02-14'])
self.assertPaymentTerm(self.pay_term_net_30_days, '2022-01-31', ['2022-03-02'])
self.assertPaymentTerm(self.pay_term_30_days_end_of_month, '2022-01-01', ['2022-01-31'])
self.assertPaymentTerm(self.pay_term_30_days_end_of_month, '2022-01-15', ['2022-02-28'])
self.assertPaymentTerm(self.pay_term_30_days_end_of_month, '2022-01-31', ['2022-03-31'])
self.assertPaymentTerm(self.pay_term_1_month_end_of_month, '2022-01-01', ['2022-02-28'])
self.assertPaymentTerm(self.pay_term_1_month_end_of_month, '2022-01-15', ['2022-02-28'])
self.assertPaymentTerm(self.pay_term_1_month_end_of_month, '2022-01-31', ['2022-02-28'])
self.assertPaymentTerm(self.pay_term_30_days_end_of_month_the_10, '2022-01-01', ['2022-02-10'])
self.assertPaymentTerm(self.pay_term_30_days_end_of_month_the_10, '2022-01-15', ['2022-03-10'])
self.assertPaymentTerm(self.pay_term_30_days_end_of_month_the_10, '2022-01-31', ['2022-04-10'])
self.assertPaymentTerm(self.pay_term_90_days_end_of_month_the_10, '2022-01-01', ['2022-05-10'])
self.assertPaymentTerm(self.pay_term_90_days_end_of_month_the_10, '2022-01-15', ['2022-05-10'])
self.assertPaymentTerm(self.pay_term_90_days_end_of_month_the_10, '2022-01-31', ['2022-06-10'])
self.assertPaymentTerm(self.pay_term_3_months_end_of_month_the_10, '2022-01-01', ['2022-05-10'])
self.assertPaymentTerm(self.pay_term_3_months_end_of_month_the_10, '2022-01-15', ['2022-05-10'])
self.assertPaymentTerm(self.pay_term_3_months_end_of_month_the_10, '2022-01-31', ['2022-05-10'])
self.assertPaymentTerm(self.pay_term_1_month_15_days_end_month_45_days, '2022-01-01', ['2022-04-14'])
self.assertPaymentTerm(self.pay_term_1_month_15_days_end_month_45_days, '2022-01-15', ['2022-05-15'])
self.assertPaymentTerm(self.pay_term_1_month_15_days_end_month_45_days, '2022-01-31', ['2022-05-15'])
def test_wrong_payment_term(self):
with self.assertRaises(ValidationError):
self.env['account.payment.term'].create({
'name': 'Wrong Payment Term',
'line_ids': [
(0, 0, {
'value': 'balance',
'months': -1,
}),
],
})
@@ -2,60 +2,6 @@
<odoo>
<data>
<record id="view_payment_term_line_tree" model="ir.ui.view">
<field name="name">account.payment.term.line.tree</field>
<field name="model">account.payment.term.line</field>
<field name="arch" type="xml">
<tree string="Payment Terms">
<field name="sequence" widget="handle"/>
<field name="value" string="Due Type"/>
<field name="value_amount" attrs="{'readonly':[('value','=','balance')]}"/>
<field name="days"/>
<field name="option" string=""/>
<field name="day_of_the_month" string="Day of the month"/>
</tree>
</field>
</record>
<record id="view_payment_term_line_form" model="ir.ui.view">
<field name="name">account.payment.term.line.form</field>
<field name="model">account.payment.term.line</field>
<field name="arch" type="xml">
<form string="Payment Terms">
<h2>Term Type</h2>
<group>
<group>
<field name="value" widget="radio"/>
</group>
<group>
<div attrs="{'invisible':[('value','=', 'balance')]}" class="o_row">
<label for="value_amount" attrs="{'invisible':[('value','=', 'balance')]}"/>
<field name="value_amount" class="oe_inline"/>
<span class="o_form_label oe_inline" attrs="{'invisible':[('value','!=','percent')]}">%</span>
</div>
</group>
</group>
<field name="sequence" invisible="1"/>
<h2>Due Date Computation</h2>
<div colspan="2">
<label for="days" string="Due" attrs="{'invisible': [('option','!=', 'day_after_invoice_date')]}"/>
<label for="days" string="Due the" attrs="{'invisible': [('option','=', 'day_after_invoice_date')]}"/>
<field name="days" class="oe_inline"/>
<label for="option" string=""/> <!--Empty label to force space between elements-->
<field name="option" class="oe_inline"/>
</div>
<div colspan="2" attrs="{'invisible': [('option','!=', 'day_after_invoice_date')]}">
<label for="day_of_the_month" string="On the"/>
<field name="day_of_the_month" class="oe_inline"/>
<span class="o_form_label">of the month</span>
</div>
</form>
</field>
</record>
<record id="view_payment_term_search" model="ir.ui.view">
<field name="name">account.payment.term.search</field>
<field name="model">account.payment.term</field>
@@ -87,21 +33,48 @@
<field name="arch" type="xml">
<form string="Payment Terms">
<sheet>
<field name="active" invisible="1"/>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<group>
<group>
<field name="active" invisible="1"/>
<field name="name"/>
<field name="display_on_invoice"/>
</group>
<group>
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
</group>
</group>
<label for="note"/>
<field name="note" placeholder="Payment term explanation for the customer..."/>
<separator string="Terms"/>
<p class="text-muted">
The last line's computation type should be "Balance" to ensure that the whole amount will be allocated.
</p>
<field name="line_ids"/>
<field name="line_ids">
<tree string="Payment Terms" editable="top" no_open="True">
<field name="value" string="Due Type"/>
<field name="value_amount" attrs="{'invisible': [('value', '=', 'balance')]}" digits="[2, 2]"/>
<field name="months"/>
<field name="days"/>
<field name="end_month" widget="boolean_toggle"/>
<field name="days_after" attrs="{'invisible': [('end_month','=', False)]}"/>
</tree>
</field>
<div class="oe_edit_only">
<separator string="Example"/>
<field name="example_invalid" invisible="1"/>
<div attrs="{'invisible': [('example_invalid', '=', False)]}">
The Payment Term must have one Balance line.
</div>
<div attrs="{'invisible': [('example_invalid', '=', True)]}" class="d-flex" >
For any invoice of
<span class="mx-1"/> <field name="example_amount" /> <span class="mx-1"/>
dated
<span class="mx-1"/> <field name="example_date" class="oe_inline" style="color: #704A66; font-weight: bold"/>,
the due date(s) and amount(s) will be:
</div>
<field name="example_preview" attrs="{'invisible': [('example_invalid', '=', True)]}"/>
</div>
</sheet>
</form>
</field>
+7 -1
View File
@@ -201,7 +201,13 @@
Please use the following communication for your payment : <b><span t-field="o.payment_reference"/></b>
</p>
<div t-if="o.invoice_payment_term_id" name="payment_term">
<span t-field="o.invoice_payment_term_id.note"/>
<div t-field="o.invoice_payment_term_id.note"/>
<t t-set="terms" t-value="o.invoice_payment_term_id.compute(o.amount_total, o.amount_total_in_currency_signed, o.invoice_date, o.currency_id)"/>
<t t-if="o.invoice_payment_term_id.display_on_invoice" t-foreach="o.invoice_payment_term_id._get_amount_by_date(terms, o.currency_id).items()" t-as="pay_term_line">
<div>
<span t-esc="pay_term_line[1]" t-options='{"widget": "monetary", "display_currency": o.currency_id}'/> are due on <span t-esc="pay_term_line[0]"/>
</div>
</t>
</div>
<div t-if="not is_html_empty(o.narration)" name="comment">
<span t-field="o.narration"/>