[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:
Nicolas (vin)
2021-06-03 10:00:26 +00:00
parent 13f74aba74
commit 04522f01e6
119 changed files with 1078 additions and 528 deletions
+54 -15
View File
@@ -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
+179 -89
View File
@@ -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 -18
View File
@@ -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
+110 -4
View File
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
120 access_account_move_reversal access_validate_account_move access.account.move.reversal access.validate.account.move model_account_move_reversal model_validate_account_move account.group_account_invoice 1 1 1 0
121 access_account_common_report access_cash_box_out access.account.common.report access.cash.box.out model_account_common_report model_cash_box_out account.group_account_user 1 1 1 0
122 access_account_common_journal_report access_account_move_reversal access.account.common.journal.report access.account.move.reversal model_account_common_journal_report model_account_move_reversal account.group_account_user account.group_account_invoice 1 1 1 0
123 access_account_common_report access.account.common.report model_account_common_report account.group_account_user 1 1 1 0
124 access_account_common_journal_report access.account.common.journal.report model_account_common_journal_report account.group_account_user 1 1 1 0
125 access_account_print_journal access.account.print.journal model_account_print_journal account.group_account_user 1 1 1 0
126 access_account_financial_year_op access.account.financial.year.op model_account_financial_year_op account.group_account_manager 1 1 1 0
127 access_account_setup_bank_manual_config access.account.setup.bank.manual.config model_account_setup_bank_manual_config account.group_account_manager 1 1 1 0
+6 -6
View File
@@ -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({
+2 -2
View File
@@ -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)
+8 -8
View File
@@ -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'), [
+9 -9
View File
@@ -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()
+34 -17
View File
@@ -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), '&amp;', ('can_group_payments', '=', True), ('group_payment', '=', False)],
+6 -1
View File
@@ -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"
/>
+1 -1
View File
@@ -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()
+2 -2
View File
@@ -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>
-1
View File
@@ -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',
-10
View File
@@ -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 -1
View File
@@ -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)
+32
View File
@@ -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)))
+34 -11
View File
@@ -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):
+30 -119
View File
@@ -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
+2 -1
View File
@@ -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,
+16 -1
View File
@@ -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), '&amp;', ('can_group_payments', '=', True), ('group_payment', '=', False)]}"/>
attrs="{'invisible': ['|', ('use_electronic_payment_method', '!=', True), '|', ('can_edit_wizard', '=', False), '&amp;', ('can_group_payments', '=', True), ('group_payment', '=', False)]}"/>
</field>
</field>
</record>
-1
View File
@@ -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):
-1
View File
@@ -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
View File
@@ -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
-1
View File
@@ -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):
-1
View File
@@ -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
View File
@@ -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
-1
View File
@@ -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):
-1
View File
@@ -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
-1
View File
@@ -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):
-1
View File
@@ -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
-1
View File
@@ -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):
-1
View File
@@ -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>
+1
View File
@@ -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
-1
View File
@@ -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):
-1
View File
@@ -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
View File
@@ -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
-1
View File
@@ -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):
-1
View File
@@ -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
View File
@@ -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
-1
View File
@@ -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):
-1
View File
@@ -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
-1
View File
@@ -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):
-1
View File
@@ -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
-1
View File
@@ -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):
-1
View File
@@ -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
View File
@@ -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