[ADD]mandates_bank_accounts_portal

This commit is contained in:
2024-09-16 17:16:39 +02:00
committed by Stéphan Sainléger
parent 31b9c39f8c
commit 37142bfa98
20 changed files with 1142 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
*.*~
*pyc

View File

@@ -0,0 +1,49 @@
===============
partner_bank_account_portal
===============
Provide portal pages and forms to manage partner's contract, bank accounts and mandates from portal home space.
Installation
============
Use Odoo normal module installation procedure to install
``partner_bank_account_portal``.
Description
===========
Provide portal pages and forms to manage partner's bank accounts and mandates from portal home space.
Also add the mandate in contract portal view
Known issues / Roadmap
======================
None yet.
Bug Tracker
===========
Bugs are tracked on `our issues website <https://github.com/elabore-coop/partner-tools/issues>`_. In case of
trouble, please check there if your issue has already been
reported. If you spotted it first, help us smashing it by providing a
detailed and welcomed feedback.
Credits
=======
Contributors
------------
* Laetitia Da Costa
Funders
-------
The development of this module has been financially supported by:
* Elabore (https://elabore.coop)
Maintainer
----------
This module is maintained by Elabore.

View File

@@ -0,0 +1,3 @@
from . import controllers
from . import models

View File

@@ -0,0 +1,48 @@
# Copyright 2022 Stéphan Sainléger (Elabore)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Mandates and bank accounts portal",
"version": "16.0.1.0.0",
"author": "Elabore",
"website": "https://elabore.coop",
"maintainer": "Laetitia Da Costa",
"license": "AGPL-3",
"category": "Tools",
"summary": "Provide portal pages and forms to manage partner's bank accounts and mandates from portal home space.",
# any module necessary for this one to work correctly
"depends": [
"base",
"account",
"portal",
"website",
"account_banking_mandate",
"contract",
"account_payment_order",
"contract_mandate",
],
"qweb": [],
"external_dependencies": {
"python": [],
},
# always loaded
"data": [
"security/members_security.xml",
"security/ir.model.access.csv",
"views/portal_my_home_template.xml",
"views/portal_my_bank_accounts_template.xml",
"views/portal_my_bank_account_template.xml",
"views/portal_my_mandates_template.xml",
"views/portal_my_mandate_template.xml",
"views/portal_my_contract_template_inherit.xml",
],
# only loaded in demonstration mode
"demo": [],
"js": [],
"css": [],
"installable": True,
# Install this module automatically if all dependency have been previously
# and independently installed. Used for synergetic or glue modules.
"auto_install": False,
"application": False,
}

View File

@@ -0,0 +1,4 @@
from . import portal_my_bank_accounts
from . import portal_my_bank_account
from . import portal_my_mandates
from . import portal_my_mandate

View File

