Files
odoo_source/addons/payment/utils.py
T
Antoine Vandevenne (anv)andToufik Ben Jaa 1969d48f15 [FIX] payment: don't send customer IP address when running a cron
If a payment request is made from a cron (e.g., Subscriptions' cron for
recurring payments), the worker is not bound to an HTTP request and
therefore the value of `request.httprequest.remote_addr` is an instance
of the 'LocalProxy' object, which is not serializable.
If we're in this scenario, don't send the customer IP address.

task-2494916

closes odoo/odoo#71717

X-original-commit: e85464ad2281f25cbaf7ded0c1e5d7659a322696
Signed-off-by: Antoine Vandevenne (anv) <AntoineVDV@users.noreply.github.com>
Co-authored-by: Toufik Ben Jaa <tbe@odoo.com>
2021-06-04 06:54:28 +00:00

159 lines
6.4 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields
from odoo.http import request
from odoo.tools import consteq, float_round, ustr
from odoo.tools.misc import hmac as hmac_tool
# Access token management
def generate_access_token(*values):
""" Generate an access token based on the provided values.
The token allows to later verify the validity of a request, based on a given set of values.
These will generally include the partner id, amount, currency id, transaction id or transaction
reference.
All values must be convertible to a string.
:param list values: The values to use for the generation of the token
:return: The generated access token
:rtype: str
"""
token_str = '|'.join(str(val) for val in values)
access_token = hmac_tool(request.env(su=True), 'generate_access_token', token_str)
return access_token
def check_access_token(access_token, *values):
""" Check the validity of the access token for the provided values.
The values must be provided in the exact same order as they were to `generate_access_token`.
All values must be convertible to a string.
:param str access_token: The access token used to verify the provided values
:param list values: The values to verify against the token
:return: True if the check is successful
:rtype: bool
"""
authentic_token = generate_access_token(*values)
return access_token and consteq(ustr(access_token), authentic_token)
# Transaction values formatting
def singularize_reference_prefix(prefix='tx', separator='-', max_length=None):
""" Make the prefix more unique by suffixing it with the current datetime.
When the prefix is a placeholder that would be part of a large sequence of references sharing
the same prefix, such as "tx" or "validation", singularizing it allows to make it part of a
single-element sequence of transactions. The computation of the full reference will then execute
faster by failing to find existing references with a matching prefix.
If the `max_length` argument is passed, the end of the prefix can be stripped before
singularizing to ensure that the result accounts for no more than `max_length` characters.
:param str prefix: The custom prefix to singularize
:param str separator: The custom separator used to separate the prefix from the suffix
:param int max_length: The maximum length of the singularized prefix
:return: The singularized prefix
:rtype: str
"""
if prefix is None:
prefix = 'tx'
if max_length:
DATETIME_LENGTH = 14
assert max_length >= 1 + len(separator) + DATETIME_LENGTH # 1 char + separator + datetime
prefix = prefix[:max_length-len(separator)-DATETIME_LENGTH]
return f'{prefix}{separator}{fields.Datetime.now().strftime("%Y%m%d%H%M%S")}'
def to_major_currency_units(minor_amount, currency, arbitrary_decimal_number=None):
""" Return the amount converted to the major units of its currency.
The conversion is done by dividing the amount by 10^k where k is the number of decimals of the
currency as per the ISO 4217 norm.
To force a different number of decimals, set it as the value of the `decimal_number` argument.
:param float minor_amount: The amount in minor units, to convert in major units
:param recordset currency: The currency of the amount, as a `res.currency` record
:param int arbitrary_decimal_number: The number of decimals to use instead of that of ISO 4217
:return: The amount in major units of its currency
:rtype: int
"""
currency.ensure_one()
if arbitrary_decimal_number is None:
decimal_number = currency.decimal_places
else:
decimal_number = arbitrary_decimal_number
return float_round(minor_amount, 0) / (10**decimal_number)
def to_minor_currency_units(major_amount, currency, arbitrary_decimal_number=None):
""" Return the amount converted to the minor units of its currency.
The conversion is done by multiplying the amount by 10^k where k is the number of decimals of
the currency as per the ISO 4217 norm.
To force a different number of decimals, set it as the value of the `decimal_number` argument.
Note: currency.ensure_one() if arbitrary_decimal_number is not provided
:param float major_amount: The amount in major units, to convert in minor units
:param recordset currency: The currency of the amount, as a `res.currency` record
:param int arbitrary_decimal_number: The number of decimals to use instead of that of ISO 4217
:return: The amount in minor units of its currency
:rtype: int
"""
if arbitrary_decimal_number is not None:
decimal_number = arbitrary_decimal_number
else:
currency.ensure_one()
decimal_number = currency.decimal_places
return int(float_round(major_amount, decimal_number) * (10**decimal_number))
# Token values formatting
def build_token_name(payment_details_short=None, final_length=16):
""" Pad plain payment details with leading X's to build a token name of the desired length.
:param str payment_details_short: The plain part of the payment details (usually last 4 digits)
:param int final_length: The desired final length of the token name (16 for a bank card)
:return: The padded token name
:rtype: str
"""
payment_details_short = payment_details_short or '????'
return f"{'X' * (final_length - len(payment_details_short))}{payment_details_short}"
# Partner values formatting
def format_partner_address(address1="", address2=""):
""" Format a two-parts partner address into a one-line address string.
:param str address1: The first part of the address, usually the `street1` field
:param str address2: The second part of the address, usually the `street2` field
:return: The formatted one-line address
:rtype: str
"""
address1 = address1 or "" # Avoid casting as "False"
address2 = address2 or "" # Avoid casting as "False"
return f"{address1} {address2}".strip()
def split_partner_name(partner_name):
""" Split a single-line partner name in a tuple of first name, last name.
:param str partner_name: The partner name
:return: The splitted first name and last name
:rtype: tuple
"""
return " ".join(partner_name.split()[:-1]), partner_name.split()[-1]
# Security
def get_customer_ip_address():
return request and request.httprequest.remote_addr or ''