Files
odoo_source/addons/account/models/account_payment_method.py
T
det f79ff3fb37 [IMP] hr_expense: do not create purchase.receipt
Rework of the hr.expense workflow so that:
- expenses paid by employee generate purchase.order
- expenses paid by company generate entry that look like payments

Main reason being that purchase.receipt are not active by default.
That makes the entry hard to find, holes in sequences, inconsitency
with payment states.

It also remove the refusal of expenses as this was dead / inaccessible code, only reports can be refused.

task-id: 3126550
[community](https://github.com/odoo/odoo/pull/110518)
[enterprise](https://github.com/odoo/enterprise/pull/36090)

closes odoo/odoo#110518

Related: odoo/upgrade#4266
Related: odoo/enterprise#36090
Signed-off-by: Laurent Smet <las@odoo.com>
2023-02-24 10:06:06 +01:00

189 lines
7.6 KiB
Python

# -*- 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
from odoo.exceptions import UserError
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()
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):
"""
:return: The domain specyfying which journal can accomodate this payment method.
"""
if not code:
return []
information = self._get_payment_method_information().get(code)
currency_ids = information.get('currency_ids')
country_id = information.get('country_id')
default_domain = [('type', 'in', ('bank', 'cash'))]
domains = [information.get('domain', default_domain)]
if currency_ids:
domains += [expression.OR([
[('currency_id', '=', False), ('company_id.currency_id', 'in', currency_ids)],
[('currency_id', 'in', currency_ids)]],
)]
if 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: Either unique if we only want one of them at a single time (payment providers for example)
or multi if we want the method on each journal fitting the domain.
domain: The domain defining the eligible journals.
currency_id: The id 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.
hidden: If set to true, the method will not be automatically added to the journal,
and will not be selectable by the user.
"""
return {
'manual': {'mode': 'multi', 'domain': [('type', 'in', ('bank', 'cash'))]},
}
@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 []
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), "
"('company_id', '=', company_id), "
"'|', ('account_type', 'in', ('asset_current', 'liability_current')), ('id', '=', parent.default_account_id)]"
)
journal_id = fields.Many2one(comodel_name='account.journal', ondelete="cascade")
# == 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')
def name_get(self):
if self.env.context.get('show_payment_journal_id'):
return [(method.id, "%s (%s)" % (method.name, method.journal_id.name)) for method in self]
return super().name_get()
@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.flush_model(['name', 'journal_id', 'payment_method_id'])
self.env['account.payment.method'].flush_model(['payment_type'])
self._cr.execute('''
SELECT apml.name, apm.payment_type
FROM account_payment_method_line apml
JOIN account_payment_method apm ON apml.payment_method_id = apm.id
WHERE apml.journal_id IS NOT NULL
GROUP BY apml.name, journal_id, apm.payment_type
HAVING count(apml.id) > 1
''')
res = self._cr.fetchall()
if res:
(name, payment_type) = res[0]
raise UserError(_("You can't have two payment method lines of the same payment type (%s) "
"and with the same name (%s) on a single journal.", payment_type, name))
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'].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') and account.internal_group != '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)