@@ -0,0 +1,159 @@
# Copyright 2020 Lokavaluto ()
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import base64
from odoo import http, tools, _
from odoo.exceptions import AccessError, MissingError
from odoo.http import request
from odoo.addons.portal.controllers.portal import CustomerPortal
class CustomerPortalBankAccount(CustomerPortal):
def _bank_account_get_page_view_values(self, bank_account, access_token, **kwargs):
values = {
"page_name": "bank_account",
"bank_account": bank_account,
}
return self._get_page_view_values(
bank_account, access_token, values, "my_bank_accounts_history", False, **kwargs
)
def _details_bank_account_form_validate(self, data, bank_account_id):
error = dict()
error_message = []
# public name uniqueness
if data.get("public_name") and request.env["res.partner"].sudo().search(
[
("name", "=", data.get("public_name")),
("is_public_profile", "=", True),
("contact_id", "!=", bank_account_id),
]
):
error["public_name"] = "error"
error_message.append(
_("This public name is already used, please find an other idea.")
)
# email validation
if data.get("email") and not tools.single_email_re.match(data.get("email")):
error["email"] = "error"
error_message.append(
_("Invalid Email! Please enter a valid email address.")
)
return error, error_message
def _get_bank_account_fields(self):
fields = [
"acc_number",
"acc_holder_name",
]
return fields
def _get_id_fields(self):
fields = [
"bank_id",
]
return fields
def _get_main_boolean_bank_account_fields(self):
'''Provides the fields for which we must check the presence
in form's kw to know the value to save in the partner field.
All of them MUST start with "main_".'''
fields = []
return fields
def _transform_res_partner_fields(self, kw, bank_account_fields, prefix=""):
'''Transforms kw's values in res_partner fields and values'''
return {key[len(prefix):]: kw[key] for key in bank_account_fields if key in kw}
def _cast_id_fields(self, kw, id_fields):
'''Cast ids fields in kw's values into a integer'''
result = {}
for key in id_fields:
if key in kw:
if kw[key] == '':
result[key] = None
elif not isinstance(kw[key], int):
result[key] = int(kw[key])
else:
result[key] = kw[key]
return result
def _add_boolean_values(self, values, kw, boolean_fields, prefix=""):
for key in boolean_fields:
values.update(
{
key[len(prefix):]: kw.get(key, "off") == "on",
}
)
return values
def _get_page_saving_bank_account_values(self, kw):
bank_account_fields = self._get_bank_account_fields()
values = self._transform_res_partner_fields(kw, bank_account_fields)
if kw["bank_id"] == '':
bank_id = None
else:
bank_id = int(kw["bank_id"])
values.update({"bank_id":bank_id})
return values
@http.route(
["/my/bank_account/<int:bank_account_id>", "/my/bank_account/save"],
type="http",
auth="user",
website=True,
)
def portal_my_bank_account(
self,bank_account_id=None, access_token=None, redirect=None, **kw
):
# The following condition is to transform profile_id to an int, as it is sent as a string from the templace "portal_my_profile"
# TODO: find a better way to retrieve the profile_id at form submit step
if not isinstance(bank_account_id, int):
bank_account_id = int(bank_account_id)
# Check that the user has the right to see this profile
try:
bank_account_sudo = self._document_check_access(
"res.partner.bank", bank_account_id, access_token
)
except (AccessError, MissingError):
return request.redirect("/my/bank_accounts")
PartnerBankAccount = request.env["res.partner.bank"]
user = request.env.user
bank_account = PartnerBankAccount.browse(bank_account_id)
values = self._bank_account_get_page_view_values(bank_account_sudo, access_token, **kw)
values.update(
{
"error": {},
"error_message": [],
}
)
if kw and request.httprequest.method == "POST":
# the user has clicked in the Save button to save new data
error, error_message = self._details_bank_account_form_validate(kw, bank_account_id)
values.update({"error": error, "error_message": error_message})
values.update(kw)
if not error:
# Update main profile
new_values = self._get_page_saving_bank_account_values(kw)
bank_account.sudo().write(new_values)
if redirect:
return request.redirect(redirect)
return request.redirect("/my/bank_accounts")
# This is just the form page opening. We send all the data needed for the form fields
can_edit_bank_account = user.partner_id == bank_account.partner_id
banks = request.env["res.bank"].sudo().search([])
values.update(
{
"bank_account_id": bank_account_id, # Sent in order to retrieve it at submit time
"can_edit_bank_account": can_edit_bank_account,
"banks": banks,
"redirect": "/my/bank_account/" + str(bank_account_id) + "?success=True"
}
)
return request.render("partner_bank_account_portal.portal_my_bank_account", values) #TODO créer le template portal_my_bank_account.xml

View File

