summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorwan <wan@odoo.com>2020-08-17 10:14:09 +0000
committerwan <wan@odoo.com>2020-08-21 07:46:07 +0000
commit8129aa40dbb236b693b21724ebf0e57bc59dbc8b (patch)
treef7d5059bcac1bdc2af0ffd9ad570251f5c9f9eef /addons
parenteb6a177a50128f2d78607fcff36aaf65e8a3034e (diff)
[IMP] account{,_check_printing}: usability
Task 2005940 account: * Use stored compute methods instead of default for {out,in}bound_payment_method_ids * Track is_move_sent in the chatter * Split 'Invoices' and 'Bills' in the smart button of payment form * Because account.payment.method can be shown on the res.partner form, we need to relax the security level to readonly for all users account_check_printing: * Add the preferred payment method for partners, with a related on account move allowing to do a group by and doing payments in batch * Add a constraint to forbid twice the same check number in the same journal * The amount in words is now readonly to prevent typos and mismatches with the amount in digits * Remove the field `check_number_int`. The check number is kept as Char so that '000012345' is not displayed (and printed) as '12,345' but it is parsed so that comparison and incrementation are possible. closes odoo/odoo#56179 Related: odoo/upgrade#1669 Related: odoo/enterprise#12527 Signed-off-by: oco-odoo <oco-odoo@users.noreply.github.com>
Diffstat (limited to 'addons')
-rw-r--r--addons/account/models/account_journal.py60
-rw-r--r--addons/account/models/account_move.py9
-rw-r--r--addons/account/models/account_payment.py63
-rw-r--r--addons/account/security/ir.model.access.csv2
-rw-r--r--addons/account/views/account_payment_view.xml9
-rw-r--r--addons/account_check_printing/__manifest__.py2
-rw-r--r--addons/account_check_printing/models/__init__.py3
-rw-r--r--addons/account_check_printing/models/account_journal.py78
-rw-r--r--addons/account_check_printing/models/account_move.py23
-rw-r--r--addons/account_check_printing/models/account_payment.py112
-rw-r--r--addons/account_check_printing/models/chart_template.py20
-rw-r--r--addons/account_check_printing/models/res_company.py52
-rw-r--r--addons/account_check_printing/models/res_config_settings.py49
-rw-r--r--addons/account_check_printing/models/res_partner.py19
-rw-r--r--addons/account_check_printing/tests/test_print_check.py4
-rw-r--r--addons/account_check_printing/views/account_move_views.xml13
-rw-r--r--addons/account_check_printing/views/account_payment_views.xml8
-rw-r--r--addons/account_check_printing/views/res_partner_views.xml13
-rw-r--r--addons/account_check_printing/wizard/print_prenumbered_checks.py3
19 files changed, 409 insertions, 133 deletions
diff --git a/addons/account/models/account_journal.py b/addons/account/models/account_journal.py
index 13c418cc099..0bab630145f 100644
--- a/addons/account/models/account_journal.py
+++ b/addons/account/models/account_journal.py
@@ -107,19 +107,41 @@ class AccountJournal(models.Model):
sequence_override_regex = fields.Text(help="Technical field used to enforce complex sequence composition that the system would normally misunderstand.\n"\
"This is a regex that can include all the following capture groups: prefix1, year, prefix2, month, prefix3, seq, suffix.\n"\
"The prefix* groups are the separators between the year, month and the actual increasing sequence number (seq).\n"\
-
+
"e.g: ^(?P<prefix1>.*?)(?P<year>\d{4})(?P<prefix2>\D*?)(?P<month>\d{2})(?P<prefix3>\D+?)(?P<seq>\d+)(?P<suffix>\D*?)$")
- inbound_payment_method_ids = fields.Many2many('account.payment.method', 'account_journal_inbound_payment_method_rel', 'journal_id', 'inbound_payment_method',
- domain=[('payment_type', '=', 'inbound')], string='Inbound Payment Methods', default=lambda self: self._default_inbound_payment_methods(),
- help="Manual: Get paid by cash, check or any other method outside of Odoo.\n"\
- "Electronic: Get paid automatically through a payment acquirer by requesting a transaction on a card saved by the customer when buying or subscribing online (payment token).\n"\
- "Batch Deposit: Encase several customer checks at once by generating a batch deposit to submit to your bank. When encoding the bank statement in Odoo,you are suggested to reconcile the transaction with the batch deposit. Enable this option from the settings.")
- outbound_payment_method_ids = fields.Many2many('account.payment.method', 'account_journal_outbound_payment_method_rel', 'journal_id', 'outbound_payment_method',
- domain=[('payment_type', '=', 'outbound')], string='Outbound Payment Methods', default=lambda self: self._default_outbound_payment_methods(),
- help="Manual:Pay bill by cash or any other method outside of Odoo.\n"\
- "Check:Pay bill by check and print it from Odoo.\n"\
- "SEPA Credit Transfer: Pay bill from a SEPA Credit Transfer file you submit to your bank. Enable this option from the settings.")
+ inbound_payment_method_ids = fields.Many2many(
+ comodel_name='account.payment.method',
+ relation='account_journal_inbound_payment_method_rel',
+ column1='journal_id',
+ column2='inbound_payment_method',
+ domain=[('payment_type', '=', 'inbound')],
+ string='Inbound Payment Methods',
+ compute='_compute_inbound_payment_method_ids',
+ store=True,
+ readonly=False,
+ help="Manual: Get paid by cash, check or any other method outside of Odoo.\n"
+ "Electronic: Get paid automatically through a payment acquirer by requesting a transaction"
+ " on a card saved by the customer when buying or subscribing online (payment token).\n"
+ "Batch Deposit: Encase several customer checks at once by generating a batch deposit to"
+ " submit to your bank. When encoding the bank statement in Odoo,you are suggested to"
+ " reconcile the transaction with the batch deposit. Enable this option from the settings."
+ )
+ outbound_payment_method_ids = fields.Many2many(
+ comodel_name='account.payment.method',
+ relation='account_journal_outbound_payment_method_rel',
+ column1='journal_id',
+ column2='outbound_payment_method',
+ domain=[('payment_type', '=', 'outbound')],
+ string='Outbound Payment Methods',
+ compute='_compute_outbound_payment_method_ids',
+ store=True,
+ readonly=False,
+ help="Manual:Pay bill by cash or any other method outside of Odoo.\n"
+ "Check:Pay bill by check and print it from Odoo.\n"
+ "SEPA Credit Transfer: Pay bill from a SEPA Credit Transfer file you submit to your"
+ " bank. Enable this option from the settings."
+ )
at_least_one_inbound = fields.Boolean(compute='_methods_compute', store=True)
at_least_one_outbound = fields.Boolean(compute='_methods_compute', store=True)
profit_account_id = fields.Many2one(
@@ -190,6 +212,22 @@ class AccountJournal(models.Model):
else:
journal.default_account_type = False
+ @api.depends('type')
+ def _compute_outbound_payment_method_ids(self):
+ for journal in self:
+ if journal.type in ('bank', 'cash'):
+ journal.outbound_payment_method_ids = self._default_outbound_payment_methods()
+ else:
+ journal.outbound_payment_method_ids = False
+
+ @api.depends('type')
+ def _compute_inbound_payment_method_ids(self):
+ for journal in self:
+ if journal.type in ('bank', 'cash'):
+ journal.inbound_payment_method_ids = self._default_inbound_payment_methods()
+ else:
+ journal.inbound_payment_method_ids = False
+
@api.depends('company_id', 'type')
def _compute_suspense_account_id(self):
for journal in self:
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index 221518dae56..7a7f2824efe 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -171,8 +171,13 @@ class AccountMove(models.Model):
country_code = fields.Char(related='company_id.country_id.code', readonly=True)
user_id = fields.Many2one(string='User', related='invoice_user_id',
help='Technical field used to fit the generic behavior in mail templates.')
- is_move_sent = fields.Boolean(readonly=True, default=False, copy=False,
- help="It indicates that the invoice/payment has been sent.")
+ is_move_sent = fields.Boolean(
+ readonly=True,
+ default=False,
+ copy=False,
+ tracking=True,
+ help="It indicates that the invoice/payment has been sent.",
+ )
partner_bank_id = fields.Many2one('res.partner.bank', string='Recipient Bank',
help='Bank Account Number to which the invoice will be paid. A Company bank account if this is a Customer Invoice or Vendor Credit Note, otherwise a Partner bank account number.',
check_company=True)
diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py
index 66c5e5df5c8..40c4c035e1a 100644
--- a/addons/account/models/account_payment.py
+++ b/addons/account/models/account_payment.py
@@ -109,6 +109,11 @@ class AccountPayment(models.Model):
help="Invoices whose journal items have been reconciled with these payments.")
reconciled_invoices_count = fields.Integer(string="# Reconciled Invoices",
compute="_compute_stat_buttons_from_reconciliation")
+ reconciled_bill_ids = fields.Many2many('account.move', string="Reconciled Bills",
+ compute='_compute_stat_buttons_from_reconciliation',
+ help="Invoices whose journal items have been reconciled with these payments.")
+ reconciled_bills_count = fields.Integer(string="# Reconciled Bills",
+ compute="_compute_stat_buttons_from_reconciliation")
reconciled_statement_ids = fields.Many2many('account.move', string="Reconciled Statements",
compute='_compute_stat_buttons_from_reconciliation',
help="Statements matched to this payment")
@@ -422,6 +427,8 @@ class AccountPayment(models.Model):
if not stored_payments:
self.reconciled_invoice_ids = False
self.reconciled_invoices_count = 0
+ self.reconciled_bill_ids = False
+ self.reconciled_bills_count = 0
self.reconciled_statement_ids = False
self.reconciled_statements_count = 0
return
@@ -433,7 +440,8 @@ class AccountPayment(models.Model):
self._cr.execute('''
SELECT
payment.id,
- ARRAY_AGG(DISTINCT invoice.id) AS invoice_ids
+ ARRAY_AGG(DISTINCT invoice.id) AS invoice_ids,
+ invoice.move_type
FROM account_payment payment
JOIN account_move move ON move.id = payment.move_id
JOIN account_move_line line ON line.move_id = move.id
@@ -448,16 +456,24 @@ class AccountPayment(models.Model):
JOIN account_move invoice ON invoice.id = counterpart_line.move_id
JOIN account_account account ON account.id = line.account_id
WHERE account.internal_type IN ('receivable', 'payable')
+ AND payment.id IN %(payment_ids)s
AND line.id != counterpart_line.id
AND invoice.move_type in ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt')
- GROUP BY payment.id
- ''')
- query_res = dict((payment_id, invoice_ids) for payment_id, invoice_ids in self._cr.fetchall())
-
- for pay in self:
- invoice_ids = query_res.get(pay.id, [])
- pay.reconciled_invoice_ids = [(6, 0, invoice_ids)]
- pay.reconciled_invoices_count = len(invoice_ids)
+ GROUP BY payment.id, invoice.move_type
+ ''', {
+ 'payment_ids': tuple(stored_payments.ids)
+ })
+ query_res = self._cr.dictfetchall()
+ self.reconciled_invoice_ids = self.reconciled_invoices_count = False
+ self.reconciled_bill_ids = self.reconciled_bills_count = False
+ for res in query_res:
+ pay = self.browse(res['id'])
+ if res['move_type'] in self.env['account.move'].get_sale_types(True):
+ pay.reconciled_invoice_ids += self.env['account.move'].browse(res.get('invoice_ids', []))
+ pay.reconciled_invoices_count = len(res.get('invoice_ids', []))
+ else:
+ pay.reconciled_bill_ids += self.env['account.move'].browse(res.get('invoice_ids', []))
+ pay.reconciled_bills_count = len(res.get('invoice_ids', []))
self._cr.execute('''
SELECT
@@ -477,10 +493,13 @@ class AccountPayment(models.Model):
OR
part.credit_move_id = counterpart_line.id
WHERE (account.id = journal.payment_debit_account_id OR account.id = journal.payment_credit_account_id)
+ AND payment.id IN %(payment_ids)s
AND line.id != counterpart_line.id
AND counterpart_line.statement_id IS NOT NULL
GROUP BY payment.id
- ''')
+ ''', {
+ 'payment_ids': tuple(stored_payments.ids)
+ })
query_res = dict((payment_id, statement_ids) for payment_id, statement_ids in self._cr.fetchall())
for pay in self:
@@ -748,6 +767,30 @@ class AccountPayment(models.Model):
})
return action
+ def button_open_bills(self):
+ ''' Redirect the user to the bill(s) paid by this payment.
+ :return: An action on account.move.
+ '''
+ self.ensure_one()
+
+ action = {
+ 'name': _("Paid Bills"),
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'account.move',
+ 'context': {'create': False},
+ }
+ if len(self.reconciled_bill_ids) == 1:
+ action.update({
+ 'view_mode': 'form',
+ 'res_id': self.reconciled_bill_ids.id,
+ })
+ else:
+ action.update({
+ 'view_mode': 'list,form',
+ 'domain': [('id', 'in', self.reconciled_bill_ids.ids)],
+ })
+ return action
+
def button_open_statements(self):
''' Redirect the user to the statement line(s) reconciled to this payment.
:return: An action on account.move.
diff --git a/addons/account/security/ir.model.access.csv b/addons/account/security/ir.model.access.csv
index 2013d5dbd4b..cddbb5372f2 100644
--- a/addons/account/security/ir.model.access.csv
+++ b/addons/account/security/ir.model.access.csv
@@ -118,7 +118,7 @@ access_account_payment_term_partner_manager,account.payment.term partner manager
access_account_payment_term_manager,account.payment.term,model_account_payment_term,account.group_account_manager,1,1,1,1
access_account_payment_term_line_partner_manager,account.payment.term.line partner manager,model_account_payment_term_line,base.group_user,1,0,0,0
access_account_payment_term_line_manager,account.payment.term.line,model_account_payment_term_line,account.group_account_manager,1,1,1,1
-access_account_payment_method_readonly,account.payment.method,model_account_payment_method,account.group_account_readonly,1,0,0,0
+access_account_payment_method_readonly,account.payment.method,model_account_payment_method,base.group_user,1,0,0,0
access_account_payment_method,account.payment.method,model_account_payment_method,account.group_account_invoice,1,1,1,1
access_account_payment_readonly,account.payment,model_account_payment,account.group_account_readonly,1,0,0,0
access_account_payment,account.payment,model_account_payment,account.group_account_invoice,1,1,1,1
diff --git a/addons/account/views/account_payment_view.xml b/addons/account/views/account_payment_view.xml
index 18894102b42..2e4a05b74a8 100644
--- a/addons/account/views/account_payment_view.xml
+++ b/addons/account/views/account_payment_view.xml
@@ -171,6 +171,15 @@
<span attrs="{'invisible': [('reconciled_invoices_count','&lt;=', 1)]}">Invoices</span>
</button>
+ <!-- Bill stat button -->
+ <button name="button_open_bills" type="object"
+ class="oe_stat_button" icon="fa-bars"
+ attrs="{'invisible': [('reconciled_bills_count','=', 0)]}">
+ <field name="reconciled_bills_count"/>
+ <span attrs="{'invisible': [('reconciled_bills_count','&gt;', 1)]}">Bill</span>
+ <span attrs="{'invisible': [('reconciled_bills_count','&lt;=', 1)]}">Bills</span>
+ </button>
+
<!-- Statement stat button -->
<button name="button_open_statements" type="object"
class="oe_stat_button" icon="fa-bars"
diff --git a/addons/account_check_printing/__manifest__.py b/addons/account_check_printing/__manifest__.py
index 4882c5ee815..0e217e4361f 100644
--- a/addons/account_check_printing/__manifest__.py
+++ b/addons/account_check_printing/__manifest__.py
@@ -16,8 +16,10 @@ The check settings are located in the accounting journals configuration page.
'security/ir.model.access.csv',
'data/account_check_printing_data.xml',
'views/account_journal_views.xml',
+ 'views/account_move_views.xml',
'views/account_payment_views.xml',
'views/res_config_settings_views.xml',
+ 'views/res_partner_views.xml',
'wizard/print_prenumbered_checks_views.xml'
],
'installable': True,
diff --git a/addons/account_check_printing/models/__init__.py b/addons/account_check_printing/models/__init__.py
index 806954020e2..7341412a7f7 100644
--- a/addons/account_check_printing/models/__init__.py
+++ b/addons/account_check_printing/models/__init__.py
@@ -2,7 +2,8 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import account_journal
+from . import account_move
from . import account_payment
-from . import chart_template
from . import res_company
from . import res_config_settings
+from . import res_partner
diff --git a/addons/account_check_printing/models/account_journal.py b/addons/account_check_printing/models/account_journal.py
index 433e00c55e8..70eb02c34f2 100644
--- a/addons/account_check_printing/models/account_journal.py
+++ b/addons/account_check_printing/models/account_journal.py
@@ -8,37 +8,67 @@ from odoo.exceptions import ValidationError
class AccountJournal(models.Model):
_inherit = "account.journal"
- @api.depends('outbound_payment_method_ids')
- def _compute_check_printing_payment_method_selected(self):
- for journal in self:
- journal.check_printing_payment_method_selected = any(pm.code == 'check_printing' for pm in journal.outbound_payment_method_ids)
+ check_manual_sequencing = fields.Boolean(
+ string='Manual Numbering',
+ default=False,
+ help="Check this option if your pre-printed checks are not numbered.",
+ )
+ check_sequence_id = fields.Many2one(
+ comodel_name='ir.sequence',
+ string='Check Sequence',
+ readonly=True,
+ copy=False,
+ help="Checks numbering sequence.",
+ )
+ check_next_number = fields.Char(
+ string='Next Check Number',
+ compute='_compute_check_next_number',
+ inverse='_inverse_check_next_number',
+ help="Sequence number of the next printed check.",
+ )
+ check_printing_payment_method_selected = fields.Boolean(
+ compute='_compute_check_printing_payment_method_selected',
+ help="Technical feature used to know whether check printing was enabled as payment method.",
+ )
@api.depends('check_manual_sequencing')
- def _get_check_next_number(self):
+ def _compute_check_next_number(self):
for journal in self:
- if journal.check_sequence_id:
- journal.check_next_number = journal.check_sequence_id.number_next_actual
+ sequence = journal.check_sequence_id
+ if sequence:
+ journal.check_next_number = sequence.get_next_char(sequence.number_next_actual)
else:
journal.check_next_number = 1
- def _set_check_next_number(self):
+ def _inverse_check_next_number(self):
for journal in self:
if journal.check_next_number and not re.match(r'^[0-9]+$', journal.check_next_number):
raise ValidationError(_('Next Check Number should only contains numbers.'))
if int(journal.check_next_number) < journal.check_sequence_id.number_next_actual:
- raise ValidationError(_("The last check number was %s. In order to avoid a check being rejected "
- "by the bank, you can only use a greater number.") % journal.check_sequence_id.number_next_actual)
+ raise ValidationError(_(
+ "The last check number was %s. In order to avoid a check being rejected "
+ "by the bank, you can only use a greater number.",
+ journal.check_sequence_id.number_next_actual
+ ))
if journal.check_sequence_id:
journal.check_sequence_id.sudo().number_next_actual = int(journal.check_next_number)
+ journal.check_sequence_id.sudo().padding = len(journal.check_next_number)
+
+ @api.depends('type')
+ def _compute_outbound_payment_method_ids(self):
+ super()._compute_outbound_payment_method_ids()
+ for journal in self:
+ if journal.type == 'cash':
+ check_method = self.env.ref('account_check_printing.account_payment_method_check')
+ journal.outbound_payment_method_ids -= check_method
- check_manual_sequencing = fields.Boolean('Manual Numbering', default=False,
- help="Check this option if your pre-printed checks are not numbered.")
- check_sequence_id = fields.Many2one('ir.sequence', 'Check Sequence', readonly=True, copy=False,
- help="Checks numbering sequence.")
- check_next_number = fields.Char('Next Check Number', compute='_get_check_next_number', inverse='_set_check_next_number',
- help="Sequence number of the next printed check.")
- check_printing_payment_method_selected = fields.Boolean(compute='_compute_check_printing_payment_method_selected',
- help="Technical feature used to know whether check printing was enabled as payment method.")
+ @api.depends('outbound_payment_method_ids')
+ def _compute_check_printing_payment_method_selected(self):
+ for journal in self:
+ journal.check_printing_payment_method_selected = any(
+ pm.code == 'check_printing'
+ for pm in journal.outbound_payment_method_ids
+ )
@api.model
def create(self, vals):
@@ -73,13 +103,10 @@ class AccountJournal(models.Model):
""" Enables check printing payment method and add a check sequence on bank journals.
Called upon module installation via data file.
"""
- check_printing = self.env.ref('account_check_printing.account_payment_method_check')
- bank_journals = self.search([('type', '=', 'bank')])
- for bank_journal in bank_journals:
+ check_method = self.env.ref('account_check_printing.account_payment_method_check')
+ for bank_journal in self.search([('type', '=', 'bank')]):
bank_journal._create_check_sequence()
- bank_journal.write({
- 'outbound_payment_method_ids': [(4, check_printing.id, None)],
- })
+ bank_journal.outbound_payment_method_ids += check_method
def get_journal_dashboard_datas(self):
domain_checks_to_print = [
@@ -93,6 +120,7 @@ class AccountJournal(models.Model):
)
def action_checks_to_print(self):
+ check_method = self.env.ref('account_check_printing.account_payment_method_check')
return {
'name': _('Checks to Print'),
'type': 'ir.actions.act_window',
@@ -104,6 +132,6 @@ class AccountJournal(models.Model):
journal_id=self.id,
default_journal_id=self.id,
default_payment_type='outbound',
- default_payment_method_id=self.env.ref('account_check_printing.account_payment_method_check').id,
+ default_payment_method_id=check_method.id,
),
}
diff --git a/addons/account_check_printing/models/account_move.py b/addons/account_check_printing/models/account_move.py
new file mode 100644
index 00000000000..c5c8dcb6d5d
--- /dev/null
+++ b/addons/account_check_printing/models/account_move.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+
+from odoo import models, fields, api
+
+
+class AccountMove(models.Model):
+ _inherit = 'account.move'
+
+ preferred_payment_method_id = fields.Many2one(
+ string="Preferred Payment Method",
+ comodel_name='account.payment.method',
+ compute='_compute_preferred_payment_method_idd',
+ store=True,
+ )
+
+ @api.depends('partner_id')
+ def _compute_preferred_payment_method_idd(self):
+ for move in self:
+ partner = move.partner_id
+ # take the payment method corresponding to the move's company
+ move.preferred_payment_method_id = partner.with_company(move.company_id).property_payment_method_id
diff --git a/addons/account_check_printing/models/account_payment.py b/addons/account_check_printing/models/account_payment.py
index d27a2cebe8c..7325186f598 100644
--- a/addons/account_check_printing/models/account_payment.py
+++ b/addons/account_check_printing/models/account_payment.py
@@ -2,7 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, fields, api, _
-from odoo.exceptions import UserError, ValidationError
+from odoo.exceptions import UserError, ValidationError, RedirectWarning
from odoo.tools.misc import formatLang, format_date
INV_LINES_PER_STUB = 9
@@ -11,15 +11,55 @@ INV_LINES_PER_STUB = 9
class AccountPayment(models.Model):
_inherit = "account.payment"
- check_amount_in_words = fields.Char(string="Amount in Words", store=True, readonly=False,
- compute='_compute_check_amount_in_words')
+ check_amount_in_words = fields.Char(
+ string="Amount in Words",
+ store=True,
+ compute='_compute_check_amount_in_words',
+ )
check_manual_sequencing = fields.Boolean(related='journal_id.check_manual_sequencing')
- check_number = fields.Char(string="Check Number", store=True, readonly=True,
+ check_number = fields.Char(
+ string="Check Number",
+ store=True,
+ readonly=True,
compute='_compute_check_number',
+ inverse='_inverse_check_number',
help="The selected journal is configured to print check numbers. If your pre-printed check paper already has numbers "
- "or if the current numbering is wrong, you can change it in the journal configuration page.")
- check_number_int = fields.Integer(string="Check Number Integer", store=True,
- compute='_compute_check_number_int')
+ "or if the current numbering is wrong, you can change it in the journal configuration page.",
+ )
+
+ @api.constrains('check_number', 'journal_id')
+ def _constrains_check_number(self):
+ if not self:
+ return
+ try:
+ self.mapped(lambda p: str(int(p.check_number)))
+ except ValueError:
+ raise ValidationError(_('Check numbers can only consist of digits'))
+ self.flush()
+ self.env.cr.execute("""
+ SELECT payment.check_number, move.journal_id
+ FROM account_payment payment
+ JOIN account_move move ON move.id = payment.move_id
+ JOIN account_journal journal ON journal.id = move.journal_id,
+ account_payment other_payment
+ JOIN account_move other_move ON other_move.id = other_payment.move_id
+ WHERE payment.check_number::INTEGER = other_payment.check_number::INTEGER
+ AND move.journal_id = other_move.journal_id
+ AND payment.id != other_payment.id
+ AND payment.id IN %(ids)s
+ """, {
+ 'ids': tuple(self.ids),
+ })
+ res = self.env.cr.dictfetchall()
+ if res:
+ raise ValidationError(_(
+ 'The following numbers are already used:\n%s',
+ '\n'.join(_(
+ '%(number)s in journal %(journal)s',
+ number=r['check_number'],
+ journal=self.env['account.journal'].browse(r['journal_id']).display_name,
+ ) for r in res)
+ ))
@api.depends('payment_method_id', 'currency_id', 'amount')
def _compute_check_amount_in_words(self):
@@ -33,21 +73,24 @@ class AccountPayment(models.Model):
def _compute_check_number(self):
for pay in self:
if pay.journal_id.check_manual_sequencing:
- pay.check_number = pay.journal_id.check_sequence_id.number_next_actual
+ sequence = pay.journal_id.check_sequence_id
+ pay.check_number = sequence.get_next_char(sequence.number_next_actual)
else:
pay.check_number = False
- @api.depends('check_number')
- def _compute_check_number_int(self):
- # store check number as int to avoid doing a lot of checks and transformations
- # when calculating next_check_number
+ def _inverse_check_number(self):
+ for payment in self:
+ if payment.check_number:
+ sequence = payment.journal_id.check_sequence_id
+ sequence.padding = len(payment.check_number)
+
+ @api.depends('payment_type', 'journal_id', 'partner_id')
+ def _compute_payment_method_id(self):
+ super()._compute_payment_method_id()
for record in self:
- number = record.check_number
- try:
- number = int(number)
- except Exception as e:
- number = 0
- record.check_number_int = number
+ preferred = record.partner_id.with_company(record.company_id).property_payment_method_id
+ if record.payment_type == 'outbound' and preferred in record.journal_id.outbound_payment_method_ids:
+ record.payment_method_id = preferred
def action_post(self):
res = super(AccountPayment, self).action_post()
@@ -71,10 +114,19 @@ class AccountPayment(models.Model):
if not self[0].journal_id.check_manual_sequencing:
# The wizard asks for the number printed on the first pre-printed check
# so payments are attributed the number of the check the'll be printed on.
- last_printed_check = self.search([
- ('journal_id', '=', self[0].journal_id.id),
- ('check_number_int', '!=', 0)], order="check_number_int desc", limit=1)
- next_check_number = last_printed_check and last_printed_check.check_number_int + 1 or 1
+ self.env.cr.execute("""
+ SELECT payment.id
+ FROM account_payment payment
+ JOIN account_move move ON movE.id = payment.move_id
+ WHERE journal_id = %(journal_id)s
+ ORDER BY check_number::INTEGER DESC
+ LIMIT 1
+ """, {
+ 'journal_id': self.journal_id.id,
+ })
+ last_printed_check = self.browse(self.env.cr.fetchone())
+ number_len = len(last_printed_check.check_number or "")
+ next_check_number = '%0{}d'.format(number_len) % (int(last_printed_check.check_number) + 1)
return {
'name': _('Print Pre-numbered Checks'),
@@ -94,13 +146,20 @@ class AccountPayment(models.Model):
def action_unmark_sent(self):
self.write({'is_move_sent': False})
+ def action_void_check(self):
+ self.action_draft()
+ self.action_cancel()
+
def do_print_checks(self):
- check_layout = self[0].company_id.account_check_printing_layout
+ check_layout = self.company_id.account_check_printing_layout
+ redirect_action = self.env.ref('account.action_account_config')
if not check_layout or check_layout == 'disabled':
- raise UserError(_("You have to choose a check layout. For this, go in Invoicing/Accounting Settings, search for 'Checks layout' and set one."))
+ msg = _("You have to choose a check layout. For this, go in Invoicing/Accounting Settings, search for 'Checks layout' and set one.")
+ raise RedirectWarning(msg, redirect_action.id, _('Go to the configuration panel'))
report_action = self.env.ref(check_layout, False)
if not report_action:
- raise UserError(_("Something went wrong with Check Layout, please select another layout in Invoicing/Accounting Settings and try again."))
+ msg = _("Something went wrong with Check Layout, please select another layout in Invoicing/Accounting Settings and try again.")
+ raise RedirectWarning(msg, redirect_action.id, _('Go to the configuration panel'))
self.write({'is_move_sent': True})
return report_action.report_action(self)
@@ -113,7 +172,8 @@ class AccountPayment(models.Model):
def _check_build_page_info(self, i, p):
multi_stub = self.company_id.account_check_printing_multi_stub
return {
- 'sequence_number': self.check_number if (self.journal_id.check_manual_sequencing and self.check_number != 0) else False,
+ 'sequence_number': self.check_number,
+ 'manual_sequencing': self.journal_id.check_manual_sequencing,
'date': format_date(self.env, self.date),
'partner_id': self.partner_id,
'partner_name': self.partner_id.name,
diff --git a/addons/account_check_printing/models/chart_template.py b/addons/account_check_printing/models/chart_template.py
deleted file mode 100644
index 35c101873fb..00000000000
--- a/addons/account_check_printing/models/chart_template.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from odoo import api, models
-
-
-class AccountChartTemplate(models.Model):
- _inherit = 'account.chart.template'
-
- def _create_bank_journals(self, company, acc_template_ref):
- '''
- When system automatically creates journals of bank and cash type when CoA is being installed
- do not enable the `Check` payment method on bank journals of type `Cash`.
-
- '''
- bank_journals = super(AccountChartTemplate, self)._create_bank_journals(company, acc_template_ref)
- payment_method_check = self.env.ref('account_check_printing.account_payment_method_check')
- bank_journals.filtered(lambda journal: journal.type == 'cash').write({
- 'outbound_payment_method_ids': [(3, payment_method_check.id)]
- })
- return bank_journals
diff --git a/addons/account_check_printing/models/res_company.py b/addons/account_check_printing/models/res_company.py
index 45d22854d88..a96ad9a5599 100644
--- a/addons/account_check_printing/models/res_company.py
+++ b/addons/account_check_printing/models/res_company.py
@@ -8,25 +8,37 @@ class res_company(models.Model):
# This field needs to be overridden with `selection_add` in the modules which intends to add report layouts.
# The xmlID of all the report actions which are actually Check Layouts has to be kept as key of the selection.
- account_check_printing_layout = fields.Selection(string="Check Layout",
- help="Select the format corresponding to the check paper you will be printing your checks on.\n"
- "In order to disable the printing feature, select 'None'.",
+ account_check_printing_layout = fields.Selection(
+ string="Check Layout",
selection=[
- ('disabled', 'None')
+ ('disabled', 'None'),
],
- default='disabled')
-
- account_check_printing_date_label = fields.Boolean('Print Date Label', default=True,
- help="This option allows you to print the date label on the check as per CPA. Disable this if your pre-printed check includes the date label.")
-
- account_check_printing_multi_stub = fields.Boolean('Multi-Pages Check Stub',
- help="This option allows you to print check details (stub) on multiple pages if they don't fit on a single page.")
-
- account_check_printing_margin_top = fields.Float('Check Top Margin', default=0.25,
- help="Adjust the margins of generated checks to make it fit your printer's settings.")
-
- account_check_printing_margin_left = fields.Float('Check Left Margin', default=0.25,
- help="Adjust the margins of generated checks to make it fit your printer's settings.")
-
- account_check_printing_margin_right = fields.Float('Right Margin', default=0.25,
- help="Adjust the margins of generated checks to make it fit your printer's settings.")
+ default='disabled',
+ help="Select the format corresponding to the check paper you will be printing your checks on.\n"
+ "In order to disable the printing feature, select 'None'.",
+ )
+ account_check_printing_date_label = fields.Boolean(
+ string='Print Date Label',
+ default=True,
+ help="This option allows you to print the date label on the check as per CPA.\n"
+ "Disable this if your pre-printed check includes the date label.",
+ )
+ account_check_printing_multi_stub = fields.Boolean(
+ string='Multi-Pages Check Stub',
+ help="This option allows you to print check details (stub) on multiple pages if they don't fit on a single page.",
+ )
+ account_check_printing_margin_top = fields.Float(
+ string='Check Top Margin',
+ default=0.25,
+ help="Adjust the margins of generated checks to make it fit your printer's settings.",
+ )
+ account_check_printing_margin_left = fields.Float(
+ string='Check Left Margin',
+ default=0.25,
+ help="Adjust the margins of generated checks to make it fit your printer's settings.",
+ )
+ account_check_printing_margin_right = fields.Float(
+ string='Right Margin',
+ default=0.25,
+ help="Adjust the margins of generated checks to make it fit your printer's settings.",
+ )
diff --git a/addons/account_check_printing/models/res_config_settings.py b/addons/account_check_printing/models/res_config_settings.py
index 2e7b9e2ea7f..5716e12801f 100644
--- a/addons/account_check_printing/models/res_config_settings.py
+++ b/addons/account_check_printing/models/res_config_settings.py
@@ -7,16 +7,41 @@ from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
- account_check_printing_layout = fields.Selection(related='company_id.account_check_printing_layout', string="Check Layout", readonly=False,
+ account_check_printing_layout = fields.Selection(
+ related='company_id.account_check_printing_layout',
+ string="Check Layout",
+ readonly=False,
help="Select the format corresponding to the check paper you will be printing your checks on.\n"
- "In order to disable the printing feature, select 'None'.")
- account_check_printing_date_label = fields.Boolean(related='company_id.account_check_printing_date_label', string="Print Date Label", readonly=False,
- help="This option allows you to print the date label on the check as per CPA. Disable this if your pre-printed check includes the date label.")
- account_check_printing_multi_stub = fields.Boolean(related='company_id.account_check_printing_multi_stub', string='Multi-Pages Check Stub', readonly=False,
- help="This option allows you to print check details (stub) on multiple pages if they don't fit on a single page.")
- account_check_printing_margin_top = fields.Float(related='company_id.account_check_printing_margin_top', string='Check Top Margin', readonly=False,
- help="Adjust the margins of generated checks to make it fit your printer's settings.")
- account_check_printing_margin_left = fields.Float(related='company_id.account_check_printing_margin_left', string='Check Left Margin', readonly=False,
- help="Adjust the margins of generated checks to make it fit your printer's settings.")
- account_check_printing_margin_right = fields.Float(related='company_id.account_check_printing_margin_right', string='Check Right Margin', readonly=False,
- help="Adjust the margins of generated checks to make it fit your printer's settings.")
+ "In order to disable the printing feature, select 'None'."
+ )
+ account_check_printing_date_label = fields.Boolean(
+ related='company_id.account_check_printing_date_label',
+ string="Print Date Label",
+ readonly=False,
+ help="This option allows you to print the date label on the check as per CPA.\n"
+ "Disable this if your pre-printed check includes the date label."
+ )
+ account_check_printing_multi_stub = fields.Boolean(
+ related='company_id.account_check_printing_multi_stub',
+ string='Multi-Pages Check Stub',
+ readonly=False,
+ help="This option allows you to print check details (stub) on multiple pages if they don't fit on a single page."
+ )
+ account_check_printing_margin_top = fields.Float(
+ related='company_id.account_check_printing_margin_top',
+ string='Check Top Margin',
+ readonly=False,
+ help="Adjust the margins of generated checks to make it fit your printer's settings."
+ )
+ account_check_printing_margin_left = fields.Float(
+ related='company_id.account_check_printing_margin_left',
+ string='Check Left Margin',
+ readonly=False,
+ help="Adjust the margins of generated checks to make it fit your printer's settings."
+ )
+ account_check_printing_margin_right = fields.Float(
+ related='company_id.account_check_printing_margin_right',
+ string='Check Right Margin',
+ readonly=False,
+ help="Adjust the margins of generated checks to make it fit your printer's settings."
+ )
diff --git a/addons/account_check_printing/models/res_partner.py b/addons/account_check_printing/models/res_partner.py
new file mode 100644
index 00000000000..93aca4befdf
--- /dev/null
+++ b/addons/account_check_printing/models/res_partner.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+
+from odoo import models, fields
+
+
+class ResPartner(models.Model):
+ _inherit = 'res.partner'
+
+ property_payment_method_id = fields.Many2one(
+ comodel_name='account.payment.method',
+ string='Payment Method',
+ company_dependent=True,
+ domain="[('payment_type', '=', 'outbound')]",
+ help="Preferred payment method when paying this vendor. This is used to filter vendor bills"
+ " by preferred payment method to register payments in mass. Use cases: create bank"
+ " files for batch wires, check runs.",
+ )
diff --git a/addons/account_check_printing/tests/test_print_check.py b/addons/account_check_printing/tests/test_print_check.py
index 82222daea88..40996542a50 100644
--- a/addons/account_check_printing/tests/test_print_check.py
+++ b/addons/account_check_printing/tests/test_print_check.py
@@ -28,7 +28,7 @@ class TestPrintCheck(AccountTestInvoicingCommon):
self.company_data['default_journal_bank'].write({
'check_manual_sequencing': True,
- 'check_next_number': 42,
+ 'check_next_number': '00042',
})
# Create 10 customer invoices.
@@ -69,7 +69,7 @@ class TestPrintCheck(AccountTestInvoicingCommon):
self.company_data['default_journal_bank'].write({
'check_manual_sequencing': True,
- 'check_next_number': 42,
+ 'check_next_number': '00042',
})
# Create 10 refunds.
diff --git a/addons/account_check_printing/views/account_move_views.xml b/addons/account_check_printing/views/account_move_views.xml
new file mode 100644
index 00000000000..905a9da9288
--- /dev/null
+++ b/addons/account_check_printing/views/account_move_views.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="view_account_invoice_filter" model="ir.ui.view">
+ <field name="name">account.invoice.select.account_check_printing</field>
+ <field name="model">account.move</field>
+ <field name="inherit_id" ref="account.view_account_invoice_filter"/>
+ <field name="arch" type="xml">
+ <xpath expr="//search/group/filter[@name='status']" position="after">
+ <filter name="preferred_payment_method" context="{'group_by': 'preferred_payment_method_id'}" groups="account.group_account_invoice,account.group_account_readonly"/>
+ </xpath>
+ </field>
+ </record>
+</odoo>
diff --git a/addons/account_check_printing/views/account_payment_views.xml b/addons/account_check_printing/views/account_payment_views.xml
index 32093a46845..0ee3c729ea6 100644
--- a/addons/account_check_printing/views/account_payment_views.xml
+++ b/addons/account_check_printing/views/account_payment_views.xml
@@ -6,16 +6,20 @@
<field name="inherit_id" ref="account.view_account_payment_form" />
<field name="arch" type="xml">
<xpath expr="//button[@name='action_post']" position="before">
- <button name="print_checks" class="oe_highlight" attrs="{'invisible': ['|', ('payment_method_code', '!=', 'check_printing'), ('state', '!=', 'posted')]}" string="Print Check" type="object"/>
+ <button name="print_checks" class="oe_highlight" attrs="{'invisible': ['|', '|', ('payment_method_code', '!=', 'check_printing'), ('state', '!=', 'posted'), ('is_move_sent', '=', True)]}" string="Print Check" type="object"/>
<button name="action_unmark_sent" attrs="{'invisible': ['|', ('payment_method_code', '!=', 'check_printing'), ('is_move_sent', '=', False)]}" string="Unmark Sent" type="object"/>
+ <button name="action_void_check" attrs="{'invisible': ['|', '|', ('payment_method_code', '!=', 'check_printing'), ('state', '!=', 'posted'), ('is_move_sent', '=', False)]}" string="Void Check" type="object"/>
</xpath>
<xpath expr="//div[@name='amount_div']" position="after">
- <field name="check_amount_in_words" attrs="{'invisible': [('payment_method_code', '!=', 'check_printing')], 'readonly': [('state', '!=', 'draft')]}" groups="base.group_no_one"/>
+ <field name="check_amount_in_words" attrs="{'invisible': [('payment_method_code', '!=', 'check_printing')]}" groups="base.group_no_one"/>
</xpath>
<xpath expr="//field[@name='ref']" position="after">
<field name="check_manual_sequencing" invisible="1"/>
<field name="check_number" attrs="{'invisible': ['|', ('payment_method_code', '!=', 'check_printing'), ('check_number', '=', False)]}"/>
</xpath>
+ <xpath expr="//field[@name='name']" position='before'>
+ <widget name="web_ribbon" title="Sent" attrs="{'invisible': [('is_move_sent', '!=', True)]}"/>
+ </xpath>
</field>
</record>
diff --git a/addons/account_check_printing/views/res_partner_views.xml b/addons/account_check_printing/views/res_partner_views.xml
new file mode 100644
index 00000000000..cdd0fa569f4
--- /dev/null
+++ b/addons/account_check_printing/views/res_partner_views.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <record id="view_partner_property_form" model="ir.ui.view">
+ <field name="name">res.partner.property.form.inherit</field>
+ <field name="model">res.partner</field>
+ <field name="inherit_id" ref="account.view_partner_property_form"/>
+ <field name="arch" type="xml">
+ <field name="property_supplier_payment_term_id" position="after">
+ <field name="property_payment_method_id" widget="selection" groups="account.group_account_invoice,account.group_account_readonly"/>
+ </field>
+ </field>
+ </record>
+</odoo>
diff --git a/addons/account_check_printing/wizard/print_prenumbered_checks.py b/addons/account_check_printing/wizard/print_prenumbered_checks.py
index 416ab4a1a11..84d4f10a9d7 100644
--- a/addons/account_check_printing/wizard/print_prenumbered_checks.py
+++ b/addons/account_check_printing/wizard/print_prenumbered_checks.py
@@ -20,10 +20,11 @@ class PrintPreNumberedChecks(models.TransientModel):
def print_checks(self):
check_number = int(self.next_check_number)
+ number_len = len(self.next_check_number or "")
payments = self.env['account.payment'].browse(self.env.context['payment_ids'])
payments.filtered(lambda r: r.state == 'draft').action_post()
payments.filtered(lambda r: r.state == 'posted' and not r.is_move_sent).write({'is_move_sent': True})
for payment in payments:
- payment.check_number = check_number
+ payment.check_number = '%0{}d'.format(number_len) % check_number
check_number += 1
return payments.do_print_checks()