feat: hide authorizer for rental orders, auto-set sale type

Rental orders no longer show the "Authorizer Required?" question or
the Authorizer field. The sale type is automatically set to 'Rentals'
when creating or confirming a rental order. Validation logic also
skips authorizer checks for rental sale type.

Made-with: Cursor
This commit is contained in:
gsinghpal
2026-02-25 23:33:23 -05:00
parent 3c8f83b8e6
commit 14fe9ab716
51 changed files with 4192 additions and 822 deletions

View File

@@ -9,6 +9,7 @@
'description': " ",
'depends': ['payment', 'account_payment', 'sale'],
'data': [
'security/security.xml',
'security/ir.model.access.csv',
'report/poynt_receipt_report.xml',

View File

@@ -253,6 +253,53 @@ class PaymentProvider(models.Model):
return result
# === BUSINESS METHODS - TOKENIZE / CHARGE === #
def _poynt_tokenize_nonce(self, nonce):
"""Exchange a Poynt Collect nonce for a long-lived payment token JWT.
:param str nonce: The one-time nonce from Poynt Collect JS.
:return: The tokenize response containing card details, cardId,
paymentToken (JWT), and AVS/CVV verification results.
:rtype: dict
:raises ValidationError: If the tokenize call fails.
"""
self.ensure_one()
return self._poynt_make_request(
'POST',
'cards/tokenize',
payload={'nonce': nonce},
)
def _poynt_charge_token(self, payment_jwt, amount, currency,
action='SALE', reference=''):
"""Charge a stored payment token JWT via the tokenize/charge endpoint.
:param str payment_jwt: The payment token JWT from _poynt_tokenize_nonce.
:param float amount: The charge amount in major currency units.
:param recordset currency: The currency record.
:param str action: SALE or AUTHORIZE (default SALE).
:param str reference: Optional reference note for the transaction.
:return: The transaction result dict from Poynt.
:rtype: dict
:raises ValidationError: If the charge fails.
"""
self.ensure_one()
payload = poynt_utils.build_token_charge_payload(
action=action,
amount=amount,
currency=currency,
payment_jwt=payment_jwt,
business_id=self.poynt_business_id,
store_id=self.poynt_store_id or '',
reference=reference,
)
return self._poynt_make_request(
'POST',
'cards/tokenize/charge',
payload=payload,
)
# === BUSINESS METHODS - INLINE FORM === #
def _poynt_get_inline_form_values(self, amount, currency, partner_id, is_validation,

View File

@@ -16,6 +16,13 @@ class PaymentToken(models.Model):
help="The unique card identifier stored on the Poynt platform.",
readonly=True,
)
poynt_payment_token = fields.Char(
string="Poynt Payment Token (JWT)",
help="Long-lived JWT issued by Poynt /cards/tokenize, used for "
"recurring charges via /cards/tokenize/charge.",
readonly=True,
groups='base.group_system',
)
def _poynt_validate_stored_card(self):
"""Validate that the stored card is still usable on Poynt.
@@ -35,7 +42,7 @@ class PaymentToken(models.Model):
)
try:
result = self.provider_id._poynt_make_request(
result = self.provider_id.sudo()._poynt_make_request(
'GET',
f'cards/{self.poynt_card_id}',
)

View File

@@ -48,6 +48,9 @@ class PaymentTransaction(models.Model):
copy=False,
)
def _get_provider_sudo(self):
return self.provider_id.sudo()
# === BUSINESS METHODS - PAYMENT FLOW === #
def _get_specific_processing_values(self, processing_values):
@@ -64,7 +67,8 @@ class PaymentTransaction(models.Model):
poynt_data = self._poynt_create_order_and_authorize()
base_url = self.provider_id.get_base_url()
provider = self._get_provider_sudo()
base_url = provider.get_base_url()
return_url = url_join(
base_url,
f'{PoyntController._return_url}?{url_encode({"reference": self.reference})}',
@@ -74,8 +78,8 @@ class PaymentTransaction(models.Model):
'poynt_order_id': poynt_data.get('order_id', ''),
'poynt_transaction_id': poynt_data.get('transaction_id', ''),
'return_url': return_url,
'business_id': self.provider_id.poynt_business_id,
'is_test': self.provider_id.state == 'test',
'business_id': provider.poynt_business_id,
'is_test': provider.state == 'test',
}
def _send_payment_request(self):
@@ -104,26 +108,29 @@ class PaymentTransaction(models.Model):
:rtype: dict
"""
try:
provider = self._get_provider_sudo()
order_payload = poynt_utils.build_order_payload(
self.reference, self.amount, self.currency_id,
business_id=self.provider_id.poynt_business_id,
store_id=self.provider_id.poynt_store_id or '',
business_id=provider.poynt_business_id,
store_id=provider.poynt_store_id or '',
)
order_result = self.provider_id._poynt_make_request(
order_result = provider._poynt_make_request(
'POST', 'orders', payload=order_payload,
)
order_id = order_result.get('id', '')
self.poynt_order_id = order_id
action = 'AUTHORIZE' if self.provider_id.capture_manually else 'SALE'
action = 'AUTHORIZE' if provider.capture_manually else 'SALE'
txn_payload = poynt_utils.build_transaction_payload(
action=action,
amount=self.amount,
currency=self.currency_id,
order_id=order_id,
reference=self.reference,
business_id=provider.poynt_business_id,
store_id=provider.poynt_store_id or '',
)
txn_result = self.provider_id._poynt_make_request(
txn_result = provider._poynt_make_request(
'POST', 'transactions', payload=txn_payload,
)
@@ -144,46 +151,68 @@ class PaymentTransaction(models.Model):
def _poynt_process_token_payment(self):
"""Process a payment using a stored token (card on file).
For token-based payments we send a SALE or AUTHORIZE using the
stored card ID from the payment token.
Uses the JWT payment token via POST /cards/tokenize/charge when
available. Falls back to the legacy cardId flow for tokens that
were created before the JWT migration.
"""
try:
action = 'AUTHORIZE' if self.provider_id.capture_manually else 'SALE'
provider = self._get_provider_sudo()
action = 'AUTHORIZE' if provider.capture_manually else 'SALE'
payment_jwt = self.token_id.poynt_payment_token
funding_source = {
'type': 'CREDIT_DEBIT',
'card': {
'cardId': self.token_id.poynt_card_id,
},
}
order_payload = poynt_utils.build_order_payload(
self.reference, self.amount, self.currency_id,
business_id=self.provider_id.poynt_business_id,
store_id=self.provider_id.poynt_store_id or '',
)
order_result = self.provider_id._poynt_make_request(
'POST', 'orders', payload=order_payload,
)
order_id = order_result.get('id', '')
self.poynt_order_id = order_id
txn_payload = poynt_utils.build_transaction_payload(
action=action,
amount=self.amount,
currency=self.currency_id,
order_id=order_id,
reference=self.reference,
funding_source=funding_source,
)
txn_result = self.provider_id._poynt_make_request(
'POST', 'transactions', payload=txn_payload,
)
if payment_jwt:
txn_result = provider._poynt_charge_token(
payment_jwt=payment_jwt,
amount=self.amount,
currency=self.currency_id,
action=action,
reference=self.reference,
)
else:
funding_source = {
'type': 'CREDIT_DEBIT',
'card': {
'cardId': self.token_id.poynt_card_id,
},
'entryDetails': {
'customerPresenceStatus': 'MOTO',
'entryMode': 'KEYED',
},
}
order_payload = poynt_utils.build_order_payload(
self.reference, self.amount, self.currency_id,
business_id=provider.poynt_business_id,
store_id=provider.poynt_store_id or '',
)
order_result = provider._poynt_make_request(
'POST', 'orders', payload=order_payload,
)
order_id = order_result.get('id', '')
self.poynt_order_id = order_id
txn_payload = poynt_utils.build_transaction_payload(
action=action,
amount=self.amount,
currency=self.currency_id,
order_id=order_id,
reference=self.reference,
funding_source=funding_source,
business_id=provider.poynt_business_id,
store_id=provider.poynt_store_id or '',
)
txn_result = provider._poynt_make_request(
'POST', 'transactions', payload=txn_payload,
)
transaction_id = txn_result.get('id', '')
self.poynt_transaction_id = transaction_id
self.provider_reference = transaction_id
order_id = txn_result.get('orderIdFromTransaction', '') or \
txn_result.get('orderId', '') or \
getattr(self, 'poynt_order_id', '') or ''
if order_id:
self.poynt_order_id = order_id
payment_data = {
'reference': self.reference,
'poynt_order_id': order_id,
@@ -211,8 +240,9 @@ class PaymentTransaction(models.Model):
parent_txn_id = source_tx.poynt_transaction_id or source_tx.provider_reference
provider = self._get_provider_sudo()
try:
txn_data = self.provider_id._poynt_make_request(
txn_data = provider._poynt_make_request(
'GET', f'transactions/{parent_txn_id}',
)
for link in txn_data.get('links', []):
@@ -248,7 +278,7 @@ class PaymentTransaction(models.Model):
'notes': f'Refund for {source_tx.reference}',
}
result = self.provider_id._poynt_make_request(
result = provider._poynt_make_request(
'POST', 'transactions', payload=refund_payload,
)
@@ -287,7 +317,7 @@ class PaymentTransaction(models.Model):
},
}
result = self.provider_id._poynt_make_request(
result = self._get_provider_sudo()._poynt_make_request(
'POST', 'transactions', payload=capture_payload,
)
@@ -313,7 +343,7 @@ class PaymentTransaction(models.Model):
txn_id = source_tx.provider_reference or source_tx.poynt_transaction_id
try:
result = self.provider_id._poynt_make_request(
result = self._get_provider_sudo()._poynt_make_request(
'POST', f'transactions/{txn_id}/void',
)
@@ -580,17 +610,18 @@ class PaymentTransaction(models.Model):
return super()._create_payment(**extra_create_values)
self.ensure_one()
provider = self._get_provider_sudo()
reference = f'{self.reference} - {self.provider_reference or ""}'
payment_method_line = self.provider_id.journal_id.inbound_payment_method_line_ids\
.filtered(lambda l: l.payment_provider_id == self.provider_id)
payment_method_line = provider.journal_id.inbound_payment_method_line_ids\
.filtered(lambda l: l.payment_provider_id == provider)
payment_values = {
'amount': abs(self.amount),
'payment_type': 'inbound' if self.amount > 0 else 'outbound',
'currency_id': self.currency_id.id,
'partner_id': self.partner_id.commercial_partner_id.id,
'partner_type': 'customer',
'journal_id': self.provider_id.journal_id.id,
'company_id': self.provider_id.company_id.id,
'journal_id': provider.journal_id.id,
'company_id': provider.company_id.id,
'payment_method_line_id': payment_method_line.id,
'payment_token_id': self.token_id.id,
'payment_transaction_id': self.id,
@@ -608,7 +639,7 @@ class PaymentTransaction(models.Model):
payment = self.env['account.payment'].create(payment_values)
bank_account = self.provider_id.journal_id.default_account_id
bank_account = provider.journal_id.default_account_id
if bank_account and bank_account.account_type == 'asset_cash':
payment.outstanding_account_id = bank_account
@@ -675,7 +706,7 @@ class PaymentTransaction(models.Model):
fields in :attr:`poynt_receipt_data` as a JSON blob."""
txn_data = {}
try:
txn_data = self.provider_id._poynt_make_request(
txn_data = self._get_provider_sudo()._poynt_make_request(
'GET', f'transactions/{self.poynt_transaction_id}',
)
except (ValidationError, Exception):
@@ -757,7 +788,7 @@ class PaymentTransaction(models.Model):
if not invoice:
return
receipt_content = self.provider_id._poynt_fetch_receipt(
receipt_content = self._get_provider_sudo()._poynt_fetch_receipt(
self.poynt_transaction_id,
)
if not receipt_content:

View File

@@ -66,17 +66,21 @@ class PoyntTerminal(models.Model):
# === BUSINESS METHODS === #
def _get_provider_sudo(self):
return self.provider_id.sudo()
def action_refresh_status(self):
"""Refresh the terminal status from Poynt Cloud."""
for terminal in self:
try:
store_id = terminal.store_id_poynt or terminal.provider_id.poynt_store_id
provider = terminal._get_provider_sudo()
store_id = terminal.store_id_poynt or provider.poynt_store_id
if store_id:
endpoint = f'stores/{store_id}/storeDevices/{terminal.device_id}'
else:
endpoint = f'storeDevices/{terminal.device_id}'
result = terminal.provider_id._poynt_make_request('GET', endpoint)
result = provider._poynt_make_request('GET', endpoint)
poynt_status = result.get('status', 'UNKNOWN')
if poynt_status == 'ACTIVATED':
@@ -130,7 +134,8 @@ class PoyntTerminal(models.Model):
if order_id:
payment_request['orderId'] = order_id
store_id = self.store_id_poynt or self.provider_id.poynt_store_id or ''
provider = self._get_provider_sudo()
store_id = self.store_id_poynt or provider.poynt_store_id or ''
data_str = json.dumps({
'action': 'sale',
@@ -142,12 +147,12 @@ class PoyntTerminal(models.Model):
})
try:
result = self.provider_id._poynt_make_request(
result = provider._poynt_make_request(
'POST',
'cloudMessages',
business_scoped=False,
payload={
'businessId': self.provider_id.poynt_business_id,
'businessId': provider.poynt_business_id,
'storeId': store_id,
'deviceId': self.device_id,
'ttl': 300,
@@ -173,7 +178,7 @@ class PoyntTerminal(models.Model):
:return: The full callback URL.
:rtype: str
"""
base_url = self.provider_id.get_base_url()
base_url = self._get_provider_sudo().get_base_url()
return f"{base_url}/payment/poynt/terminal/callback"
def action_check_terminal_payment_status(self, reference):
@@ -188,8 +193,9 @@ class PoyntTerminal(models.Model):
"""
self.ensure_one()
provider = self._get_provider_sudo()
try:
txn_result = self.provider_id._poynt_make_request(
txn_result = provider._poynt_make_request(
'GET',
'transactions',
params={
@@ -201,7 +207,7 @@ class PoyntTerminal(models.Model):
transactions = txn_result.get('transactions', [])
if not transactions:
txn_result = self.provider_id._poynt_make_request(
txn_result = provider._poynt_make_request(
'GET',
'transactions',
params={

View File

@@ -1,7 +1,10 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_poynt_terminal_user,poynt.terminal.user,model_poynt_terminal,base.group_user,1,0,0,0
access_poynt_terminal_admin,poynt.terminal.admin,model_poynt_terminal,base.group_system,1,1,1,1
access_poynt_payment_wizard_user,poynt.payment.wizard.user,model_poynt_payment_wizard,account.group_account_invoice,1,1,1,0
access_poynt_payment_wizard_admin,poynt.payment.wizard.admin,model_poynt_payment_wizard,base.group_system,1,1,1,1
access_poynt_refund_wizard_user,poynt.refund.wizard.user,model_poynt_refund_wizard,account.group_account_invoice,1,1,1,0
access_poynt_refund_wizard_admin,poynt.refund.wizard.admin,model_poynt_refund_wizard,base.group_system,1,1,1,1
access_poynt_terminal_user,poynt.terminal.user,model_poynt_terminal,group_fusion_poynt_user,1,0,0,0
access_poynt_terminal_admin,poynt.terminal.admin,model_poynt_terminal,group_fusion_poynt_admin,1,1,1,1
access_poynt_payment_wizard_user,poynt.payment.wizard.user,model_poynt_payment_wizard,group_fusion_poynt_user,1,1,1,0
access_poynt_payment_wizard_admin,poynt.payment.wizard.admin,model_poynt_payment_wizard,group_fusion_poynt_admin,1,1,1,1
access_poynt_refund_wizard_user,poynt.refund.wizard.user,model_poynt_refund_wizard,group_fusion_poynt_user,1,1,1,0
access_poynt_refund_wizard_admin,poynt.refund.wizard.admin,model_poynt_refund_wizard,group_fusion_poynt_admin,1,1,1,1
access_payment_provider_poynt_user,payment.provider.poynt.user,payment.model_payment_provider,group_fusion_poynt_user,1,0,0,0
access_payment_transaction_poynt_user,payment.transaction.poynt.user,payment.model_payment_transaction,group_fusion_poynt_user,1,1,1,0
access_payment_method_poynt_user,payment.method.poynt.user,payment.model_payment_method,group_fusion_poynt_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_poynt_terminal_user poynt.terminal.user model_poynt_terminal base.group_user group_fusion_poynt_user 1 0 0 0
3 access_poynt_terminal_admin poynt.terminal.admin model_poynt_terminal base.group_system group_fusion_poynt_admin 1 1 1 1
4 access_poynt_payment_wizard_user poynt.payment.wizard.user model_poynt_payment_wizard account.group_account_invoice group_fusion_poynt_user 1 1 1 0
5 access_poynt_payment_wizard_admin poynt.payment.wizard.admin model_poynt_payment_wizard base.group_system group_fusion_poynt_admin 1 1 1 1
6 access_poynt_refund_wizard_user poynt.refund.wizard.user model_poynt_refund_wizard account.group_account_invoice group_fusion_poynt_user 1 1 1 0
7 access_poynt_refund_wizard_admin poynt.refund.wizard.admin model_poynt_refund_wizard base.group_system group_fusion_poynt_admin 1 1 1 1
8 access_payment_provider_poynt_user payment.provider.poynt.user payment.model_payment_provider group_fusion_poynt_user 1 0 0 0
9 access_payment_transaction_poynt_user payment.transaction.poynt.user payment.model_payment_transaction group_fusion_poynt_user 1 1 1 0
10 access_payment_method_poynt_user payment.method.poynt.user payment.model_payment_method group_fusion_poynt_user 1 0 0 0

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ================================================================== -->
<!-- MODULE CATEGORY (required for user settings section rendering) -->
<!-- Odoo 19 organizes privileges by ir.module.category. -->
<!-- Without this, groups fall into the generic Extra Rights list. -->
<!-- ================================================================== -->
<record id="module_category_fusion_poynt" model="ir.module.category">
<field name="name">Fusion Poynt</field>
<field name="sequence">47</field>
</record>
<!-- ================================================================== -->
<!-- FUSION POYNT PRIVILEGE (Odoo 19 pattern) -->
<!-- Linked to module_category_fusion_poynt so all groups appear -->
<!-- under a "FUSION POYNT" section in user settings. -->
<!-- ================================================================== -->
<record id="res_groups_privilege_fusion_poynt" model="res.groups.privilege">
<field name="name">Fusion Poynt</field>
<field name="sequence">47</field>
<field name="category_id" ref="module_category_fusion_poynt"/>
</record>
<!-- ================================================================== -->
<!-- USER GROUP -->
<!-- Can view terminals, collect payments, and send receipts. -->
<!-- Implies base.group_user and account invoice access. -->
<!-- ================================================================== -->
<record id="group_fusion_poynt_user" model="res.groups">
<field name="name">User</field>
<field name="sequence">10</field>
<field name="implied_ids" eval="[(4, ref('base.group_user')), (4, ref('account.group_account_invoice'))]"/>
<field name="privilege_id" ref="res_groups_privilege_fusion_poynt"/>
</record>
<!-- ================================================================== -->
<!-- ADMINISTRATOR GROUP -->
<!-- Full access: configure providers, manage terminals, process -->
<!-- payments, voids, and refunds. -->
<!-- ================================================================== -->
<record id="group_fusion_poynt_admin" model="res.groups">
<field name="name">Administrator</field>
<field name="sequence">20</field>
<field name="privilege_id" ref="res_groups_privilege_fusion_poynt"/>
<field name="implied_ids" eval="[(4, ref('group_fusion_poynt_user'))]"/>
<field name="user_ids" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
</record>
</odoo>

View File

@@ -239,7 +239,8 @@ def build_order_payload(reference, amount, currency, business_id='',
def build_transaction_payload(
action, amount, currency, order_id=None, reference='', funding_source=None
action, amount, currency, order_id=None, reference='',
funding_source=None, business_id='', store_id='',
):
"""Build a Poynt transaction payload for charge/auth/capture.
@@ -249,11 +250,23 @@ def build_transaction_payload(
:param str order_id: The Poynt order UUID (optional).
:param str reference: The Odoo transaction reference.
:param dict funding_source: The funding source / card data (optional).
:param str business_id: The Poynt business UUID (optional).
:param str store_id: The Poynt store UUID (optional).
:return: The Poynt-formatted transaction payload.
:rtype: dict
"""
minor_amount = format_poynt_amount(amount, currency)
context = {
'source': 'WEB',
'sourceApp': 'odoo.fusion_poynt',
'transactionInstruction': 'ONLINE_AUTH_REQUIRED',
}
if business_id:
context['businessId'] = business_id
if store_id:
context['storeId'] = store_id
payload = {
'action': action,
'amounts': {
@@ -263,11 +276,7 @@ def build_transaction_payload(
'cashbackAmount': 0,
'currency': currency.name,
},
'context': {
'source': 'WEB',
'sourceApp': 'odoo.fusion_poynt',
'transactionInstruction': 'ONLINE_AUTH_REQUIRED',
},
'context': context,
'notes': reference,
}
@@ -281,3 +290,44 @@ def build_transaction_payload(
payload['fundingSource'] = funding_source
return payload
def build_token_charge_payload(
action, amount, currency, payment_jwt,
business_id='', store_id='', reference='',
):
"""Build a payload for POST /cards/tokenize/charge.
:param str action: SALE or AUTHORIZE.
:param float amount: Amount in major currency units.
:param recordset currency: Currency record.
:param str payment_jwt: The payment token JWT from /cards/tokenize.
:param str business_id: Poynt business UUID.
:param str store_id: Poynt store UUID.
:param str reference: Optional reference note.
:return: The charge payload dict.
:rtype: dict
"""
minor_amount = format_poynt_amount(amount, currency)
context = {}
if business_id:
context['businessId'] = business_id
if store_id:
context['storeId'] = store_id
payload = {
'action': action,
'context': context,
'amounts': {
'transactionAmount': minor_amount,
'orderAmount': minor_amount,
'currency': currency.name,
},
'fundingSource': {
'cardToken': payment_jwt,
},
}
if reference:
payload['notes'] = reference
return payload

View File

@@ -27,7 +27,7 @@
class="btn-secondary"
icon="fa-credit-card"
invisible="state != 'posted' or payment_state not in ('not_paid', 'partial') or move_type != 'out_invoice'"
groups="account.group_account_invoice"
groups="fusion_poynt.group_fusion_poynt_user"
data-hotkey="p"/>
</xpath>
@@ -39,7 +39,7 @@
class="btn-secondary"
icon="fa-undo"
invisible="state != 'posted' or payment_state not in ('not_paid', 'partial') or move_type != 'out_refund' or poynt_refunded"
groups="account.group_account_invoice"
groups="fusion_poynt.group_fusion_poynt_user"
data-hotkey="r"/>
</xpath>
@@ -51,7 +51,7 @@
class="btn-secondary"
icon="fa-envelope"
invisible="state != 'posted' or move_type != 'out_invoice' or not has_poynt_receipt"
groups="account.group_account_invoice"/>
groups="fusion_poynt.group_fusion_poynt_user"/>
</xpath>
<!-- Resend Receipt button on credit notes (refunded via Poynt) -->
@@ -62,7 +62,7 @@
class="btn-secondary"
icon="fa-envelope"
invisible="state != 'posted' or move_type != 'out_refund' or not poynt_refunded"
groups="account.group_account_invoice"/>
groups="fusion_poynt.group_fusion_poynt_user"/>
</xpath>
<!-- Refunded banner on credit notes -->

View File

@@ -105,6 +105,7 @@
name="Poynt Terminals"
parent="account.root_payment_menu"
action="action_poynt_terminal"
sequence="15"/>
sequence="15"
groups="fusion_poynt.group_fusion_poynt_user"/>
</odoo>

View File

@@ -14,6 +14,7 @@
class="btn-secondary"
icon="fa-credit-card"
invisible="state not in ('sale', 'done')"
groups="fusion_poynt.group_fusion_poynt_user"
data-hotkey="p"/>
</xpath>
</field>

View File

@@ -42,6 +42,11 @@ class PoyntPaymentWizard(models.TransientModel):
required=True,
domain="[('code', '=', 'poynt'), ('state', '!=', 'disabled')]",
)
provider_name = fields.Char(
related='provider_id.name',
string="Poynt Provider",
readonly=True,
)
payment_mode = fields.Selection(
selection=[
@@ -109,7 +114,7 @@ class PoyntPaymentWizard(models.TransientModel):
res['amount'] = invoice.amount_residual
res['currency_id'] = invoice.currency_id.id
provider = self.env['payment.provider'].search([
provider = self.env['payment.provider'].sudo().search([
('code', '=', 'poynt'),
('state', '!=', 'disabled'),
], limit=1)
@@ -120,10 +125,15 @@ class PoyntPaymentWizard(models.TransientModel):
return res
def _get_provider_sudo(self):
return self.provider_id.sudo()
@api.onchange('provider_id')
def _onchange_provider_id(self):
if self.provider_id and self.provider_id.poynt_default_terminal_id:
self.terminal_id = self.provider_id.poynt_default_terminal_id
if self.provider_id:
provider = self._get_provider_sudo()
if provider.poynt_default_terminal_id:
self.terminal_id = provider.poynt_default_terminal_id
def action_collect_payment(self):
"""Dispatch to the appropriate payment method."""
@@ -213,7 +223,8 @@ class PoyntPaymentWizard(models.TransientModel):
},
}
action = 'AUTHORIZE' if self.provider_id.capture_manually else 'SALE'
provider = self._get_provider_sudo()
action = 'AUTHORIZE' if provider.capture_manually else 'SALE'
minor_amount = poynt_utils.format_poynt_amount(
self.amount, self.currency_id,
)
@@ -232,7 +243,7 @@ class PoyntPaymentWizard(models.TransientModel):
'source': 'WEB',
'sourceApp': 'odoo.fusion_poynt',
'transactionInstruction': 'ONLINE_AUTH_REQUIRED',
'businessId': self.provider_id.poynt_business_id,
'businessId': provider.poynt_business_id,
},
'notes': reference,
}
@@ -243,7 +254,7 @@ class PoyntPaymentWizard(models.TransientModel):
'type': 'POYNT_ORDER',
}]
result = self.provider_id._poynt_make_request(
result = provider._poynt_make_request(
'POST', 'transactions', payload=txn_payload,
)
@@ -303,7 +314,7 @@ class PoyntPaymentWizard(models.TransientModel):
if not terminal:
raise UserError(_("No terminal associated with this payment."))
provider = self.provider_id
provider = self._get_provider_sudo()
try:
txn = self._find_terminal_transaction(provider)
@@ -517,14 +528,15 @@ class PoyntPaymentWizard(models.TransientModel):
def _create_poynt_order(self, reference):
"""Create a Poynt order via the API."""
provider = self._get_provider_sudo()
order_payload = poynt_utils.build_order_payload(
reference,
self.amount,
self.currency_id,
business_id=self.provider_id.poynt_business_id,
store_id=self.provider_id.poynt_store_id or '',
business_id=provider.poynt_business_id,
store_id=provider.poynt_store_id or '',
)
return self.provider_id._poynt_make_request(
return provider._poynt_make_request(
'POST', 'orders', payload=order_payload,
)

View File

@@ -8,6 +8,7 @@
<form string="Collect Poynt Payment">
<field name="state" invisible="1"/>
<field name="poynt_transaction_ref" invisible="1"/>
<field name="provider_id" invisible="1"/>
<!-- Status banner for waiting / done / error -->
<div class="alert alert-info" role="alert"
@@ -34,8 +35,7 @@
<field name="partner_id"/>
<field name="amount"/>
<field name="currency_id"/>
<field name="provider_id"
readonly="state != 'draft'"/>
<field name="provider_name"/>
</group>
<group string="Payment Mode"
invisible="state not in ('draft', 'error')">

View File

@@ -51,6 +51,11 @@ class PoyntRefundWizard(models.TransientModel):
required=True,
readonly=True,
)
provider_name = fields.Char(
related='provider_id.name',
string="Poynt Provider",
readonly=True,
)
original_transaction_id = fields.Many2one(
'payment.transaction',
string="Original Transaction",
@@ -104,6 +109,9 @@ class PoyntRefundWizard(models.TransientModel):
)
status_message = fields.Text(string="Status", readonly=True)
def _get_provider_sudo(self):
return self.provider_id.sudo()
@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
@@ -130,8 +138,9 @@ class PoyntRefundWizard(models.TransientModel):
res['original_invoice_id'] = credit_note.reversed_entry_id.id
res['original_poynt_txn_id'] = orig_tx.poynt_transaction_id
if orig_tx.provider_id.poynt_default_terminal_id:
res['terminal_id'] = orig_tx.provider_id.poynt_default_terminal_id.id
provider = orig_tx.provider_id.sudo()
if provider.poynt_default_terminal_id:
res['terminal_id'] = provider.poynt_default_terminal_id.id
age_days = 0
if orig_tx.create_date:
@@ -201,7 +210,7 @@ class PoyntRefundWizard(models.TransientModel):
still showing ``status: CAPTURED``. We must check the full chain.
"""
orig_tx = self.original_transaction_id
provider = self.provider_id
provider = self._get_provider_sudo()
txn_id = orig_tx.poynt_transaction_id
try:
@@ -278,7 +287,7 @@ class PoyntRefundWizard(models.TransientModel):
def _process_referenced_refund(self):
"""Send a referenced REFUND using the original transaction's parentId."""
orig_tx = self.original_transaction_id
provider = self.provider_id
provider = self._get_provider_sudo()
parent_txn_id = orig_tx.poynt_transaction_id
try:
@@ -346,7 +355,7 @@ class PoyntRefundWizard(models.TransientModel):
"The customer's card must be present on the device."
))
provider = self.provider_id
provider = self._get_provider_sudo()
orig_tx = self.original_transaction_id
minor_amount = poynt_utils.format_poynt_amount(
self.amount, self.currency_id,

View File

@@ -44,7 +44,8 @@
<field name="currency_id" invisible="1"/>
</group>
<group string="Original Payment">
<field name="provider_id" readonly="1"/>
<field name="provider_id" invisible="1"/>
<field name="provider_name"/>
<field name="original_transaction_id" readonly="1"/>
<field name="original_poynt_txn_id" readonly="1"/>
<field name="card_info" readonly="1"