@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import http, _
from odoo.osv import expression
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
from odoo.exceptions import AccessError, MissingError
from collections import OrderedDict
from odoo.http import request
class CustomerPortalBankAccounts(CustomerPortal):
def _prepare_home_portal_values(self, counters):
values = super()._prepare_home_portal_values(counters)
if 'bank_account_count' in counters:
bank_account_count = request.env['res.partner.bank'].search_count([])
values['bank_account_count'] = bank_account_count
return values
def _get_account_searchbar_sortings(self):
res = super()._get_account_searchbar_sortings()
res['acc_number'] = {'label': _('IBAN'), 'order': 'acc_number'}
return res
@http.route(['/my/bank_accounts', '/my/bank_accounts/page/<int:page>'], type='http', auth="user", website=True)
def portal_my_bank_accounts(self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, **kw):
values = self._prepare_my_bank_accounts_values(page, date_begin, date_end, sortby, filterby)
# pager
pager = portal_pager(**values['pager'])
# content according to pager and archive selected
bank_accounts = values['bank_accounts'](pager['offset'])
request.session['bank_accounts_history'] = bank_accounts.ids[:100]
values.update({
'bank_accounts': bank_accounts,
'pager': pager,
})
return request.render("partner_bank_account_portal.portal_my_bank_accounts", values)
def _get_bank_accounts_domain(self):
return [('active', '=', True)]
def _prepare_my_bank_accounts_values(self, page, date_begin, date_end, sortby, filterby, domain=None, url="/my/bank_accounts"):
values = self._prepare_portal_layout_values()
res_partner_bank = request.env['res.partner.bank']
domain = expression.AND([
domain or [],
self._get_bank_accounts_domain(),
])
searchbar_sortings = self._get_account_searchbar_sortings()
# default sort by order
if not sortby:
sortby = 'acc_number'
order = searchbar_sortings[sortby]['order']
# default filter by value
if not filterby:
filterby = 'all'
if date_begin and date_end:
domain += [('create_date', '>', date_begin), ('create_date', '<=', date_end)]
values.update({
'date': date_begin,
# content according to pager and archive selected
# lambda function to get the invoices recordset when the pager will be defined in the main method of a route
'bank_accounts': lambda pager_offset: (
res_partner_bank.search(domain, order=order, limit=self._items_per_page, offset=pager_offset)
if res_partner_bank.check_access_rights('read', raise_exception=False) else
res_partner_bank
),
'page_name': 'bank_accounts',
'pager': { # vals to define the pager.
"url": url,
"url_args": {'date_begin': date_begin, 'date_end': date_end, 'sortby': sortby},
"total": res_partner_bank.search_count(domain) if res_partner_bank.check_access_rights('read', raise_exception=False) else 0,
"page": page,
"step": self._items_per_page,
},
'default_url': url,
'sortby': sortby,
'filterby': filterby,
})
return values

View File

@@ -0,0 +1,152 @@
# Copyright 2020 Lokavaluto ()
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import base64
from odoo import http, tools, _
from odoo.exceptions import AccessError, MissingError
from odoo.http import request
from odoo.addons.portal.controllers.portal import CustomerPortal
class CustomerPortalMandate(CustomerPortal):
def _mandate_get_page_view_values(self, mandate, access_token, **kwargs):
values = {
"page_name": "mandate",
"mandate": mandate,
}
return self._get_page_view_values(
mandate, access_token, values, "my_mandates_history", False, **kwargs
)
def _details_mandate_form_validate(self, data, mandate_id):
error = dict()
error_message = []
# public name uniqueness
if data.get("public_name") and request.env["res.partner"].sudo().search(
[
("name", "=", data.get("public_name")),
("is_public_profile", "=", True),
("contact_id", "!=", mandate_id),
]
):
error["public_name"] = "error"
error_message.append(
_("This public name is already used, please find an other idea.")
)
# email validation
if data.get("email") and not tools.single_email_re.match(data.get("email")):
error["email"] = "error"
error_message.append(
_("Invalid Email! Please enter a valid email address.")
)
return error, error_message
def _get_mandate_fields(self):
fields = [
"unique_mandate_reference",
"signature_date",
"last_debit_date",
"state",
]
return fields
def _get_main_boolean_mandate_fields(self):
'''Provides the fields for which we must check the presence
in form's kw to know the value to save in the partner field.
All of them MUST start with "main_".'''
fields = []
return fields
def _transform_in_res_partner_fields(self, kw, mandate_fields, prefix=""):
'''Transforms kw's values in res_partner fields and values'''
return {key[len(prefix):]: kw[key] for key in mandate_fields if key in kw}
def _add_boolean_values(self, values, kw, boolean_fields, prefix=""):
for key in boolean_fields:
values.update(
{
key[len(prefix):]: kw.get(key, "off") == "on",
}
)
return values
def _get_page_saving_mandate_values(self, kw):
mandate_fields = self._get_mandate_fields()
values = self._transform_in_res_partner_fields(kw, mandate_fields)
# boolean_fields = self._get_main_boolean_bank_account_fields()
# values = self._add_boolean_values(values, kw, boolean_fields)
return values
def _get_page_opening_values(self):
# Just retrieve the values to display for Selection fields
countries = request.env["res.country"].sudo().search([])
values = {
"countries": countries,
}
return values
@http.route(
["/my/mandate/<int:mandate_id>", "/my/mandate/save"],
type="http",
auth="user",
website=True,
)
def portal_my_mandate(
self,mandate_id=None, access_token=None, redirect=None, **kw
):
# The following condition is to transform profile_id to an int, as it is sent as a string from the templace "portal_my_profile"
# TODO: find a better way to retrieve the profile_id at form submit step
if not isinstance(mandate_id, int):
mandate_id = int(mandate_id)
# Check that the user has the right to see this profile
try:
mandate_sudo = self._document_check_access(
"account.banking.mandate", mandate_id, access_token
)
except (AccessError, MissingError):
return request.redirect("/my/mandates")
Mandate = request.env["account.banking.mandate"]
user = request.env.user
mandate = Mandate.browse(mandate_id)
values = self._mandate_get_page_view_values(mandate_sudo, access_token, **kw)
values.update(
{
"error": {},
"error_message": [],
}
)
if kw and request.httprequest.method == "POST":
# the user has clicked in the Save button to save new data
error, error_message = self._details_mandate_form_validate(kw, mandate_id)
values.update({"error": error, "error_message": error_message})
values.update(kw)
if not error:
# Update main profile
new_values = self._get_page_saving_mandate_values(kw)
mandate.sudo().write(new_values)
# Update public profile
# new_values = self._get_page_saving_public_structure_values(kw)
# public_profile.sudo().write(new_values)
# Update position profile
# new_values = self._get_page_saving_position_structure_values(kw)
# position_profile.sudo().write(new_values)
# End of updates
if redirect:
return request.redirect(redirect)
return request.redirect("/my/mandates")
# This is just the form page opening. We send all the data needed for the form fields
can_edit_mandate = user.partner_id == mandate.partner_id
values.update(self._get_page_opening_values())
values.update(
{
"mandate_id": mandate_id, # Sent in order to retrieve it at submit time
"can_edit_mandate": can_edit_mandate,
"redirect": "/my/mandate/" + str(mandate_id) + "?success=True"
}
)
return request.render("partner_bank_account_portal.portal_my_mandate", values) #TODO créer le template portal_my_bank_account.xml

View File

@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import http, _
from odoo.osv import expression
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
from odoo.exceptions import AccessError, MissingError
from collections import OrderedDict
from odoo.http import request
class CustomerPortalMandates(CustomerPortal):
def _prepare_home_portal_values(self, counters):
values = super()._prepare_home_portal_values(counters)
if 'mandate_count' in counters:
mandate_count = request.env['account.banking.mandate'].search_count([])
values['mandate_count'] = mandate_count
return values
@http.route(['/my/mandates', '/my/mandates/page/<int:page>'], type='http', auth="user", website=True)
def portal_mandates(self, page=1, date_begin=None, date_end=None, filterby=None, **kw):
values = self._prepare_my_mandates_values(page, date_begin, date_end)
# pager
pager = portal_pager(**values['pager'])
# content according to pager and archive selected
mandates = values['mandates'](pager['offset'])
request.session['mandates_history'] = mandates.ids[:100]
values.update({
'mandates': mandates,
'pager': pager,
})
return request.render("partner_bank_account_portal.portal_my_mandates", values)
def _get_mandate_domain(self):
return []
def _prepare_my_mandates_values(self, page, date_begin, date_end, domain=None, url="/my/mandates"):
values = self._prepare_portal_layout_values()
Mandate = request.env['account.banking.mandate']
domain = expression.AND([
domain or [],
self._get_mandate_domain(),
])
if date_begin and date_end:
domain += [('create_date', '>', date_begin), ('create_date', '<=', date_end)]
values.update({
'date': date_begin,
# content according to pager and archive selected
# lambda function to get the invoices recordset when the pager will be defined in the main method of a route
'mandates': lambda pager_offset: (
Mandate.search(domain, limit=self._items_per_page, offset=pager_offset)
if Mandate.check_access_rights('read', raise_exception=False) else
Mandate
),
'page_name': 'mandates',
'pager': { # vals to define the pager.
"url": url,
"url_args": {'date_begin': date_begin, 'date_end': date_end},
"total": Mandate.search_count(domain) if Mandate.check_access_rights('read', raise_exception=False) else 0,
"page": page,
"step": self._items_per_page,
},
'default_url': url,
})
return values

View File

