From c509a1be9bcacb5c5a3f05bcbf0bc94360537e9f Mon Sep 17 00:00:00 2001 From: madmooose <31863594+madmooose@users.noreply.github.com> Date: Wed, 30 Jul 2025 12:26:09 +0200 Subject: [PATCH] [112] [ADD] account_payment_portal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [WIP] account_payment_portal: first draft * [ADD] account_payment_portal * [IMP] account_payment_portal: Fix user rights and add test --------- Co-authored-by: Niels Göttsch --- account_payment_portal/README.rst | 68 +++ account_payment_portal/__init__.py | 2 + account_payment_portal/__manifest__.py | 15 + .../controllers/__init__.py | 1 + account_payment_portal/controllers/portal.py | 151 +++++++ account_payment_portal/i18n/de.po | 125 +++++ account_payment_portal/models/__init__.py | 1 + .../models/account_payment.py | 14 + .../readme/CONTRIBUTORS.rst | 2 + account_payment_portal/readme/DESCRIPTION.rst | 1 + account_payment_portal/readme/HISTORY.rst | 3 + .../security/account_security.xml | 14 + .../security/ir.model.access.csv | 2 + .../static/description/icon.png | Bin 0 -> 12282 bytes .../static/description/index.html | 426 ++++++++++++++++++ account_payment_portal/tests/__init__.py | 1 + .../tests/test_account_payment.py | 58 +++ .../views/portal_templates.xml | 95 ++++ .../odoo/addons/account_payment_portal | 1 + setup/account_payment_portal/setup.py | 6 + 20 files changed, 986 insertions(+) create mode 100644 account_payment_portal/README.rst create mode 100644 account_payment_portal/__init__.py create mode 100644 account_payment_portal/__manifest__.py create mode 100644 account_payment_portal/controllers/__init__.py create mode 100644 account_payment_portal/controllers/portal.py create mode 100644 account_payment_portal/i18n/de.po create mode 100644 account_payment_portal/models/__init__.py create mode 100644 account_payment_portal/models/account_payment.py create mode 100644 account_payment_portal/readme/CONTRIBUTORS.rst create mode 100644 account_payment_portal/readme/DESCRIPTION.rst create mode 100644 account_payment_portal/readme/HISTORY.rst create mode 100644 account_payment_portal/security/account_security.xml create mode 100644 account_payment_portal/security/ir.model.access.csv create mode 100644 account_payment_portal/static/description/icon.png create mode 100644 account_payment_portal/static/description/index.html create mode 100644 account_payment_portal/tests/__init__.py create mode 100644 account_payment_portal/tests/test_account_payment.py create mode 100644 account_payment_portal/views/portal_templates.xml create mode 120000 setup/account_payment_portal/odoo/addons/account_payment_portal create mode 100644 setup/account_payment_portal/setup.py diff --git a/account_payment_portal/README.rst b/account_payment_portal/README.rst new file mode 100644 index 0000000..9c2708d --- /dev/null +++ b/account_payment_portal/README.rst @@ -0,0 +1,68 @@ +====================== +Account Payment Portal +====================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:d961a48b7e2fd11928567081091441bb33770a0f2b9b3b1f4f853a95d5589cec + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-bruecksen%2Fnfu-lightgray.png?logo=github + :target: https://github.com/bruecksen/nfu/tree/16.0/account_payment_portal + :alt: bruecksen/nfu + +|badge1| |badge2| |badge3| + +Add a Portal View for Customer Payments + +**Table of contents** + +.. contents:: + :local: + +Changelog +========= + +- 16.0.1.0.0: Initial module +- 16.0.1.0.1: Fix security rules +- 16.0.1.0.2: Add tests + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* BAKEUP + +Contributors +~~~~~~~~~~~~ + +* Niels Göttsch +* Matthias Brück + +Maintainers +~~~~~~~~~~~ + +This module is part of the `bruecksen/nfu `_ project on GitHub. + +You are welcome to contribute. diff --git a/account_payment_portal/__init__.py b/account_payment_portal/__init__.py new file mode 100644 index 0000000..91c5580 --- /dev/null +++ b/account_payment_portal/__init__.py @@ -0,0 +1,2 @@ +from . import controllers +from . import models diff --git a/account_payment_portal/__manifest__.py b/account_payment_portal/__manifest__.py new file mode 100644 index 0000000..204c12b --- /dev/null +++ b/account_payment_portal/__manifest__.py @@ -0,0 +1,15 @@ +{ + "name": "Account Payment Portal", + "summary": "Add a Portal View for Customer Payments", + "author": "BAKEUP", + "website": "https://www.bakeup.org", + "category": "Accounting", + "version": "16.0.1.0.2", + "depends": ["account", "portal"], + "data": [ + "security/ir.model.access.csv", + "security/account_security.xml", + "views/portal_templates.xml", + ], + "license": "LGPL-3", +} diff --git a/account_payment_portal/controllers/__init__.py b/account_payment_portal/controllers/__init__.py new file mode 100644 index 0000000..8c3feb6 --- /dev/null +++ b/account_payment_portal/controllers/__init__.py @@ -0,0 +1 @@ +from . import portal diff --git a/account_payment_portal/controllers/portal.py b/account_payment_portal/controllers/portal.py new file mode 100644 index 0000000..be6d430 --- /dev/null +++ b/account_payment_portal/controllers/portal.py @@ -0,0 +1,151 @@ +from collections import OrderedDict + +from odoo import _, http +from odoo.http import request +from odoo.osv import expression + +from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager + + +class AccountPaymentPortal(CustomerPortal): + def _prepare_home_portal_values(self, counters): + values = super()._prepare_home_portal_values(counters) + if "payment_count" in counters: + payment_count = ( + request.env["account.payment"].search_count(self._get_payment_domain()) + if request.env["account.payment"].check_access_rights( + "read", raise_exception=False + ) + else 0 + ) + values["payment_count"] = payment_count + return values + + # ------------------------------------------------------------ + # My Payments + # ------------------------------------------------------------ + + def _payment_get_page_view_values(self, payment, access_token, **kwargs): + values = {"page_name": "payment", "payment": payment} + return self._get_page_view_values( + payment, access_token, values, "my_invoices_history", False, **kwargs + ) + + def _get_payment_domain(self): + return [("is_internal_transfer", "=", False)] + + def _get_account_searchbar_sortings(self): + return { + "date": {"label": _("Date"), "order": "date desc"}, + "name": {"label": _("Reference"), "order": "name desc"}, + "state": {"label": _("Status"), "order": "state"}, + } + + def _get_account_searchbar_filters(self): + # Add filter for payments and credit notes? + return { + "all": {"label": _("All"), "domain": []}, + "sent": { + "label": _("Sent"), + "domain": [("payment_type", "=", "inbound")], + }, + "received": { + "label": _("Received"), + "domain": [("payment_type", "=", "outbound")], + }, + } + + @http.route( + ["/my/payments", "/my/payments/page/"], + type="http", + auth="user", + website=True, + ) + def portal_my_payments( + self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, **kw + ): + values = self._prepare_my_payments_values( + page, date_begin, date_end, sortby, filterby + ) + + # pager + pager = portal_pager(**values["pager"]) + + # content according to pager and archive selected + payments = values["payments"](pager["offset"]) + request.session["my_payments_history"] = payments.ids[:100] + + values.update({"payments": payments, "pager": pager}) + + return request.render("account_payment_portal.portal_my_payments", values) + + def _prepare_my_payments_values( + self, + page, + date_begin, + date_end, + sortby, + filterby, + domain=None, + url="/my/payments", + ): + values = self._prepare_portal_layout_values() + AccountPayment = request.env["account.payment"] + + domain = expression.AND([domain or [], self._get_payment_domain()]) + + searchbar_sortings = self._get_account_searchbar_sortings() + # default sort by order + if not sortby: + sortby = "date" + order = searchbar_sortings[sortby]["order"] + + searchbar_filters = self._get_account_searchbar_filters() + # default filter by value + if not filterby: + filterby = "all" + domain += searchbar_filters[filterby]["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 + "payments": lambda pager_offset: ( + AccountPayment.search( + domain, + order=order, + limit=self._items_per_page, + offset=pager_offset, + ) + if AccountPayment.check_access_rights("read", raise_exception=False) + else AccountPayment + ), + "page_name": "payment", + "pager": { # vals to define the pager. + "url": url, + "url_args": { + "date_begin": date_begin, + "date_end": date_end, + "sortby": sortby, + }, + "total": AccountPayment.search_count(domain) + if AccountPayment.check_access_rights("read", raise_exception=False) + else 0, + "page": page, + "step": self._items_per_page, + }, + "default_url": url, + "searchbar_sortings": searchbar_sortings, + "sortby": sortby, + "searchbar_filters": OrderedDict(sorted(searchbar_filters.items())), + "filterby": filterby, + } + ) + return values diff --git a/account_payment_portal/i18n/de.po b/account_payment_portal/i18n/de.po new file mode 100644 index 0000000..04500ad --- /dev/null +++ b/account_payment_portal/i18n/de.po @@ -0,0 +1,125 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_payment_portal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-07-29 10:44+0000\n" +"PO-Revision-Date: 2025-07-29 10:44+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: account_payment_portal +#: model_terms:ir.ui.view,arch_db:account_payment_portal.portal_my_payments +msgid "" +" Draft" +msgstr "" +" Entwurf" + +#. module: account_payment_portal +#: model_terms:ir.ui.view,arch_db:account_payment_portal.portal_my_payments +msgid "" +" Posted" +msgstr "" +" Registriert" + +#. module: account_payment_portal +#: model_terms:ir.ui.view,arch_db:account_payment_portal.portal_my_payments +msgid "" +" Cancelled" +msgstr "" +" Abgebrochen" + +#. module: account_payment_portal +#. odoo-python +#: code:addons/account_payment_portal/controllers/portal.py:0 +#, python-format +msgid "All" +msgstr "Alle" + +#. module: account_payment_portal +#: model_terms:ir.ui.view,arch_db:account_payment_portal.portal_my_payments +msgid "Amount" +msgstr "Betrag" + +#. module: account_payment_portal +#. odoo-python +#: code:addons/account_payment_portal/controllers/portal.py:0 +#, python-format +msgid "Date" +msgstr "Datum" + +#. module: account_payment_portal +#: model_terms:ir.ui.view,arch_db:account_payment_portal.portal_my_payments +msgid "Payment #" +msgstr "Zahlung #" + +#. module: account_payment_portal +#: model:ir.model.fields,field_description:account_payment_portal.field_account_payment__payment_count +msgid "Payment Count" +msgstr "" + +#. module: account_payment_portal +#: model_terms:ir.ui.view,arch_db:account_payment_portal.portal_my_payments +msgid "Payment Date" +msgstr "Zahldatum" + +#. module: account_payment_portal +#: model:ir.model,name:account_payment_portal.model_account_payment +#: model_terms:ir.ui.view,arch_db:account_payment_portal.portal_my_home_menu_payment +#: model_terms:ir.ui.view,arch_db:account_payment_portal.portal_my_home_payment +#: model_terms:ir.ui.view,arch_db:account_payment_portal.portal_my_payments +msgid "Payments" +msgstr "Zahlungen" + +#. module: account_payment_portal +#. odoo-python +#: code:addons/account_payment_portal/controllers/portal.py:0 +#, python-format +msgid "Received" +msgstr "Empfangen" + +#. module: account_payment_portal +#. odoo-python +#: code:addons/account_payment_portal/controllers/portal.py:0 +#, python-format +msgid "Reference" +msgstr "Referenz" + +#. module: account_payment_portal +#. odoo-python +#: code:addons/account_payment_portal/controllers/portal.py:0 +#, python-format +msgid "Sent" +msgstr "Gesendet" + +#. module: account_payment_portal +#. odoo-python +#: code:addons/account_payment_portal/controllers/portal.py:0 +#: model_terms:ir.ui.view,arch_db:account_payment_portal.portal_my_payments +#, python-format +msgid "Status" +msgstr "" + +#. module: account_payment_portal +#: model_terms:ir.ui.view,arch_db:account_payment_portal.portal_my_payments +msgid "There are currently no payments for your account." +msgstr "Es gibt keine Zahlungen für diesen Account." diff --git a/account_payment_portal/models/__init__.py b/account_payment_portal/models/__init__.py new file mode 100644 index 0000000..ab350b8 --- /dev/null +++ b/account_payment_portal/models/__init__.py @@ -0,0 +1 @@ +from . import account_payment diff --git a/account_payment_portal/models/account_payment.py b/account_payment_portal/models/account_payment.py new file mode 100644 index 0000000..08d40e6 --- /dev/null +++ b/account_payment_portal/models/account_payment.py @@ -0,0 +1,14 @@ +from odoo import api, fields, models + + +class AccountPayment(models.Model): + _inherit = "account.payment" + + payment_count = fields.Integer(compute="_compute_count_payment") + + @api.depends("partner_id") + def _compute_count_payment(self): + for payment in self: + payment.payment_count = self.env["account.payment"].search_count( + [("partner_id", "=", payment.partner_id.id)] + ) diff --git a/account_payment_portal/readme/CONTRIBUTORS.rst b/account_payment_portal/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..a4ec453 --- /dev/null +++ b/account_payment_portal/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Niels Göttsch +* Matthias Brück \ No newline at end of file diff --git a/account_payment_portal/readme/DESCRIPTION.rst b/account_payment_portal/readme/DESCRIPTION.rst new file mode 100644 index 0000000..267f175 --- /dev/null +++ b/account_payment_portal/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Add a Portal View for Customer Payments \ No newline at end of file diff --git a/account_payment_portal/readme/HISTORY.rst b/account_payment_portal/readme/HISTORY.rst new file mode 100644 index 0000000..9af7916 --- /dev/null +++ b/account_payment_portal/readme/HISTORY.rst @@ -0,0 +1,3 @@ +- 16.0.1.0.0: Initial module +- 16.0.1.0.1: Fix security rules +- 16.0.1.0.2: Add tests diff --git a/account_payment_portal/security/account_security.xml b/account_payment_portal/security/account_security.xml new file mode 100644 index 0000000..9c7332d --- /dev/null +++ b/account_payment_portal/security/account_security.xml @@ -0,0 +1,14 @@ + + + + + Portal Personal Account Payments + + [('partner_id','=', user.partner_id.id),('move_type','=','entry')] + + + + + + + diff --git a/account_payment_portal/security/ir.model.access.csv b/account_payment_portal/security/ir.model.access.csv new file mode 100644 index 0000000..17cc2c5 --- /dev/null +++ b/account_payment_portal/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_payment_portal,account.payment.portal,account.model_account_payment,base.group_portal,1,0,0,0 diff --git a/account_payment_portal/static/description/icon.png b/account_payment_portal/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..126771222c96d9acfa22f7f2d27f6f4b9ce7feb6 GIT binary patch literal 12282 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

