[IMP] account: allows multiple payment acquirers on a journal.
Users may want to be able to have transactions coming from multiple payment acquirers to be registered in the same journal. This will allows that. Task id #2414749 closes odoo/odoo#67331 Related: odoo/upgrade#2500 Related: odoo/enterprise#17258 Signed-off-by: William André (wan) <wan@odoo.com>
This commit is contained in:
@@ -131,22 +131,57 @@ class AccountAccount(models.Model):
|
||||
self.env['account.journal'].flush([
|
||||
'currency_id',
|
||||
'default_account_id',
|
||||
'payment_debit_account_id',
|
||||
'payment_credit_account_id',
|
||||
'suspense_account_id',
|
||||
])
|
||||
self.env['account.payment.method'].flush(['payment_type'])
|
||||
self.env['account.payment.method.line'].flush(['payment_method_id', 'payment_account_id'])
|
||||
|
||||
self._cr.execute('''
|
||||
SELECT account.id, journal.id
|
||||
FROM account_account account
|
||||
JOIN res_company company ON company.id = account.company_id
|
||||
JOIN account_journal journal ON
|
||||
journal.default_account_id = account.id
|
||||
WHERE account.id IN %s
|
||||
AND journal.type IN ('bank', 'cash')
|
||||
AND journal.currency_id IS NOT NULL
|
||||
SELECT
|
||||
account.id,
|
||||
journal.id
|
||||
FROM account_journal journal
|
||||
JOIN res_company company ON company.id = journal.company_id
|
||||
JOIN account_account account ON account.id = journal.default_account_id
|
||||
WHERE journal.currency_id IS NOT NULL
|
||||
AND journal.currency_id != company.currency_id
|
||||
AND account.currency_id != journal.currency_id
|
||||
''', [tuple(self.ids)])
|
||||
AND account.id IN %(accounts)s
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
account.id,
|
||||
journal.id
|
||||
FROM account_journal journal
|
||||
JOIN res_company company ON company.id = journal.company_id
|
||||
JOIN account_payment_method_line apml ON apml.journal_id = journal.id
|
||||
JOIN account_payment_method apm on apm.id = apml.payment_method_id
|
||||
JOIN account_account account ON account.id = COALESCE(apml.payment_account_id, company.account_journal_payment_debit_account_id)
|
||||
WHERE journal.currency_id IS NOT NULL
|
||||
AND journal.currency_id != company.currency_id
|
||||
AND account.currency_id != journal.currency_id
|
||||
AND apm.payment_type = 'inbound'
|
||||
AND account.id IN %(accounts)s
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
account.id,
|
||||
journal.id
|
||||
FROM account_journal journal
|
||||
JOIN res_company company ON company.id = journal.company_id
|
||||
JOIN account_payment_method_line apml ON apml.journal_id = journal.id
|
||||
JOIN account_payment_method apm on apm.id = apml.payment_method_id
|
||||
JOIN account_account account ON account.id = COALESCE(apml.payment_account_id, company.account_journal_payment_credit_account_id)
|
||||
WHERE journal.currency_id IS NOT NULL
|
||||
AND journal.currency_id != company.currency_id
|
||||
AND account.currency_id != journal.currency_id
|
||||
AND apm.payment_type = 'outbound'
|
||||
AND account.id IN %(accounts)s
|
||||
''', {
|
||||
'accounts': tuple(self.ids)
|
||||
})
|
||||
res = self._cr.fetchone()
|
||||
if res:
|
||||
account = self.env['account.account'].browse(res[0])
|
||||
@@ -200,14 +235,18 @@ class AccountAccount(models.Model):
|
||||
return
|
||||
|
||||
self.flush(['reconcile'])
|
||||
self.env['account.payment.method.line'].flush(['journal_id', 'payment_account_id'])
|
||||
|
||||
self._cr.execute('''
|
||||
SELECT journal.id
|
||||
FROM account_journal journal
|
||||
WHERE journal.payment_credit_account_id in %(credit_account)s
|
||||
OR journal.payment_debit_account_id in %(debit_account)s ;
|
||||
JOIN res_company company on journal.company_id = company.id
|
||||
LEFT JOIN account_payment_method_line apml ON journal.id = apml.journal_id
|
||||
WHERE company.account_journal_payment_credit_account_id in %(accounts)s
|
||||
OR company.account_journal_payment_debit_account_id in %(accounts)s
|
||||
OR apml.payment_account_id in %(accounts)s
|
||||
''', {
|
||||
'credit_account': tuple(accounts.ids),
|
||||
'debit_account': tuple(accounts.ids)
|
||||
'accounts': tuple(accounts.ids),
|
||||
})
|
||||
|
||||
rows = self._cr.fetchall()
|
||||
|
||||
@@ -1189,10 +1189,7 @@ class AccountBankStatementLine(models.Model):
|
||||
new_lines = self.env['account.move.line'].create(line_vals_list)
|
||||
new_lines = new_lines.with_context(skip_account_move_synchronization=True)
|
||||
for reconciliation_vals, line in zip(reconciliation_overview, new_lines):
|
||||
if reconciliation_vals.get('payment'):
|
||||
accounts = (self.journal_id.payment_debit_account_id, self.journal_id.payment_credit_account_id)
|
||||
counterpart_line = reconciliation_vals['payment'].line_ids.filtered(lambda line: line.account_id in accounts)
|
||||
elif reconciliation_vals.get('counterpart_line'):
|
||||
if reconciliation_vals.get('counterpart_line'):
|
||||
counterpart_line = reconciliation_vals['counterpart_line']
|
||||
else:
|
||||
continue
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, fields, models, _
|
||||
from odoo import api, Command, fields, models, _
|
||||
from odoo.osv import expression
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.addons.base.models.res_bank import sanitize_account_number
|
||||
@@ -79,26 +79,6 @@ class AccountJournal(models.Model):
|
||||
domain="[('deprecated', '=', False), ('company_id', '=', company_id),"
|
||||
"'|', ('user_type_id', '=', default_account_type), ('user_type_id', 'in', type_control_ids),"
|
||||
"('user_type_id.type', 'not in', ('receivable', 'payable'))]")
|
||||
payment_debit_account_id = fields.Many2one(
|
||||
comodel_name='account.account', check_company=True, ondelete='restrict',
|
||||
compute='_compute_payment_debit_account_id', store=True, readonly=False,
|
||||
help="Incoming payments entries triggered by invoices/refunds will be posted on the Outstanding Receipts Account "
|
||||
"and displayed as blue lines in the bank reconciliation widget. During the reconciliation process, concerned "
|
||||
"transactions will be reconciled with entries on the Outstanding Receipts Account instead of the "
|
||||
"receivable account.", string='Outstanding Receipts Account',
|
||||
domain=lambda self: "[('deprecated', '=', False), ('company_id', '=', company_id), \
|
||||
('user_type_id.type', 'not in', ('receivable', 'payable')), \
|
||||
'|', ('user_type_id', '=', %s), ('id', '=', default_account_id)]" % self.env.ref('account.data_account_type_current_assets').id)
|
||||
payment_credit_account_id = fields.Many2one(
|
||||
comodel_name='account.account', check_company=True, ondelete='restrict',
|
||||
compute='_compute_payment_credit_account_id', store=True, readonly=False,
|
||||
help="Outgoing payments entries triggered by bills/credit notes will be posted on the Outstanding Payments Account "
|
||||
"and displayed as blue lines in the bank reconciliation widget. During the reconciliation process, concerned "
|
||||
"transactions will be reconciled with entries on the Outstanding Payments Account instead of the "
|
||||
"payable account.", string='Outstanding Payments Account',
|
||||
domain=lambda self: "[('deprecated', '=', False), ('company_id', '=', company_id), \
|
||||
('user_type_id.type', 'not in', ('receivable', 'payable')), \
|
||||
'|', ('user_type_id', '=', %s), ('id', '=', default_account_id)]" % self.env.ref('account.data_account_type_current_assets').id)
|
||||
suspense_account_id = fields.Many2one(
|
||||
comodel_name='account.account', check_company=True, ondelete='restrict', readonly=False, store=True,
|
||||
compute='_compute_suspense_account_id',
|
||||
@@ -127,42 +107,35 @@ class AccountJournal(models.Model):
|
||||
|
||||
"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(
|
||||
comodel_name='account.payment.method',
|
||||
relation='account_journal_inbound_payment_method_rel',
|
||||
column1='journal_id',
|
||||
column2='inbound_payment_method',
|
||||
inbound_payment_method_line_ids = fields.One2many(
|
||||
comodel_name='account.payment.method.line',
|
||||
domain=[('payment_type', '=', 'inbound')],
|
||||
compute='_compute_inbound_payment_method_line_ids',
|
||||
store=True,
|
||||
readonly=False,
|
||||
string='Inbound Payment Methods',
|
||||
compute='_compute_inbound_payment_method_ids',
|
||||
ondelete="restrict",
|
||||
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."
|
||||
inverse_name='journal_id',
|
||||
copy=False,
|
||||
check_company=True,
|
||||
help="Manual :Get paid by any method outside of Odoo."
|
||||
"Payment Acquirers : Each payment acquirer has its own Payment Method. Request a transaction on/to a card thanks to a payment token saved by the partner when buying or subscribing online."
|
||||
"Batch Deposit: Collect several customer checks at once generating and submitting a batch deposit to your bank. Module account_batch_payment is necessary."
|
||||
"SEPA Direct Debit : Get paid in the SEPA zone thanks to a mandate your partner will have granted to you. Module account_sepa is necessary"
|
||||
)
|
||||
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',
|
||||
outbound_payment_method_line_ids = fields.One2many(
|
||||
comodel_name='account.payment.method.line',
|
||||
domain=[('payment_type', '=', 'outbound')],
|
||||
string='Outbound Payment Methods',
|
||||
compute='_compute_outbound_payment_method_ids',
|
||||
ondelete="restrict",
|
||||
compute='_compute_outbound_payment_method_line_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."
|
||||
string='Outbound Payment Methods',
|
||||
inverse_name='journal_id',
|
||||
copy=False,
|
||||
check_company=True,
|
||||
help="Manual : Pay by any method outside of Odoo."
|
||||
"Check: Pay bills by check and print it from Odoo."
|
||||
"SEPA Credit Transfer : Pay in the SEPA zone by submitting a SEPA Credit Transfer file to your bank. Module account_sepa is necessary"
|
||||
)
|
||||
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(
|
||||
comodel_name='account.account', check_company=True,
|
||||
help="Used to register a profit when the ending balance of a cash register differs from what the system computes",
|
||||
@@ -212,10 +185,87 @@ class AccountJournal(models.Model):
|
||||
check_company=True,
|
||||
readonly=True, copy=False)
|
||||
|
||||
available_payment_method_ids = fields.Many2many(
|
||||
comodel_name='account.payment.method',
|
||||
compute='_compute_available_payment_method_ids'
|
||||
)
|
||||
|
||||
selected_payment_method_codes = fields.Char(
|
||||
compute='_compute_selected_payment_method_codes',
|
||||
help='Technical field used to hide or show payment method options if needed.'
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
('code_company_uniq', 'unique (code, company_id)', 'Journal codes must be unique per company.'),
|
||||
]
|
||||
|
||||
@api.depends('outbound_payment_method_line_ids', 'inbound_payment_method_line_ids')
|
||||
def _compute_available_payment_method_ids(self):
|
||||
"""
|
||||
Compute the available payment methods id by respecting the following rules:
|
||||
Methods of mode 'unique' cannot be used twice on the same company
|
||||
Methods of mode 'multi' cannot be used twice on the same journal
|
||||
"""
|
||||
method_information = self.env['account.payment.method']._get_payment_method_information()
|
||||
pay_methods = self.env['account.payment.method'].search([('code', 'in', list(method_information.keys()))])
|
||||
pay_method_by_code = {x.code + x.payment_type: x for x in pay_methods}
|
||||
unique_pay_methods = [k for k, v in method_information.items() if v['mode'] == 'unique']
|
||||
installed_acquirers = self.env['payment.acquirer'].search([])
|
||||
|
||||
pay_methods_by_company = {}
|
||||
pay_methods_by_journal = {}
|
||||
if unique_pay_methods:
|
||||
self._cr.execute('''
|
||||
SELECT
|
||||
journal.id,
|
||||
journal.company_id,
|
||||
ARRAY_AGG(DISTINCT apm.id)
|
||||
FROM account_payment_method_line apml
|
||||
JOIN account_journal journal ON journal.id = apml.journal_id
|
||||
JOIN account_payment_method apm ON apm.id = apml.payment_method_id
|
||||
WHERE apm.code IN %s
|
||||
GROUP BY
|
||||
journal.id,
|
||||
journal.company_id
|
||||
''', [tuple(unique_pay_methods)])
|
||||
for journal_id, company_id, payment_method_ids in self._cr.fetchall():
|
||||
pay_methods_by_company[company_id] = set(payment_method_ids)
|
||||
pay_methods_by_journal[journal_id] = set(payment_method_ids)
|
||||
|
||||
pay_method_ids_commands_x_journal = {j: [Command.clear()] for j in self}
|
||||
for payment_type in ('inbound', 'outbound'):
|
||||
for code, vals in method_information.items():
|
||||
payment_method = pay_method_by_code.get(code + payment_type)
|
||||
|
||||
if not payment_method:
|
||||
continue
|
||||
|
||||
# Get the domain of the journals on which the current method is usable.
|
||||
method_domain = payment_method._get_payment_method_domain()
|
||||
|
||||
for journal in self.filtered_domain(method_domain):
|
||||
protected_pay_method_ids = pay_methods_by_company.get(journal.company_id._origin.id, set()) \
|
||||
- pay_methods_by_journal.get(journal._origin.id, set())
|
||||
available_providers = installed_acquirers.filtered(
|
||||
lambda a: a.company_id == journal.company_id
|
||||
).mapped('provider')
|
||||
|
||||
if payment_type == 'inbound':
|
||||
lines = journal.inbound_payment_method_line_ids
|
||||
else:
|
||||
lines = journal.outbound_payment_method_line_ids
|
||||
|
||||
available = payment_method.code in available_providers
|
||||
already_used = payment_method in lines.payment_method_id
|
||||
is_protected = payment_method.id in protected_pay_method_ids
|
||||
if vals['mode'] == 'unique' and (already_used or is_protected or not available):
|
||||
continue
|
||||
|
||||
pay_method_ids_commands_x_journal[journal].append(Command.link(payment_method.id))
|
||||
|
||||
for journal, pay_method_ids_commands in pay_method_ids_commands_x_journal.items():
|
||||
journal.available_payment_method_ids = pay_method_ids_commands
|
||||
|
||||
@api.depends('type')
|
||||
def _compute_default_account_type(self):
|
||||
default_account_id_types = {
|
||||
@@ -232,20 +282,38 @@ class AccountJournal(models.Model):
|
||||
journal.default_account_type = False
|
||||
|
||||
@api.depends('type')
|
||||
def _compute_outbound_payment_method_ids(self):
|
||||
def _compute_inbound_payment_method_line_ids(self):
|
||||
for journal in self:
|
||||
pay_method_line_ids_commands = [Command.clear()]
|
||||
if journal.type in ('bank', 'cash'):
|
||||
journal.outbound_payment_method_ids = self._default_outbound_payment_methods()
|
||||
else:
|
||||
journal.outbound_payment_method_ids = False
|
||||
default_methods = journal._default_inbound_payment_methods()
|
||||
pay_method_line_ids_commands += [Command.create({
|
||||
'name': pay_method.name,
|
||||
'payment_method_id': pay_method.id,
|
||||
}) for pay_method in default_methods]
|
||||
journal.inbound_payment_method_line_ids = pay_method_line_ids_commands
|
||||
|
||||
@api.depends('type')
|
||||
def _compute_inbound_payment_method_ids(self):
|
||||
def _compute_outbound_payment_method_line_ids(self):
|
||||
for journal in self:
|
||||
pay_method_line_ids_commands = [Command.clear()]
|
||||
if journal.type in ('bank', 'cash'):
|
||||
journal.inbound_payment_method_ids = self._default_inbound_payment_methods()
|
||||
else:
|
||||
journal.inbound_payment_method_ids = False
|
||||
default_methods = journal._default_outbound_payment_methods()
|
||||
pay_method_line_ids_commands += [Command.create({
|
||||
'name': pay_method.name,
|
||||
'payment_method_id': pay_method.id,
|
||||
}) for pay_method in default_methods]
|
||||
journal.outbound_payment_method_line_ids = pay_method_line_ids_commands
|
||||
|
||||
@api.depends('outbound_payment_method_line_ids', 'inbound_payment_method_line_ids')
|
||||
def _compute_selected_payment_method_codes(self):
|
||||
"""
|
||||
Set the selected payment method as a list of comma separated codes like: ,manual,check_printing,...
|
||||
These will be then used to display or not payment method specific fields in the view.
|
||||
"""
|
||||
for journal in self:
|
||||
codes = [line.code for line in journal.inbound_payment_method_line_ids + journal.outbound_payment_method_line_ids]
|
||||
journal.selected_payment_method_codes = ',' + ','.join(codes) + ','
|
||||
|
||||
@api.depends('company_id', 'type')
|
||||
def _compute_suspense_account_id(self):
|
||||
@@ -259,30 +327,6 @@ class AccountJournal(models.Model):
|
||||
else:
|
||||
journal.suspense_account_id = False
|
||||
|
||||
@api.depends('company_id', 'type')
|
||||
def _compute_payment_debit_account_id(self):
|
||||
for journal in self:
|
||||
if journal.type not in ('bank', 'cash'):
|
||||
journal.payment_debit_account_id = False
|
||||
elif journal.payment_debit_account_id:
|
||||
journal.payment_debit_account_id = journal.payment_debit_account_id
|
||||
elif journal.company_id.account_journal_payment_debit_account_id:
|
||||
journal.payment_debit_account_id = journal.company_id.account_journal_payment_debit_account_id
|
||||
else:
|
||||
journal.payment_debit_account_id = False
|
||||
|
||||
@api.depends('company_id', 'type')
|
||||
def _compute_payment_credit_account_id(self):
|
||||
for journal in self:
|
||||
if journal.type not in ('bank', 'cash'):
|
||||
journal.payment_credit_account_id = False
|
||||
elif journal.payment_credit_account_id:
|
||||
journal.payment_credit_account_id = journal.payment_credit_account_id
|
||||
elif journal.company_id.account_journal_payment_credit_account_id:
|
||||
journal.payment_credit_account_id = journal.company_id.account_journal_payment_credit_account_id
|
||||
else:
|
||||
journal.payment_credit_account_id = False
|
||||
|
||||
def _compute_alias_domain(self):
|
||||
alias_domain = self._default_alias_domain()
|
||||
for record in self:
|
||||
@@ -351,6 +395,38 @@ class AccountJournal(models.Model):
|
||||
if journal.type in ('sale', 'purchase') and journal.default_account_id.user_type_id.type in ('receivable', 'payable'):
|
||||
raise ValidationError(_("The type of the journal's default credit/debit account shouldn't be 'receivable' or 'payable'."))
|
||||
|
||||
@api.constrains('inbound_payment_method_line_ids', 'outbound_payment_method_line_ids')
|
||||
def _check_payment_method_line_ids_multiplicity(self):
|
||||
"""
|
||||
Check and ensure that the payment method lines multiplicity is respected.
|
||||
"""
|
||||
method_info = self.env['account.payment.method']._get_payment_method_information()
|
||||
unique_codes = tuple(code for code, info in method_info.items() if info.get('mode') == 'unique')
|
||||
|
||||
self.flush(['inbound_payment_method_line_ids', 'outbound_payment_method_line_ids', 'company_id'])
|
||||
self.env['account.payment.method.line'].flush(['payment_method_id', 'journal_id'])
|
||||
self.env['account.payment.method'].flush(['code'])
|
||||
|
||||
if unique_codes:
|
||||
self._cr.execute('''
|
||||
SELECT apm.id
|
||||
FROM account_payment_method apm
|
||||
JOIN account_payment_method_line apml on apm.id = apml.payment_method_id
|
||||
JOIN account_journal journal on journal.id = apml.journal_id
|
||||
JOIN res_company company on journal.company_id = company.id
|
||||
WHERE apm.code in %s
|
||||
GROUP BY
|
||||
company.id,
|
||||
apm.id
|
||||
HAVING array_length(array_agg(journal.id), 1) > 1;
|
||||
''', [unique_codes])
|
||||
|
||||
method_ids = [res[0] for res in self._cr.fetchall()]
|
||||
if method_ids:
|
||||
methods = self.env['account.payment.method'].browse(method_ids)
|
||||
raise ValidationError(_("Some payment methods supposed to be unique already exists somewhere else.\n"
|
||||
"(%s)", ', '.join([method.display_name for method in methods])))
|
||||
|
||||
@api.onchange('type')
|
||||
def _onchange_type(self):
|
||||
self.refund_sequence = self.type in ('sale', 'purchase')
|
||||
@@ -600,12 +676,6 @@ class AccountJournal(models.Model):
|
||||
domain = [connector, ('code', operator, name), ('name', operator, name)]
|
||||
return self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid)
|
||||
|
||||
@api.depends('inbound_payment_method_ids', 'outbound_payment_method_ids')
|
||||
def _methods_compute(self):
|
||||
for journal in self:
|
||||
journal.at_least_one_inbound = bool(len(journal.inbound_payment_method_ids))
|
||||
journal.at_least_one_outbound = bool(len(journal.outbound_payment_method_ids))
|
||||
|
||||
def action_configure_bank_journal(self):
|
||||
""" This function is called by the "configure" button of bank journals,
|
||||
visible on dashboard if no bank statement source has been defined yet
|
||||
@@ -721,6 +791,26 @@ class AccountJournal(models.Model):
|
||||
nb_lines, balance, amount_currency = self._cr.fetchone()
|
||||
return amount_currency if journal_currency else balance, nb_lines
|
||||
|
||||
def _get_journal_inbound_outstanding_payment_accounts(self):
|
||||
"""
|
||||
:return: A recordset with all the account.account used by this journal for inbound transactions.
|
||||
"""
|
||||
self.ensure_one()
|
||||
account_ids = set()
|
||||
for line in self.inbound_payment_method_line_ids:
|
||||
account_ids.add(line.payment_account_id.id or self.company_id.account_journal_payment_debit_account_id.id)
|
||||
return self.env['account.account'].browse(account_ids)
|
||||
|
||||
def _get_journal_outbound_outstanding_payment_accounts(self):
|
||||
"""
|
||||
:return: A recordset with all the account.account used by this journal for outbound transactions.
|
||||
"""
|
||||
self.ensure_one()
|
||||
account_ids = set()
|
||||
for line in self.outbound_payment_method_line_ids:
|
||||
account_ids.add(line.payment_account_id.id or self.company_id.account_journal_payment_credit_account_id.id)
|
||||
return self.env['account.account'].browse(account_ids)
|
||||
|
||||
def _get_journal_outstanding_payments_account_balance(self, domain=None, date=None):
|
||||
''' Get the outstanding payments balance of the current journal by filtering the journal items using the
|
||||
journal's accounts.
|
||||
@@ -733,7 +823,7 @@ class AccountJournal(models.Model):
|
||||
self.env['account.move.line'].check_access_rights('read')
|
||||
conversion_date = date or fields.Date.context_today(self)
|
||||
|
||||
accounts = self.payment_debit_account_id + self.payment_credit_account_id
|
||||
accounts = self._get_journal_inbound_outstanding_payment_accounts().union(self._get_journal_outbound_outstanding_payment_accounts())
|
||||
if not accounts:
|
||||
return 0.0, 0
|
||||
|
||||
|
||||
@@ -52,11 +52,12 @@ class AccountPayment(models.Model):
|
||||
readonly=False, store=True,
|
||||
compute='_compute_payment_method_id',
|
||||
domain="[('id', 'in', available_payment_method_ids)]",
|
||||
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"\
|
||||
"Check: Pay bill by check and print it from Odoo.\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.To enable batch deposit, module account_batch_payment must be installed.\n"\
|
||||
"SEPA Credit Transfer: Pay bill from a SEPA Credit Transfer file you submit to your bank. To enable sepa credit transfer, module account_sepa must be installed ")
|
||||
help="Manual : Pay or Get paid by any method outside of Odoo."
|
||||
"Payment Acquirers : Each payment acquirer has its own Payment Method. Request a transaction on/to a card thanks to a payment token saved by the partner when buying or subscribing online."
|
||||
"Check: Pay bills by check and print it from Odoo."
|
||||
"Batch Deposit: Collect several customer checks at once generating and submitting a batch deposit to your bank. Module account_batch_payment is necessary."
|
||||
"SEPA Credit Transfer : Pay in the SEPA zone by submitting a SEPA Credit Transfer file to your bank. Module account_sepa is necessary"
|
||||
"SEPA Direct Debit : Get paid in the SEPA zone thanks to a mandate your partner will have granted to you. Module account_sepa is necessary")
|
||||
available_payment_method_ids = fields.Many2many('account.payment.method',
|
||||
compute='_compute_payment_method_fields')
|
||||
hide_payment_method = fields.Boolean(
|
||||
@@ -85,6 +86,12 @@ class AccountPayment(models.Model):
|
||||
compute='_compute_partner_id',
|
||||
domain="['|', ('parent_id','=', False), ('is_company','=', True)]",
|
||||
check_company=True)
|
||||
outstanding_account_id = fields.Many2one(
|
||||
comodel_name='account.account',
|
||||
string="Outstanding Account",
|
||||
store=True,
|
||||
compute='_compute_outstanding_account_id',
|
||||
check_company=True)
|
||||
destination_account_id = fields.Many2one(
|
||||
comodel_name='account.account',
|
||||
string='Destination Account',
|
||||
@@ -162,11 +169,20 @@ class AccountPayment(models.Model):
|
||||
counterpart_lines = self.env['account.move.line']
|
||||
writeoff_lines = self.env['account.move.line']
|
||||
|
||||
if self.payment_type == 'inbound':
|
||||
payment_method_account_ids = self.journal_id.inbound_payment_method_line_ids
|
||||
else:
|
||||
payment_method_account_ids = self.journal_id.outbound_payment_method_line_ids
|
||||
|
||||
payment_method_account_id = payment_method_account_ids\
|
||||
.filtered(lambda line: line.code == self.payment_method_id.code).payment_account_id
|
||||
|
||||
for line in self.move_id.line_ids:
|
||||
if line.account_id in (
|
||||
self.journal_id.default_account_id,
|
||||
self.journal_id.payment_debit_account_id,
|
||||
self.journal_id.payment_credit_account_id,
|
||||
payment_method_account_id,
|
||||
self.journal_id.company_id.account_journal_payment_debit_account_id,
|
||||
self.journal_id.company_id.account_journal_payment_credit_account_id,
|
||||
):
|
||||
liquidity_lines += line
|
||||
elif line.account_id.internal_type in ('receivable', 'payable') or line.partner_id == line.company_id.partner_id:
|
||||
@@ -187,10 +203,10 @@ class AccountPayment(models.Model):
|
||||
self.ensure_one()
|
||||
write_off_line_vals = write_off_line_vals or {}
|
||||
|
||||
if not self.journal_id.payment_debit_account_id or not self.journal_id.payment_credit_account_id:
|
||||
if not self.outstanding_account_id:
|
||||
raise UserError(_(
|
||||
"You can't create a new payment without an outstanding payments/receipts account set on the %s journal.",
|
||||
self.journal_id.display_name))
|
||||
"You can't create a new payment without an outstanding payments/receipts account set either on the company or the %s payment method in the %s journal.",
|
||||
self.payment_method_id.name, self.journal_id.display_name))
|
||||
|
||||
# Compute amounts.
|
||||
write_off_amount_currency = write_off_line_vals.get('amount', 0.0)
|
||||
@@ -256,7 +272,7 @@ class AccountPayment(models.Model):
|
||||
'debit': liquidity_balance if liquidity_balance > 0.0 else 0.0,
|
||||
'credit': -liquidity_balance if liquidity_balance < 0.0 else 0.0,
|
||||
'partner_id': self.partner_id.id,
|
||||
'account_id': self.journal_id.payment_credit_account_id.id if liquidity_balance < 0.0 else self.journal_id.payment_debit_account_id.id,
|
||||
'account_id': self.outstanding_account_id.id,
|
||||
},
|
||||
# Receivable / Payable.
|
||||
{
|
||||
@@ -371,9 +387,9 @@ class AccountPayment(models.Model):
|
||||
'''
|
||||
for pay in self:
|
||||
if pay.payment_type == 'inbound':
|
||||
available_payment_methods = pay.journal_id.inbound_payment_method_ids
|
||||
available_payment_methods = pay.journal_id.inbound_payment_method_line_ids.mapped('payment_method_id')
|
||||
else:
|
||||
available_payment_methods = pay.journal_id.outbound_payment_method_ids
|
||||
available_payment_methods = pay.journal_id.outbound_payment_method_line_ids.mapped('payment_method_id')
|
||||
|
||||
# Select the first available one by default.
|
||||
if pay.payment_method_id in available_payment_methods:
|
||||
@@ -384,14 +400,14 @@ class AccountPayment(models.Model):
|
||||
pay.payment_method_id = False
|
||||
|
||||
@api.depends('payment_type',
|
||||
'journal_id.inbound_payment_method_ids',
|
||||
'journal_id.outbound_payment_method_ids')
|
||||
'journal_id.inbound_payment_method_line_ids',
|
||||
'journal_id.outbound_payment_method_line_ids')
|
||||
def _compute_payment_method_fields(self):
|
||||
for pay in self:
|
||||
if pay.payment_type == 'inbound':
|
||||
pay.available_payment_method_ids = pay.journal_id.inbound_payment_method_ids
|
||||
pay.available_payment_method_ids = pay.journal_id.inbound_payment_method_line_ids.mapped('payment_method_id')
|
||||
else:
|
||||
pay.available_payment_method_ids = pay.journal_id.outbound_payment_method_ids
|
||||
pay.available_payment_method_ids = pay.journal_id.outbound_payment_method_line_ids.mapped('payment_method_id')
|
||||
to_exclude = self._get_payment_method_codes_to_exclude()
|
||||
if to_exclude:
|
||||
pay.available_payment_method_ids = pay.available_payment_method_ids.filtered(lambda x: x.code not in to_exclude)
|
||||
@@ -417,6 +433,24 @@ class AccountPayment(models.Model):
|
||||
else:
|
||||
pay.partner_id = pay.partner_id
|
||||
|
||||
@api.depends('journal_id', 'payment_type')
|
||||
def _compute_outstanding_account_id(self):
|
||||
for pay in self:
|
||||
if pay.payment_type == 'inbound':
|
||||
payment_method_account_id = pay.journal_id.inbound_payment_method_line_ids\
|
||||
.filtered(lambda line: line.code == pay.payment_method_id.code).payment_account_id
|
||||
|
||||
pay.outstanding_account_id = (payment_method_account_id
|
||||
or pay.journal_id.company_id.account_journal_payment_debit_account_id)
|
||||
elif pay.payment_type == 'outbound':
|
||||
payment_method_account_id = pay.journal_id.outbound_payment_method_line_ids\
|
||||
.filtered(lambda line: line.code == pay.payment_method_id.code).payment_account_id
|
||||
|
||||
pay.outstanding_account_id = (payment_method_account_id
|
||||
or pay.journal_id.company_id.account_journal_payment_credit_account_id)
|
||||
else:
|
||||
pay.outstanding_account_id = False
|
||||
|
||||
@api.depends('journal_id', 'partner_id', 'partner_type', 'is_internal_transfer')
|
||||
def _compute_destination_account_id(self):
|
||||
self.destination_account_id = False
|
||||
@@ -542,7 +576,7 @@ class AccountPayment(models.Model):
|
||||
part.debit_move_id = counterpart_line.id
|
||||
OR
|
||||
part.credit_move_id = counterpart_line.id
|
||||
WHERE (account.id = journal.payment_debit_account_id OR account.id = journal.payment_credit_account_id)
|
||||
WHERE account.id = payment.outstanding_account_id
|
||||
AND payment.id IN %(payment_ids)s
|
||||
AND line.id != counterpart_line.id
|
||||
AND counterpart_line.statement_id IS NOT NULL
|
||||
|
||||
@@ -1,14 +1,120 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models, fields
|
||||
from odoo import api, fields, models
|
||||
from odoo.osv import expression
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_name = "account.payment.method"
|
||||
_description = "Payment Methods"
|
||||
_order = 'sequence'
|
||||
|
||||
name = fields.Char(required=True, translate=True)
|
||||
code = fields.Char(required=True) # For internal identification
|
||||
payment_type = fields.Selection([('inbound', 'Inbound'), ('outbound', 'Outbound')], required=True)
|
||||
sequence = fields.Integer(help='Used to order Methods in the form view', default=10)
|
||||
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)
|
||||
limit = None if information.get('mode') == 'multi' else 1
|
||||
|
||||
method_domain = method._get_payment_method_domain()
|
||||
|
||||
journals = self.env['account.journal'].search(method_domain, limit=limit)
|
||||
|
||||
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
|
||||
|
||||
def _get_payment_method_domain(self):
|
||||
"""
|
||||
:return: The domain specyfying which journal can accomodate this payment method.
|
||||
"""
|
||||
self.ensure_one()
|
||||
information = self._get_payment_method_information().get(self.code)
|
||||
|
||||
unique = information.get('mode') == 'unique'
|
||||
currency_id = information.get('currency_id')
|
||||
country_id = information.get('country_id')
|
||||
default_domain = [('type', 'in', ('bank', 'cash'))]
|
||||
domains = [information.get('domain', default_domain)]
|
||||
|
||||
if currency_id:
|
||||
domains += [expression.OR([
|
||||
[('currency_id', '=', False), ('company_id.currency_id', '=', currency_id)],
|
||||
[('currency_id', '=', currency_id)]],
|
||||
)]
|
||||
|
||||
if country_id:
|
||||
domains += [[('company_id.account_fiscal_country_id', '=', country_id)]]
|
||||
|
||||
if unique:
|
||||
company_ids = self.env['payment.acquirer'].search([('provider', '=', self.code)]).mapped('company_id')
|
||||
if company_ids:
|
||||
domains += [[('company_id', 'in', company_ids.ids)]]
|
||||
|
||||
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 acquirers 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'))]},
|
||||
}
|
||||
|
||||
|
||||
class AccountPaymentMethodLine(models.Model):
|
||||
_name = "account.payment.method.line"
|
||||
_description = "Payment Methods"
|
||||
|
||||
# == 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=lambda self: "[('deprecated', '=', False), "
|
||||
"('company_id', '=', company_id), "
|
||||
"('user_type_id.type', 'not in', ('receivable', 'payable')), "
|
||||
"('user_type_id', '=', %s)]" % self.env.ref('account.data_account_type_current_assets').id
|
||||
)
|
||||
journal_id = fields.Many2one(comodel_name='account.journal', required=True, 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')
|
||||
|
||||
@api.depends('payment_method_id.name')
|
||||
def _compute_name(self):
|
||||
for method in self:
|
||||
method.name = method.payment_method_id.name
|
||||
|
||||
@@ -120,6 +120,8 @@ 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_line_readonly,account.payment.method.line,model_account_payment_method_line,base.group_user,1,0,0,0
|
||||
access_account_payment_method_line,account.payment.method.line,model_account_payment_method_line,account.group_account_invoice,1,1,1,1
|
||||
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
|
||||
|
||||
|
@@ -170,6 +170,12 @@ class AccountTestInvoicingCommon(TransactionCase):
|
||||
'rounding_method': 'DOWN',
|
||||
})
|
||||
|
||||
# ==== Payment methods ====
|
||||
bank_journal = cls.company_data['default_journal_bank']
|
||||
|
||||
cls.inbound_payment_method = bank_journal.inbound_payment_method_line_ids[0].payment_method_id
|
||||
cls.outbound_payment_method = bank_journal.outbound_payment_method_line_ids[0].payment_method_id
|
||||
|
||||
@classmethod
|
||||
def setup_company_data(cls, company_name, chart_template=None, **kwargs):
|
||||
''' Create a new company having the name passed as parameter.
|
||||
@@ -541,12 +547,6 @@ class TestAccountReconciliationCommon(AccountTestInvoicingCommon):
|
||||
cls.diff_income_account = cls.company.income_currency_exchange_account_id
|
||||
cls.diff_expense_account = cls.company.expense_currency_exchange_account_id
|
||||
|
||||
cls.inbound_payment_method = cls.env['account.payment.method'].create({
|
||||
'name': 'inbound',
|
||||
'code': 'IN',
|
||||
'payment_type': 'inbound',
|
||||
})
|
||||
|
||||
cls.expense_account = cls.company_data['default_account_expense']
|
||||
# cash basis intermediary account
|
||||
cls.tax_waiting_account = cls.env['account.account'].create({
|
||||
|
||||
@@ -132,6 +132,6 @@ class TestAccountAccount(AccountTestInvoicingCommon):
|
||||
is configured with this account as the payment credit or debit account.
|
||||
Since such an account should be reconcilable by nature, a ValidationError is raised.'''
|
||||
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||||
self.company_data['default_journal_bank'].payment_debit_account_id.reconcile = False
|
||||
self.company_data['default_journal_bank'].company_id.account_journal_payment_debit_account_id.reconcile = False
|
||||
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||||
self.company_data['default_journal_bank'].payment_credit_account_id.reconcile = False
|
||||
self.company_data['default_journal_bank'].company_id.account_journal_payment_credit_account_id.reconcile = False
|
||||
|
||||
@@ -1146,7 +1146,7 @@ class TestAccountBankStatementLine(TestAccountBankStatementCommon):
|
||||
|
||||
receivable_acc_1 = self.company_data['default_account_receivable']
|
||||
receivable_acc_2 = self.copy_account(self.company_data['default_account_receivable'])
|
||||
payment_account = self.bank_journal_1.payment_debit_account_id
|
||||
payment_account = self.bank_journal_1.company_id.account_journal_payment_debit_account_id
|
||||
random_acc_1 = self.company_data['default_account_revenue']
|
||||
random_acc_2 = self.copy_account(self.company_data['default_account_revenue'])
|
||||
test_move = self.env['account.move'].create({
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.addons.account.models.account_payment_method import AccountPaymentMethod
|
||||
from odoo.tests import tagged
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
@@ -82,3 +85,49 @@ class TestAccountJournal(AccountTestInvoicingCommon):
|
||||
# Assigning both should be allowed
|
||||
self.company_data['default_journal_misc'].account_control_ids = \
|
||||
self.company_data['default_account_revenue'] + self.company_data['default_account_expense']
|
||||
|
||||
def test_account_journal_add_new_payment_method_unique(self):
|
||||
"""
|
||||
Test the automatic creation of payment method lines with the mode set to unique
|
||||
"""
|
||||
Method_get_payment_method_information = AccountPaymentMethod._get_payment_method_information
|
||||
|
||||
def _get_payment_method_information(self):
|
||||
res = Method_get_payment_method_information(self)
|
||||
res['unique'] = {'mode': 'unique', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
|
||||
with patch.object(AccountPaymentMethod, '_get_payment_method_information', _get_payment_method_information):
|
||||
self.env['account.payment.method'].create({
|
||||
'name': 'Unique method',
|
||||
'code': 'unique',
|
||||
'payment_type': 'inbound'
|
||||
})
|
||||
|
||||
journals = self.env['account.journal'].search([('inbound_payment_method_line_ids.code', '=', 'unique')])
|
||||
|
||||
# Only one of the bank journals has been set
|
||||
self.assertEqual(len(journals), 1)
|
||||
|
||||
def test_account_journal_add_new_payment_method_multi(self):
|
||||
"""
|
||||
Test the automatic creation of payment method lines with the mode set to multi
|
||||
"""
|
||||
Method_get_payment_method_information = AccountPaymentMethod._get_payment_method_information
|
||||
|
||||
def _get_payment_method_information(self):
|
||||
res = Method_get_payment_method_information(self)
|
||||
res['multi'] = {'mode': 'multi', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
|
||||
with patch.object(AccountPaymentMethod, '_get_payment_method_information', _get_payment_method_information):
|
||||
self.env['account.payment.method'].create({
|
||||
'name': 'Multi method',
|
||||
'code': 'multi',
|
||||
'payment_type': 'inbound'
|
||||
})
|
||||
|
||||
journals = self.env['account.journal'].search([('inbound_payment_method_line_ids.code', '=', 'multi')])
|
||||
|
||||
# The two bank journals have been set
|
||||
self.assertEqual(len(journals), 2)
|
||||
|
||||
@@ -11,8 +11,10 @@ class TestAccountPayment(AccountTestInvoicingCommon):
|
||||
def setUpClass(cls, chart_template_ref=None):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
|
||||
cls.payment_debit_account_id = cls.copy_account(cls.company_data['default_journal_bank'].payment_debit_account_id)
|
||||
cls.payment_credit_account_id = cls.copy_account(cls.company_data['default_journal_bank'].payment_credit_account_id)
|
||||
company_id = cls.company_data['default_journal_bank'].company_id
|
||||
|
||||
cls.payment_debit_account_id = cls.copy_account(company_id.account_journal_payment_debit_account_id)
|
||||
cls.payment_credit_account_id = cls.copy_account(company_id.account_journal_payment_credit_account_id)
|
||||
|
||||
cls.partner_bank_account = cls.env['res.partner.bank'].create({
|
||||
'acc_number': 'BE32707171912447',
|
||||
@@ -20,11 +22,9 @@ class TestAccountPayment(AccountTestInvoicingCommon):
|
||||
'acc_type': 'bank',
|
||||
})
|
||||
|
||||
cls.company_data['default_journal_bank'].write({
|
||||
'payment_debit_account_id': cls.payment_debit_account_id.id,
|
||||
'payment_credit_account_id': cls.payment_credit_account_id.id,
|
||||
'inbound_payment_method_ids': [(6, 0, cls.env.ref('account.account_payment_method_manual_in').ids)],
|
||||
'outbound_payment_method_ids': [(6, 0, cls.env.ref('account.account_payment_method_manual_out').ids)],
|
||||
company_id.write({
|
||||
'account_journal_payment_debit_account_id': cls.payment_debit_account_id.id,
|
||||
'account_journal_payment_credit_account_id': cls.payment_credit_account_id.id
|
||||
})
|
||||
|
||||
cls.partner_a.write({
|
||||
@@ -640,7 +640,7 @@ class TestAccountPayment(AccountTestInvoicingCommon):
|
||||
'credit': 50.0,
|
||||
'amount_currency': -50.0,
|
||||
'currency_id': self.company_data['currency'].id,
|
||||
'account_id': self.company_data['default_journal_cash'].payment_credit_account_id.id,
|
||||
'account_id': self.company_data['default_journal_cash'].company_id.account_journal_payment_credit_account_id.id,
|
||||
}
|
||||
expected_counterpart_line = {
|
||||
'debit': 50.0,
|
||||
|
||||
@@ -18,36 +18,8 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
|
||||
'currency_subunit_label': "Broken Umbrella",
|
||||
}, rate2017=0.01)
|
||||
|
||||
cls.payment_debit_account_id = cls.company_data['default_journal_bank'].payment_debit_account_id.copy()
|
||||
cls.payment_credit_account_id = cls.company_data['default_journal_bank'].payment_credit_account_id.copy()
|
||||
|
||||
cls.custom_payment_method_in = cls.env['account.payment.method'].create({
|
||||
'name': 'custom_payment_method_in',
|
||||
'code': 'CUSTOMIN',
|
||||
'payment_type': 'inbound',
|
||||
})
|
||||
cls.manual_payment_method_in = cls.env.ref('account.account_payment_method_manual_in')
|
||||
|
||||
cls.custom_payment_method_out = cls.env['account.payment.method'].create({
|
||||
'name': 'custom_payment_method_out',
|
||||
'code': 'CUSTOMOUT',
|
||||
'payment_type': 'outbound',
|
||||
})
|
||||
cls.manual_payment_method_out = cls.env.ref('account.account_payment_method_manual_out')
|
||||
|
||||
cls.company_data['default_journal_bank'].write({
|
||||
'payment_debit_account_id': cls.payment_debit_account_id.id,
|
||||
'payment_credit_account_id': cls.payment_credit_account_id.id,
|
||||
'inbound_payment_method_ids': [(6, 0, (
|
||||
cls.manual_payment_method_in.id,
|
||||
cls.custom_payment_method_in.id,
|
||||
))],
|
||||
'outbound_payment_method_ids': [(6, 0, (
|
||||
cls.env.ref('account.account_payment_method_manual_out').id,
|
||||
cls.custom_payment_method_out.id,
|
||||
cls.manual_payment_method_out.id,
|
||||
))],
|
||||
})
|
||||
cls.payment_debit_account_id = cls.company_data['default_journal_bank'].company_id.account_journal_payment_debit_account_id.copy()
|
||||
cls.payment_credit_account_id = cls.company_data['default_journal_bank'].company_id.account_journal_payment_credit_account_id.copy()
|
||||
|
||||
# Customer invoices sharing the same batch.
|
||||
cls.out_invoice_1 = cls.env['account.move'].create({
|
||||
@@ -115,12 +87,12 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
|
||||
'group_payment': True,
|
||||
'payment_difference_handling': 'open',
|
||||
'currency_id': self.currency_data['currency'].id,
|
||||
'payment_method_id': self.custom_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
})._create_payments()
|
||||
|
||||
self.assertRecordValues(payments, [{
|
||||
'ref': 'INV/2017/01/0001 INV/2017/01/0002',
|
||||
'payment_method_id': self.custom_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
}])
|
||||
self.assertRecordValues(payments.line_ids.sorted('balance'), [
|
||||
# Receivable line:
|
||||
@@ -149,12 +121,12 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
|
||||
'group_payment': True,
|
||||
'payment_difference_handling': 'open',
|
||||
'currency_id': self.currency_data['currency'].id,
|
||||
'payment_method_id': self.custom_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
})._create_payments()
|
||||
|
||||
self.assertRecordValues(payments, [{
|
||||
'ref': 'INV/2017/01/0001 INV/2017/01/0002',
|
||||
'payment_method_id': self.custom_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
}])
|
||||
self.assertRecordValues(payments.line_ids.sorted('balance'), [
|
||||
# Receivable line:
|
||||
@@ -184,12 +156,12 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
|
||||
'payment_difference_handling': 'reconcile',
|
||||
'writeoff_account_id': self.company_data['default_account_revenue'].id,
|
||||
'writeoff_label': 'writeoff',
|
||||
'payment_method_id': self.custom_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
})._create_payments()
|
||||
|
||||
self.assertRecordValues(payments, [{
|
||||
'ref': 'INV/2017/01/0001 INV/2017/01/0002',
|
||||
'payment_method_id': self.custom_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
}])
|
||||
self.assertRecordValues(payments.line_ids.sorted('balance'), [
|
||||
# Receivable line:
|
||||
@@ -227,12 +199,12 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
|
||||
'payment_difference_handling': 'reconcile',
|
||||
'writeoff_account_id': self.company_data['default_account_revenue'].id,
|
||||
'writeoff_label': 'writeoff',
|
||||
'payment_method_id': self.custom_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
})._create_payments()
|
||||
|
||||
self.assertRecordValues(payments, [{
|
||||
'ref': 'INV/2017/01/0001 INV/2017/01/0002',
|
||||
'payment_method_id': self.custom_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
}])
|
||||
self.assertRecordValues(payments.line_ids.sorted('balance'), [
|
||||
# Receivable line:
|
||||
@@ -270,12 +242,12 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
|
||||
'payment_difference_handling': 'reconcile',
|
||||
'writeoff_account_id': self.company_data['default_account_revenue'].id,
|
||||
'writeoff_label': 'writeoff',
|
||||
'payment_method_id': self.custom_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
})._create_payments()
|
||||
|
||||
self.assertRecordValues(payments, [{
|
||||
'ref': 'BILL/2017/01/0001 BILL/2017/01/0002',
|
||||
'payment_method_id': self.custom_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
}])
|
||||
self.assertRecordValues(payments.line_ids.sorted('balance'), [
|
||||
# Writeoff line:
|
||||
@@ -313,12 +285,12 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
|
||||
'payment_difference_handling': 'reconcile',
|
||||
'writeoff_account_id': self.company_data['default_account_revenue'].id,
|
||||
'writeoff_label': 'writeoff',
|
||||
'payment_method_id': self.custom_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
})._create_payments()
|
||||
|
||||
self.assertRecordValues(payments, [{
|
||||
'ref': 'BILL/2017/01/0001 BILL/2017/01/0002',
|
||||
'payment_method_id': self.custom_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
}])
|
||||
self.assertRecordValues(payments.line_ids.sorted('balance'), [
|
||||
# Liquidity line:
|
||||
@@ -357,11 +329,11 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
|
||||
self.assertRecordValues(payments, [
|
||||
{
|
||||
'ref': 'INV/2017/01/0001',
|
||||
'payment_method_id': self.manual_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
},
|
||||
{
|
||||
'ref': 'INV/2017/01/0002',
|
||||
'payment_method_id': self.manual_payment_method_in.id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
},
|
||||
])
|
||||
self.assertRecordValues(payments[0].line_ids.sorted('balance') + payments[1].line_ids.sorted('balance'), [
|
||||
@@ -413,11 +385,11 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
|
||||
self.assertRecordValues(payments, [
|
||||
{
|
||||
'ref': 'BILL/2017/01/0001 BILL/2017/01/0002',
|
||||
'payment_method_id': self.manual_payment_method_out.id,
|
||||
'payment_method_id': self.outbound_payment_method.id,
|
||||
},
|
||||
{
|
||||
'ref': 'BILL/2017/01/0003',
|
||||
'payment_method_id': self.manual_payment_method_out.id,
|
||||
'payment_method_id': self.outbound_payment_method.id,
|
||||
},
|
||||
])
|
||||
self.assertRecordValues(payments[0].line_ids.sorted('balance') + payments[1].line_ids.sorted('balance'), [
|
||||
@@ -469,15 +441,15 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
|
||||
self.assertRecordValues(payments, [
|
||||
{
|
||||
'ref': 'BILL/2017/01/0001',
|
||||
'payment_method_id': self.manual_payment_method_out.id,
|
||||
'payment_method_id': self.outbound_payment_method.id,
|
||||
},
|
||||
{
|
||||
'ref': 'BILL/2017/01/0002',
|
||||
'payment_method_id': self.manual_payment_method_out.id,
|
||||
'payment_method_id': self.outbound_payment_method.id,
|
||||
},
|
||||
{
|
||||
'ref': 'BILL/2017/01/0003',
|
||||
'payment_method_id': self.manual_payment_method_out.id,
|
||||
'payment_method_id': self.outbound_payment_method.id,
|
||||
},
|
||||
])
|
||||
self.assertRecordValues(payments[0].line_ids.sorted('balance') + payments[1].line_ids.sorted('balance') + payments[2].line_ids.sorted('balance'), [
|
||||
|
||||
@@ -271,7 +271,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
'company_id': self.company.id,
|
||||
'date': time.strftime('%Y') + '-' + '07' + '-01',
|
||||
'partner_id': self.partner_agrolait_id,
|
||||
'payment_method_id': self.env.ref('account.account_payment_method_manual_out').id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
'partner_type': 'supplier'})
|
||||
|
||||
# One that will complete the payment of a, the rest goes to b
|
||||
@@ -282,7 +282,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
'company_id': self.company.id,
|
||||
'date': time.strftime('%Y') + '-' + '08' + '-01',
|
||||
'partner_id': self.partner_agrolait_id,
|
||||
'payment_method_id': self.env.ref('account.account_payment_method_manual_out').id,
|
||||
'payment_method_id': self.outbound_payment_method.id,
|
||||
'partner_type': 'supplier'})
|
||||
|
||||
# The last one will complete the payment of b
|
||||
@@ -293,7 +293,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
'company_id': self.company.id,
|
||||
'date': time.strftime('%Y') + '-' + '09' + '-01',
|
||||
'partner_id': self.partner_agrolait_id,
|
||||
'payment_method_id': self.env.ref('account.account_payment_method_manual_out').id,
|
||||
'payment_method_id': self.outbound_payment_method.id,
|
||||
'partner_type': 'supplier'})
|
||||
|
||||
payment_a.action_post()
|
||||
@@ -339,7 +339,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
inv2 = self.create_invoice(invoice_amount=20, currency_id=self.currency_usd_id)
|
||||
payment = self.env['account.payment'].create({
|
||||
'payment_type': 'inbound',
|
||||
'payment_method_id': self.env.ref('account.account_payment_method_manual_in').id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
'partner_type': 'customer',
|
||||
'partner_id': self.partner_agrolait_id,
|
||||
'amount': 100,
|
||||
@@ -389,7 +389,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
inv = self.create_invoice(invoice_amount=111, currency_id=self.currency_usd_id)
|
||||
payment = self.env['account.payment'].create({
|
||||
'payment_type': 'inbound',
|
||||
'payment_method_id': self.env.ref('account.account_payment_method_manual_in').id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
'partner_type': 'customer',
|
||||
'partner_id': self.partner_agrolait_id,
|
||||
'amount': 111,
|
||||
@@ -426,7 +426,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
|
||||
self.assertEqual(len(payment.line_ids), 2)
|
||||
|
||||
bank_line = payment.line_ids.filtered(lambda l: l.account_id.id == self.bank_journal_usd.payment_debit_account_id.id)
|
||||
bank_line = payment.line_ids.filtered(lambda l: l.account_id.id == self.bank_journal_usd.company_id.account_journal_payment_debit_account_id.id)
|
||||
customer_line = payment.line_ids - bank_line
|
||||
|
||||
self.assertEqual(len(bank_line), 1)
|
||||
@@ -443,7 +443,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
self.assertEqual(len(reversed_move.line_ids), 2)
|
||||
|
||||
# Testing the reconciliation matching between the move lines and their reversed counterparts
|
||||
reversed_bank_line = reversed_move.line_ids.filtered(lambda l: l.account_id.id == self.bank_journal_usd.payment_debit_account_id.id)
|
||||
reversed_bank_line = reversed_move.line_ids.filtered(lambda l: l.account_id.id == self.bank_journal_usd.company_id.account_journal_payment_debit_account_id.id)
|
||||
reversed_customer_line = reversed_move.line_ids - reversed_bank_line
|
||||
|
||||
self.assertEqual(len(reversed_bank_line), 1)
|
||||
@@ -495,7 +495,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
inv = self.create_invoice(invoice_amount=111, currency_id=self.currency_usd_id)
|
||||
payment = self.env['account.payment'].create({
|
||||
'payment_type': 'inbound',
|
||||
'payment_method_id': self.env.ref('account.account_payment_method_manual_in').id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
'partner_type': 'customer',
|
||||
'partner_id': self.partner_agrolait_id,
|
||||
'amount': 111,
|
||||
@@ -617,7 +617,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon):
|
||||
payment = self.env['account.payment'].create({
|
||||
'date': time.strftime('%Y') + '-07-15',
|
||||
'payment_type': 'inbound',
|
||||
'payment_method_id': self.env.ref('account.account_payment_method_manual_in').id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
'partner_type': 'customer',
|
||||
'partner_id': self.partner_agrolait_id,
|
||||
'amount': 25,
|
||||
|
||||
@@ -597,7 +597,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
|
||||
'debit': 10,
|
||||
}),
|
||||
(0, 0, {
|
||||
'account_id': self.bank_journal.payment_credit_account_id.id,
|
||||
'account_id': self.bank_journal.company_id.account_journal_payment_credit_account_id.id,
|
||||
'partner_id': partner.id,
|
||||
'name': 'I\'m gonna cut you into little pieces',
|
||||
'credit': 10,
|
||||
@@ -605,7 +605,7 @@ class TestReconciliationMatchingRules(AccountTestInvoicingCommon):
|
||||
],
|
||||
})
|
||||
|
||||
payment_bnk_line = move.line_ids.filtered(lambda l: l.account_id == self.bank_journal.payment_credit_account_id)
|
||||
payment_bnk_line = move.line_ids.filtered(lambda l: l.account_id == self.bank_journal.company_id.account_journal_payment_credit_account_id)
|
||||
|
||||
move.action_post()
|
||||
move_reversed = move._reverse_moves()
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
<field name="currency_id" groups="base.group_multi_currency" optional="hide"/>
|
||||
<field name="code" optional="show"/>
|
||||
<field name="default_account_id" optional="show"/>
|
||||
<field name="payment_debit_account_id" optional="hide"/>
|
||||
<field name="payment_credit_account_id" optional="hide"/>
|
||||
<field name="active" optional="hide"/>
|
||||
<field name="company_id" groups="base.group_multi_company" optional="hide"/>
|
||||
</tree>
|
||||
@@ -89,23 +87,42 @@
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page name="journal_entries" string="Payments Configuration" attrs="{'invisible': [('type', 'in', ['sale', 'purchase', 'general'])]}">
|
||||
<group>
|
||||
<group string="Incoming Payments">
|
||||
<field name="payment_debit_account_id"
|
||||
attrs="{'required': [('id', '!=', False), ('type', 'in', ('bank', 'cash'))], 'invisible': [('type', 'not in', ('bank', 'cash'))]}"
|
||||
<page id="inbound_payment_settings" string="Incoming Payments" attrs="{'invisible': [('type', 'not in', ['cash', 'bank'])]}">
|
||||
<field name="available_payment_method_ids" invisible="1"/>
|
||||
<field name="inbound_payment_method_line_ids" nolabel="1" context="{'default_payment_type': 'inbound'}">
|
||||
<tree string="Payment Methods" editable="bottom">
|
||||
<field name="available_payment_method_ids" invisible="1"/>
|
||||
<field name="payment_type" invisible="1"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="payment_method_id" options="{'no_create': True, 'no_open': True}"/>
|
||||
<field name="payment_account_id"
|
||||
placeholder="Let empty to use the default outstanding account"
|
||||
string="Outstanding Receipts accounts"
|
||||
optional="show"
|
||||
groups="account.group_account_readonly"/>
|
||||
<field name="inbound_payment_method_ids" string="Methods" widget="many2many_checkboxes" attrs="{'invisible': [('type', 'not in', ['bank', 'cash'])]}"/>
|
||||
</group>
|
||||
<group string="Outgoing Payments">
|
||||
<field name="id" invisible="1"/>
|
||||
<field name="payment_credit_account_id"
|
||||
attrs="{'required': [('id', '!=', False), ('type', 'in', ('bank', 'cash'))], 'invisible': [('type', 'not in', ('bank', 'cash'))]}"
|
||||
groups="account.group_account_readonly"/>
|
||||
<field name="outbound_payment_method_ids" string="Methods" widget="many2many_checkboxes" attrs="{'invisible': [('type', 'not in', ['bank', 'cash'])]}"/>
|
||||
</group>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page id="outbound_payment_settings" string="Outgoing Payments" attrs="{'invisible': [('type', 'not in', ['cash', 'bank'])]}">
|
||||
<field name="outbound_payment_method_line_ids" nolabel="1" context="{'default_payment_type': 'outbound'}">
|
||||
<tree string="Payment Methods" editable="bottom" nolabel="1">
|
||||
<field name="available_payment_method_ids" invisible="1"/>
|
||||
<field name="payment_type" invisible="1"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="payment_method_id" options="{'no_create': True, 'no_open': True}"/>
|
||||
<field name="payment_account_id"
|
||||
placeholder="Let empty to use the default outstanding account"
|
||||
string="Outstanding Payments accounts"
|
||||
optional="show"
|
||||
groups="account.group_account_readonly"/>
|
||||
</tree>
|
||||
</field>
|
||||
<field name="selected_payment_method_codes" invisible="1"/>
|
||||
<group name="outgoing_payment" />
|
||||
</group>
|
||||
</page>
|
||||
<page name="advanced_settings" string="Advanced Settings">
|
||||
<group>
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
</div>
|
||||
<div class="alert alert-warning text-center" role="alert" attrs="{
|
||||
'invisible': ['|', '|', ('is_internal_transfer','=',False), ('require_partner_bank_account', '=', False), ('partner_bank_id', '!=', False)]}">
|
||||
The selected payment method requires a bank account but none is set on
|
||||
The selected payment method requires a bank account but none is set on
|
||||
<button class="oe_link alert-link" type="object" name="action_open_destination_journal" style="padding: 0; vertical-align: baseline;">the destination journal</button>.
|
||||
</div>
|
||||
<sheet>
|
||||
@@ -237,7 +237,7 @@
|
||||
<group>
|
||||
<group name="group1">
|
||||
<field name="is_internal_transfer" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
|
||||
<field name="payment_type" widget="radio" options="{'horizontal': True}"
|
||||
<field name="payment_type" widget="radio" options="{'horizontal': True}"
|
||||
attrs="{'readonly': [('state', '!=', 'draft')]}"/>
|
||||
<field name="partner_id" context="{'default_is_company': True}" string="Customer"
|
||||
attrs="{'readonly':[('state', '!=', 'draft')],
|
||||
@@ -265,7 +265,7 @@
|
||||
<field name="journal_id"
|
||||
domain="[('type', 'in', ('bank', 'cash'))]"
|
||||
attrs="{'readonly': [('state', '!=', 'draft')]}"/>
|
||||
<field name="payment_method_id" required="1" options="{'no_open': True, 'no_create': True}"
|
||||
<field name="payment_method_id" required="1" options="{'no_create': True, 'no_open': True}"
|
||||
attrs="{'readonly': [('state', '!=', 'draft')], 'invisible': [('hide_payment_method', '=', True)]}"/>
|
||||
|
||||
<field name="partner_bank_id" context="{'default_partner_id': partner_id}" string="Customer Bank Account"
|
||||
|
||||
@@ -73,13 +73,13 @@ class AccountPaymentRegister(models.TransientModel):
|
||||
readonly=False, store=True,
|
||||
compute='_compute_payment_method_id',
|
||||
domain="[('id', 'in', available_payment_method_ids)]",
|
||||
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"\
|
||||
"Check: Pay bill by check and print it from Odoo.\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.To enable batch deposit, module account_batch_payment must be installed.\n"\
|
||||
"SEPA Credit Transfer: Pay bill from a SEPA Credit Transfer file you submit to your bank. To enable sepa credit transfer, module account_sepa must be installed ")
|
||||
available_payment_method_ids = fields.Many2many('account.payment.method',
|
||||
compute='_compute_payment_method_fields')
|
||||
help="Manual : Pay or Get paid by any method outside of Odoo."
|
||||
"Payment Acquirers : Each payment acquirer has its own Payment Method. Request a transaction on/to a card thanks to a payment token saved by the partner when buying or subscribing online."
|
||||
"Check: Pay bills by check and print it from Odoo."
|
||||
"Batch Deposit: Collect several customer checks at once generating and submitting a batch deposit to your bank. Module account_batch_payment is necessary."
|
||||
"SEPA Credit Transfer : Pay in the SEPA zone by submitting a SEPA Credit Transfer file to your bank. Module account_sepa is necessary"
|
||||
"SEPA Direct Debit : Get paid in the SEPA zone thanks to a mandate your partner will have granted to you. Module account_sepa is necessary")
|
||||
available_payment_method_ids = fields.Many2many('account.payment.method', compute='_compute_payment_method_fields')
|
||||
hide_payment_method = fields.Boolean(
|
||||
compute='_compute_payment_method_fields',
|
||||
help="Technical field used to hide the payment method if the selected journal has only one available which is 'manual'")
|
||||
@@ -296,9 +296,9 @@ class AccountPaymentRegister(models.TransientModel):
|
||||
payment_type = batches[0]['key_values']['payment_type']
|
||||
|
||||
if payment_type == 'inbound':
|
||||
available_payment_methods = wizard.journal_id.inbound_payment_method_ids
|
||||
available_payment_methods = wizard.journal_id.inbound_payment_method_line_ids.mapped('payment_method_id')
|
||||
else:
|
||||
available_payment_methods = wizard.journal_id.outbound_payment_method_ids
|
||||
available_payment_methods = wizard.journal_id.outbound_payment_method_line_ids.mapped('payment_method_id')
|
||||
|
||||
# Select the first available one by default.
|
||||
if available_payment_methods:
|
||||
@@ -307,26 +307,26 @@ class AccountPaymentRegister(models.TransientModel):
|
||||
wizard.payment_method_id = False
|
||||
|
||||
@api.depends('payment_type',
|
||||
'journal_id.inbound_payment_method_ids',
|
||||
'journal_id.outbound_payment_method_ids')
|
||||
'journal_id.inbound_payment_method_line_ids',
|
||||
'journal_id.outbound_payment_method_line_ids')
|
||||
def _compute_payment_method_fields(self):
|
||||
for wizard in self:
|
||||
if wizard.payment_type == 'inbound':
|
||||
wizard.available_payment_method_ids = wizard.journal_id.inbound_payment_method_ids
|
||||
wizard.available_payment_method_ids = wizard.journal_id.inbound_payment_method_line_ids.mapped('payment_method_id')
|
||||
else:
|
||||
wizard.available_payment_method_ids = wizard.journal_id.outbound_payment_method_ids
|
||||
wizard.available_payment_method_ids = wizard.journal_id.outbound_payment_method_line_ids.mapped('payment_method_id')
|
||||
|
||||
wizard.hide_payment_method = len(wizard.available_payment_method_ids) == 1 and wizard.available_payment_method_ids.code == 'manual'
|
||||
|
||||
@api.depends('payment_type',
|
||||
'journal_id.inbound_payment_method_ids',
|
||||
'journal_id.outbound_payment_method_ids')
|
||||
'journal_id.inbound_payment_method_line_ids',
|
||||
'journal_id.outbound_payment_method_line_ids')
|
||||
def _compute_payment_method_id(self):
|
||||
for wizard in self:
|
||||
if wizard.payment_type == 'inbound':
|
||||
available_payment_methods = wizard.journal_id.inbound_payment_method_ids
|
||||
available_payment_methods = wizard.journal_id.inbound_payment_method_line_ids.mapped('payment_method_id')
|
||||
else:
|
||||
available_payment_methods = wizard.journal_id.outbound_payment_method_ids
|
||||
available_payment_methods = wizard.journal_id.outbound_payment_method_line_ids.mapped('payment_method_id')
|
||||
|
||||
# Select the first available one by default.
|
||||
if available_payment_methods:
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
<group>
|
||||
<group name="group1">
|
||||
<field name="journal_id" options="{'no_open': True, 'no_create': True}" required="1"/>
|
||||
<field name="payment_method_id" widget="radio"
|
||||
required="1"
|
||||
<field name="payment_method_id"
|
||||
required="1" options="{'no_create': True, 'no_open': True}"
|
||||
attrs="{'invisible': [('hide_payment_method', '=', True)]}"/>
|
||||
<field name="partner_bank_id"
|
||||
attrs="{'invisible': ['|', ('show_partner_bank_account', '=', False), '|', ('can_edit_wizard', '=', False), '&', ('can_group_payments', '=', True), ('group_payment', '=', False)],
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, SUPERUSER_ID
|
||||
from . import models
|
||||
from . import wizard
|
||||
|
||||
|
||||
def create_check_sequence_on_bank_journals(cr, registry):
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
env['account.journal'].search([('type', '=', 'bank')])._create_check_sequence()
|
||||
|
||||
@@ -24,4 +24,5 @@ The check settings are located in the accounting journals configuration page.
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
'post_init_hook': 'create_check_sequence_on_bank_journals',
|
||||
}
|
||||
|
||||
@@ -6,12 +6,8 @@
|
||||
<field name="name">Checks</field>
|
||||
<field name="code">check_printing</field>
|
||||
<field name="payment_type">outbound</field>
|
||||
<field name="sequence">20</field>
|
||||
</record>
|
||||
|
||||
<function model="account.journal" name="_enable_check_printing_on_bank_journals"/>
|
||||
|
||||
|
||||
<record model="ir.actions.server" id="action_account_print_checks">
|
||||
<field name="name">Print Checks</field>
|
||||
<field name="model_id" ref="account.model_account_payment"/>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from . import account_journal
|
||||
from . import account_move
|
||||
from . import account_payment
|
||||
from . import account_payment_method
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
from . import res_partner
|
||||
|
||||
@@ -5,9 +5,16 @@ import re
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class AccountJournal(models.Model):
|
||||
_inherit = "account.journal"
|
||||
|
||||
def _default_outbound_payment_methods(self):
|
||||
res = super()._default_outbound_payment_methods()
|
||||
if self.type == 'bank':
|
||||
res |= self.env.ref('account_check_printing.account_payment_method_check')
|
||||
return res
|
||||
|
||||
check_manual_sequencing = fields.Boolean(
|
||||
string='Manual Numbering',
|
||||
default=False,
|
||||
@@ -26,10 +33,6 @@ class AccountJournal(models.Model):
|
||||
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):
|
||||
@@ -54,22 +57,6 @@ class AccountJournal(models.Model):
|
||||
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)
|
||||
@@ -94,20 +81,6 @@ class AccountJournal(models.Model):
|
||||
'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),
|
||||
|
||||
@@ -93,7 +93,8 @@ class AccountPayment(models.Model):
|
||||
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:
|
||||
if (record.payment_type == 'outbound'
|
||||
and preferred in record.journal_id.outbound_payment_method_line_ids.mapped('payment_method_id')):
|
||||
record.payment_method_id = preferred
|
||||
|
||||
def action_post(self):
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_inherit = 'account.payment.method'
|
||||
|
||||
@api.model
|
||||
def _get_payment_method_information(self):
|
||||
res = super()._get_payment_method_information()
|
||||
res['check_printing'] = {'mode': 'multi', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
@@ -15,13 +15,6 @@ class TestPrintCheck(AccountTestInvoicingCommon):
|
||||
|
||||
cls.payment_method_check = cls.env.ref("account_check_printing.account_payment_method_check")
|
||||
|
||||
cls.company_data['default_journal_bank'].write({
|
||||
'outbound_payment_method_ids': [(6, 0, (
|
||||
cls.env.ref('account.account_payment_method_manual_out').id,
|
||||
cls.payment_method_check.id,
|
||||
))],
|
||||
})
|
||||
|
||||
def test_in_invoice_check_manual_sequencing(self):
|
||||
''' Test the check generation for vendor bills. '''
|
||||
nb_invoices_to_test = INV_LINES_PER_STUB + 1
|
||||
|
||||
@@ -26,10 +26,9 @@
|
||||
<field name="model">account.journal</field>
|
||||
<field name="inherit_id" ref="account.view_account_journal_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='journal_entries']//group[@name='outgoing_payment']" position="after">
|
||||
<xpath expr="//page[@id='outbound_payment_settings']//group[@name='outgoing_payment']" position="inside">
|
||||
<group string="Check Printing"
|
||||
attrs="{'invisible': ['|', ('type', '!=', 'bank'), ('check_printing_payment_method_selected', '=', False)]}">
|
||||
<field name="check_printing_payment_method_selected" invisible="1"/>
|
||||
attrs="{'invisible': ['|', '!', ('selected_payment_method_codes', 'ilike', ',check_printing,'), ('type', '!=', 'bank')]}">
|
||||
<field name="check_sequence_id" invisible="1"/>
|
||||
<field name="check_manual_sequencing"
|
||||
/>
|
||||
|
||||
@@ -16,7 +16,7 @@ class TestExpenses(TestExpenseCommon):
|
||||
payment_register = self.env['account.payment.register'].with_context(**ctx).create({
|
||||
'amount': amount,
|
||||
'journal_id': self.company_data['default_journal_bank'].id,
|
||||
'payment_method_id': self.env.ref('account.account_payment_method_manual_in').id,
|
||||
'payment_method_id': self.inbound_payment_method.id,
|
||||
})
|
||||
return payment_register._create_payments()
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
<field name="payment_method_id" model="account.journal"
|
||||
eval="obj().search([
|
||||
('type', '=', 'cash'),
|
||||
('company_id', '=', ref('l10n_in.demo_company_in'))], limit=1).inbound_payment_method_ids[0].id"/>
|
||||
('company_id', '=', ref('l10n_in.demo_company_in'))], limit=1).inbound_payment_method_line_ids[0].id"/>
|
||||
</record>
|
||||
|
||||
<function model="account.payment" name="action_post">
|
||||
<value eval="[ref('demo_payment_at')]"/>
|
||||
</function>
|
||||
</odoo>
|
||||
</odoo>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
'description': """Payment Acquirer Base Module""",
|
||||
'depends': ['account'],
|
||||
'data': [
|
||||
'data/account_data.xml',
|
||||
'data/payment_icon_data.xml',
|
||||
'data/payment_acquirer_data.xml',
|
||||
'data/payment_cron.xml',
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="account_payment_method_electronic_in" model="account.payment.method">
|
||||
<field name="name">Electronic</field>
|
||||
<field name="code">electronic</field>
|
||||
<field name="payment_type">inbound</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -1,6 +1,5 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_chart_template
|
||||
from . import account_payment
|
||||
from . import account_move
|
||||
from . import ir_http
|
||||
@@ -11,3 +10,4 @@ from . import payment_token
|
||||
from . import payment_transaction
|
||||
from . import res_company
|
||||
from . import res_partner
|
||||
from . import account_journal
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class AccountChartTemplate(models.Model):
|
||||
_inherit = 'account.chart.template'
|
||||
|
||||
def _create_bank_journals(self, company, acc_template_ref):
|
||||
res = super()._create_bank_journals(company, acc_template_ref)
|
||||
|
||||
# Try to generate the missing journals
|
||||
return res + self.env['payment.acquirer']._create_missing_journals(company=company)
|
||||
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _, api, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class AccountJournal(models.Model):
|
||||
_inherit = "account.journal"
|
||||
|
||||
@api.constrains('inbound_payment_method_line_ids')
|
||||
def _check_inbound_payment_method_line_ids(self):
|
||||
"""
|
||||
Check and ensure that the user do not remove a apml that is linked to an acquirer in the test or enabled state.
|
||||
"""
|
||||
self.env['account.payment.method'].flush(['code', 'payment_type'])
|
||||
self.env['account.payment.method.line'].flush(['payment_method_id'])
|
||||
self.env['payment.acquirer'].flush(['provider', 'state'])
|
||||
|
||||
self._cr.execute('''
|
||||
SELECT acquirer.id
|
||||
FROM payment_acquirer acquirer
|
||||
JOIN account_payment_method apm ON apm.code = acquirer.provider
|
||||
LEFT JOIN account_payment_method_line apml ON apm.id = apml.payment_method_id
|
||||
WHERE acquirer.state IN ('enabled', 'test') AND apm.payment_type = 'inbound'
|
||||
AND apml.id IS NULL
|
||||
''')
|
||||
ids = [r[0] for r in self._cr.fetchall()]
|
||||
acquirers = self.env['payment.acquirer'].browse(ids)
|
||||
if acquirers:
|
||||
raise UserError(_("You can't delete a payment method that is linked to an acquirer in the enabled or test state.\n"
|
||||
"Linked acquirer(s): %s", ', '.join(a.display_name for a in acquirers)))
|
||||
@@ -1,35 +1,58 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo import _, api, Command, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class AccountPayment(models.Model):
|
||||
_inherit = 'account.payment'
|
||||
|
||||
# == Business fields ==
|
||||
payment_transaction_id = fields.Many2one(
|
||||
string="Payment Transaction", comodel_name='payment.transaction', readonly=True)
|
||||
payment_token_id = fields.Many2one(
|
||||
string="Saved Payment Token", comodel_name='payment.token', domain="""[
|
||||
(payment_method_code == 'electronic', '=', 1),
|
||||
('company_id', '=', company_id),
|
||||
('acquirer_id.capture_manually', '=', False),
|
||||
('acquirer_id.journal_id', '=', journal_id),
|
||||
('partner_id', 'in', related_partner_ids),
|
||||
('id', 'in', suitable_payment_token_ids),
|
||||
]""",
|
||||
help="Note that only tokens from acquirers allowing to capture the amount are available.")
|
||||
related_partner_ids = fields.Many2many(
|
||||
comodel_name='res.partner', compute='_compute_related_partners')
|
||||
|
||||
@api.depends('partner_id.commercial_partner_id.child_ids')
|
||||
def _compute_related_partners(self):
|
||||
# == Display purpose fields ==
|
||||
suitable_payment_token_ids = fields.Many2many(
|
||||
comodel_name='payment.token',
|
||||
compute='_compute_suitable_payment_token_ids'
|
||||
)
|
||||
use_electronic_payment_method = fields.Boolean(
|
||||
compute='_compute_use_electronic_payment_method',
|
||||
help='Technical field used to hide or show the payment_token_id if needed.'
|
||||
)
|
||||
|
||||
@api.depends('payment_method_id')
|
||||
def _compute_suitable_payment_token_ids(self):
|
||||
for payment in self:
|
||||
payment.related_partner_ids = (
|
||||
related_partner_ids = (
|
||||
payment.partner_id
|
||||
| payment.partner_id.commercial_partner_id
|
||||
| payment.partner_id.commercial_partner_id.child_ids
|
||||
)._origin
|
||||
|
||||
if payment.use_electronic_payment_method:
|
||||
payment.suitable_payment_token_ids = self.env['payment.token'].search([
|
||||
('company_id', '=', payment.company_id.id),
|
||||
('acquirer_id.capture_manually', '=', False),
|
||||
('partner_id', 'in', related_partner_ids.ids),
|
||||
]).filtered(lambda t: t.acquirer_id.journal_id == payment.journal_id.id)
|
||||
else:
|
||||
payment.suitable_payment_token_ids = [Command.clear()]
|
||||
|
||||
@api.depends('payment_method_id')
|
||||
def _compute_use_electronic_payment_method(self):
|
||||
for payment in self:
|
||||
# Get a list of all electronic payment method codes.
|
||||
# These codes are comprised of 'electronic' and the providers of each payment acquirer.
|
||||
codes = [key for key in dict(self.env['payment.acquirer']._fields['provider']._description_selection(self.env))]
|
||||
codes.append('electronic')
|
||||
payment.use_electronic_payment_method = payment.payment_method_code in codes
|
||||
|
||||
@api.onchange('partner_id', 'payment_method_id', 'journal_id')
|
||||
def _onchange_set_payment_token_id(self):
|
||||
if not (self.payment_method_code == 'electronic' and self.partner_id and self.journal_id):
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
import psycopg2
|
||||
|
||||
from odoo import _, api, fields, models, SUPERUSER_ID
|
||||
from odoo.exceptions import ValidationError
|
||||
@@ -10,12 +9,6 @@ from odoo.osv import expression
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_missing_journals(cr, registry):
|
||||
""" Post-init hook responsible for the creation of a journal for all acquirers missing one. """
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
env['payment.acquirer']._create_missing_journals()
|
||||
|
||||
|
||||
class PaymentAcquirer(models.Model):
|
||||
_name = 'payment.acquirer'
|
||||
_description = 'Payment Acquirer'
|
||||
@@ -66,9 +59,9 @@ class PaymentAcquirer(models.Model):
|
||||
"If none is set, it is available for all countries.")
|
||||
journal_id = fields.Many2one(
|
||||
string="Payment Journal", comodel_name='account.journal',
|
||||
copy=False,
|
||||
compute='_compute_journal_id', inverse='_inverse_journal_id',
|
||||
help="The journal in which the successful transactions are posted",
|
||||
domain="[('type', 'in', ['bank', 'cash']), ('company_id', '=', company_id)]")
|
||||
domain="[('type', '=', 'bank'), ('company_id', '=', company_id)]")
|
||||
|
||||
# Fees fields
|
||||
fees_active = fields.Boolean(string="Add Extra Fees")
|
||||
@@ -171,30 +164,33 @@ class PaymentAcquirer(models.Model):
|
||||
'show_cancel_msg': True,
|
||||
})
|
||||
|
||||
#=== ONCHANGE METHODS ===#
|
||||
|
||||
@api.onchange('state')
|
||||
def _onchange_state(self):
|
||||
""" Only enable dashboard display for journals of enabled acquirers.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
def _compute_journal_id(self):
|
||||
for acquirer in self:
|
||||
acquirer.journal_id.show_on_dashboard = acquirer.state == 'enabled'
|
||||
payment_method = self.env['account.payment.method.line'].search([
|
||||
('journal_id.company_id', '=', acquirer.company_id.id),
|
||||
('code', '=', acquirer.provider)
|
||||
], limit=1)
|
||||
if payment_method:
|
||||
acquirer.journal_id = payment_method.journal_id
|
||||
else:
|
||||
acquirer.journal_id = False
|
||||
|
||||
@api.onchange('allow_tokenization')
|
||||
def _onchange_allow_tokenization(self):
|
||||
""" Add (remove) the electronic payment method for acquirers (not) allowing tokenization.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
electronic = self.env.ref('payment.account_payment_method_electronic_in')
|
||||
def _inverse_journal_id(self):
|
||||
for acquirer in self:
|
||||
if acquirer.allow_tokenization:
|
||||
if electronic not in acquirer.journal_id.inbound_payment_method_ids:
|
||||
acquirer.journal_id.inbound_payment_method_ids = [(4, electronic.id)]
|
||||
elif electronic in acquirer.journal_id.inbound_payment_method_ids:
|
||||
acquirer.journal_id.inbound_payment_method_ids = [(3, electronic.id)]
|
||||
payment_method = self.env['account.payment.method.line'].search([
|
||||
('journal_id.company_id', '=', acquirer.company_id.id),
|
||||
('code', '=', acquirer.provider)
|
||||
], limit=1)
|
||||
if acquirer.journal_id:
|
||||
if not payment_method:
|
||||
self.env['account.payment.method.line'].create({
|
||||
'payment_method_id': self._get_default_payment_method(),
|
||||
'journal_id': self.journal_id.id,
|
||||
})
|
||||
else:
|
||||
payment_method.journal_id = acquirer.journal_id
|
||||
elif payment_method:
|
||||
payment_method.unlink()
|
||||
|
||||
#=== CONSTRAINT METHODS ===#
|
||||
|
||||
@@ -250,95 +246,6 @@ class PaymentAcquirer(models.Model):
|
||||
_("The following fields must be filled: %s", ", ".join(field_names))
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _create_missing_journals(self, company=None):
|
||||
""" Create a journal for installed acquirers missing one.
|
||||
|
||||
Each acquirer must have its own journal. It can't however be created along the
|
||||
`payment.acquirer` record because there is no guarantee that the chart template is already
|
||||
installed.
|
||||
|
||||
:param recordset company: The company for which the journals are created, as a `res.company`
|
||||
recordset
|
||||
:return: The created journals
|
||||
:rtype: recordset of `account.journal`
|
||||
"""
|
||||
# Search for installed acquirer modules having no journal for the current company
|
||||
company = company or self.env.company
|
||||
acquirers = self.env['payment.acquirer'].search([
|
||||
('journal_id', '=', False),
|
||||
('company_id', '=', company.id),
|
||||
('module_state', 'in', ('to install', 'installed')),
|
||||
])
|
||||
|
||||
# Create or find the missing journals.
|
||||
# This is done in this order and not the other way around because the most common cause for
|
||||
# a missing journal is the first install of an acquirer's module. The other (less common)
|
||||
# cause is a re-install. In this last case, the creation will fail because of a unique
|
||||
# constraint violation, we catch the error, and fallback on searching the previous journal.
|
||||
Journal = journals = self.env['account.journal']
|
||||
for acquirer in acquirers.filtered('company_id.chart_template_id'):
|
||||
try:
|
||||
with self.env.cr.savepoint():
|
||||
journal = Journal.create(acquirer._get_journal_create_values())
|
||||
except psycopg2.IntegrityError as error: # Journal already exists
|
||||
if error.pgcode == psycopg2.errorcodes.UNIQUE_VIOLATION:
|
||||
journal = Journal.search(acquirer._get_journal_search_domain(), limit=1)
|
||||
else:
|
||||
raise error
|
||||
acquirer.journal_id = journal
|
||||
journals += journal
|
||||
return journals
|
||||
|
||||
def _get_journal_create_values(self):
|
||||
""" Return a dict of values to create the acquirer's journal.
|
||||
|
||||
Note: self.ensure_one()
|
||||
|
||||
:return: The dict of create values for `account.journal`
|
||||
:rtype: dict
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
account_vals = self.company_id.chart_template_id. \
|
||||
_prepare_transfer_account_for_direct_creation(self.name, self.company_id)
|
||||
account = self.env['account.account'].create(account_vals)
|
||||
inbound_payment_method_ids = []
|
||||
if self.allow_tokenization:
|
||||
inbound_payment_method_ids.append(
|
||||
(4, self.env.ref('payment.account_payment_method_electronic_in').id)
|
||||
)
|
||||
return {
|
||||
'name': self.name,
|
||||
'code': self.name.upper(),
|
||||
'sequence': 999,
|
||||
'type': 'bank',
|
||||
'company_id': self.company_id.id,
|
||||
'default_account_id': account.id,
|
||||
# Show the journal on dashboard if the acquirer is published on the website.
|
||||
'show_on_dashboard': self.state == 'enabled',
|
||||
# Don't show payment methods in the backend
|
||||
'inbound_payment_method_ids': inbound_payment_method_ids,
|
||||
'outbound_payment_method_ids': [],
|
||||
}
|
||||
|
||||
def _get_journal_search_domain(self):
|
||||
""" Return a domain for searching a journal corresponding to the acquirer.
|
||||
|
||||
Note: self.ensure_one()
|
||||
|
||||
:return: The search domain
|
||||
:rtype: list
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
code_cutoff = self.env['account.journal']._fields['code'].size
|
||||
return [
|
||||
('name', '=', self.name),
|
||||
('code', '=', self.name.upper()[:code_cutoff]),
|
||||
('company_id', '=', self.company_id.id),
|
||||
]
|
||||
|
||||
#=== ACTION METHODS ===#
|
||||
|
||||
def button_immediate_install(self):
|
||||
@@ -481,3 +388,7 @@ class PaymentAcquirer(models.Model):
|
||||
"""
|
||||
self.ensure_one()
|
||||
return self.journal_id.currency_id or self.company_id.currency_id
|
||||
|
||||
def _get_default_payment_method(self):
|
||||
self.ensure_one()
|
||||
return self.env.ref('account.account_payment_method_manual_in').id
|
||||
|
||||
@@ -819,6 +819,7 @@ class PaymentTransaction(models.Model):
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
payment_method = self.env['account.payment.method'].search([('code', '=', self.acquirer_id.provider)], limit=1)
|
||||
payment_values = {
|
||||
'amount': self.amount,
|
||||
'payment_type': 'inbound' if self.amount > 0 else 'outbound',
|
||||
@@ -827,7 +828,7 @@ class PaymentTransaction(models.Model):
|
||||
'partner_type': 'customer',
|
||||
'journal_id': self.acquirer_id.journal_id.id,
|
||||
'company_id': self.acquirer_id.company_id.id,
|
||||
'payment_method_id': self.env.ref('payment.account_payment_method_electronic_in').id,
|
||||
'payment_method_id': payment_method.id,
|
||||
'payment_token_id': self.token_id.id,
|
||||
'payment_transaction_id': self.id,
|
||||
'ref': self.reference,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
|
||||
from unittest.mock import patch
|
||||
from odoo.addons.account.models.account_payment_method import AccountPaymentMethod
|
||||
from odoo.fields import Command
|
||||
|
||||
from odoo.addons.payment.tests.utils import PaymentTestUtils
|
||||
@@ -15,6 +16,13 @@ class PaymentCommon(PaymentTestUtils):
|
||||
def setUpClass(cls, chart_template_ref=None):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
|
||||
Method_get_payment_method_information = AccountPaymentMethod._get_payment_method_information
|
||||
|
||||
def _get_payment_method_information(self):
|
||||
res = Method_get_payment_method_information(self)
|
||||
res['none'] = {'mode': 'multi', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
|
||||
cls.currency_euro = cls._prepare_currency('EUR')
|
||||
cls.currency_usd = cls._prepare_currency('USD')
|
||||
|
||||
@@ -68,6 +76,13 @@ class PaymentCommon(PaymentTestUtils):
|
||||
'type': 'qweb',
|
||||
'arch': arch,
|
||||
})
|
||||
|
||||
with patch.object(AccountPaymentMethod, '_get_payment_method_information', _get_payment_method_information):
|
||||
cls.env['account.payment.method'].create({
|
||||
'name': 'Dummy method',
|
||||
'code': 'none',
|
||||
'payment_type': 'inbound'
|
||||
})
|
||||
cls.dummy_acquirer = cls.env['payment.acquirer'].create({
|
||||
'name': "Dummy Acquirer",
|
||||
'provider': 'none',
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
<field name="inherit_id" ref="account.view_account_payment_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr='//group[2]' position="inside">
|
||||
<field name="payment_transaction_id" groups="base.group_no_one" attrs="{'invisible': [('payment_method_code', '!=', 'electronic')]}"/>
|
||||
<field name="payment_transaction_id" groups="base.group_no_one" attrs="{'invisible': [('use_electronic_payment_method', '!=', True)]}"/>
|
||||
</xpath>
|
||||
<field name="payment_method_id" position="after">
|
||||
<field name="payment_method_code" invisible="1"/>
|
||||
<field name="related_partner_ids" invisible="1"/>
|
||||
<field name="suitable_payment_token_ids" invisible="1"/>
|
||||
<field name="use_electronic_payment_method" invisible="1"/>
|
||||
<field name="payment_token_id" options="{'no_create': True}"
|
||||
attrs="{'invisible': [('payment_method_code', '!=', 'electronic')], 'readonly': [('state', '!=', 'draft')]}"/>
|
||||
attrs="{'invisible': [('use_electronic_payment_method', '!=', True)], 'readonly': [('state', '!=', 'draft')]}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from odoo import models, fields, api, _
|
||||
from odoo import api, Command, fields, models
|
||||
|
||||
|
||||
class AccountPaymentRegister(models.TransientModel):
|
||||
@@ -11,19 +11,20 @@ class AccountPaymentRegister(models.TransientModel):
|
||||
store=True, readonly=False,
|
||||
compute='_compute_payment_token_id',
|
||||
domain='''[
|
||||
(payment_method_code == 'electronic', '=', 1),
|
||||
('company_id', '=', company_id),
|
||||
('acquirer_id.capture_manually', '=', False),
|
||||
('acquirer_id.journal_id', '=', journal_id),
|
||||
('partner_id', 'in', suitable_payment_token_partner_ids),
|
||||
('id', 'in', suitable_payment_token_ids),
|
||||
]''',
|
||||
help="Note that tokens from acquirers set to only authorize transactions (instead of capturing the amount) are "
|
||||
"not available.")
|
||||
|
||||
# == Display purpose fields ==
|
||||
suitable_payment_token_partner_ids = fields.Many2many(
|
||||
comodel_name='res.partner',
|
||||
compute='_compute_suitable_payment_token_partner_ids')
|
||||
suitable_payment_token_ids = fields.Many2many(
|
||||
comodel_name='payment.token',
|
||||
compute='_compute_suitable_payment_token_ids'
|
||||
)
|
||||
use_electronic_payment_method = fields.Boolean(
|
||||
compute='_compute_use_electronic_payment_method',
|
||||
help='Technical field used to hide or show the payment_token_id if needed.'
|
||||
)
|
||||
payment_method_code = fields.Char(
|
||||
related='payment_method_id.code')
|
||||
|
||||
@@ -31,17 +32,32 @@ class AccountPaymentRegister(models.TransientModel):
|
||||
# COMPUTE METHODS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@api.depends('can_edit_wizard')
|
||||
def _compute_suitable_payment_token_partner_ids(self):
|
||||
@api.depends('payment_method_id')
|
||||
def _compute_suitable_payment_token_ids(self):
|
||||
for wizard in self:
|
||||
if wizard.can_edit_wizard:
|
||||
lines = wizard._get_batches()[0]['lines']
|
||||
partners = lines.partner_id
|
||||
commercial_partners = partners.commercial_partner_id
|
||||
children_partners = commercial_partners.child_ids
|
||||
wizard.suitable_payment_token_partner_ids = (partners + commercial_partners + children_partners)._origin
|
||||
if wizard.can_edit_wizard and wizard.use_electronic_payment_method:
|
||||
related_partner_ids = (
|
||||
wizard.partner_id
|
||||
| wizard.partner_id.commercial_partner_id
|
||||
| wizard.partner_id.commercial_partner_id.child_ids
|
||||
)._origin
|
||||
|
||||
wizard.suitable_payment_token_ids = self.env['payment.token'].search([
|
||||
('company_id', '=', wizard.company_id.id),
|
||||
('acquirer_id.capture_manually', '=', False),
|
||||
('partner_id', 'in', related_partner_ids.ids),
|
||||
]).filtered(lambda t: t.acquirer_id.journal_id == wizard.journal_id.id)
|
||||
else:
|
||||
wizard.suitable_payment_token_partner_ids = False
|
||||
wizard.suitable_payment_token_ids = [Command.clear()]
|
||||
|
||||
@api.depends('payment_method_id')
|
||||
def _compute_use_electronic_payment_method(self):
|
||||
for wizard in self:
|
||||
# Get a list of all electronic payment method codes.
|
||||
# These codes are comprised of 'electronic' and the providers of each payment acquirer.
|
||||
codes = [key for key in dict(self.env['payment.acquirer']._fields['provider']._description_selection(self.env))]
|
||||
codes.append('electronic')
|
||||
wizard.use_electronic_payment_method = wizard.payment_method_code in codes
|
||||
|
||||
@api.onchange('can_edit_wizard', 'payment_method_id', 'journal_id')
|
||||
def _compute_payment_token_id(self):
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
<field name="arch" type="xml">
|
||||
<field name="payment_method_id" position="after">
|
||||
<field name="payment_method_code" invisible="1"/>
|
||||
<field name="suitable_payment_token_partner_ids" invisible="1"/>
|
||||
<field name="suitable_payment_token_ids" invisible="1"/>
|
||||
<field name="use_electronic_payment_method" invisible="1"/>
|
||||
<field name="payment_token_id"
|
||||
options="{'no_create': True}"
|
||||
attrs="{'invisible': ['|', ('payment_method_code', '!=', 'electronic'), '|', ('can_edit_wizard', '=', False), '&', ('can_group_payments', '=', True), ('group_payment', '=', False)]}"/>
|
||||
attrs="{'invisible': ['|', ('use_electronic_payment_method', '!=', True), '|', ('can_edit_wizard', '=', False), '&', ('can_group_payments', '=', True), ('group_payment', '=', False)]}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -4,7 +4,6 @@ from . import controllers
|
||||
from . import models
|
||||
|
||||
from odoo.addons.payment import reset_payment_acquirer
|
||||
from odoo.addons.payment.models.payment_acquirer import create_missing_journals # post-init hook
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
'data/payment_acquirer_data.xml', # Depends on views/payment_adyen_templates.xml
|
||||
],
|
||||
'application': True,
|
||||
'post_init_hook': 'create_missing_journals',
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
'assets': {
|
||||
'web.assets_frontend': [
|
||||
|
||||
@@ -10,4 +10,10 @@
|
||||
<field name="allow_tokenization">True</field>
|
||||
</record>
|
||||
|
||||
<record id="payment_method_adyen" model="account.payment.method">
|
||||
<field name="name">Adyen</field>
|
||||
<field name="code">adyen</field>
|
||||
<field name="payment_type">inbound</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_payment_method
|
||||
from . import payment_acquirer
|
||||
from . import payment_token
|
||||
from . import payment_transaction
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_inherit = 'account.payment.method'
|
||||
|
||||
@api.model
|
||||
def _get_payment_method_information(self):
|
||||
res = super()._get_payment_method_information()
|
||||
res['adyen'] = {'mode': 'unique', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
@@ -117,3 +117,9 @@ class PaymentAcquirer(models.Model):
|
||||
:rtype: str
|
||||
"""
|
||||
return f'ODOO_PARTNER_{partner_id}'
|
||||
|
||||
def _get_default_payment_method(self):
|
||||
self.ensure_one()
|
||||
if self.provider != 'adyen':
|
||||
return super()._get_default_payment_method()
|
||||
return self.env.ref('payment_adyen.payment_method_adyen').id
|
||||
|
||||
@@ -4,7 +4,6 @@ from . import controllers
|
||||
from . import models
|
||||
|
||||
from odoo.addons.payment import reset_payment_acquirer
|
||||
from odoo.addons.payment.models.payment_acquirer import create_missing_journals # post-init hook
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
|
||||
@@ -14,6 +14,5 @@
|
||||
'data/payment_acquirer_data.xml',
|
||||
],
|
||||
'application': True,
|
||||
'post_init_hook': 'create_missing_journals',
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
}
|
||||
|
||||
@@ -9,4 +9,10 @@
|
||||
<field name="support_tokenization">False</field>
|
||||
</record>
|
||||
|
||||
<record id="payment_method_alipay" model="account.payment.method">
|
||||
<field name="name">Alipay</field>
|
||||
<field name="code">alipay</field>
|
||||
<field name="payment_type">inbound</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_payment_method
|
||||
from . import payment_acquirer
|
||||
from . import payment_transaction
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_inherit = 'account.payment.method'
|
||||
|
||||
@api.model
|
||||
def _get_payment_method_information(self):
|
||||
res = super()._get_payment_method_information()
|
||||
res['alipay'] = {'mode': 'unique', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
@@ -58,3 +58,9 @@ class PaymentAcquirer(models.Model):
|
||||
return 'https://mapi.alipay.com/gateway.do'
|
||||
else: # test environment
|
||||
return 'https://openapi.alipaydev.com/gateway.do'
|
||||
|
||||
def _get_default_payment_method(self):
|
||||
self.ensure_one()
|
||||
if self.provider != 'alipay':
|
||||
return super()._get_default_payment_method()
|
||||
return self.env.ref('payment_alipay.payment_method_alipay').id
|
||||
|
||||
@@ -4,7 +4,6 @@ from . import controllers
|
||||
from . import models
|
||||
|
||||
from odoo.addons.payment import reset_payment_acquirer
|
||||
from odoo.addons.payment.models.payment_acquirer import create_missing_journals # post-init hook
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
'data/payment_acquirer_data.xml',
|
||||
],
|
||||
'application': True,
|
||||
'post_init_hook': 'create_missing_journals',
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
'assets': {
|
||||
'web.assets_frontend': [
|
||||
|
||||
@@ -10,4 +10,10 @@
|
||||
<field name="allow_tokenization">True</field>
|
||||
</record>
|
||||
|
||||
<record id="payment_method_authorize" model="account.payment.method">
|
||||
<field name="name">Authorize.Net</field>
|
||||
<field name="code">authorize</field>
|
||||
<field name="payment_type">inbound</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_payment_method
|
||||
from . import payment_acquirer
|
||||
from . import payment_token
|
||||
from . import payment_transaction
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_inherit = 'account.payment.method'
|
||||
|
||||
@api.model
|
||||
def _get_payment_method_information(self):
|
||||
res = super()._get_payment_method_information()
|
||||
res['authorize'] = {'mode': 'unique', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
@@ -92,3 +92,9 @@ class PaymentAcquirer(models.Model):
|
||||
return res
|
||||
|
||||
return self.authorize_currency_id
|
||||
|
||||
def _get_default_payment_method(self):
|
||||
self.ensure_one()
|
||||
if self.provider != 'authorize':
|
||||
return super()._get_default_payment_method()
|
||||
return self.env.ref('payment_authorize.payment_method_authorize').id
|
||||
|
||||
@@ -4,7 +4,6 @@ from . import controllers
|
||||
from . import models
|
||||
|
||||
from odoo.addons.payment import reset_payment_acquirer
|
||||
from odoo.addons.payment.models.payment_acquirer import create_missing_journals # post-init hook
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
|
||||
@@ -14,6 +14,5 @@
|
||||
'data/payment_acquirer_data.xml',
|
||||
],
|
||||
'application': True,
|
||||
'post_init_hook': 'create_missing_journals',
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
}
|
||||
|
||||
@@ -9,4 +9,10 @@
|
||||
<field name="support_tokenization">False</field>
|
||||
</record>
|
||||
|
||||
<record id="payment_method_buckaroo" model="account.payment.method">
|
||||
<field name="name">Buckaroo</field>
|
||||
<field name="code">buckaroo</field>
|
||||
<field name="payment_type">inbound</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_payment_method
|
||||
from . import payment_acquirer
|
||||
from . import payment_transaction
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_inherit = 'account.payment.method'
|
||||
|
||||
@api.model
|
||||
def _get_payment_method_information(self):
|
||||
res = super()._get_payment_method_information()
|
||||
res['buckaroo'] = {'mode': 'unique', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
@@ -63,3 +63,9 @@ class PaymentAcquirer(models.Model):
|
||||
sign_string += self.buckaroo_secret_key
|
||||
# Calculate the SHA-1 hash over the signing string
|
||||
return sha1(sign_string.encode('utf-8')).hexdigest()
|
||||
|
||||
def _get_default_payment_method(self):
|
||||
self.ensure_one()
|
||||
if self.provider != 'buckaroo':
|
||||
return super()._get_default_payment_method()
|
||||
return self.env.ref('payment_buckaroo.payment_method_buckaroo').id
|
||||
|
||||
@@ -4,7 +4,6 @@ from . import controllers
|
||||
from . import models
|
||||
|
||||
from odoo.addons.payment import reset_payment_acquirer
|
||||
from odoo.addons.payment.models.payment_acquirer import create_missing_journals # post-init hook
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
|
||||
@@ -15,6 +15,5 @@
|
||||
'data/payment_acquirer_data.xml',
|
||||
],
|
||||
'application': True,
|
||||
'post_init_hook': 'create_missing_journals',
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
}
|
||||
|
||||
@@ -10,4 +10,10 @@
|
||||
<field name="allow_tokenization">True</field>
|
||||
</record>
|
||||
|
||||
<record id="payment_method_odoo" model="account.payment.method">
|
||||
<field name="name">Odoo</field>
|
||||
<field name="code">odoo</field>
|
||||
<field name="payment_type">inbound</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from . import account_move
|
||||
from . import adyen_account
|
||||
from . import adyen_transaction
|
||||
from . import account_payment_method
|
||||
from . import payment_acquirer
|
||||
from . import payment_token
|
||||
from . import payment_transaction
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_inherit = 'account.payment.method'
|
||||
|
||||
@api.model
|
||||
def _get_payment_method_information(self):
|
||||
res = super()._get_payment_method_information()
|
||||
res['odoo'] = {'mode': 'unique', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
@@ -32,3 +32,9 @@ class PaymentAcquirer(models.Model):
|
||||
:rtype: str
|
||||
"""
|
||||
return f'{self.odoo_adyen_account_id.adyen_uuid}_{partner_id}'
|
||||
|
||||
def _get_default_payment_method(self):
|
||||
self.ensure_one()
|
||||
if self.provider != 'odoo':
|
||||
return super()._get_default_payment_method()
|
||||
return self.env.ref('payment_odoo.payment_method_odoo').id
|
||||
|
||||
@@ -4,7 +4,6 @@ from . import controllers
|
||||
from . import models
|
||||
|
||||
from odoo.addons.payment import reset_payment_acquirer
|
||||
from odoo.addons.payment.models.payment_acquirer import create_missing_journals # post-init hook
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
|
||||
@@ -14,6 +14,5 @@
|
||||
'data/payment_acquirer_data.xml',
|
||||
],
|
||||
'application': True,
|
||||
'post_init_hook': 'create_missing_journals',
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
}
|
||||
|
||||
@@ -9,5 +9,11 @@
|
||||
<field name="support_tokenization">True</field>
|
||||
<field name="allow_tokenization">True</field>
|
||||
</record>
|
||||
|
||||
<record id="payment_method_ogone" model="account.payment.method">
|
||||
<field name="name">Ogone</field>
|
||||
<field name="code">ogone</field>
|
||||
<field name="payment_type">inbound</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_payment_method
|
||||
from . import payment_acquirer
|
||||
from . import payment_token
|
||||
from . import payment_transaction
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_inherit = 'account.payment.method'
|
||||
|
||||
@api.model
|
||||
def _get_payment_method_information(self):
|
||||
res = super()._get_payment_method_information()
|
||||
res['ogone'] = {'mode': 'unique', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
@@ -120,3 +120,9 @@ class PaymentAcquirer(models.Model):
|
||||
_logger.exception("invalid API request at %s with data %s", url, payload)
|
||||
raise ValidationError("Ogone: " + _("The communication with the API failed."))
|
||||
return response.content
|
||||
|
||||
def _get_default_payment_method(self):
|
||||
self.ensure_one()
|
||||
if self.provider != 'ogone':
|
||||
return super()._get_default_payment_method()
|
||||
return self.env.ref('payment_ogone.payment_method_ogone').id
|
||||
|
||||
@@ -4,7 +4,6 @@ from . import controllers
|
||||
from . import models
|
||||
|
||||
from odoo.addons.payment import reset_payment_acquirer
|
||||
from odoo.addons.payment.models.payment_acquirer import create_missing_journals # post-init hook
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
|
||||
@@ -15,6 +15,5 @@
|
||||
'data/payment_paypal_email_data.xml',
|
||||
],
|
||||
'application': True,
|
||||
'post_init_hook': 'create_missing_journals',
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
}
|
||||
|
||||
@@ -9,4 +9,10 @@
|
||||
<field name="support_tokenization">False</field>
|
||||
</record>
|
||||
|
||||
<record id="payment_method_paypal" model="account.payment.method">
|
||||
<field name="name">Paypal</field>
|
||||
<field name="code">paypal</field>
|
||||
<field name="payment_type">inbound</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_payment_method
|
||||
from . import payment_acquirer
|
||||
from . import payment_transaction
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_inherit = 'account.payment.method'
|
||||
|
||||
@api.model
|
||||
def _get_payment_method_information(self):
|
||||
res = super()._get_payment_method_information()
|
||||
res['paypal'] = {'mode': 'unique', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
@@ -65,3 +65,9 @@ class PaymentAcquirer(models.Model):
|
||||
'author_id': self.create_uid.partner_id.id,
|
||||
}
|
||||
self.env['mail.mail'].sudo().create(mail_values).send()
|
||||
|
||||
def _get_default_payment_method(self):
|
||||
self.ensure_one()
|
||||
if self.provider != 'paypal':
|
||||
return super()._get_default_payment_method()
|
||||
return self.env.ref('payment_paypal.payment_method_paypal').id
|
||||
|
||||
@@ -4,7 +4,6 @@ from . import controllers
|
||||
from . import models
|
||||
|
||||
from odoo.addons.payment import reset_payment_acquirer
|
||||
from odoo.addons.payment.models.payment_acquirer import create_missing_journals # post-init hook
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
|
||||
@@ -14,6 +14,5 @@
|
||||
'data/payment_acquirer_data.xml',
|
||||
],
|
||||
'application': True,
|
||||
'post_init_hook': 'create_missing_journals',
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
}
|
||||
|
||||
@@ -9,4 +9,10 @@
|
||||
<field name="support_tokenization">False</field>
|
||||
</record>
|
||||
|
||||
<record id="payment_method_payulatam" model="account.payment.method">
|
||||
<field name="name">payulatam</field>
|
||||
<field name="code">payulatam</field>
|
||||
<field name="payment_type">inbound</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_payment_method
|
||||
from . import payment_acquirer
|
||||
from . import payment_transaction
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_inherit = 'account.payment.method'
|
||||
|
||||
@api.model
|
||||
def _get_payment_method_information(self):
|
||||
res = super()._get_payment_method_information()
|
||||
res['payulatam'] = {'mode': 'unique', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
@@ -68,3 +68,9 @@ class PaymentAcquirer(models.Model):
|
||||
values['currency'],
|
||||
])
|
||||
return md5(data_string.encode('utf-8')).hexdigest()
|
||||
|
||||
def _get_default_payment_method(self):
|
||||
self.ensure_one()
|
||||
if self.provider != 'payulatam':
|
||||
return super()._get_default_payment_method()
|
||||
return self.env.ref('payment_payulatam.payment_method_payulatam').id
|
||||
|
||||
@@ -4,7 +4,6 @@ from . import controllers
|
||||
from . import models
|
||||
|
||||
from odoo.addons.payment import reset_payment_acquirer
|
||||
from odoo.addons.payment.models.payment_acquirer import create_missing_journals # post-init hook
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
|
||||
@@ -18,6 +18,5 @@ PayUmoney payment gateway supports only INR currency.
|
||||
'data/payment_acquirer_data.xml',
|
||||
],
|
||||
'application': True,
|
||||
'post_init_hook': 'create_missing_journals',
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
}
|
||||
|
||||
@@ -9,4 +9,10 @@
|
||||
<field name="support_tokenization">False</field>
|
||||
</record>
|
||||
|
||||
<record id="payment_method_payumoney" model="account.payment.method">
|
||||
<field name="name">PayUmoney</field>
|
||||
<field name="code">payumoney</field>
|
||||
<field name="payment_type">inbound</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_payment_method
|
||||
from . import payment_acquirer
|
||||
from . import payment_transaction
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_inherit = 'account.payment.method'
|
||||
|
||||
@api.model
|
||||
def _get_payment_method_information(self):
|
||||
res = super()._get_payment_method_information()
|
||||
res['payumoney'] = {'mode': 'unique', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
@@ -49,3 +49,9 @@ class PaymentAcquirer(models.Model):
|
||||
keys = 'key|txnid|amount|productinfo|firstname|email|udf1|udf2|udf3|udf4|udf5||||||salt'
|
||||
sign = '|'.join(f'{sign_values.get(k) or ""}' for k in keys.split('|'))
|
||||
return hashlib.sha512(sign.encode('utf-8')).hexdigest()
|
||||
|
||||
def _get_default_payment_method(self):
|
||||
self.ensure_one()
|
||||
if self.provider != 'payumoney':
|
||||
return super()._get_default_payment_method()
|
||||
return self.env.ref('payment_payumoney.payment_method_payumoney').id
|
||||
|
||||
@@ -4,7 +4,6 @@ from . import models
|
||||
from . import controllers
|
||||
|
||||
from odoo.addons.payment import reset_payment_acquirer
|
||||
from odoo.addons.payment.models.payment_acquirer import create_missing_journals # post-init hook
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
|
||||
@@ -19,6 +19,5 @@ not guaranteed.""",
|
||||
'data/payment_acquirer_data.xml',
|
||||
],
|
||||
'application': True,
|
||||
'post_init_hook': 'create_missing_journals',
|
||||
'uninstall_hook': 'uninstall_hook',
|
||||
}
|
||||
|
||||
@@ -9,4 +9,10 @@
|
||||
<field name="support_tokenization">False</field>
|
||||
</record>
|
||||
|
||||
<record id="payment_method_sips" model="account.payment.method">
|
||||
<field name="name">Sips</field>
|
||||
<field name="code">sips</field>
|
||||
<field name="payment_type">inbound</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_payment_method
|
||||
from . import payment_acquirer
|
||||
from . import payment_transaction
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class AccountPaymentMethod(models.Model):
|
||||
_inherit = 'account.payment.method'
|
||||
|
||||
@api.model
|
||||
def _get_payment_method_information(self):
|
||||
res = super()._get_payment_method_information()
|
||||
res['sips'] = {'mode': 'unique', 'domain': [('type', '=', 'bank')]}
|
||||
return res
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user