@@ -0,0 +1,192 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * partner_bank_account_portal
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-05 13:58+0000\n"
"PO-Revision-Date: 2024-08-05 13:58+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_accounts
msgid "1 registration"
msgstr "1 enregistrement"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_account
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandate
msgid "<i class=\"fa fa-fw fa-check-circle\"/> Data saved!"
msgstr "Données enregistrées!"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_account
msgid "<span class=\"fa fa-arrow-left\"/> Back to my bank accounts list"
msgstr "<span class=\"fa fa-arrow-left\"/> Retour à la liste des comptes bancaires"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandate
msgid "<span class=\"fa fa-arrow-left\"/> Back to my mandates list"
msgstr "<span class=\"fa fa-arrow-left\"/> Retour à la liste des mandats"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandates
msgid "Associed IBAN"
msgstr "IBAN associé"
#. module: partner_bank_account_portal
#: model:ir.model.fields,field_description:partner_bank_account_portal.field_account_setup_bank_manual_config__associated_mandate_count
#: model:ir.model.fields,field_description:partner_bank_account_portal.field_res_partner_bank__associated_mandate_count
msgid "Associated mandate count"
msgstr "Décompte de mandats associés au compte bancaire"
#. module: partner_bank_account_portal
#: model:ir.model,name:partner_bank_account_portal.model_res_partner_bank
msgid "Bank Accounts"
msgstr "Comptes bancaires"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_accounts
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_home_bank_accounts
msgid "Bank accounts"
msgstr "Comptes bancaires"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_accounts
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandates
msgid "Contact name"
msgstr "Nom du contact"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_account
msgid "Contact name:"
msgstr "Nom du contact : "
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandates
msgid "Format"
msgstr ""
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandate
msgid "Format :"
msgstr ""
#. module: partner_bank_account_portal
#. odoo-python
#: code:addons/partner_bank_account_portal/controllers/portal_my_bank_accounts.py:0
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_accounts
#, python-format
msgid "IBAN"
msgstr ""
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_account
msgid "IBAN :"
msgstr ""
#. module: partner_bank_account_portal
#. odoo-python
#: code:addons/partner_bank_account_portal/controllers/portal_my_bank_account.py:0
#: code:addons/partner_bank_account_portal/controllers/portal_my_mandate.py:0
#, python-format
msgid "Invalid Email! Please enter a valid email address."
msgstr "Email invalide ! Entrez une adresse email valide."
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandates
msgid "Last debit date"
msgstr "Date du dernier débit"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_home_bank_accounts
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_home_menu_contract
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandates
msgid "Mandates"
msgstr "Mandats"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_account
msgid "My bank account Details"
msgstr "Détails du compte bancaire"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandate
msgid "My mandate details"
msgstr "Details du mandat"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_accounts
msgid "Name of IBAN holder if different"
msgstr "Nom du titulaire de l'IBAN si différent :"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_account
msgid "Name of IBAN holder if different :"
msgstr "Nom du titulaire de l'IBAN si différent :"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_accounts
msgid "No registrations"
msgstr "Pas d'enregistrement"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_accounts
msgid "Number of associated mandates"
msgstr "Nombre de mandats associés"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandates
msgid "Reference"
msgstr "Référence"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandate
msgid "Reference :"
msgstr "Référence :"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_account
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandate
msgid "Save"
msgstr "Enregistrer"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandates
msgid "Signature date"
msgstr "Date de signature"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandates
msgid "Status"
msgstr "Statut"
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_accounts
msgid "There are currently no bank accounts for your account."
msgstr "Aucun compte bancaire n'est accessible depuis votre compte."
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_mandates
msgid "There are currently no mandates for your account."
msgstr "Aucun mandat n'est accessible depuis votre compte."
#. module: partner_bank_account_portal
#. odoo-python
#: code:addons/partner_bank_account_portal/controllers/portal_my_bank_account.py:0
#: code:addons/partner_bank_account_portal/controllers/portal_my_mandate.py:0
#, python-format
msgid "This public name is already used, please find an other idea."
msgstr "Ce nom est déjà utilisé, veuillez trouver un autre nom."
#. module: partner_bank_account_portal
#: model_terms:ir.ui.view,arch_db:partner_bank_account_portal.portal_my_bank_accounts
msgid "records"
msgstr "enregistrements"

View File

@@ -0,0 +1,2 @@
from . import res_partner_bank

View File

