mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-21 18:12:02 +02:00
19.0 vanilla
This commit is contained in:
parent
d1963a3c3a
commit
2d3ee4855a
7430 changed files with 2687981 additions and 2965473 deletions
|
|
@ -1,9 +1,10 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import ir_http
|
||||
from . import payment_method
|
||||
from . import payment_provider
|
||||
from . import payment_icon
|
||||
from . import payment_token
|
||||
from . import payment_transaction
|
||||
from . import res_company
|
||||
from . import res_country
|
||||
from . import res_partner
|
||||
|
|
|
|||
|
|
@ -8,5 +8,5 @@ class IrHttp(models.AbstractModel):
|
|||
|
||||
@classmethod
|
||||
def _get_translation_frontend_modules_name(cls):
|
||||
mods = super(IrHttp, cls)._get_translation_frontend_modules_name()
|
||||
mods = super()._get_translation_frontend_modules_name()
|
||||
return mods + ['payment']
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class PaymentIcon(models.Model):
|
||||
_name = 'payment.icon'
|
||||
_description = 'Payment Icon'
|
||||
_order = 'sequence, name'
|
||||
|
||||
name = fields.Char(string="Name")
|
||||
provider_ids = fields.Many2many(
|
||||
string="Providers", comodel_name='payment.provider',
|
||||
help="The list of providers supporting this payment icon")
|
||||
image = fields.Image(
|
||||
string="Image", max_width=64, max_height=64,
|
||||
help="This field holds the image used for this payment icon, limited to 64x64 px")
|
||||
image_payment_form = fields.Image(
|
||||
string="Image displayed on the payment form", related='image', store=True, max_width=45,
|
||||
max_height=30)
|
||||
sequence = fields.Integer('Sequence', default=1)
|
||||
341
odoo-bringout-oca-ocb-payment/payment/models/payment_method.py
Normal file
341
odoo-bringout-oca-ocb-payment/payment/models/payment_method.py
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.fields import Command, Domain
|
||||
|
||||
from odoo.addons.payment import utils as payment_utils
|
||||
from odoo.addons.payment.const import REPORT_REASONS_MAPPING
|
||||
|
||||
|
||||
class PaymentMethod(models.Model):
|
||||
_name = 'payment.method'
|
||||
_description = "Payment Method"
|
||||
_order = 'active desc, sequence, name'
|
||||
|
||||
name = fields.Char(string="Name", required=True, translate=True)
|
||||
code = fields.Char(
|
||||
string="Code", help="The technical code of this payment method.", required=True
|
||||
)
|
||||
sequence = fields.Integer(string="Sequence", default=1)
|
||||
primary_payment_method_id = fields.Many2one(
|
||||
string="Primary Payment Method",
|
||||
help="The primary payment method of the current payment method, if the latter is a brand."
|
||||
"\nFor example, \"Card\" is the primary payment method of the card brand \"VISA\".",
|
||||
comodel_name='payment.method',
|
||||
index='btree_not_null',
|
||||
)
|
||||
brand_ids = fields.One2many(
|
||||
string="Brands",
|
||||
help="The brands of the payment methods that will be displayed on the payment form.",
|
||||
comodel_name='payment.method',
|
||||
inverse_name='primary_payment_method_id',
|
||||
)
|
||||
is_primary = fields.Boolean(
|
||||
string="Is Primary Payment Method",
|
||||
compute='_compute_is_primary',
|
||||
search='_search_is_primary',
|
||||
)
|
||||
provider_ids = fields.Many2many(
|
||||
string="Providers",
|
||||
help="The list of providers supporting this payment method.",
|
||||
comodel_name='payment.provider',
|
||||
)
|
||||
active = fields.Boolean(string="Active", default=True)
|
||||
image = fields.Image(
|
||||
string="Image",
|
||||
help="The base image used for this payment method; in a 64x64 px format.",
|
||||
max_width=64,
|
||||
max_height=64,
|
||||
required=True,
|
||||
)
|
||||
image_payment_form = fields.Image(
|
||||
string="The resized image displayed on the payment form.",
|
||||
related='image',
|
||||
store=True,
|
||||
max_width=45,
|
||||
max_height=30,
|
||||
)
|
||||
|
||||
# Feature support fields.
|
||||
support_tokenization = fields.Boolean(
|
||||
string="Tokenization",
|
||||
help="Tokenization is the process of saving the payment details as a token that can later"
|
||||
" be reused without having to enter the payment details again.",
|
||||
)
|
||||
support_express_checkout = fields.Boolean(
|
||||
string="Express Checkout",
|
||||
help="Express checkout allows customers to pay faster by using a payment method that"
|
||||
" provides all required billing and shipping information, thus allowing to skip the"
|
||||
" checkout process.",
|
||||
)
|
||||
support_manual_capture = fields.Selection(
|
||||
string="Manual Capture",
|
||||
help="The payment is authorized and captured in two steps instead of one.",
|
||||
selection=[
|
||||
('none', "Unsupported"),
|
||||
('full_only', "Full Only"),
|
||||
('partial', "Full & Partial"),
|
||||
],
|
||||
required=True,
|
||||
default='none'
|
||||
)
|
||||
support_refund = fields.Selection(
|
||||
string="Refund",
|
||||
help="Refund is a feature allowing to refund customers directly from the payment in Odoo.",
|
||||
selection=[
|
||||
('none', "Unsupported"),
|
||||
('full_only', "Full Only"),
|
||||
('partial', "Full & Partial"),
|
||||
],
|
||||
required=True,
|
||||
default='none',
|
||||
)
|
||||
supported_country_ids = fields.Many2many(
|
||||
string="Countries",
|
||||
comodel_name='res.country',
|
||||
help="The list of countries in which this payment method can be used (if the provider"
|
||||
" allows it). In other countries, this payment method is not available to customers."
|
||||
)
|
||||
supported_currency_ids = fields.Many2many(
|
||||
string="Currencies",
|
||||
comodel_name='res.currency',
|
||||
help="The list of currencies for that are supported by this payment method (if the provider"
|
||||
" allows it). When paying with another currency, this payment method is not available "
|
||||
"to customers.",
|
||||
context={'active_test': False},
|
||||
)
|
||||
|
||||
# === COMPUTE METHODS === #
|
||||
|
||||
def _compute_is_primary(self):
|
||||
for payment_method in self:
|
||||
payment_method.is_primary = not payment_method.primary_payment_method_id
|
||||
|
||||
def _search_is_primary(self, operator, value):
|
||||
if operator not in ('in', 'not in'):
|
||||
return NotImplemented
|
||||
return [('primary_payment_method_id', operator, [False])]
|
||||
|
||||
# === ONCHANGE METHODS === #
|
||||
|
||||
@api.onchange('active', 'provider_ids', 'support_tokenization')
|
||||
def _onchange_warn_before_disabling_tokens(self):
|
||||
""" Display a warning about the consequences of archiving the payment method, detaching it
|
||||
from a provider, or removing its support for tokenization.
|
||||
|
||||
Let the user know that the related tokens will be archived.
|
||||
|
||||
:return: A client action with the warning message, if any.
|
||||
:rtype: dict
|
||||
"""
|
||||
disabling = self._origin.active and not self.active
|
||||
detached_providers = self._origin.provider_ids.filtered(
|
||||
lambda p: p.id not in self.provider_ids.ids
|
||||
) # Cannot use recordset difference operation because self.provider_ids is a set of NewIds.
|
||||
blocking_tokenization = self._origin.support_tokenization and not self.support_tokenization
|
||||
if disabling or detached_providers or blocking_tokenization:
|
||||
related_tokens = self.env['payment.token'].with_context(active_test=True).search(
|
||||
Domain('payment_method_id', 'in', (self._origin + self._origin.brand_ids).ids)
|
||||
& (Domain('provider_id', 'in', detached_providers.ids) if detached_providers else Domain.TRUE),
|
||||
) # Fix `active_test` in the context forwarded by the view.
|
||||
if related_tokens:
|
||||
return {
|
||||
'warning': {
|
||||
'title': _("Warning"),
|
||||
'message': _(
|
||||
"This action will also archive %s tokens that are registered with this "
|
||||
"payment method.", len(related_tokens)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@api.onchange('provider_ids')
|
||||
def _onchange_provider_ids_warn_before_attaching_payment_method(self):
|
||||
""" Display a warning before attaching a payment method to a provider.
|
||||
|
||||
:return: A client action with the warning message, if any.
|
||||
:rtype: dict
|
||||
"""
|
||||
attached_providers = self.provider_ids.filtered(
|
||||
lambda p: p.id.origin not in self._origin.provider_ids.ids
|
||||
)
|
||||
if attached_providers:
|
||||
return {
|
||||
'warning': {
|
||||
'title': _("Warning"),
|
||||
'message': _(
|
||||
"Please make sure that %(payment_method)s is supported by %(provider)s.",
|
||||
payment_method=self.name,
|
||||
provider=', '.join(attached_providers.mapped('name'))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
# === CONSTRAINT METHODS === #
|
||||
|
||||
@api.constrains('active', 'support_manual_capture')
|
||||
def _check_manual_capture_supported_by_providers(self):
|
||||
incompatible_pms = self.filtered(
|
||||
lambda pm:
|
||||
pm.active
|
||||
and (pm.primary_payment_method_id or pm).support_manual_capture == 'none'
|
||||
and any(provider.capture_manually for provider in pm.provider_ids),
|
||||
)
|
||||
if incompatible_pms:
|
||||
raise ValidationError(_(
|
||||
"The following payment methods cannot be enabled because their payment provider has"
|
||||
" manual capture activated: %s", ", ".join(incompatible_pms.mapped('name'))
|
||||
))
|
||||
|
||||
# === CRUD METHODS === #
|
||||
|
||||
def write(self, vals):
|
||||
# Handle payment methods being archived, detached from providers, or blocking tokenization.
|
||||
archiving = vals.get('active') is False
|
||||
detached_provider_ids = [
|
||||
v[0] for command, *v in vals['provider_ids'] if command == Command.UNLINK
|
||||
] if 'provider_ids' in vals else []
|
||||
blocking_tokenization = vals.get('support_tokenization') is False
|
||||
if archiving or detached_provider_ids or blocking_tokenization:
|
||||
linked_tokens = self.env['payment.token'].with_context(active_test=True).search(
|
||||
Domain('payment_method_id', 'in', (self + self.brand_ids).ids)
|
||||
& (Domain('provider_id', 'in', detached_provider_ids) if detached_provider_ids else Domain.TRUE),
|
||||
) # Fix `active_test` in the context forwarded by the view.
|
||||
linked_tokens.active = False
|
||||
|
||||
# Prevent enabling a payment method if it is not linked to an enabled provider.
|
||||
if vals.get('active'):
|
||||
for pm in self:
|
||||
primary_pm = pm if pm.is_primary else pm.primary_payment_method_id
|
||||
if (
|
||||
not primary_pm.active # Don't bother for already enabled payment methods.
|
||||
and all(p.state == 'disabled' for p in primary_pm.provider_ids)
|
||||
):
|
||||
raise UserError(_(
|
||||
"This payment method needs a partner in crime; you should enable a payment"
|
||||
" provider supporting this method first."
|
||||
))
|
||||
|
||||
return super().write(vals)
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _unlink_if_not_default_payment_method(self):
|
||||
payment_method_unknown = self.env.ref('payment.payment_method_unknown')
|
||||
if payment_method_unknown in self:
|
||||
raise UserError(_("You cannot delete the default payment method."))
|
||||
|
||||
# === BUSINESS METHODS === #
|
||||
|
||||
def _get_compatible_payment_methods(
|
||||
self, provider_ids, partner_id, currency_id=None, force_tokenization=False,
|
||||
is_express_checkout=False, report=None, **kwargs
|
||||
):
|
||||
""" Search and return the payment methods matching the compatibility criteria.
|
||||
|
||||
The compatibility criteria are that payment methods must: be supported by at least one of
|
||||
the providers; support the country of the partner if it exists; be primary payment methods
|
||||
(not a brand). If provided, the optional keyword arguments further refine the criteria.
|
||||
|
||||
:param list provider_ids: The list of providers by which the payment methods must be at
|
||||
least partially supported to be considered compatible, as a list
|
||||
of `payment.provider` ids.
|
||||
:param int partner_id: The partner making the payment, as a `res.partner` id.
|
||||
:param int currency_id: The payment currency, if known beforehand, as a `res.currency` id.
|
||||
:param bool force_tokenization: Whether only payment methods supporting tokenization can be
|
||||
matched.
|
||||
:param bool is_express_checkout: Whether the payment is made through express checkout.
|
||||
:param dict report: The report in which each provider's availability status and reason must
|
||||
be logged.
|
||||
:param dict kwargs: Optional data. This parameter is not used here.
|
||||
:return: The compatible payment methods.
|
||||
:rtype: payment.method
|
||||
"""
|
||||
# Search compatible payment methods with the base domain.
|
||||
payment_methods = self.env['payment.method'].search([('is_primary', '=', True)])
|
||||
payment_utils.add_to_report(report, payment_methods)
|
||||
|
||||
# Filter by compatible providers.
|
||||
unfiltered_pms = payment_methods
|
||||
payment_methods = payment_methods.filtered(
|
||||
lambda pm: any(p in provider_ids for p in pm.provider_ids.ids)
|
||||
)
|
||||
payment_utils.add_to_report(
|
||||
report,
|
||||
unfiltered_pms - payment_methods,
|
||||
available=False,
|
||||
reason=REPORT_REASONS_MAPPING['provider_not_available'],
|
||||
)
|
||||
|
||||
# Handle the partner country; allow all countries if the list is empty.
|
||||
partner = self.env['res.partner'].browse(partner_id)
|
||||
if partner.country_id: # The partner country must either not be set or be supported.
|
||||
unfiltered_pms = payment_methods
|
||||
payment_methods = payment_methods.filtered(
|
||||
lambda pm: (
|
||||
not pm.supported_country_ids
|
||||
or partner.country_id.id in pm.supported_country_ids.ids
|
||||
)
|
||||
)
|
||||
payment_utils.add_to_report(
|
||||
report,
|
||||
unfiltered_pms - payment_methods,
|
||||
available=False,
|
||||
reason=REPORT_REASONS_MAPPING['incompatible_country'],
|
||||
)
|
||||
|
||||
# Handle the supported currencies; allow all currencies if the list is empty.
|
||||
if currency_id:
|
||||
unfiltered_pms = payment_methods
|
||||
payment_methods = payment_methods.filtered(
|
||||
lambda pm: (
|
||||
not pm.supported_currency_ids
|
||||
or currency_id in pm.supported_currency_ids.ids
|
||||
)
|
||||
)
|
||||
payment_utils.add_to_report(
|
||||
report,
|
||||
unfiltered_pms - payment_methods,
|
||||
available=False,
|
||||
reason=REPORT_REASONS_MAPPING['incompatible_currency'],
|
||||
)
|
||||
|
||||
# Handle tokenization support requirements.
|
||||
if force_tokenization:
|
||||
unfiltered_pms = payment_methods
|
||||
payment_methods = payment_methods.filtered('support_tokenization')
|
||||
payment_utils.add_to_report(
|
||||
report,
|
||||
unfiltered_pms - payment_methods,
|
||||
available=False,
|
||||
reason=REPORT_REASONS_MAPPING['tokenization_not_supported'],
|
||||
)
|
||||
|
||||
# Handle express checkout.
|
||||
if is_express_checkout:
|
||||
unfiltered_pms = payment_methods
|
||||
payment_methods = payment_methods.filtered('support_express_checkout')
|
||||
payment_utils.add_to_report(
|
||||
report,
|
||||
unfiltered_pms - payment_methods,
|
||||
available=False,
|
||||
reason=REPORT_REASONS_MAPPING['express_checkout_not_supported'],
|
||||
)
|
||||
|
||||
return payment_methods
|
||||
|
||||
def _get_from_code(self, code, mapping=None):
|
||||
""" Get the payment method corresponding to the given provider-specific code.
|
||||
|
||||
If a mapping is given, the search uses the generic payment method code that corresponds to
|
||||
the given provider-specific code.
|
||||
|
||||
:param str code: The provider-specific code of the payment method to get.
|
||||
:param dict mapping: A non-exhaustive mapping of generic payment method codes to
|
||||
provider-specific codes.
|
||||
:return: The corresponding payment method, if any.
|
||||
:rtype: payment.method
|
||||
"""
|
||||
generic_to_specific_mapping = mapping or {}
|
||||
specific_to_generic_mapping = {v: k for k, v in generic_to_specific_mapping.items()}
|
||||
return self.search([('code', '=', specific_to_generic_mapping.get(code, code))], limit=1)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,37 +1,53 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
class PaymentToken(models.Model):
|
||||
_name = 'payment.token'
|
||||
_order = 'partner_id, id desc'
|
||||
_description = 'Payment Token'
|
||||
_check_company_auto = True
|
||||
_rec_names_search = ['payment_details', 'partner_id', 'provider_id']
|
||||
|
||||
provider_id = fields.Many2one(string="Provider", comodel_name='payment.provider', required=True)
|
||||
provider_code = fields.Selection(related='provider_id.code')
|
||||
provider_code = fields.Selection(string="Provider Code", related='provider_id.code')
|
||||
company_id = fields.Many2one(
|
||||
related='provider_id.company_id', store=True, index=True
|
||||
) # Indexed to speed-up ORM searches (from ir_rule or others).
|
||||
payment_method_id = fields.Many2one(
|
||||
string="Payment Method", comodel_name='payment.method', readonly=True, required=True
|
||||
)
|
||||
payment_method_code = fields.Char(
|
||||
string="Payment Method Code", related='payment_method_id.code'
|
||||
)
|
||||
payment_details = fields.Char(
|
||||
string="Payment Details", help="The clear part of the payment method's payment details.",
|
||||
)
|
||||
partner_id = fields.Many2one(string="Partner", comodel_name='res.partner', required=True)
|
||||
company_id = fields.Many2one( # Indexed to speed-up ORM searches (from ir_rule or others)
|
||||
related='provider_id.company_id', store=True, index=True)
|
||||
partner_id = fields.Many2one(string="Partner", comodel_name='res.partner', required=True, index=True)
|
||||
provider_ref = fields.Char(
|
||||
string="Provider Reference", help="The provider reference of the token of the transaction",
|
||||
required=True) # This is not the same thing as the provider reference of the transaction.
|
||||
string="Provider Reference",
|
||||
help="The provider reference of the token of the transaction.",
|
||||
required=True,
|
||||
) # This is not the same thing as the provider reference of the transaction.
|
||||
transaction_ids = fields.One2many(
|
||||
string="Payment Transactions", comodel_name='payment.transaction', inverse_name='token_id')
|
||||
verified = fields.Boolean(string="Verified")
|
||||
string="Payment Transactions", comodel_name='payment.transaction', inverse_name='token_id'
|
||||
)
|
||||
active = fields.Boolean(string="Active", default=True)
|
||||
|
||||
#=== CRUD METHODS ===#
|
||||
# === COMPUTE METHODS === #
|
||||
|
||||
@api.depends('payment_details', 'create_date')
|
||||
def _compute_display_name(self):
|
||||
for token in self:
|
||||
token.display_name = token._build_display_name()
|
||||
|
||||
# === CRUD METHODS === #
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, values_list):
|
||||
for values in values_list:
|
||||
def create(self, vals_list):
|
||||
for values in vals_list:
|
||||
if 'provider_id' in values:
|
||||
provider = self.env['payment.provider'].browse(values['provider_id'])
|
||||
|
||||
|
|
@ -40,7 +56,7 @@ class PaymentToken(models.Model):
|
|||
else:
|
||||
pass # Let psycopg warn about the missing required field.
|
||||
|
||||
return super().create(values_list)
|
||||
return super().create(vals_list)
|
||||
|
||||
@api.model
|
||||
def _get_specific_create_values(self, provider_code, values):
|
||||
|
|
@ -57,22 +73,36 @@ class PaymentToken(models.Model):
|
|||
"""
|
||||
return dict()
|
||||
|
||||
def write(self, values):
|
||||
def write(self, vals):
|
||||
""" Prevent unarchiving tokens and handle their archiving.
|
||||
|
||||
:return: The result of the call to the parent method.
|
||||
:rtype: bool
|
||||
:raise UserError: If at least one token is being unarchived.
|
||||
"""
|
||||
if 'active' in values:
|
||||
if values['active']:
|
||||
if any(not token.active for token in self):
|
||||
raise UserError(_("A token cannot be unarchived once it has been archived."))
|
||||
if 'active' in vals:
|
||||
if vals['active']:
|
||||
if any(
|
||||
not token.payment_method_id.active
|
||||
or token.provider_id.state == 'disabled'
|
||||
for token in self
|
||||
):
|
||||
raise UserError(_(
|
||||
"You can't unarchive tokens linked to inactive payment methods or disabled"
|
||||
" providers."
|
||||
))
|
||||
else:
|
||||
# Call the handlers in sudo mode because this method might have been called by RPC.
|
||||
self.filtered('active').sudo()._handle_archiving()
|
||||
|
||||
return super().write(values)
|
||||
return super().write(vals)
|
||||
|
||||
@api.constrains('partner_id')
|
||||
def _check_partner_is_never_public(self):
|
||||
""" Check that the partner associated with the token is never public. """
|
||||
for token in self:
|
||||
if token.partner_id.is_public:
|
||||
raise ValidationError(_("No token can be assigned to the public partner."))
|
||||
|
||||
def _handle_archiving(self):
|
||||
""" Handle the archiving of tokens.
|
||||
|
|
@ -84,10 +114,32 @@ class PaymentToken(models.Model):
|
|||
"""
|
||||
return
|
||||
|
||||
def name_get(self):
|
||||
return [(token.id, token._build_display_name()) for token in self]
|
||||
# === BUSINESS METHODS === #
|
||||
|
||||
#=== BUSINESS METHODS ===#
|
||||
def _get_available_tokens(self, providers_ids, partner_id, is_validation=False, **kwargs):
|
||||
""" Return the available tokens linked to the given providers and partner.
|
||||
|
||||
For a module to retrieve the available tokens, it must override this method and add
|
||||
information in the kwargs to define the context of the request.
|
||||
|
||||
:param list providers_ids: The ids of the providers available for the transaction.
|
||||
:param int partner_id: The id of the partner.
|
||||
:param bool is_validation: Whether the transaction is a validation operation.
|
||||
:param dict kwargs: Locally unused keywords arguments.
|
||||
:return: The available tokens.
|
||||
:rtype: payment.token
|
||||
"""
|
||||
if not is_validation:
|
||||
return self.env['payment.token'].search(
|
||||
[('provider_id', 'in', providers_ids), ('partner_id', '=', partner_id)]
|
||||
)
|
||||
else:
|
||||
# Get all the tokens of the partner and of their commercial partner, regardless of
|
||||
# whether the providers are available.
|
||||
partner = self.env['res.partner'].browse(partner_id)
|
||||
return self.env['payment.token'].search(
|
||||
[('partner_id', 'in', [partner.id, partner.commercial_partner_id.id])]
|
||||
)
|
||||
|
||||
def _build_display_name(self, *args, max_length=34, should_pad=True, **kwargs):
|
||||
""" Build a token name of the desired maximum length with the format `•••• 1234`.
|
||||
|
|
@ -112,6 +164,9 @@ class PaymentToken(models.Model):
|
|||
"""
|
||||
self.ensure_one()
|
||||
|
||||
if not self.create_date:
|
||||
return ''
|
||||
|
||||
padding_length = max_length - len(self.payment_details or '')
|
||||
if not self.payment_details:
|
||||
create_date_str = self.create_date.strftime('%Y/%m/%d')
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,72 +1,24 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
payment_provider_onboarding_state = fields.Selection(
|
||||
string="State of the onboarding payment provider step",
|
||||
selection=[('not_done', "Not done"), ('just_done', "Just done"), ('done', "Done")],
|
||||
default='not_done')
|
||||
payment_onboarding_payment_method = fields.Selection(
|
||||
string="Selected onboarding payment method",
|
||||
selection=[
|
||||
('paypal', "PayPal"),
|
||||
('stripe', "Stripe"),
|
||||
('manual', "Manual"),
|
||||
('other', "Other"),
|
||||
])
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
companies = super().create(vals_list)
|
||||
|
||||
def _run_payment_onboarding_step(self, menu_id):
|
||||
""" Install the suggested payment modules and configure the providers.
|
||||
|
||||
It's checked that the current company has a Chart of Account.
|
||||
|
||||
:param int menu_id: The menu from which the user started the onboarding step, as an
|
||||
`ir.ui.menu` id
|
||||
:return: The action returned by `action_stripe_connect_account`
|
||||
:rtype: dict
|
||||
"""
|
||||
self.env.company.get_chart_of_accounts_or_fail()
|
||||
|
||||
self._install_modules(['payment_stripe', 'account_payment'])
|
||||
|
||||
# Create a new env including the freshly installed module(s)
|
||||
new_env = api.Environment(self.env.cr, self.env.uid, self.env.context)
|
||||
|
||||
# Configure Stripe
|
||||
default_journal = new_env['account.journal'].search(
|
||||
[('type', '=', 'bank'), ('company_id', '=', new_env.company.id)], limit=1
|
||||
# Duplicate installed providers in the new companies.
|
||||
providers_sudo = self.env['payment.provider'].sudo().search(
|
||||
[('company_id', '=', self.env.user.company_id.id), ('module_state', '=', 'installed')]
|
||||
)
|
||||
for company in companies:
|
||||
if company.parent_id: # The company is a branch.
|
||||
continue # Only consider top-level companies for provider duplication.
|
||||
|
||||
stripe_provider = new_env['payment.provider'].search(
|
||||
[('company_id', '=', self.env.company.id), ('code', '=', 'stripe')], limit=1
|
||||
)
|
||||
if not stripe_provider:
|
||||
base_provider = self.env.ref('payment.payment_provider_stripe')
|
||||
# Use sudo to access payment provider record that can be in different company.
|
||||
stripe_provider = base_provider.sudo().with_context(
|
||||
stripe_connect_onboarding=True,
|
||||
).copy(default={'company_id': self.env.company.id})
|
||||
stripe_provider.journal_id = stripe_provider.journal_id or default_journal
|
||||
for provider_sudo in providers_sudo:
|
||||
provider_sudo.copy({'company_id': company.id})
|
||||
|
||||
return stripe_provider.action_stripe_connect_account(menu_id=menu_id)
|
||||
|
||||
def _install_modules(self, module_names):
|
||||
modules_sudo = self.env['ir.module.module'].sudo().search([('name', 'in', module_names)])
|
||||
STATES = ['installed', 'to install', 'to upgrade']
|
||||
modules_sudo.filtered(lambda m: m.state not in STATES).button_immediate_install()
|
||||
|
||||
def _mark_payment_onboarding_step_as_done(self):
|
||||
""" Mark the payment onboarding step as done.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.set_onboarding_step_done('payment_provider_onboarding_state')
|
||||
|
||||
def get_account_invoice_onboarding_steps_states_names(self):
|
||||
""" Override of account. """
|
||||
steps = super().get_account_invoice_onboarding_steps_states_names()
|
||||
return steps + ['payment_provider_onboarding_state']
|
||||
return companies
|
||||
|
|
|
|||
32
odoo-bringout-oca-ocb-payment/payment/models/res_country.py
Normal file
32
odoo-bringout-oca-ocb-payment/payment/models/res_country.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
# Import the payment provider modules with aliases to avoid circular import errors.
|
||||
try:
|
||||
import odoo.addons.payment_mercado_pago as mercado_pago
|
||||
import odoo.addons.payment_stripe as stripe
|
||||
except ModuleNotFoundError:
|
||||
mercado_pago = None
|
||||
stripe = None
|
||||
|
||||
|
||||
class ResCountry(models.Model):
|
||||
_inherit = 'res.country'
|
||||
|
||||
is_mercado_pago_supported_country = fields.Boolean(compute='_compute_provider_support')
|
||||
is_stripe_supported_country = fields.Boolean(compute='_compute_provider_support')
|
||||
|
||||
@api.depends('code')
|
||||
def _compute_provider_support(self):
|
||||
for country in self:
|
||||
country.is_stripe_supported_country = (
|
||||
stripe is not None
|
||||
and stripe.const.COUNTRY_MAPPING.get(
|
||||
country.code, country.code
|
||||
) in stripe.const.SUPPORTED_COUNTRIES
|
||||
)
|
||||
country.is_mercado_pago_supported_country = (
|
||||
mercado_pago
|
||||
and country.code in mercado_pago.const.SUPPORTED_COUNTRIES
|
||||
)
|
||||
|
|
@ -14,9 +14,8 @@ class ResPartner(models.Model):
|
|||
@api.depends('payment_token_ids')
|
||||
def _compute_payment_token_count(self):
|
||||
payments_data = self.env['payment.token']._read_group(
|
||||
[('partner_id', 'in', self.ids)], ['partner_id'], ['partner_id']
|
||||
[('partner_id', 'in', self.ids)], ['partner_id'], ['__count'],
|
||||
)
|
||||
partners_data = {payment_data['partner_id'][0]: payment_data['partner_id_count']
|
||||
for payment_data in payments_data}
|
||||
partners_data = {partner.id: count for partner, count in payments_data}
|
||||
for partner in self:
|
||||
partner.payment_token_count = partners_data.get(partner.id, 0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue