summaryrefslogtreecommitdiff
path: root/addons/account_check_printing/models
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/account_check_printing/models
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/account_check_printing/models')
-rw-r--r--addons/account_check_printing/models/__init__.py9
-rw-r--r--addons/account_check_printing/models/account_journal.py138
-rw-r--r--addons/account_check_printing/models/account_move.py23
-rw-r--r--addons/account_check_printing/models/account_payment.py321
-rw-r--r--addons/account_check_printing/models/res_company.py44
-rw-r--r--addons/account_check_printing/models/res_config_settings.py47
-rw-r--r--addons/account_check_printing/models/res_partner.py19
7 files changed, 601 insertions, 0 deletions
diff --git a/addons/account_check_printing/models/__init__.py b/addons/account_check_printing/models/__init__.py
new file mode 100644
index 00000000..7341412a
--- /dev/null
+++ b/addons/account_check_printing/models/__init__.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-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 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
new file mode 100644
index 00000000..004293a1
--- /dev/null
+++ b/addons/account_check_printing/models/account_journal.py
@@ -0,0 +1,138 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import re
+from odoo import models, fields, api, _
+from odoo.exceptions import ValidationError
+
+class AccountJournal(models.Model):
+ _inherit = "account.journal"
+
+ 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 _compute_check_next_number(self):
+ for journal in self:
+ 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 _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
+ ))
+ 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
+
+ @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):
+ rec = super(AccountJournal, self).create(vals)
+ if not rec.check_sequence_id:
+ rec._create_check_sequence()
+ return rec
+
+ @api.returns('self', lambda value: value.id)
+ def copy(self, default=None):
+ rec = super(AccountJournal, self).copy(default)
+ rec._create_check_sequence()
+ return rec
+
+ def _create_check_sequence(self):
+ """ Create a check sequence for the journal """
+ for journal in self:
+ journal.check_sequence_id = self.env['ir.sequence'].sudo().create({
+ 'name': journal.name + _(" : Check Number Sequence"),
+ 'implementation': 'no_gap',
+ 'padding': 5,
+ 'number_increment': 1,
+ 'company_id': journal.company_id.id,
+ })
+
+ def _default_outbound_payment_methods(self):
+ methods = super(AccountJournal, self)._default_outbound_payment_methods()
+ return methods + self.env.ref('account_check_printing.account_payment_method_check')
+
+ @api.model
+ def _enable_check_printing_on_bank_journals(self):
+ """ Enables check printing payment method and add a check sequence on bank journals.
+ Called upon module installation via data file.
+ """
+ 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.outbound_payment_method_ids += check_method
+
+ def get_journal_dashboard_datas(self):
+ domain_checks_to_print = [
+ ('journal_id', '=', self.id),
+ ('payment_method_id.code', '=', 'check_printing'),
+ ('state', '=', 'posted'),
+ ('is_move_sent','=', False),
+ ]
+ return dict(
+ super(AccountJournal, self).get_journal_dashboard_datas(),
+ num_checks_to_print=self.env['account.payment'].search_count(domain_checks_to_print),
+ )
+
+ 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',
+ 'view_mode': 'list,form,graph',
+ 'res_model': 'account.payment',
+ 'context': dict(
+ self.env.context,
+ search_default_checks_to_send=1,
+ journal_id=self.id,
+ default_journal_id=self.id,
+ default_payment_type='outbound',
+ 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 00000000..c5c8dcb6
--- /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
new file mode 100644
index 00000000..f8c3a9c4
--- /dev/null
+++ b/addons/account_check_printing/models/account_payment.py
@@ -0,0 +1,321 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models, fields, api, _
+from odoo.exceptions import UserError, ValidationError, RedirectWarning
+from odoo.tools.misc import formatLang, format_date
+
+INV_LINES_PER_STUB = 9
+
+
+class AccountPaymentRegister(models.TransientModel):
+ _inherit = "account.payment.register"
+
+ @api.depends('payment_type', 'journal_id', 'partner_id')
+ def _compute_payment_method_id(self):
+ super()._compute_payment_method_id()
+ for record in self:
+ 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
+
+class AccountPayment(models.Model):
+ _inherit = "account.payment"
+
+ 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,
+ copy=False,
+ 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.",
+ )
+
+ @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
+ AND move.state = 'posted'
+ AND other_move.state = 'posted'
+ """, {
+ '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):
+ for pay in self:
+ if pay.currency_id:
+ pay.check_amount_in_words = pay.currency_id.amount_to_text(pay.amount)
+ else:
+ pay.check_amount_in_words = False
+
+ @api.depends('journal_id', 'payment_method_code')
+ def _compute_check_number(self):
+ for pay in self:
+ if pay.journal_id.check_manual_sequencing and pay.payment_method_code == 'check_printing':
+ sequence = pay.journal_id.check_sequence_id
+ pay.check_number = sequence.get_next_char(sequence.number_next_actual)
+ else:
+ pay.check_number = False
+
+ def _inverse_check_number(self):
+ for payment in self:
+ if payment.check_number:
+ sequence = payment.journal_id.check_sequence_id.sudo()
+ 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:
+ 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()
+ payment_method_check = self.env.ref('account_check_printing.account_payment_method_check')
+ for payment in self.filtered(lambda p: p.payment_method_id == payment_method_check and p.check_manual_sequencing):
+ sequence = payment.journal_id.check_sequence_id
+ payment.check_number = sequence.next_by_id()
+ return res
+
+ def print_checks(self):
+ """ Check that the recordset is valid, set the payments state to sent and call print_checks() """
+ # Since this method can be called via a client_action_multi, we need to make sure the received records are what we expect
+ self = self.filtered(lambda r: r.payment_method_id.code == 'check_printing' and r.state != 'reconciled')
+
+ if len(self) == 0:
+ raise UserError(_("Payments to print as a checks must have 'Check' selected as payment method and "
+ "not have already been reconciled"))
+ if any(payment.journal_id != self[0].journal_id for payment in self):
+ raise UserError(_("In order to print multiple checks at once, they must belong to the same bank journal."))
+
+ 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.
+ 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
+ AND check_number IS NOT NULL
+ 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'),
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'print.prenumbered.checks',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': {
+ 'payment_ids': self.ids,
+ 'default_next_check_number': next_check_number,
+ }
+ }
+ else:
+ self.filtered(lambda r: r.state == 'draft').action_post()
+ return self.do_print_checks()
+
+ 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.company_id.account_check_printing_layout
+ redirect_action = self.env.ref('account.action_account_config')
+ if not check_layout or check_layout == 'disabled':
+ 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:
+ 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)
+
+ #######################
+ #CHECK PRINTING METHODS
+ #######################
+ def _check_fill_line(self, amount_str):
+ return amount_str and (amount_str + ' ').ljust(200, '*') or ''
+
+ def _check_build_page_info(self, i, p):
+ multi_stub = self.company_id.account_check_printing_multi_stub
+ return {
+ '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,
+ 'currency': self.currency_id,
+ 'state': self.state,
+ 'amount': formatLang(self.env, self.amount, currency_obj=self.currency_id) if i == 0 else 'VOID',
+ 'amount_in_word': self._check_fill_line(self.check_amount_in_words) if i == 0 else 'VOID',
+ 'memo': self.ref,
+ 'stub_cropped': not multi_stub and len(self.move_id._get_reconciled_invoices()) > INV_LINES_PER_STUB,
+ # If the payment does not reference an invoice, there is no stub line to display
+ 'stub_lines': p,
+ }
+
+ def _check_get_pages(self):
+ """ Returns the data structure used by the template : a list of dicts containing what to print on pages.
+ """
+ stub_pages = self._check_make_stub_pages() or [False]
+ pages = []
+ for i, p in enumerate(stub_pages):
+ pages.append(self._check_build_page_info(i, p))
+ return pages
+
+ def _check_make_stub_pages(self):
+ """ The stub is the summary of paid invoices. It may spill on several pages, in which case only the check on
+ first page is valid. This function returns a list of stub lines per page.
+ """
+ self.ensure_one()
+
+ def prepare_vals(invoice, partials):
+ number = ' - '.join([invoice.name, invoice.ref] if invoice.ref else [invoice.name])
+
+ if invoice.is_outbound():
+ invoice_sign = 1
+ partial_field = 'debit_amount_currency'
+ else:
+ invoice_sign = -1
+ partial_field = 'credit_amount_currency'
+
+ if invoice.currency_id.is_zero(invoice.amount_residual):
+ amount_residual_str = '-'
+ else:
+ amount_residual_str = formatLang(self.env, invoice_sign * invoice.amount_residual, currency_obj=invoice.currency_id)
+
+ return {
+ 'due_date': format_date(self.env, invoice.invoice_date_due),
+ 'number': number,
+ 'amount_total': formatLang(self.env, invoice_sign * invoice.amount_total, currency_obj=invoice.currency_id),
+ 'amount_residual': amount_residual_str,
+ 'amount_paid': formatLang(self.env, invoice_sign * sum(partials.mapped(partial_field)), currency_obj=self.currency_id),
+ 'currency': invoice.currency_id,
+ }
+
+ # Decode the reconciliation to keep only invoices.
+ term_lines = self.line_ids.filtered(lambda line: line.account_id.internal_type in ('receivable', 'payable'))
+ invoices = (term_lines.matched_debit_ids.debit_move_id.move_id + term_lines.matched_credit_ids.credit_move_id.move_id)\
+ .filtered(lambda x: x.is_outbound())
+ invoices = invoices.sorted(lambda x: x.invoice_date_due or x.date)
+
+ # Group partials by invoices.
+ invoice_map = {invoice: self.env['account.partial.reconcile'] for invoice in invoices}
+ for partial in term_lines.matched_debit_ids:
+ invoice = partial.debit_move_id.move_id
+ if invoice in invoice_map:
+ invoice_map[invoice] |= partial
+ for partial in term_lines.matched_credit_ids:
+ invoice = partial.credit_move_id.move_id
+ if invoice in invoice_map:
+ invoice_map[invoice] |= partial
+
+ # Prepare stub_lines.
+ if 'out_refund' in invoices.mapped('move_type'):
+ stub_lines = [{'header': True, 'name': "Bills"}]
+ stub_lines += [prepare_vals(invoice, partials)
+ for invoice, partials in invoice_map.items()
+ if invoice.move_type == 'in_invoice']
+ stub_lines += [{'header': True, 'name': "Refunds"}]
+ stub_lines += [prepare_vals(invoice, partials)
+ for invoice, partials in invoice_map.items()
+ if invoice.move_type == 'out_refund']
+ else:
+ stub_lines = [prepare_vals(invoice, partials)
+ for invoice, partials in invoice_map.items()
+ if invoice.move_type == 'in_invoice']
+
+ # Crop the stub lines or split them on multiple pages
+ if not self.company_id.account_check_printing_multi_stub:
+ # If we need to crop the stub, leave place for an ellipsis line
+ num_stub_lines = len(stub_lines) > INV_LINES_PER_STUB and INV_LINES_PER_STUB - 1 or INV_LINES_PER_STUB
+ stub_pages = [stub_lines[:num_stub_lines]]
+ else:
+ stub_pages = []
+ i = 0
+ while i < len(stub_lines):
+ # Make sure we don't start the credit section at the end of a page
+ if len(stub_lines) >= i + INV_LINES_PER_STUB and stub_lines[i + INV_LINES_PER_STUB - 1].get('header'):
+ num_stub_lines = INV_LINES_PER_STUB - 1 or INV_LINES_PER_STUB
+ else:
+ num_stub_lines = INV_LINES_PER_STUB
+ stub_pages.append(stub_lines[i:i + num_stub_lines])
+ i += num_stub_lines
+
+ return stub_pages
+
+ def _check_make_stub_line(self, invoice):
+ """ Return the dict used to display an invoice/refund in the stub
+ """
+ # DEPRECATED: TO BE REMOVED IN MASTER
+ # Find the account.partial.reconcile which are common to the invoice and the payment
+ if invoice.move_type in ['in_invoice', 'out_refund']:
+ invoice_sign = 1
+ invoice_payment_reconcile = invoice.line_ids.mapped('matched_debit_ids').filtered(lambda r: r.debit_move_id in self.line_ids)
+ else:
+ invoice_sign = -1
+ invoice_payment_reconcile = invoice.line_ids.mapped('matched_credit_ids').filtered(lambda r: r.credit_move_id in self.line_ids)
+
+ if self.currency_id != self.journal_id.company_id.currency_id:
+ amount_paid = abs(sum(invoice_payment_reconcile.mapped('amount_currency')))
+ else:
+ amount_paid = abs(sum(invoice_payment_reconcile.mapped('amount')))
+
+ amount_residual = invoice_sign * invoice.amount_residual
+
+ return {
+ 'due_date': format_date(self.env, invoice.invoice_date_due),
+ 'number': invoice.ref and invoice.name + ' - ' + invoice.ref or invoice.name,
+ 'amount_total': formatLang(self.env, invoice_sign * invoice.amount_total, currency_obj=invoice.currency_id),
+ 'amount_residual': formatLang(self.env, amount_residual, currency_obj=invoice.currency_id) if amount_residual * 10**4 != 0 else '-',
+ 'amount_paid': formatLang(self.env, invoice_sign * amount_paid, currency_obj=self.currency_id),
+ 'currency': invoice.currency_id,
+ }
diff --git a/addons/account_check_printing/models/res_company.py b/addons/account_check_printing/models/res_company.py
new file mode 100644
index 00000000..a96ad9a5
--- /dev/null
+++ b/addons/account_check_printing/models/res_company.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, fields
+
+
+class res_company(models.Model):
+ _inherit = "res.company"
+
+ # 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",
+ selection=[
+ ('disabled', 'None'),
+ ],
+ 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
new file mode 100644
index 00000000..5716e128
--- /dev/null
+++ b/addons/account_check_printing/models/res_config_settings.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+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,
+ 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.\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 00000000..93aca4be
--- /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.",
+ )