@@ -0,0 +1,17 @@
# Copyright 2022 Elabore (https://elabore.coop)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class res_partner_bank(models.Model):
_inherit = "res.partner.bank"
#count all mandate linked to the IBAN, even draft or canceled mandates
associated_mandate_count = fields.Integer("Associated mandate count", compute="_compute_associated_mandate_count")
def _compute_associated_mandate_count(self):
for record in self:
count = self.env["account.banking.mandate"].search_count(
[("partner_bank_id", "=", record.id)]
)
record.associated_mandate_count = count

View File

@@ -0,0 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
"access_res_partner_bank_group_portal","res_partner_bank group_portal","base.model_res_partner_bank","base.group_portal",1,0,0,0
"access_res_bank_group_portal","res_bank group_portal","base.model_res_bank","base.group_portal",1,0,0,0
"access_account_banking_mandate_group_portal","account_banking_mandate group_portal","account_banking_mandate.model_account_banking_mandate","base.group_portal",1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_res_partner_bank_group_portal res_partner_bank group_portal base.model_res_partner_bank base.group_portal 1 0 0 0
3 access_res_bank_group_portal res_bank group_portal base.model_res_bank base.group_portal 1 0 0 0
4 access_account_banking_mandate_group_portal account_banking_mandate group_portal account_banking_mandate.model_account_banking_mandate base.group_portal 1 0 0 0

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.rule" id="res_partner_portal_members_bank_accounts_read_rule">
<field name="name">res_partner: portal: read access on my bank accounts</field>
<field name="model_id" ref="base.model_res_partner_bank" />
<field name="domain_force">[('partner_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]" />
<field name="perm_read" eval="True" />
<field name="perm_write" eval="False" />
<field name="perm_create" eval="False" />
<field name="perm_unlink" eval="False" />
</record>
<record model="ir.rule" id="res_partner_portal_bank_read_rule">
<field name="name">res_partner: portal: read access on my banks</field>
<field name="model_id" ref="base.model_res_bank" />
<field name="groups" eval="[(4, ref('base.group_portal'))]" />
<field name="perm_read" eval="True" />
<field name="perm_write" eval="False" />
<field name="perm_create" eval="False" />
<field name="perm_unlink" eval="False" />
</record>
<record model="ir.rule" id="res_partner_portal_mandate_read_rule">
<field name="name">res_partner: portal: read access on my mandates</field>
<field name="model_id" ref="account_banking_mandate.model_account_banking_mandate" />
<field name="domain_force">[('partner_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]" />
<field name="perm_read" eval="True" />
<field name="perm_write" eval="False" />
<field name="perm_create" eval="False" />
<field name="perm_unlink" eval="False" />
</record>
</odoo>

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="portal_my_bank_account" name="Bank account details">
<t t-call="portal.portal_layout">
<t t-set="additional_title">My bank account Details</t>
<form action="/my/bank_account/save" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
<div class="row o_portal_details">
<div class="col-lg-12">
<br />
<div t-if="success" class="alert alert-success py-1 mb-2">
<i class="fa fa-fw fa-check-circle" /> Data saved! </div>
<div t-if="error_message" role="alert" class="col-lg-12 alert alert-danger">
<t t-foreach="error_message" t-as="err">
<t t-out="err" />
<br />
</t>
</div>
<div class="row">
<div id="iban"
t-attf-class="form-group #{error.get('acc_number') and 'o_has_error' or ''} col-xl-12">
<label class="col-form-label" for="acc_number">IBAN : </label>
<label class="text-danger"> *</label>
<input t-if="can_edit_bank_account" type="text" name="acc_number"
required="True"
t-attf-class="form-control #{error.get('acc_number') and 'is-invalid' or ''}"
t-att-value="acc_number or bank_account.acc_number" />
<span t-if="not can_edit_bank_account" t-field="bank_account.acc_number" />
</div>
<div id="partner_id"
t-attf-class="form-group #{error.get('partner_id') and 'o_has_error' or ''} col-xl-12">
<label class="col-form-label" for="partner_id">Contact name: </label>
<input t-if="can_edit_bank_account" type="text" name="partner_id"
required="False"
readonly="True"
t-attf-class="form-control #{error.get('partner_id') and 'is-invalid' or ''}"
t-att-value="partner_id or bank_account.partner_id.name" />
<span t-if="not can_edit_bank_account" t-field="bank_account.partner_id.name" />
</div>
<div id="iban_holder_name_if_different"
t-attf-class="form-group #{error.get('acc_holder_name') and 'o_has_error' or ''} col-xl-12">
<label class="col-form-label" for="bank_id">Name of IBAN holder if different : </label>
<input t-if="can_edit_bank_account" type="text" name="acc_holder_name"
t-attf-class="form-control #{error.get('acc_holder_name') and 'is-invalid' or ''}"
t-att-value="acc_holder_name or bank_account.acc_holder_name" />
<span t-if="not can_edit_bank_account" t-field="bank_account.acc_holder_name" />
</div>
<div id="bank_id"
t-attf-class="form-group #{error.get('bank_id') and 'o_has_error' or ''} col-xl-12">
<label class="col-form-label" for="bank_id">Banque name : </label>
<select t-if="can_edit_bank_account" type="text" name="bank_id"
t-attf-class="form-control #{error.get('bank_id') and 'is-invalid' or ''}">
<option value="">My bank is not in the list</option>
<t t-foreach="banks or []" t-as="bank">
<option t-att-value="bank.id" t-att-selected="bank.id == bank_account.bank_id.id">
<t t-esc="bank.name" />
</option>
</t>
</select>
<span t-if="not can_edit_bank_account" t-field="bank_account.acc_holder_name" />
</div>
</div>
<br />
<input type="hidden" name="bank_account_id" t-att-value="bank_account_id" />
<input type="hidden" name="redirect" t-att-value="redirect" />
<div style="text-align:right;">
<button type="submit"
class="btn btn-primary ">Save
</button>
</div>
</div>
</div>
</form>
<div style="text-align:right">
<br />
<a href="/my/bank_accounts">
<span class="fa fa-arrow-left" /> Back to my bank accounts list </a>
</div>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="portal_my_bank_accounts" name="My bank accounts">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True" />
<t t-call="portal.portal_searchbar">
<t t-set="title">Bank accounts</t>
</t>
<t t-if="not bank_accounts">
<p>There are currently no bank accounts for your account.</p>
</t>
<t t-if="bank_accounts" t-call="portal.portal_table">
<thead>
<tr class="active">
<th>IBAN</th>
<th>Contact name</th>
<th>Name of IBAN holder if different</th>
<th>Number of associated mandates</th>
</tr>
</thead>
<tbody>
<t t-foreach="bank_accounts" t-as="bank_account">
<tr>
<td>
<a
t-attf-href="/my/bank_account/#{bank_account.id}?{{ keep_query() }}">
<span t-field="bank_account.acc_number" />
</a>
</td>
<td>
<span t-field="bank_account.partner_id.name" />
</td>
<td>
<span t-field="bank_account.acc_holder_name" />
</td>
<td>
<t t-if="bank_account.associated_mandate_count == 0">
No registrations
</t>
<t t-if="bank_account.associated_mandate_count == 1">
1 registration
</t>
<t t-if="bank_account.associated_mandate_count > 1">
<t t-esc="bank_account.associated_mandate_count" />
records </t>
</td>
</tr>
</t>
</tbody>
</t>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="portal_contract_page_inherit_mandate" name="My Contract inherit mandate"
inherit_id="contract.portal_contract_page" customize_show="True" priority="100">
<xpath expr="//div[@id='product_information']/div" position="inside">
<div t-if="contract.mandate_id" class="row mb-2 mb-sm-1">
<div class="col-12 col-sm-4">
<strong>Direct Debit Mandate</strong>
</div>
<div class="col-12 col-sm-8">
<span
t-field="contract.mandate_id"
/>
</div>
</div>
</xpath>
</template>
</odoo>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template
id="portal_my_home_menu_contract"
name="Portal layout : Mandate menu entries"
inherit_id="portal.portal_breadcrumbs"
priority="35"
>
<xpath expr="//ol[hasclass('o_portal_submenu')]" position="inside">
<li
t-if="page_name == 'Mandates'"
t-attf-class="breadcrumb-item #{'active ' if not contract else ''}"
>
<a
t-if="mandate"
t-attf-href="/my/mandates?{{ keep_query() }}"
>Mandates</a>
<t t-else="">Mandates</t>
</li>
<li t-if="mandate" class="breadcrumb-item active">
<t t-esc="mandate.reference" />
</li>
</xpath>
</template>
<template id="portal_my_home_bank_accounts" name="Bank accounts"
inherit_id="portal.portal_my_home" customize_show="True" priority="100">
<xpath expr="//div[hasclass('o_portal_docs')]" position="inside">
<t t-call="portal.portal_docs_entry">
<t t-set="title">Bank accounts</t>
<t t-set="url" t-value="'/my/bank_accounts'" />
<t t-set="placeholder_count" t-value="'bank_account_count'" />
</t>
<t t-call="portal.portal_docs_entry">
<t t-set="title">Mandates</t>
<t t-set="url" t-value="'/my/mandates'" />
<t t-set="placeholder_count" t-value="'mandate_count'"/>
</t>
</xpath>
</template>
</odoo>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="portal_my_mandate" name="Mandate details">
<t t-call="portal.portal_layout">
<t t-set="additional_title">My mandate details</t>
<form action="/my/mandate/save" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
<div class="row o_portal_details">
<div class="col-lg-12">
<br />
<div t-if="success" class="alert alert-success py-1 mb-2">
<i class="fa fa-fw fa-check-circle" /> Data saved! </div>
<div t-if="error_message" role="alert" class="col-lg-12 alert alert-danger">
<t t-foreach="error_message" t-as="err">
<t t-out="err" />
<br />
</t>
</div>
<div class="row">
<div id="reference"
t-attf-class="form-group #{error.get('unique_mandate_reference') and 'o_has_error' or ''} col-xl-12">
<label class="col-form-label" for="unique_mandate_reference">Reference : </label>
<label class="text-danger"> *</label>
<input t-if="can_edit_mandate" type="text" name="unique_mandate_reference"
required="True"
t-attf-class="form-control #{error.get('unique_mandate_reference') and 'is-invalid' or ''}"
t-att-value="unique_mandate_reference or mandate.unique_mandate_reference" />
<span t-if="not can_edit_mandate" t-field="mandate.unique_mandate_reference" />
</div>
<div id="format"
t-attf-class="form-group #{error.get('format') and 'o_has_error' or ''} col-xl-12">
<label class="col-form-label" for="format">Format : </label>
<label class="text-danger"> *</label>
<input t-if="can_edit_mandate" type="text" name="format"
required="True"
t-attf-class="form-control #{error.get('format') and 'is-invalid' or ''}"
t-att-value="format or mandate.format" />
<span t-if="not can_edit_mandate" t-field="mandate.format" />
</div>
</div>
<br />
<input type="hidden" name="mandate_id" t-att-value="mandate_id" />
<input type="hidden" name="redirect" t-att-value="redirect" />
<div style="text-align:right;">
<button type="submit"
class="btn btn-primary ">Save
</button>
</div>
</div>
</div>
</form>
<div style="text-align:right">
<br />
<a href="/my/mandates">
<span class="fa fa-arrow-left" /> Back to my mandates list </a>
</div>
</t>
</template>
</odoo>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="portal_my_mandates" name="My mandates">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True" />
<t t-call="portal.portal_searchbar">
<t t-set="title">Mandates</t>
</t>
<t t-if="not mandates">
<p>There are currently no mandates for your account.</p>
</t>
<t t-if="mandates" t-call="portal.portal_table">
<thead>
<tr class="active">
<th>Reference</th>
<th>Format</th>
<th>Contact name</th>
<th>Signature date</th>
<th>Last debit date</th>
<th>Status</th>
<th>Associed IBAN</th>
</tr>
</thead>
<tbody>
<t t-foreach="mandates" t-as="mandate">
<tr>
<td>
<span t-field="mandate.unique_mandate_reference" />
</td>
<td>
<span t-field="mandate.format" />
</td>
<td>
<span t-field="mandate.partner_id" />
</td>
<td>
<span t-field="mandate.last_debit_date" />
</td>
<td>
<span t-field="mandate.signature_date" />
</td>
<td>
<span t-field="mandate.state" />
</td>
<td>
<a t-attf-href="/my/bank_account/#{mandate.partner_bank_id.id}?{{ keep_query() }}">
<span t-field="mandate.partner_bank_id.acc_number" />
</a>
</td>
</tr>
</t>
</tbody>
</t>
</t>
</template>
</odoo>