Odoo18-Base/addons/account/models/account_payment_method.py

188 lines
7.7 KiB
Python
Raw Permalink Normal View History

2025-01-06 10:57:38 +07:00
# -*- 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', '=', default_account_id)]"
)
journal_id = fields.Many2one(
comodel_name='account.journal',
check_company=True,
)
default_account_id = fields.Many2one(
related='journal_id.default_account_id'
)
# == 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)