mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[IMP]pms: added pay button in folio portal
This commit is contained in:
@@ -4,6 +4,7 @@ from odoo import _, fields, http, tools
|
|||||||
from odoo.exceptions import AccessError, MissingError
|
from odoo.exceptions import AccessError, MissingError
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
|
|
||||||
|
from odoo.addons.payment.controllers.portal import PaymentProcessing
|
||||||
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
|
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
|
||||||
|
|
||||||
|
|
||||||
@@ -26,10 +27,128 @@ class PortalFolio(CustomerPortal):
|
|||||||
|
|
||||||
def _folio_get_page_view_values(self, folio, access_token, **kwargs):
|
def _folio_get_page_view_values(self, folio, access_token, **kwargs):
|
||||||
values = {"folio": folio, "token": access_token}
|
values = {"folio": folio, "token": access_token}
|
||||||
|
payment_inputs = request.env["payment.acquirer"]._get_available_payment_input(
|
||||||
|
partner=folio.partner_id, company=folio.company_id
|
||||||
|
)
|
||||||
|
is_public_user = request.env.user._is_public()
|
||||||
|
if is_public_user:
|
||||||
|
payment_inputs.pop("pms", None)
|
||||||
|
token_count = (
|
||||||
|
request.env["payment.token"]
|
||||||
|
.sudo()
|
||||||
|
.search_count(
|
||||||
|
[
|
||||||
|
("acquirer_id.company_id", "=", folio.company_id.id),
|
||||||
|
("partner_id", "=", folio.partner_id.id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
values["existing_token"] = token_count > 0
|
||||||
|
values.update(payment_inputs)
|
||||||
|
values["partner_id"] = (
|
||||||
|
folio.partner_id if is_public_user else request.env.user.partner_id,
|
||||||
|
)
|
||||||
return self._get_page_view_values(
|
return self._get_page_view_values(
|
||||||
folio, access_token, values, "my_folios_history", False, **kwargs
|
folio, access_token, values, "my_folios_history", False, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@http.route(
|
||||||
|
"/folio/pay/<int:folio_id>/form_tx", type="json", auth="public", website=True
|
||||||
|
)
|
||||||
|
def folio_pay_form(
|
||||||
|
self, acquirer_id, folio_id, save_token=False, access_token=None, **kwargs
|
||||||
|
):
|
||||||
|
folio_sudo = request.env["pms.folio"].sudo().browse(folio_id)
|
||||||
|
if not folio_sudo:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
acquirer_id = int(acquirer_id)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if request.env.user._is_public():
|
||||||
|
save_token = False # we avoid to create a token for the public user
|
||||||
|
|
||||||
|
success_url = kwargs.get(
|
||||||
|
"success_url",
|
||||||
|
"%s?%s" % (folio_sudo.access_url, access_token if access_token else ""),
|
||||||
|
)
|
||||||
|
vals = {
|
||||||
|
"acquirer_id": acquirer_id,
|
||||||
|
"return_url": success_url,
|
||||||
|
}
|
||||||
|
|
||||||
|
if save_token:
|
||||||
|
vals["type"] = "form_save"
|
||||||
|
transaction = folio_sudo._create_payment_transaction(vals)
|
||||||
|
PaymentProcessing.add_payment_transaction(transaction)
|
||||||
|
if not transaction:
|
||||||
|
return False
|
||||||
|
tx_ids_list = set(request.session.get("__payment_tx_ids__", [])) | set(
|
||||||
|
transaction.ids
|
||||||
|
)
|
||||||
|
request.session["__payment_tx_ids__"] = list(tx_ids_list)
|
||||||
|
|
||||||
|
return transaction.render_invoice_button(
|
||||||
|
folio_sudo,
|
||||||
|
submit_txt=_("Pay & Confirm"),
|
||||||
|
render_values={
|
||||||
|
"type": "form_save" if save_token else "form",
|
||||||
|
"alias_usage": _(
|
||||||
|
"If we store your payment information on our server, "
|
||||||
|
"subscription payments will be made automatically."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# @http.route(
|
||||||
|
# '/invoice/pay/<int:invoice_id>/s2s_token_tx',
|
||||||
|
# type='http',
|
||||||
|
# auth='public',
|
||||||
|
# website=True
|
||||||
|
# )
|
||||||
|
# def invoice_pay_token(self, invoice_id, pm_id=None, **kwargs):
|
||||||
|
# """ Use a token to perform a s2s transaction """
|
||||||
|
# error_url = kwargs.get('error_url', '/my')
|
||||||
|
# access_token = kwargs.get('access_token')
|
||||||
|
# params = {}
|
||||||
|
# if access_token:
|
||||||
|
# params['access_token'] = access_token
|
||||||
|
#
|
||||||
|
# invoice_sudo = request.env['account.move'].sudo().browse(invoice_id).exists()
|
||||||
|
# if not invoice_sudo:
|
||||||
|
# params['error'] = 'pay_invoice_invalid_doc'
|
||||||
|
# return request.redirect(_build_url_w_params(error_url, params))
|
||||||
|
#
|
||||||
|
# success_url = kwargs.get(
|
||||||
|
# 'success_url',
|
||||||
|
# "%s?%s" % (
|
||||||
|
# invoice_sudo.access_url,
|
||||||
|
# url_encode({'access_token': access_token}) if access_token else '')
|
||||||
|
# )
|
||||||
|
# try:
|
||||||
|
# token = request.env['payment.token'].sudo().browse(int(pm_id))
|
||||||
|
# except (ValueError, TypeError):
|
||||||
|
# token = False
|
||||||
|
# token_owner = invoice_sudo.partner_id if \
|
||||||
|
# request.env.user._is_public() else request.env.user.partner_id
|
||||||
|
# if not token or token.partner_id != token_owner:
|
||||||
|
# params['error'] = 'pay_invoice_invalid_token'
|
||||||
|
# return request.redirect(_build_url_w_params(error_url, params))
|
||||||
|
#
|
||||||
|
# vals = {
|
||||||
|
# 'payment_token_id': token.id,
|
||||||
|
# 'type': 'server2server',
|
||||||
|
# 'return_url': _build_url_w_params(success_url, params),
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# tx = invoice_sudo._create_payment_transaction(vals)
|
||||||
|
# PaymentProcessing.add_payment_transaction(tx)
|
||||||
|
#
|
||||||
|
# params['success'] = 'pay_invoice'
|
||||||
|
# return request.redirect('/payment/process')
|
||||||
|
|
||||||
@http.route(
|
@http.route(
|
||||||
["/my/folios", "/my/folios/page/<int:page>"],
|
["/my/folios", "/my/folios/page/<int:page>"],
|
||||||
type="http",
|
type="http",
|
||||||
|
|||||||
@@ -1955,3 +1955,78 @@ class PmsFolio(models.Model):
|
|||||||
}
|
}
|
||||||
self.env["res.partner.id_number"].create(number_values)
|
self.env["res.partner.id_number"].create(number_values)
|
||||||
record.partner_id = partner
|
record.partner_id = partner
|
||||||
|
|
||||||
|
def _create_payment_transaction(self, vals):
|
||||||
|
# Ensure the currencies are the same.
|
||||||
|
currency = self[0].currency_id
|
||||||
|
if any(folio.currency_id != currency for folio in self):
|
||||||
|
raise ValidationError(
|
||||||
|
_(
|
||||||
|
"A transaction can't be linked to folios having different currencies."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ensure the partner are the same.
|
||||||
|
partner = self[0].partner_id
|
||||||
|
if any(folio.partner_id != partner for folio in self):
|
||||||
|
raise ValidationError(
|
||||||
|
_("A transaction can't be linked to folios having different partners.")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Try to retrieve the acquirer. However, fallback to the token's acquirer.
|
||||||
|
acquirer_id = vals.get("acquirer_id")
|
||||||
|
acquirer = None
|
||||||
|
payment_token_id = vals.get("payment_token_id")
|
||||||
|
|
||||||
|
if payment_token_id:
|
||||||
|
payment_token = self.env["payment.token"].sudo().browse(payment_token_id)
|
||||||
|
|
||||||
|
# Check payment_token/acquirer matching or take the acquirer from token
|
||||||
|
if acquirer_id:
|
||||||
|
acquirer = self.env["payment.acquirer"].browse(acquirer_id)
|
||||||
|
if payment_token and payment_token.acquirer_id != acquirer:
|
||||||
|
raise ValidationError(
|
||||||
|
_("Invalid token found! Token acquirer %s != %s")
|
||||||
|
% (payment_token.acquirer_id.name, acquirer.name)
|
||||||
|
)
|
||||||
|
if payment_token and payment_token.partner_id != partner:
|
||||||
|
raise ValidationError(
|
||||||
|
_("Invalid token found! Token partner %s != %s")
|
||||||
|
% (payment_token.partner.name, partner.name)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
acquirer = payment_token.acquirer_id
|
||||||
|
|
||||||
|
# Check an acquirer is there.
|
||||||
|
if not acquirer_id and not acquirer:
|
||||||
|
raise ValidationError(
|
||||||
|
_("A payment acquirer is required to create a transaction.")
|
||||||
|
)
|
||||||
|
|
||||||
|
if not acquirer:
|
||||||
|
acquirer = self.env["payment.acquirer"].browse(acquirer_id)
|
||||||
|
|
||||||
|
# Check a journal is set on acquirer.
|
||||||
|
if not acquirer.journal_id:
|
||||||
|
raise ValidationError(
|
||||||
|
_("A journal must be specified for the acquirer %s.", acquirer.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not acquirer_id and acquirer:
|
||||||
|
vals["acquirer_id"] = acquirer.id
|
||||||
|
|
||||||
|
vals.update(
|
||||||
|
{
|
||||||
|
"amount": sum(self.mapped("amount_total")),
|
||||||
|
"currency_id": currency.id,
|
||||||
|
"partner_id": partner.id,
|
||||||
|
"folio_ids": [(6, 0, self.ids)],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
transaction = self.env["payment.transaction"].create(vals)
|
||||||
|
|
||||||
|
# Process directly if payment_token
|
||||||
|
if transaction.payment_token_id:
|
||||||
|
transaction.s2s_do_transaction()
|
||||||
|
|
||||||
|
return transaction
|
||||||
|
|||||||
@@ -77,6 +77,116 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</t>
|
</t>
|
||||||
</t>
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="portal_folio_payment" name="Folio Payment">
|
||||||
|
<div
|
||||||
|
class="row"
|
||||||
|
t-if="not tx_ids and folio.state in ('confirm','done') and folio.payment_state in ('not_paid', 'partial') and folio.amount_total"
|
||||||
|
id="portal_pay"
|
||||||
|
>
|
||||||
|
<div class="modal fade" id="pay_with" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="modal-title">Pay with</h3>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="close"
|
||||||
|
data-dismiss="modal"
|
||||||
|
aria-label="Close"
|
||||||
|
>×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div
|
||||||
|
t-if="pms or acquirers"
|
||||||
|
id="payment_method"
|
||||||
|
class="text-left col-md-13"
|
||||||
|
>
|
||||||
|
<t t-call="payment.payment_tokens_list">
|
||||||
|
<t t-set="mode" t-value="'payment'" />
|
||||||
|
<t
|
||||||
|
t-set="partner_id"
|
||||||
|
t-value="folio.partner_id.id if request.env.user._is_public() else request.env.user.partner_id.id"
|
||||||
|
/>
|
||||||
|
<t
|
||||||
|
t-set="success_url"
|
||||||
|
t-value="folio.get_portal_url()"
|
||||||
|
/>
|
||||||
|
<t
|
||||||
|
t-set="error_url"
|
||||||
|
t-value="folio.get_portal_url()"
|
||||||
|
/>
|
||||||
|
<t
|
||||||
|
t-set="access_token"
|
||||||
|
t-value="access_token or ''"
|
||||||
|
/>
|
||||||
|
<t t-set="callback_method" t-value="''" />
|
||||||
|
<t
|
||||||
|
t-set="form_action"
|
||||||
|
t-value="'/folio/pay/' + str(folio.id) + '/s2s_token_tx/'"
|
||||||
|
/>
|
||||||
|
<t
|
||||||
|
t-set="prepare_tx_url"
|
||||||
|
t-value="'/folio/pay/' + str(folio.id) + '/form_tx/'"
|
||||||
|
/>
|
||||||
|
<t t-set="submit_txt">Pay Now</t>
|
||||||
|
<t t-set="icon_class" t-value="'fa-lock'" />
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="portal_folio_page_payment" name="Payment on My Folios">
|
||||||
|
<t
|
||||||
|
t-set="tx_ids"
|
||||||
|
t-value="folio.transaction_ids.filtered(lambda tx: tx.state in ('pending', 'authorized', 'done'))"
|
||||||
|
/>
|
||||||
|
<t
|
||||||
|
t-set="pending_manual_txs"
|
||||||
|
t-value="tx_ids.filtered(lambda tx: tx.state == 'pending' and tx.acquirer_id.provider in ('transfer', 'manual'))"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
t-if="folio.state in ('confirm','done') and folio.payment_state in ('not_paid', 'partial') and folio.amount_total and (pending_manual_txs or not tx_ids)"
|
||||||
|
class="btn btn-primary btn-block mb-2"
|
||||||
|
data-toggle="modal"
|
||||||
|
data-target="#pay_with"
|
||||||
|
>
|
||||||
|
<i class="fa fa-fw fa-arrow-circle-right" /> Pay Now
|
||||||
|
</a>
|
||||||
|
<div
|
||||||
|
t-if="tx_ids and not pending_manual_txs and folio.payment_state not in ('paid', 'in_payment')"
|
||||||
|
class="alert alert-info py-1 mb-2"
|
||||||
|
>
|
||||||
|
<i class="fa fa-fw fa-check-circle" /> Pending
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
t-if="folio.payment_state in ('paid', 'in_payment')"
|
||||||
|
class="alert alert-success py-1 mb-2"
|
||||||
|
>
|
||||||
|
<i class="fa fa-fw fa-check-circle" /> Paid
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<t
|
||||||
|
t-set="tx_ids"
|
||||||
|
t-value="folio.transaction_ids.filtered(lambda tx: tx.state in ('authorized', 'done'))"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
t-if="not tx_ids and folio.state in('confirm','done') and folio.payment_state in ('not_paid', 'partial') and folio.amount_total"
|
||||||
|
id="portal_pay"
|
||||||
|
>
|
||||||
|
<div t-if="pms or acquirers" id="payment_method">
|
||||||
|
<t t-call="pms.portal_folio_payment" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template
|
||||||
id="folio_portal_template"
|
id="folio_portal_template"
|
||||||
@@ -99,6 +209,9 @@
|
|||||||
class="list-group list-group-flush flex-wrap flex-row flex-lg-column"
|
class="list-group list-group-flush flex-wrap flex-row flex-lg-column"
|
||||||
>
|
>
|
||||||
<li class="list-group-item flex-grow-1">
|
<li class="list-group-item flex-grow-1">
|
||||||
|
<div>
|
||||||
|
<div t-call="pms.portal_folio_page_payment" />
|
||||||
|
</div>
|
||||||
<div class="o_download_pdf btn-toolbar flex-sm-nowrap">
|
<div class="o_download_pdf btn-toolbar flex-sm-nowrap">
|
||||||
<div class="btn-group flex-grow-1 mr-1 mb-1">
|
<div class="btn-group flex-grow-1 mr-1 mb-1">
|
||||||
<a
|
<a
|
||||||
|
|||||||
Reference in New Issue
Block a user