1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, fields, http
from odoo.exceptions import AccessError, MissingError, ValidationError
from odoo.http import request
from odoo.addons.account.controllers import portal
from odoo.addons.payment import utils as payment_utils
from odoo.addons.payment.controllers.portal import PaymentPortal
class PortalAccount(portal.PortalAccount, PaymentPortal):
def _invoice_get_page_view_values(self, invoice, access_token, payment=False, **kwargs):
# EXTENDS account
values = super()._invoice_get_page_view_values(invoice, access_token, **kwargs)
if not invoice._has_to_be_paid():
# Do not compute payment-related stuff if given invoice doesn't have to be paid.
return {
**values,
'payment': payment, # We want to show the dialog even when everything has been paid (with a custom message)
}
epd = values.get('epd_discount_amount_currency', 0.0)
discounted_amount = invoice.amount_residual - epd
common_view_values = self._get_common_page_view_values(
invoices_data={
'partner': invoice.partner_id,
'company': invoice.company_id,
'total_amount': invoice.amount_total,
'currency': invoice.currency_id,
'amount_residual': discounted_amount,
'landing_route': invoice.get_portal_url(),
'transaction_route': f'/invoice/transaction/{invoice.id}',
},
access_token=access_token,
**kwargs)
amount_custom = float(kwargs['amount']) if kwargs.get('amount') else 0.0
values |= {
**common_view_values,
'amount_custom': amount_custom,
'payment': payment,
'invoice_id': invoice.id,
'invoice_name': invoice.name,
'show_epd': epd,
}
return values
@http.route(['/my/invoices/overdue'], type='http', auth='public', methods=['GET'], website=True, sitemap=False)
def portal_my_overdue_invoices(self, access_token=None, **kw):
try:
request.env['account.move'].check_access('read')
except (AccessError, MissingError):
return request.redirect('/my')
overdue_invoices = request.env['account.move'].search(self._get_overdue_invoices_domain())
values = self._overdue_invoices_get_page_view_values(overdue_invoices, **kw)
return request.render("account_payment.portal_overdue_invoices_page", values) if 'payment' in values else request.redirect('/my/invoices')
def _overdue_invoices_get_page_view_values(self, overdue_invoices, **kwargs):
values = {'page_name': 'overdue_invoices'}
if len(overdue_invoices) == 0:
return values
first_invoice = overdue_invoices[0]
partner = first_invoice.partner_id
company = first_invoice.company_id
currency = first_invoice.currency_id
if any(invoice.partner_id != partner for invoice in overdue_invoices):
raise ValidationError(_("Overdue invoices should share the same partner."))
if any(invoice.company_id != company for invoice in overdue_invoices):
raise ValidationError(_("Overdue invoices should share the same company."))
if any(invoice.currency_id != currency for invoice in overdue_invoices):
raise ValidationError(_("Overdue invoices should share the same currency."))
total_amount = sum(overdue_invoices.mapped('amount_total'))
amount_residual = sum(overdue_invoices.mapped('amount_residual'))
batch_name = company.get_next_batch_payment_communication() if len(overdue_invoices) > 1 else first_invoice.name
values['payment'] = {
'date': fields.Date.today(),
'reference': batch_name,
'amount': total_amount,
'currency': currency,
}
common_view_values = self._get_common_page_view_values(
invoices_data={
'partner': partner,
'company': company,
'total_amount': total_amount,
'currency': currency,
'amount_residual': amount_residual,
'payment_reference': batch_name,
'landing_route': '/my/invoices/',
'transaction_route': '/invoice/transaction/overdue',
},
**kwargs)
values |= common_view_values
return values
def _get_common_page_view_values(self, invoices_data, access_token=None, **kwargs):
logged_in = not request.env.user._is_public()
# We set partner_id to the partner id of the current user if logged in, otherwise we set it
# to the invoice partner id. We do this to ensure that payment tokens are assigned to the
# correct partner and to avoid linking tokens to the public user.
partner_sudo = request.env.user.partner_id if logged_in else invoices_data['partner']
invoice_company = invoices_data['company'] or request.env.company
availability_report = {}
# Select all the payment methods and tokens that match the payment context.
providers_sudo = request.env['payment.provider'].sudo()._get_compatible_providers(
invoice_company.id,
partner_sudo.id,
invoices_data['total_amount'],
currency_id=invoices_data['currency'].id,
report=availability_report,
) # In sudo mode to read the fields of providers and partner (if logged out).
payment_methods_sudo = request.env['payment.method'].sudo()._get_compatible_payment_methods(
providers_sudo.ids,
partner_sudo.id,
currency_id=invoices_data['currency'].id,
report=availability_report,
) # In sudo mode to read the fields of providers.
tokens_sudo = request.env['payment.token'].sudo()._get_available_tokens(
providers_sudo.ids, partner_sudo.id
) # In sudo mode to read the partner's tokens (if logged out) and provider fields.
# Make sure that the partner's company matches the invoice's company.
company_mismatch = not PaymentPortal._can_partner_pay_in_company(
partner_sudo, invoice_company
)
portal_page_values = {
'company_mismatch': company_mismatch,
'expected_company': invoice_company,
}
payment_form_values = {
'show_tokenize_input_mapping': PaymentPortal._compute_show_tokenize_input_mapping(
providers_sudo
),
}
payment_context = {
'currency': invoices_data['currency'],
'partner_id': partner_sudo.id,
'providers_sudo': providers_sudo,
'payment_methods_sudo': payment_methods_sudo,
'tokens_sudo': tokens_sudo,
'availability_report': availability_report,
'transaction_route': invoices_data['transaction_route'],
'landing_route': invoices_data['landing_route'],
'access_token': access_token,
'payment_reference': invoices_data.get('payment_reference', False),
}
# Merge the dictionaries while allowing the redefinition of keys.
values = portal_page_values | payment_form_values | payment_context | self._get_extra_payment_form_values(**kwargs)
return values
@http.route()
def portal_my_invoice_detail(self, invoice_id, payment_token=None, amount=None, **kw):
# EXTENDS account
# If we have a custom payment amount, make sure it hasn't been tampered with
if amount and not payment_utils.check_access_token(payment_token, invoice_id, amount):
return request.redirect('/my')
return super().portal_my_invoice_detail(invoice_id, amount=amount, **kw)
|