Merge PR #76 into 14.0

Signed-off-by DarioLodeiros
This commit is contained in:
OCA-git-bot
2021-11-09 05:32:50 +00:00
7 changed files with 350 additions and 5 deletions

View File

@@ -88,6 +88,7 @@
"views/precheckin_portal_templates.xml",
"wizards/wizard_massive_changes.xml",
"wizards/wizard_advanced_filters.xml",
"views/payment_transaction_views.xml",
],
"demo": [
"demo/pms_master_data.xml",

View File

@@ -4,6 +4,7 @@ from odoo import _, fields, http, tools
from odoo.exceptions import AccessError, MissingError
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
@@ -26,10 +27,127 @@ class PortalFolio(CustomerPortal):
def _folio_get_page_view_values(self, folio, access_token, **kwargs):
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(
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_folio_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(
["/my/folios", "/my/folios/page/<int:page>"],
type="http",

View File

@@ -1,4 +1,4 @@
from odoo import fields, models
from odoo import _, fields, models
class PaymentTransaction(models.Model):
@@ -20,3 +20,23 @@ class PaymentTransaction(models.Model):
if self.folio_ids:
add_payment_vals["folio_ids"] = [(6, 0, self.folio_ids.ids)]
return super(PaymentTransaction, self)._create_payment(add_payment_vals)
def render_folio_button(self, folio, submit_txt=None, render_values=None):
values = {
"partner_id": folio.partner_id.id,
"type": self.type,
}
if render_values:
values.update(render_values)
return (
self.acquirer_id.with_context(
submit_class="btn btn-primary", submit_txt=submit_txt or _("Pay Now")
)
.sudo()
.render(
self.reference,
folio.pending_amount,
folio.currency_id.id,
values=values,
)
)

View File

@@ -964,7 +964,9 @@ class PmsFolio(models.Model):
}
record.update(vals)
else:
journals = record.pms_property_id._get_payment_methods()
journals = record.pms_property_id._get_payment_methods(
automatic_included=True
)
paid_out = 0
for journal in journals:
paid_out += sum(
@@ -1955,3 +1957,78 @@ class PmsFolio(models.Model):
}
self.env["res.partner.id_number"].create(number_values)
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

View File

@@ -428,12 +428,14 @@ class PmsProperty(models.Model):
dt = dt.replace(tzinfo=None)
return dt
def _get_payment_methods(self):
def _get_payment_methods(self, automatic_included=False):
# We use automatic_included to True to see absolutely
# all the journals with associated payments, if it is
# false, we will only see those journals that can be used
# to pay manually
self.ensure_one()
payment_methods = self.env["account.journal"].search(
[
("allowed_pms_payments", "=", True),
"&",
("type", "in", ["cash", "bank"]),
"|",
("pms_property_ids", "in", self.id),
@@ -446,6 +448,8 @@ class PmsProperty(models.Model):
("company_id", "=", False),
]
)
if not automatic_included:
payment_methods = payment_methods.filtered(lambda p: p.allowed_pms_payments)
return payment_methods
@api.model

View File

@@ -77,6 +77,116 @@
</tbody>
</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
id="folio_portal_template"
@@ -99,6 +209,9 @@
class="list-group list-group-flush flex-wrap flex-row flex-lg-column"
>
<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="btn-group flex-grow-1 mr-1 mb-1">
<a

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="payment_transaction_inherit_view_form" model="ir.ui.view">
<field name="model">payment.transaction</field>
<field name="inherit_id" ref="payment.transaction_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='date']" position="before">
<field name="folio_ids" />
</xpath>
</field>
</record>
</odoo>