1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo.osv import expression
class AccountPaymentMethod(models.Model):
_name = "account.payment.method"
_description = "Payment Methods"
name = fields.Char(required=True, translate=True)
code = fields.Char(required=True) # For internal identification
payment_type = fields.Selection(selection=[('inbound', 'Inbound'), ('outbound', 'Outbound')], required=True)
_sql_constraints = [
('name_code_unique', 'unique (code, payment_type)', 'The combination code/payment type already exists!'),
]
@api.model_create_multi
def create(self, vals_list):
payment_methods = super().create(vals_list)
methods_info = self._get_payment_method_information()
return self._auto_link_payment_methods(payment_methods, methods_info)
def _auto_link_payment_methods(self, payment_methods, methods_info):
# This method was extracted from create so it can be overriden in the upgrade script.
# In said script we can then allow for a custom behavior for the payment.method.line on the journals.
for method in payment_methods:
information = methods_info.get(method.code, {})
if information.get('mode') == 'multi':
method_domain = method._get_payment_method_domain(method.code)
journals = self.env['account.journal'].search(method_domain)
self.env['account.payment.method.line'].create([{
'name': method.name,
'payment_method_id': method.id,
'journal_id': journal.id
} for journal in journals])
return payment_methods
@api.model
def _get_payment_method_domain(self, code, with_currency=True, with_country=True):
"""
:param code: string of the payment method line code to check.
:param with_currency: if False (default True), ignore the currency_id domain if it exists.
:return: The domain specifying which journal can accommodate this payment method.
"""
if not code:
return []
information = self._get_payment_method_information().get(code)
journal_types = information.get('type', ('bank', 'cash', 'credit'))
domains = [[('type', 'in', journal_types)]]
if with_currency and (currency_ids := information.get('currency_ids')):
domains += [expression.OR([
[('currency_id', '=', False), ('company_id.currency_id', 'in', currency_ids)],
[('currency_id', 'in', currency_ids)],
])]
if with_country and (country_id := information.get('country_id')):
domains += [[('company_id.account_fiscal_country_id', '=', country_id)]]
return expression.AND(domains)
@api.model
def _get_payment_method_information(self):
"""
Contains details about how to initialize a payment method with the code x.
The contained info are:
- ``mode``: One of the following:
"unique" if the method cannot be used twice on the same company,
"electronic" if the method cannot be used twice on the same company for the same 'payment_provider_id',
"multi" if the method can be duplicated on the same journal.
- ``type``: Tuple containing one or both of these items: "bank" and "cash"
- ``currency_ids``: The ids of the currency necessary on the journal (or company) for it to be eligible.
- ``country_id``: The id of the country needed on the company for it to be eligible.
"""
return {
'manual': {'mode': 'multi', 'type': ('bank', 'cash', 'credit')},
}
@api.model
def _get_sdd_payment_method_code(self):
"""
TO OVERRIDE
This hook will be used to return the list of sdd payment method codes
"""
return []
def unlink(self):
self.env['account.payment.method.line'].search([('payment_method_id', 'in', self.ids)]).unlink()
return super().unlink()
class AccountPaymentMethodLine(models.Model):
_name = "account.payment.method.line"
_description = "Payment Methods"
_order = 'sequence, id'
# == Business fields ==
name = fields.Char(compute='_compute_name', readonly=False, store=True)
sequence = fields.Integer(default=10)
payment_method_id = fields.Many2one(
string='Payment Method',
comodel_name='account.payment.method',
domain="[('payment_type', '=?', payment_type), ('id', 'in', available_payment_method_ids)]",
required=True,
)
payment_account_id = fields.Many2one(
comodel_name='account.account',
check_company=True,
copy=False,
ondelete='restrict',
domain="[('deprecated', '=', False), "
"'|', ('account_type', 'in', ('asset_current', 'liability_current')), ('id', '=', parent.default_account_id)]"
)
journal_id = fields.Many2one(
comodel_name='account.journal',
check_company=True,
)
# == Display purpose fields ==
code = fields.Char(related='payment_method_id.code')
payment_type = fields.Selection(related='payment_method_id.payment_type')
company_id = fields.Many2one(related='journal_id.company_id')
available_payment_method_ids = fields.Many2many(related='journal_id.available_payment_method_ids')
@api.depends('journal_id')
@api.depends_context('hide_payment_journal_id')
def _compute_display_name(self):
for method in self:
if self.env.context.get('hide_payment_journal_id'):
return super()._compute_display_name()
method.display_name = f"{method.name} ({method.journal_id.name})"
@api.depends('payment_method_id.name')
def _compute_name(self):
for method in self:
if not method.name:
method.name = method.payment_method_id.name
@api.constrains('name')
def _ensure_unique_name_for_journal(self):
self.journal_id._check_payment_method_line_ids_multiplicity()
def unlink(self):
"""
Payment method lines which are used in a payment should not be deleted from the database,
only the link betweend them and the journals must be broken.
"""
unused_payment_method_lines = self
for line in self:
payment_count = self.env['account.payment'].sudo().search_count([('payment_method_line_id', '=', line.id)])
if payment_count > 0:
unused_payment_method_lines -= line
(self - unused_payment_method_lines).write({'journal_id': False})
return super(AccountPaymentMethodLine, unused_payment_method_lines).unlink()
@api.model
def _auto_toggle_account_to_reconcile(self, account_id):
""" Automatically toggle the account to reconcile if allowed.
:param account_id: The id of an account.account.
"""
account = self.env['account.account'].browse(account_id)
if not account.reconcile and account.account_type not in ('asset_cash', 'liability_credit_card', 'off_balance'):
account.reconcile = True
@api.model_create_multi
def create(self, vals_list):
# OVERRIDE
for vals in vals_list:
if vals.get('payment_account_id'):
self._auto_toggle_account_to_reconcile(vals['payment_account_id'])
return super().create(vals_list)
def write(self, vals):
# OVERRIDE
if vals.get('payment_account_id'):
self._auto_toggle_account_to_reconcile(vals['payment_account_id'])
return super().write(vals)
|