[IMP] account: easier payment registration for bills

Description of the issue/feature this commit addresses:

Currently, the payment registration for vendor bills is kinda hard to figure.
Noticing which bills are overdue, which require a payment is not made so easy
by the interface and could be improved. Also there is no way to register a
payment for multiple lines at a time which could improve the UX.

Desired behavior after the commit is merged :

The buttons "Bills to Pay" and "Late Bills" redirect to a Journal Items view
called Payment Items with specific filters to show only the necessary lines.

The order of the columns in the Journal Items has been changed and their
visibility has been changed too.

The default filters of the Journal Items when coming from the dashboard buttons
has been changed. Payable has been replaced by Purchases.

Filters, Group by and Search by options have been added for the Due Date
and the Discount Date fields in the Journal Items.

task-3516496

closes odoo/odoo#138188

Related: odoo/enterprise#48679
Signed-off-by: William André (wan) <wan@odoo.com>
This commit is contained in:
Thomas Becquevort (thbe)
2023-10-25 22:14:51 +00:00
committed by william-andre
parent 060e13ad6d
commit f25ede851e
12 changed files with 424 additions and 37 deletions
+1
View File
@@ -92,6 +92,7 @@ You could use this simplified accounting in case you work with an (external) acc
'account/static/src/components/**/*',
'account/static/src/js/tours/account.js',
'account/static/src/views/**/*.js',
'account/static/src/js/search/search_bar/search_bar.js',
],
'web.assets_frontend': [
'account/static/src/js/account_portal_sidebar.js',
@@ -426,9 +426,11 @@ class account_journal(models.Model):
def _fill_sale_purchase_dashboard_data(self, dashboard_data):
"""Populate all sale and purchase journal's data dict with relevant information for the kanban card."""
sale_purchase_journals = self.filtered(lambda journal: journal.type in ('sale', 'purchase'))
purchase_journals = self.filtered(lambda journal: journal.type == 'purchase')
sale_journals = self.filtered(lambda journal: journal.type == 'sale')
if not sale_purchase_journals:
return
field_list = [
bills_field_list = [
"account_move.journal_id",
"(CASE WHEN account_move.move_type IN ('out_refund', 'in_refund') THEN -1 ELSE 1 END) * account_move.amount_residual AS amount_total",
"(CASE WHEN account_move.move_type IN ('in_invoice', 'in_refund', 'in_receipt') THEN -1 ELSE 1 END) * account_move.amount_residual_signed AS amount_total_company",
@@ -437,17 +439,45 @@ class account_journal(models.Model):
"account_move.invoice_date",
"account_move.company_id",
]
query, params = sale_purchase_journals._get_open_bills_to_pay_query().select(*field_list)
self.env.cr.execute(query, params)
query_results_to_pay = group_by_journal(self.env.cr.dictfetchall())
query, params = sale_purchase_journals._get_draft_bills_query().select(*field_list)
payment_field_list = [
"account_move_line.journal_id",
"account_move_line.move_id",
"-account_move_line.amount_residual AS amount_total_company",
]
# DRAFTS
query, params = sale_purchase_journals._get_draft_bills_query().select(*bills_field_list)
self.env.cr.execute(query, params)
query_results_drafts = group_by_journal(self.env.cr.dictfetchall())
query, params = sale_purchase_journals._get_late_bills_query().select(*field_list)
self.env.cr.execute(query, params)
late_query_results = group_by_journal(self.env.cr.dictfetchall())
# WAITING BILLS AND PAYMENTS
query_results_to_pay = {}
if purchase_journals:
query, params = purchase_journals._get_open_payments_query().select(*payment_field_list)
self.env.cr.execute(query, params)
query_results_payments_to_pay = group_by_journal(self.env.cr.dictfetchall())
for journal in purchase_journals:
query_results_to_pay[journal.id] = query_results_payments_to_pay[journal.id]
if sale_journals:
query, params = sale_journals._get_open_bills_to_pay_query().select(*bills_field_list)
self.env.cr.execute(query, params)
query_results_bills_to_pay = group_by_journal(self.env.cr.dictfetchall())
for journal in sale_journals:
query_results_to_pay[journal.id] = query_results_bills_to_pay[journal.id]
# LATE BILLS AND PAYMENTS
late_query_results = {}
if purchase_journals:
query, params = purchase_journals._get_late_payment_query().select(*payment_field_list)
self.env.cr.execute(query, params)
late_payments_query_results = group_by_journal(self.env.cr.dictfetchall())
for journal in purchase_journals:
late_query_results[journal.id] = late_payments_query_results[journal.id]
if sale_journals:
query, params = sale_journals._get_late_bills_query().select(*bills_field_list)
self.env.cr.execute(query, params)
late_bills_query_results = group_by_journal(self.env.cr.dictfetchall())
for journal in sale_journals:
late_query_results[journal.id] = late_bills_query_results[journal.id]
to_check_vals = {
journal: (amount_total_signed_sum, count)
@@ -529,6 +559,26 @@ class account_journal(models.Model):
('move_type', 'in', self.env['account.move'].get_invoice_types(include_receipts=True)),
])
def _get_open_payments_query(self):
return self.env['account.move.line']._where_calc([
*self.env['account.move.line']._check_company_domain(self.env.companies),
('journal_id', 'in', self.ids),
('move_id.payment_state', 'in', ('not_paid', 'partial')),
('date_maturity', '!=', False),
('amount_residual', '!=', False),
('parent_state', '=', 'posted'),
])
def _get_late_payment_query(self):
return self.env['account.move.line']._where_calc([
*self.env['account.move.line']._check_company_domain(self.env.companies),
('journal_id', 'in', self.ids),
('move_id.payment_state', 'in', ('not_paid', 'partial')),
('date_maturity', '<', fields.Date.context_today(self)),
('amount_residual', '!=', False),
('parent_state', '=', 'posted'),
])
def _count_results_and_sum_amounts(self, results_dict, target_currency):
""" Loops on a query result to count the total number of invoices and sum
their amount_total field (expressed in the given target currency).
+1 -15
View File
@@ -3835,21 +3835,7 @@ class AccountMove(models.Model):
})
def action_register_payment(self):
''' Open the account.payment.register wizard to pay the selected journal entries.
:return: An action opening the account.payment.register wizard.
'''
return {
'name': _('Register Payment'),
'res_model': 'account.payment.register',
'view_mode': 'form',
'views': [[False, 'form']],
'context': {
'active_model': 'account.move',
'active_ids': self.ids,
},
'target': 'new',
'type': 'ir.actions.act_window',
}
return self.line_ids.action_register_payment()
def action_duplicate(self):
# offer the possibility to duplicate thanks to a button instead of a hidden menu, which is more visible
+51 -1
View File
@@ -1,7 +1,7 @@
import ast
from collections import defaultdict
from contextlib import contextmanager
from datetime import date, timedelta
from datetime import date
from odoo import api, fields, models, Command, _
from odoo.exceptions import ValidationError, UserError
@@ -230,6 +230,7 @@ class AccountMoveLine(models.Model):
amount_residual_currency = fields.Monetary(
string='Residual Amount in Currency',
compute='_compute_amount_residual', store=True,
group_operator=None,
help="The residual amount on a journal item expressed in its currency (possibly not the "
"company currency).",
)
@@ -384,6 +385,7 @@ class AccountMoveLine(models.Model):
string='Discount amount in Currency',
store=True,
currency_field='currency_id',
group_operator=None,
)
# Discounted balance when the early payment discount is applied
discount_balance = fields.Monetary(
@@ -392,6 +394,14 @@ class AccountMoveLine(models.Model):
currency_field='company_currency_id',
)
# === Payment Fields === #
# payment_date is the closest date to the date the aml was created between discount_date and date_maturity.
payment_date = fields.Date(
string='Payment Date',
compute='_compute_payment_date',
search='_search_payment_date',
)
# === Misc Information === #
blocked = fields.Boolean(
string='No Follow-up',
@@ -433,6 +443,14 @@ class AccountMoveLine(models.Model):
),
]
@api.model
def get_views(self, views, options=None):
res = super().get_views(views, options)
if res['views'].get('list') and self.env['ir.ui.view'].browse(res['views']['list']['id']).name == "account.move.line.payment.tree":
# We dont want any additionnal action in the "account.move.line.payment.tree" view toolbar
res['views']['list']['toolbar']['action'] = []
return res
# -------------------------------------------------------------------------
# COMPUTE METHODS
# -------------------------------------------------------------------------
@@ -1102,6 +1120,38 @@ class AccountMoveLine(models.Model):
})
line.analytic_distribution = distribution or line.analytic_distribution
@api.depends('discount_date', 'date_maturity')
def _compute_payment_date(self):
for line in self:
line.payment_date = line.discount_date if line.discount_date and date.today() <= line.discount_date else line.date_maturity
def _search_payment_date(self, operator, value):
assert operator == '='
return [
'|',
'|',
'&', ('discount_date', '>=', str(date.today())), ('discount_date', '<=', value),
'&', ('discount_date', '<', str(date.today())), ('date_maturity', '<=', value),
'&', ('discount_date', '=', False), ('date_maturity', '<=', value),
]
def action_register_payment(self):
''' Open the account.payment.register wizard to pay the selected journal items.
:return: An action opening the account.payment.register wizard.
'''
return {
'name': _('Register Payment'),
'res_model': 'account.payment.register',
'view_mode': 'form',
'views': [[False, 'form']],
'context': {
'active_model': 'account.move.line',
'active_ids': self.ids,
},
'target': 'new',
'type': 'ir.actions.act_window',
}
# -------------------------------------------------------------------------
# INVERSE METHODS
# -------------------------------------------------------------------------
@@ -0,0 +1,15 @@
/** @odoo-module **/
import { _t } from "@web/core/l10n/translation";
import { patch } from "@web/core/utils/patch";
import { SearchBar } from "@web/search/search_bar/search_bar";
patch(SearchBar.prototype, {
getPreposition(searchItem) {
let preposition = super.getPreposition(searchItem);
if (this.fields[searchItem.fieldName].name == 'payment_date') {
preposition = _t("until");
}
return preposition
}
});
+1
View File
@@ -33,6 +33,7 @@ from . import test_tax_report
from . import test_transfer_wizard
from . import test_account_incoming_supplier_invoice
from . import test_payment_term
from . import test_account_payment_items
from . import test_account_payment_register
from . import test_tour
from . import test_early_payment_discount
@@ -164,3 +164,90 @@ class TestAccountJournalDashboard(AccountTestInvoicingCommon):
dashboard_data = default_journal_sale._get_journal_dashboard_data_batched()[default_journal_sale.id]
self.assertEqual(format_amount(self.env, 55, company_currency), dashboard_data['sum_waiting'])
self.assertEqual(format_amount(self.env, 55, company_currency), dashboard_data['sum_late'])
@freeze_time("2023-03-15")
def test_purchase_journal_numbers_and_sums(self):
company_currency = self.company_data['currency']
journal = self.company_data['default_journal_purchase']
#Setup multiple payments term
twentyfive_now_term = self.env['account.payment.term'].create({
'name': '25% now, rest in 30 days',
'note': 'Pay 25% on invoice date and 75% 30 days later',
'line_ids': [
(0, 0, {
'value': 'percent',
'value_amount': 25.00,
'delay_type': 'days_after',
'nb_days': 0,
}),
(0, 0, {
'value': 'percent',
'value_amount': 75.00,
'delay_type': 'days_after',
'nb_days': 30,
}),
],
})
self.env['account.move'].create({
'move_type': 'in_invoice',
'journal_id': journal.id,
'partner_id': self.partner_a.id,
'invoice_date': '2023-04-01',
'date': '2023-03-15',
'invoice_payment_term_id': twentyfive_now_term.id,
'invoice_line_ids': [(0, 0, {
'product_id': self.product_a.id,
'quantity': 1,
'name': 'product test 1',
'price_unit': 4000,
'tax_ids': [],
})]
}).action_post()
# This bill has two amls of 10$. Both are waiting for payment and due in 16 and 46 days.
# number_waiting += 2, sum_waiting += -4000$, number_late += 0, sum_late += 0$
self.env['account.move'].create({
'move_type': 'in_invoice',
'journal_id': journal.id,
'partner_id': self.partner_a.id,
'invoice_date': '2023-03-01',
'date': '2023-03-15',
'invoice_payment_term_id': twentyfive_now_term.id,
'invoice_line_ids': [(0, 0, {
'product_id': self.product_a.id,
'quantity': 1,
'name': 'product test 1',
'price_unit': 400,
'tax_ids': [],
})]
}).action_post()
# This bill has two amls of 100$. One which is late and due 14 days prior and one which is waiting for payment and due in 15 days.
# number_waiting += 2, sum_waiting += -400$, number_late += 1, sum_late += -100$
self.env['account.move'].create({
'move_type': 'in_invoice',
'journal_id': journal.id,
'partner_id': self.partner_a.id,
'invoice_date': '2023-02-01',
'date': '2023-03-15',
'invoice_payment_term_id': twentyfive_now_term.id,
'invoice_line_ids': [(0, 0, {
'product_id': self.product_a.id,
'quantity': 1,
'name': 'product test 1',
'price_unit': 40,
'tax_ids': [],
})]
}).action_post()
# This bill has two amls of 1000$. Both of them are late and due 45 and 15 days prior.
# number_waiting += 2, sum_waiting += -40$, number_late += 2, sum_late += -40$
dashboard_data = journal._get_journal_dashboard_data_batched()[journal.id]
# Expected behavior is to have six amls waiting for payment for a total amount of 4440$
# three of which would be late for a total amount of 140$
self.assertEqual(6, dashboard_data['number_waiting'])
self.assertEqual(format_amount(self.env, 4440, company_currency), dashboard_data['sum_waiting'])
self.assertEqual(3, dashboard_data['number_late'])
self.assertEqual(format_amount(self.env, 140, company_currency), dashboard_data['sum_late'])
@@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
from datetime import date, datetime, timedelta
from freezegun import freeze_time
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.tests import tagged
@tagged('post_install', '-at_install')
class TestAccountPaymentItems(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.bill = cls.create_bill(due_date='2023-03-20')
cls.late_bill = cls.create_bill(due_date='2023-03-01')
cls.discount_bill = cls.create_bill(due_date='2023-03-20', discount_days=19)
cls.late_discount_bill = cls.create_bill(due_date='2023-04-20', discount_days=9)
@classmethod
def create_bill(cls, due_date, discount_days=None):
payment_term = cls.create_payment_term(due_date, discount_days)
bill = cls.env['account.move'].create({
'move_type': 'in_invoice',
'journal_id': cls.company_data['default_journal_purchase'].id,
'partner_id': cls.partner_a.id,
'date': '2023-03-15',
'invoice_date': '2023-03-01',
'invoice_date_due': due_date,
'invoice_payment_term_id': payment_term.id,
'invoice_line_ids': [(0, 0, {
'product_id': cls.product_a.id,
'quantity': 1,
'name': 'product_a',
'discount': 10.00,
'price_unit': 100,
'tax_ids': [],
'discount_date': date(2023, 3, 1) + timedelta(days=discount_days) if discount_days else False,
'date_maturity': due_date,
})]
})
bill.action_post()
return bill
@classmethod
def create_payment_term(cls, due_date, discount_days=None):
due_days = (datetime.strptime(due_date, '%Y-%m-%d').date() - date(2023, 3, 1)).days
payment_term = cls.env['account.payment.term'].create({
'name': 'Payment Term For Testing',
'early_discount': bool(discount_days),
'discount_days': discount_days if discount_days else False,
'discount_percentage': 5,
'line_ids': [
(0, 0, {
'value': 'percent',
'value_amount': 100,
'delay_type': 'days_after',
'nb_days': due_days,
}),
],
})
return payment_term
@freeze_time("2023-03-15")
def test_payment_date(self):
self.assertEqual(str(self.bill.line_ids[0].payment_date), '2023-03-20')
self.assertEqual(str(self.late_bill.line_ids[0].payment_date), '2023-03-01')
self.assertEqual(str(self.discount_bill.line_ids[0].payment_date), '2023-03-20')
self.assertEqual(str(self.late_discount_bill.line_ids[0].payment_date), '2023-04-20')
def test_search_payment_date(self):
for today, search, expected in [
('2023-03-05', '2023-03-01', self.late_bill),
('2023-03-05', '2023-03-30', self.bill + self.late_bill + self.discount_bill + self.late_discount_bill),
('2023-03-15', '2023-03-01', self.late_bill),
('2023-03-15', '2023-03-30', self.bill + self.late_bill + self.discount_bill),
('2023-03-25', '2023-03-01', self.late_bill),
('2023-03-25', '2023-03-30', self.bill + self.late_bill + self.discount_bill),
('2023-03-25', '2023-06-30', self.bill + self.late_bill + self.discount_bill + self.late_discount_bill),
]:
with freeze_time(today):
self.assertEqual(self.env['account.move.line'].search([
('payment_date', '=', search),
('partner_id', '=', self.partner_a.id),
]).move_id, expected)
@@ -334,9 +334,8 @@
context="{'search_default_open':1, 'search_default_posted':1, 'search_default_partial': 1}" id="account_dashboard_sale_pay_link">
<t t-out="dashboard.number_waiting"/> Unpaid Invoices
</a>
<a type="object" t-if="journal_type == 'purchase'" name="open_action"
context="{'search_default_open':1, 'search_default_posted':1, 'search_default_partial': 1}" id="account_dashboard_purchase_pay_link">
<a type="object" name="open_action" t-if="journal_type == 'purchase'"
context="{'action_name': 'action_open_payment_items', 'search_default_purchases': '1'}">
<t t-out="dashboard.number_waiting"/> Bills to Pay
</a>
</div>
@@ -346,9 +345,12 @@
</div>
<div class="row" t-if="dashboard.number_late">
<div class="col overflow-hidden text-start">
<a type="object" name="open_action" context="{'search_default_late': '1'}">
<span t-if="journal_type == 'sale'" title="Late Invoices"><t t-out="dashboard.number_late"/> Late Invoices</span>
<span t-if="journal_type == 'purchase'" title="Late Bills"><t t-out="dashboard.number_late"/> Late Bills</span>
<a type="object" name="open_action" t-if="journal_type == 'sale'" context="{'search_default_late': '1'}">
<span><t t-out="dashboard.number_late"/> Late Invoices</span>
</a>
<a type="object" t-if="journal_type == 'purchase'" name="open_action"
context="{'action_name': 'action_open_payment_items', 'search_default_purchases': '1', 'search_default_late': '1'}">
<span><t t-out="dashboard.number_late"/> Late Bills</span>
</a>
</div>
<div class="col-auto text-end">
@@ -19,6 +19,7 @@
<menuitem id="menu_action_move_in_invoice_type" action="action_move_in_invoice_type" sequence="1"/>
<menuitem id="menu_action_move_in_refund_type" action="action_move_in_refund_type" sequence="2"/>
<menuitem id="menu_action_move_in_receipt_type" action="action_move_in_receipt_type" groups="account.group_purchase_receipts" sequence="3"/>
<menuitem id="menu_action_open_payment_items" action="action_open_payment_items" sequence="10"/>
<menuitem id="menu_action_account_payments_payable" name="Payments" action="action_account_payments_payable" sequence="20"/>
<menuitem id="menu_account_supplier_accounts" name="Bank Accounts" action="action_account_supplier_accounts" sequence="80"/>
<menuitem id="product_product_menu_purchasable" name="Products" action="product_product_action_purchasable" sequence="100"/>
+100
View File
@@ -305,6 +305,7 @@
<record id="view_account_move_line_filter" model="ir.ui.view">
<field name="name">account.move.line.search</field>
<field name="model">account.move.line</field>
<field eval="16" name="priority"/>
<field name="arch" type="xml">
<search string="Search Journal Items">
<field name="name" string="Journal Item" filter_domain="[
@@ -314,6 +315,8 @@
<field name="ref"/>
<field name="date"/>
<field name="invoice_date"/>
<field name="date_maturity" string="Due Date"/>
<field name="discount_date" string="Discount Date"/>
<field name="balance" string="Amount" filter_domain="['|', ('credit', '=', self), ('debit', '=', self)]"/>
<field name="account_id"/>
<field name="account_type"/>
@@ -369,6 +372,86 @@
</field>
</record>
<!-- account.move.line (Payment Items) -->
<record id="view_move_line_payment_tree" model="ir.ui.view">
<field name="name">account.move.line.payment.tree</field>
<field name="model">account.move.line</field>
<field eval="200" name="priority"/>
<field name="arch" type="xml">
<tree string="Payment Items" create="false" edit="true" expand="context.get('expand', False)" multi_edit="1" sample="1" js_class="account_search_bar">
<header>
<button name="action_register_payment" type="object" string="Register Payment"/>
</header>
<field name="move_id" column_invisible="True"/>
<field name="invoice_date" string="Invoice Date"/>
<field name="date" readonly="1" optional="hide"/>
<field name="date_maturity" string="Invoice Due Date" readonly="1" optional="hide"/>
<field name="discount_date" string="Discount Date" optional="hide"/>
<field name="payment_date" string="Due Date" readonly="1"/>
<field name="company_id" column_invisible="True"/>
<field name="company_id" groups="base.group_multi_company" readonly="1" optional="hide"/>
<field name="journal_id" readonly="1" options='{"no_open":True}' optional="hide"/>
<field name="move_name" string="Journal Entry" widget="open_move_widget"/>
<field name="partner_id" optional="show" readonly="move_type != 'entry'"/>
<field name="ref" readonly="False"/>
<field name="display_name" optional="show"/>
<field name="discount_amount_currency" string="Discount Amount" optional="show" invisible="not discount_amount_currency"/>
<field name="amount_residual" sum="Total Residual" string="Residual" readonly="1" invisible="not is_account_reconcile"/>
<field name="amount_residual_currency" string="Residual in Currency" optional="hide" readonly="1" invisible="is_same_currency or not is_account_reconcile"/>
<field name="currency_id" groups="base.group_multi_currency" optional="hide" string="Currency" readonly="1" invisible="is_same_currency"/>
<field name="company_currency_id" column_invisible="True"/>
<field name="move_type" column_invisible="True"/>
<field name="is_same_currency" column_invisible="True"/>
<field name="is_account_reconcile" column_invisible="True"/>
<field name="currency_id" groups="base.group_multi_currency" invisible="1"/>
<groupby name="partner_id">
<button name="edit" type="edit" icon="fa-edit" title="Edit"/>
</groupby>
</tree>
</field>
</record>
<record id="view_account_move_line_payment_filter" model="ir.ui.view">
<field name="name">account.move.line.payment.search</field>
<field name="model">account.move.line</field>
<field eval="200" name="priority"/>
<field name="arch" type="xml">
<search string="Search Journal Items">
<field name="name" string="Journal Item" filter_domain="[
'|', '|', '|',
('name', 'ilike', self), ('ref', 'ilike', self), ('account_id', 'ilike', self), ('partner_id', 'ilike', self)]"/>
<field name="name"/>
<field name="ref"/>
<field name="payment_date" string="Due Date"/>
<field name="partner_id"/>
<field name="journal_id"/>
<field name="currency_id" groups="base.group_multi_currency" invisible="1"/>
<field name="company_currency_id" invisible="1"/>
<separator/>
<filter string="Posted" name="posted" domain="[('parent_state', '=', 'posted')]" help="Posted Journal Items" invisible="1"/>
<separator/>
<filter string="Sales" name="sales" domain="[('journal_id.type', '=', 'sale')]" context="{'default_journal_type': 'sale'}"/>
<filter string="Purchases" name="purchases" domain="[('journal_id.type', '=', 'purchase')]" context="{'default_journal_type': 'purchase'}"/>
<separator/>
<filter string="Invoice Date" name="invoice_date" date="invoice_date"/>
<filter string="Due Date" name="date_maturity" date="date"/>
<filter string="Overdue" name="late" domain="[('date_maturity', '&lt;', time.strftime('%Y-%m-%d'))]" help="Overdue payments, due date passed"/>
<separator/>
<filter string="Report Dates" name="date_between" domain="[('date', '&gt;=', context.get('date_from')), ('date', '&lt;=', context.get('date_to'))]" invisible="1"/>
<filter string="Report Dates" name="date_before" domain="[('date', '&lt;=', context.get('date_to'))]" invisible="1"/>
<separator/>
<group expand="0" string="Group By">
<filter string="Currency" name="group_by_currencies" domain="[]" context="{'group_by': 'currency_id'}"/>
<filter string="Partner" name="group_by_partner" domain="[]" context="{'group_by': 'partner_id'}"/>
<filter string="Journal" name="journal" domain="[]" context="{'group_by': 'journal_id'}"/>
<filter string="Invoice Date" name="groupby_invoice_date" domain="[]" context="{'group_by': 'invoice_date'}"/>
<filter string="Due Date" name="groupby_payment_date" domain="[]" context="{'group_by': 'payment_date'}"/>
</group>
</search>
</field>
</record>
<!-- account.move (Journal Entry) -->
<record id="view_move_tree" model="ir.ui.view">
@@ -1606,6 +1689,23 @@
</field>
</record>
<record id="action_open_payment_items" model="ir.actions.act_window">
<field name="name">Amounts to Settle</field>
<field name="res_model">account.move.line</field>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_move_line_payment_tree"/>
<field name="search_view_id" ref="view_account_move_line_payment_filter"/>
<field name="context">{'search_default_group_by_partner':1, 'expand':1}</field>
<field name="domain">[('parent_state', '=', 'posted'), ('amount_residual', '!=', 0), ('account_id.reconcile', '=', True)]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Amounts to settle
</p><p>
Cool, it looks like you don't have anything to pay either for vendor bills or for customer credit notes.
</p>
</field>
</record>
<record id="action_move_out_receipt_type" model="ir.actions.act_window">
<field name="name">Receipts</field>
<field name="res_model">account.move</field>
@@ -126,14 +126,10 @@ export class SearchBar extends Component {
const items = [];
const isFieldProperty = searchItem.type === "field_property";
const { type } = isFieldProperty
? searchItem.propertyFieldDefinition
: this.fields[searchItem.fieldName];
const fieldType = type === "reference" ? "char" : type;
const fieldType = this.getFieldType(searchItem);
/** @todo do something with respect to localization (rtl) */
let preposition = _t(["date", "datetime"].includes(fieldType) ? "at" : "for");
let preposition = this.getPreposition(searchItem);
if ((isFieldProperty && FOLDABLE_TYPES.includes(fieldType)) || fieldType === "properties") {
// Do not chose preposition for foldable properties
@@ -236,6 +232,20 @@ export class SearchBar extends Component {
return items;
}
getPreposition(searchItem) {
const fieldType = this.getFieldType(searchItem);
return ["date", "datetime"].includes(fieldType) ? _t("at") : _t("for");
}
getFieldType(searchItem) {
const { type } = searchItem.type === "field_property"
? searchItem.propertyFieldDefinition
: this.fields[searchItem.fieldName];
const fieldType = type === "reference" ? "char" : type;
return fieldType
}
/**
* @param {Object} searchItem
* @returns {Object[]}