W$<1~*|NZw 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 literal 0 HcmV?d00001 diff --git a/account_payment_portal/static/description/index.html b/account_payment_portal/static/description/index.html new file mode 100644 index 0000000..9478a9b --- /dev/null +++ b/account_payment_portal/static/description/index.html @@ -0,0 +1,426 @@ + + + + + +Account Payment Portal + + + +

+

Account Payment Portal

+ + +

Beta License: LGPL-3 bruecksen/nfu

+

Add a Portal View for Customer Payments

+

Table of contents

+ +
+

Changelog

+
    +
  • 16.0.1.0.0: Initial module
  • +
  • 16.0.1.0.1: Fix security rules
  • +
  • 16.0.1.0.2: Add tests
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • BAKEUP
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the bruecksen/nfu project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/account_payment_portal/tests/__init__.py b/account_payment_portal/tests/__init__.py new file mode 100644 index 0000000..0c265a1 --- /dev/null +++ b/account_payment_portal/tests/__init__.py @@ -0,0 +1 @@ +from . import test_account_payment diff --git a/account_payment_portal/tests/test_account_payment.py b/account_payment_portal/tests/test_account_payment.py new file mode 100644 index 0000000..bc702eb --- /dev/null +++ b/account_payment_portal/tests/test_account_payment.py @@ -0,0 +1,58 @@ +from odoo.tests import tagged +from odoo.tests.common import TransactionCase + + +@tagged("post_install", "-at_install") +class TestAccountPaymentPortal(TransactionCase): + def setUp(self): + super().setUp() + # Create two partners + portal_partner = self.env["res.partner"].create({"name": "Portal User"}) + other_partner = self.env["res.partner"].create({"name": "Other User"}) + + # Create two users, one portal + portal_group = self.env.ref("base.group_portal") + portal_user = self.env["res.users"].create( + { + "name": "Portal User", + "login": "portaluser@example.com", + "partner_id": portal_partner.id, + "groups_id": [(6, 0, [portal_group.id])], + } + ) + other_user = self.env["res.users"].create( + { + "name": "Other User", + "login": "otheruser@example.com", + "partner_id": other_partner.id, + } + ) + + # Create two payments + portal_partner_payment = self.env["account.payment"].create( + { + "partner_id": portal_partner.id, + "amount": 100, + "payment_type": "inbound", + "state": "draft", + } + ) + other_partner_payment = self.env["account.payment"].create( + { + "partner_id": other_partner.id, + "amount": 200, + "payment_type": "inbound", + "state": "draft", + } + ) + + self.portal_user = portal_user + self.other_user = other_user + self.portal_partner_payment = portal_partner_payment + self.other_partner_payment = other_partner_payment + + def test_portal_user_sees_only_own_payment(self): + # Switch to portal user context + payments = self.env["account.payment"].with_user(self.portal_user.id).search([]) + self.assertIn(self.portal_partner_payment, payments) + self.assertNotIn(self.other_partner_payment, payments) diff --git a/account_payment_portal/views/portal_templates.xml b/account_payment_portal/views/portal_templates.xml new file mode 100644 index 0000000..c93d6a5 --- /dev/null +++ b/account_payment_portal/views/portal_templates.xml @@ -0,0 +1,95 @@ + + + + + + + diff --git a/setup/account_payment_portal/odoo/addons/account_payment_portal b/setup/account_payment_portal/odoo/addons/account_payment_portal new file mode 120000 index 0000000..0bdecbb --- /dev/null +++ b/setup/account_payment_portal/odoo/addons/account_payment_portal @@ -0,0 +1 @@ +../../../../account_payment_portal \ No newline at end of file diff --git a/setup/account_payment_portal/setup.py b/setup/account_payment_portal/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/account_payment_portal/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)