changes
This commit is contained in:
@@ -58,6 +58,37 @@ class PoyntPaymentWizard(models.TransientModel):
|
||||
default='terminal',
|
||||
)
|
||||
|
||||
# --- Card type & surcharge fields ---
|
||||
card_type = fields.Selection(
|
||||
selection=[
|
||||
('visa', "Visa"),
|
||||
('mastercard', "Mastercard"),
|
||||
('amex', "American Express"),
|
||||
('debit', "Debit"),
|
||||
('other', "Other"),
|
||||
],
|
||||
string="Card Type",
|
||||
)
|
||||
surcharge_enabled = fields.Boolean(
|
||||
compute='_compute_surcharge_enabled',
|
||||
)
|
||||
surcharge_rate = fields.Float(
|
||||
string="Surcharge Rate (%)",
|
||||
digits=(5, 2),
|
||||
readonly=True,
|
||||
)
|
||||
surcharge_amount = fields.Monetary(
|
||||
string="Surcharge Amount",
|
||||
currency_field='currency_id',
|
||||
readonly=True,
|
||||
)
|
||||
surcharge_applied = fields.Boolean(default=False)
|
||||
original_amount = fields.Monetary(
|
||||
string="Invoice Amount",
|
||||
currency_field='currency_id',
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
# --- Terminal fields ---
|
||||
terminal_id = fields.Many2one(
|
||||
'poynt.terminal',
|
||||
@@ -102,6 +133,58 @@ class PoyntPaymentWizard(models.TransientModel):
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
@api.depends_context('uid')
|
||||
def _compute_surcharge_enabled(self):
|
||||
enabled = self.env['ir.config_parameter'].sudo().get_param(
|
||||
'fusion_poynt.surcharge_enabled', 'False',
|
||||
) == 'True'
|
||||
for rec in self:
|
||||
rec.surcharge_enabled = enabled
|
||||
|
||||
@staticmethod
|
||||
def _detect_card_brand(card_number):
|
||||
num = (card_number or '').replace(' ', '')
|
||||
if len(num) < 2:
|
||||
return 'other'
|
||||
if num[:2] in ('34', '37'):
|
||||
return 'amex'
|
||||
if num[0] == '4':
|
||||
return 'visa'
|
||||
prefix2 = int(num[:2])
|
||||
if 51 <= prefix2 <= 55:
|
||||
return 'mastercard'
|
||||
if len(num) >= 4:
|
||||
prefix4 = int(num[:4])
|
||||
if 2221 <= prefix4 <= 2720:
|
||||
return 'mastercard'
|
||||
return 'other'
|
||||
|
||||
def _get_surcharge_rate(self, card_type):
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
rate_key = {
|
||||
'visa': 'fusion_poynt.surcharge_visa_rate',
|
||||
'mastercard': 'fusion_poynt.surcharge_mastercard_rate',
|
||||
'amex': 'fusion_poynt.surcharge_amex_rate',
|
||||
'debit': 'fusion_poynt.surcharge_debit_rate',
|
||||
}.get(card_type, 'fusion_poynt.surcharge_other_rate')
|
||||
return float(ICP.get_param(rate_key, '0') or 0)
|
||||
|
||||
@api.onchange('card_number')
|
||||
def _onchange_card_number(self):
|
||||
if self.payment_mode == 'card' and self.card_number:
|
||||
self.card_type = self._detect_card_brand(self.card_number)
|
||||
|
||||
@api.onchange('card_type')
|
||||
def _onchange_card_type(self):
|
||||
if not self.card_type or not self.surcharge_enabled:
|
||||
self.surcharge_rate = 0.0
|
||||
self.surcharge_amount = 0.0
|
||||
return
|
||||
rate = self._get_surcharge_rate(self.card_type)
|
||||
base_amount = self.original_amount or self.amount
|
||||
self.surcharge_rate = rate
|
||||
self.surcharge_amount = round(base_amount * rate / 100.0, 2)
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
res = super().default_get(fields_list)
|
||||
@@ -112,6 +195,7 @@ class PoyntPaymentWizard(models.TransientModel):
|
||||
invoice = self.env['account.move'].browse(invoice_id)
|
||||
res['invoice_id'] = invoice.id
|
||||
res['amount'] = invoice.amount_residual
|
||||
res['original_amount'] = invoice.amount_residual
|
||||
res['currency_id'] = invoice.currency_id.id
|
||||
|
||||
provider = self.env['payment.provider'].sudo().search([
|
||||
@@ -135,10 +219,103 @@ class PoyntPaymentWizard(models.TransientModel):
|
||||
if provider.poynt_default_terminal_id:
|
||||
self.terminal_id = provider.poynt_default_terminal_id
|
||||
|
||||
def _apply_surcharge_if_needed(self):
|
||||
"""Add the surcharge invoice line if surcharge is enabled and not yet applied."""
|
||||
if self.surcharge_applied or not self.surcharge_enabled:
|
||||
return
|
||||
if not self.card_type:
|
||||
raise UserError(_("Please select the card type to calculate the surcharge."))
|
||||
|
||||
rate = self._get_surcharge_rate(self.card_type)
|
||||
if rate <= 0:
|
||||
return
|
||||
|
||||
base_amount = self.original_amount or self.amount
|
||||
fee_amount = round(base_amount * rate / 100.0, 2)
|
||||
if fee_amount <= 0:
|
||||
return
|
||||
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
product_id = int(ICP.get_param('fusion_poynt.surcharge_product_id', '0') or 0)
|
||||
product = self.env['product.product'].sudo().browse(product_id).exists()
|
||||
if not product:
|
||||
product = self.env.ref('fusion_poynt.product_cc_processing_fee', raise_if_not_found=False)
|
||||
if not product:
|
||||
raise UserError(
|
||||
_("Surcharge product not configured. "
|
||||
"Go to Settings > Fusion Poynt to set it up.")
|
||||
)
|
||||
|
||||
invoice = self.invoice_id.sudo()
|
||||
|
||||
was_posted = invoice.state == 'posted'
|
||||
if was_posted:
|
||||
invoice.button_draft()
|
||||
|
||||
description = _("Credit Card Processing Fee (%(rate).2f%% surcharge)", rate=rate)
|
||||
invoice.write({
|
||||
'invoice_line_ids': [(0, 0, {
|
||||
'product_id': product.id,
|
||||
'name': description,
|
||||
'quantity': 1,
|
||||
'price_unit': fee_amount,
|
||||
'tax_ids': [(5, 0, 0)],
|
||||
})],
|
||||
})
|
||||
|
||||
if was_posted:
|
||||
invoice.action_post()
|
||||
|
||||
self.write({
|
||||
'surcharge_applied': True,
|
||||
'surcharge_rate': rate,
|
||||
'surcharge_amount': fee_amount,
|
||||
'amount': invoice.amount_residual,
|
||||
})
|
||||
|
||||
def _remove_surcharge_line(self):
|
||||
"""Remove the surcharge line from the invoice if it was applied."""
|
||||
if not self.surcharge_applied:
|
||||
return
|
||||
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
product_id = int(ICP.get_param('fusion_poynt.surcharge_product_id', '0') or 0)
|
||||
product = self.env['product.product'].sudo().browse(product_id).exists()
|
||||
if not product:
|
||||
product = self.env.ref('fusion_poynt.product_cc_processing_fee', raise_if_not_found=False)
|
||||
if not product:
|
||||
return
|
||||
|
||||
invoice = self.invoice_id.sudo()
|
||||
surcharge_lines = invoice.invoice_line_ids.filtered(
|
||||
lambda l: l.product_id.id == product.id
|
||||
)
|
||||
if not surcharge_lines:
|
||||
self.surcharge_applied = False
|
||||
return
|
||||
|
||||
was_posted = invoice.state == 'posted'
|
||||
if was_posted:
|
||||
invoice.button_draft()
|
||||
|
||||
surcharge_lines.unlink()
|
||||
|
||||
if was_posted:
|
||||
invoice.action_post()
|
||||
|
||||
self.write({
|
||||
'surcharge_applied': False,
|
||||
'surcharge_amount': 0.0,
|
||||
'surcharge_rate': 0.0,
|
||||
'amount': invoice.amount_residual,
|
||||
})
|
||||
|
||||
def action_collect_payment(self):
|
||||
"""Dispatch to the appropriate payment method."""
|
||||
self.ensure_one()
|
||||
|
||||
self._apply_surcharge_if_needed()
|
||||
|
||||
if self.amount <= 0:
|
||||
raise UserError(_("Payment amount must be greater than zero."))
|
||||
|
||||
@@ -187,6 +364,7 @@ class PoyntPaymentWizard(models.TransientModel):
|
||||
|
||||
except (ValidationError, UserError) as e:
|
||||
self._cleanup_draft_transaction()
|
||||
self._remove_surcharge_line()
|
||||
self.write({
|
||||
'state': 'error',
|
||||
'status_message': str(e),
|
||||
@@ -273,6 +451,31 @@ class PoyntPaymentWizard(models.TransientModel):
|
||||
'poynt_status': status,
|
||||
'funding_source': result.get('fundingSource', {}),
|
||||
}
|
||||
|
||||
if status in ('DECLINED', 'FAILED', 'REFUND_FAILED'):
|
||||
tx._set_error(
|
||||
_("Payment was %(status)s by the processor.",
|
||||
status=status.lower())
|
||||
)
|
||||
self._cleanup_draft_transaction()
|
||||
self._remove_surcharge_line()
|
||||
processor = result.get('processorResponse', {})
|
||||
decline_msg = (
|
||||
processor.get('statusMessage')
|
||||
or processor.get('message')
|
||||
or status.lower()
|
||||
)
|
||||
self.write({
|
||||
'state': 'error',
|
||||
'status_message': _(
|
||||
"Payment %(status)s: %(reason)s",
|
||||
status=status.lower(),
|
||||
reason=decline_msg,
|
||||
),
|
||||
'poynt_transaction_ref': transaction_id,
|
||||
})
|
||||
return self._reopen_wizard()
|
||||
|
||||
tx._process('poynt', payment_data)
|
||||
|
||||
self.write({
|
||||
@@ -288,6 +491,7 @@ class PoyntPaymentWizard(models.TransientModel):
|
||||
|
||||
except (ValidationError, UserError) as e:
|
||||
self._cleanup_draft_transaction()
|
||||
self._remove_surcharge_line()
|
||||
self.write({
|
||||
'state': 'error',
|
||||
'status_message': str(e),
|
||||
@@ -352,6 +556,7 @@ class PoyntPaymentWizard(models.TransientModel):
|
||||
|
||||
if status in ('DECLINED', 'VOIDED', 'REFUNDED'):
|
||||
self._cleanup_draft_transaction()
|
||||
self._remove_surcharge_line()
|
||||
self.write({
|
||||
'state': 'error',
|
||||
'status_message': _(
|
||||
@@ -473,6 +678,7 @@ class PoyntPaymentWizard(models.TransientModel):
|
||||
"""Cancel the payment and clean up the draft transaction."""
|
||||
self.ensure_one()
|
||||
self._cleanup_draft_transaction()
|
||||
self._remove_surcharge_line()
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
def _cleanup_draft_transaction(self):
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="poynt_transaction_ref" invisible="1"/>
|
||||
<field name="provider_id" invisible="1"/>
|
||||
<field name="surcharge_enabled" invisible="1"/>
|
||||
<field name="surcharge_applied" invisible="1"/>
|
||||
<field name="original_amount" invisible="1"/>
|
||||
|
||||
<!-- Status banner for waiting / done / error -->
|
||||
<div class="alert alert-info" role="alert"
|
||||
@@ -44,6 +47,25 @@
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<!-- Card Type & Surcharge section -->
|
||||
<group string="Card Type & Surcharge"
|
||||
invisible="state == 'done' or not surcharge_enabled">
|
||||
<group>
|
||||
<field name="card_type"
|
||||
widget="radio"
|
||||
required="surcharge_enabled and state in ('draft', 'error')"
|
||||
readonly="state not in ('draft', 'error')"/>
|
||||
</group>
|
||||
<group invisible="not card_type">
|
||||
<field name="surcharge_rate" string="Rate (%)"/>
|
||||
<field name="surcharge_amount"/>
|
||||
<div class="text-muted" colspan="2"
|
||||
invisible="surcharge_amount == 0">
|
||||
A surcharge line will be added to the invoice before payment.
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<!-- Terminal section -->
|
||||
<group string="Terminal"
|
||||
invisible="payment_mode != 'terminal' or state == 'done'">
|
||||
|
||||
Reference in New Issue
Block a user