diff --git a/fieldservice_portal/README.rst b/fieldservice_portal/README.rst new file mode 100644 index 0000000000..7ea89b8e83 --- /dev/null +++ b/fieldservice_portal/README.rst @@ -0,0 +1,94 @@ +====================== +Field Service - Portal +====================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:4e8d8551196ecf8c3769f0a0284b7620794bfbd83a438d3eb54113c400f629d8 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Ffield--service-lightgray.png?logo=github + :target: https://github.com/OCA/field-service/tree/17.0/fieldservice_portal + :alt: OCA/field-service +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/field-service-17-0/field-service-17-0-fieldservice_portal + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/field-service&target_branch=17.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Bridge module between fieldservice and portal that allows portal users +to monitor work orders related to their locations. + +**Table of contents** + +.. contents:: + :local: + +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 +------- + +* PyTech SRL + +Contributors +------------ + +- `PyTech SRL `__: + + - Alessio Renda + +- `APSL-Nagarro `__: + + - Antoni Marroig + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-aleuffre| image:: https://github.com/aleuffre.png?size=40px + :target: https://github.com/aleuffre + :alt: aleuffre +.. |maintainer-renda-dev| image:: https://github.com/renda-dev.png?size=40px + :target: https://github.com/renda-dev + :alt: renda-dev + +Current `maintainers `__: + +|maintainer-aleuffre| |maintainer-renda-dev| + +This module is part of the `OCA/field-service `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fieldservice_portal/__init__.py b/fieldservice_portal/__init__.py new file mode 100644 index 0000000000..e046e49fbe --- /dev/null +++ b/fieldservice_portal/__init__.py @@ -0,0 +1 @@ +from . import controllers diff --git a/fieldservice_portal/__manifest__.py b/fieldservice_portal/__manifest__.py new file mode 100644 index 0000000000..da12e3d5b2 --- /dev/null +++ b/fieldservice_portal/__manifest__.py @@ -0,0 +1,33 @@ +{ + "name": "Field Service - Portal", + "version": "17.0.1.0.0", + "summary": """ + Bridge module between fieldservice and portal. + """, + "depends": [ + "fieldservice", + "portal", + ], + "author": "PyTech SRL, Odoo Community Association (OCA)", + "maintainers": ["aleuffre", "renda-dev"], + "website": "https://github.com/OCA/field-service", + "category": "Field Service", + "license": "AGPL-3", + "data": [ + "security/ir.model.access.csv", + "security/portal_security.xml", + "views/fsm_order_template.xml", + "views/portal_template.xml", + ], + "demo": [ + "demo/fsm_location_demo.xml", + "demo/fsm_order_demo.xml", + ], + "assets": { + "web.assets_frontend": [ + "fieldservice_portal/static/src/js/fsm_order_portal.js", + ], + }, + "installable": True, + "application": False, +} diff --git a/fieldservice_portal/controllers/__init__.py b/fieldservice_portal/controllers/__init__.py new file mode 100644 index 0000000000..d09bc16471 --- /dev/null +++ b/fieldservice_portal/controllers/__init__.py @@ -0,0 +1 @@ +from . import fsm_order_portal diff --git a/fieldservice_portal/controllers/fsm_order_portal.py b/fieldservice_portal/controllers/fsm_order_portal.py new file mode 100644 index 0000000000..81f3282562 --- /dev/null +++ b/fieldservice_portal/controllers/fsm_order_portal.py @@ -0,0 +1,213 @@ +from collections import OrderedDict +from operator import itemgetter + +from odoo import _, http +from odoo.exceptions import AccessError +from odoo.http import request +from odoo.osv.expression import OR +from odoo.tools import groupby as groupbyelem + +from odoo.addons.portal.controllers.portal import CustomerPortal +from odoo.addons.portal.controllers.portal import pager as portal_pager + + +class CustomerPortal(CustomerPortal): + def _prepare_home_portal_values(self, counters): + values = super()._prepare_home_portal_values(counters) + if "fsm_order_count" in counters: + fsm_order_count = ( + request.env["fsm.order"].search_count([]) + if request.env["fsm.order"].check_access_rights( + "read", raise_exception=False + ) + else 0 + ) + values["fsm_order_count"] = fsm_order_count + return values + + def _fsm_order_check_access(self, order_id): + order = request.env["fsm.order"].browse([order_id]) + + try: + order.check_access_rights("read") + order.check_access_rule("read") + except AccessError: + raise + return order.sudo() + + def fsm_order_get_page_view_values(self, order, **kwargs): + values = { + "page_name": "fsm_order", + "order": order, + } + + if kwargs.get("error"): + values["error"] = kwargs["error"] + if kwargs.get("warning"): + values["warning"] = kwargs["warning"] + if kwargs.get("success"): + values["success"] = kwargs["success"] + + return values + + @http.route( + ["/my/fsm_orders", "/my/fsm_orders/page/"], + type="http", + auth="user", + website=True, + ) + def portal_my_fsm_orders( + self, + page=1, + date_begin=None, + date_end=None, + sortby=None, + filterby=None, + groupby=None, + search=None, + search_in="all", + **kw, + ): + values = self._prepare_portal_layout_values() + FsmOrder = request.env["fsm.order"] + domain = [] + + searchbar_sortings = { + "date": {"label": _("Newest"), "order": "request_early desc"}, + "name": {"label": _("Name"), "order": "name"}, + "stage": {"label": _("Stage"), "order": "stage_id"}, + "location": {"label": _("Location"), "order": "location_id"}, + "type": {"label": _("Type"), "order": "type"}, + } + + searchbar_groupby = { + "none": {"input": "none", "label": _("None")}, + "location_id": {"input": "location", "label": _("Location")}, + "ticket_id": {"input": "ticket", "label": _("Ticket")}, + "stage_id": {"input": "stage", "label": _("Stage")}, + "type": {"input": "type", "label": _("Type")}, + } + + # search input (text) + searchbar_inputs = OrderedDict( + ( + ("all", {"input": "all", "label": _("Search in All")}), + ("name", {"input": "name", "label": _("Search in WO Number")}), + ( + "description", + { + "input": "description", + "label": _("Search in Description"), + }, + ), + ( + "location_id.name", + { + "input": "location", + "label": _("Search in Location Numbers"), + }, + ), + ) + ) + + if search and search_in: + search_domain = [] + for search_property in [ + k + for (k, v) in searchbar_inputs.items() + if search_in in (v["input"], "all") and k != "all" + ]: + search_domain = OR( + [search_domain, [(search_property, "ilike", search)]] + ) + domain += search_domain + + # search filters (by stage) + searchbar_filters = OrderedDict( + ( + str(stage.name), + { + "label": stage.name, + "domain": [("stage_id", "=", stage.id)], + }, + ) + for stage in request.env["fsm.stage"].search([("stage_type", "=", "order")]) + ) + searchbar_filters.update( + { + "all": {"label": _("All"), "domain": []}, + "open": {"label": _("Open"), "domain": [("is_closed", "=", False)]}, + } + ) + + # default group by value + if not groupby: + groupby = "location_id" + # default sort by order + if not sortby: + sortby = "date" + order = searchbar_sortings[sortby]["order"] + # default filter by value + if not filterby: + filterby = "open" + domain += searchbar_filters[filterby]["domain"] + + # count for pager + fsm_order_count = FsmOrder.search_count(domain) + # pager + pager = portal_pager( + url="/my/fsm_orders", + url_args={}, + total=fsm_order_count, + page=page, + step=self._items_per_page, + ) + # content according to pager and archive selected + fsm_orders = FsmOrder.search( + domain, + order=order, + limit=self._items_per_page, + offset=pager["offset"], + ) + + if groupby == "none": + grouped_orders = [fsm_orders] if fsm_orders else [] + else: + grouped_orders = [ + FsmOrder.concat(*g) + for k, g in groupbyelem(fsm_orders, itemgetter(groupby)) + ] + + values.update( + { + "date": date_begin, + "grouped_orders": grouped_orders, + "page_name": "fsm_order", + "pager": pager, + "default_url": "/my/fsm_orders", + "searchbar_sortings": searchbar_sortings, + "searchbar_groupby": searchbar_groupby, + "searchbar_inputs": searchbar_inputs, + "search_in": search_in, + "sortby": sortby, + "groupby": groupby, + "searchbar_filters": searchbar_filters, + "filterby": filterby, + } + ) + return request.render("fieldservice_portal.portal_my_fsm_orders", values) + + @http.route( + ["/my/fsm_order/"], + type="http", + website=True, + ) + def portal_my_fsm_order(self, order_id=None, **kw): + try: + order_sudo = self._fsm_order_check_access(order_id) + except AccessError: + return request.redirect("/my") + values = self.fsm_order_get_page_view_values(order_sudo, **kw) + return request.render( + "fieldservice_portal.portal_fieldservice_order_page", values + ) diff --git a/fieldservice_portal/demo/fsm_location_demo.xml b/fieldservice_portal/demo/fsm_location_demo.xml new file mode 100644 index 0000000000..d2137b34d0 --- /dev/null +++ b/fieldservice_portal/demo/fsm_location_demo.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/fieldservice_portal/demo/fsm_order_demo.xml b/fieldservice_portal/demo/fsm_order_demo.xml new file mode 100644 index 0000000000..0bde574bd9 --- /dev/null +++ b/fieldservice_portal/demo/fsm_order_demo.xml @@ -0,0 +1,8 @@ + + + + Demo Order + Description for the new demo order + + + diff --git a/fieldservice_portal/i18n/ca.po b/fieldservice_portal/i18n/ca.po new file mode 100644 index 0000000000..8d956e30bf --- /dev/null +++ b/fieldservice_portal/i18n/ca.po @@ -0,0 +1,207 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fieldservice_portal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-10-30 08:08+0000\n" +"PO-Revision-Date: 2024-10-30 08:08+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: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "" +"
\n" +" Arrival On Site:" +msgstr "" +"
\n" +" Arribada al lloc:" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "" +"
\n" +" City:" +msgstr "" +"
\n" +" Ciutat:" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "" +"
\n" +" Country:" +msgstr "" +"
\n" +" País:" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "" +"
\n" +" Work Type:" +msgstr "" +"
\n" +" Tipus de treball:" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "N/A" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "Location:" +msgstr "Ubicació:" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "Stage:" +msgstr "Etapa:" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "All" +msgstr "Tot" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +msgid "Description" +msgstr "Descripció" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_home_fsm_order +msgid "FSM Orders" +msgstr "Comandes del servei de camp" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "History" +msgstr "Historial" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +#, python-format +msgid "Location" +msgstr "Ubicació" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +#, python-format +msgid "Name" +msgstr "Nom" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Newest" +msgstr "El més nou" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "None" +msgstr "Cap" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "Not started yet" +msgstr "Encara no ha començat" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Open" +msgstr "Obert" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Search in All" +msgstr "Cerca a tot" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Search in Description" +msgstr "Cerca a la descripció" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Search in Location Numbers" +msgstr "Cerca als números d'ubicació" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Search in WO Number" +msgstr "Cerca al número de la ordre de treball" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +#, python-format +msgid "Stage" +msgstr "Etapa" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +msgid "There are no Work Orders in your account." +msgstr "No hi ha ordres de treball al vostre compte." + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Ticket" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +#, python-format +msgid "Type" +msgstr "Tipus" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_home_fsm_order +msgid "View your fieldservice orders" +msgstr "Consulteu les vostres comandes de servei de camp" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +msgid "Work Orders" +msgstr "Ordres de treball" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +msgid "Work Orders for" +msgstr "Ordres de treball per" diff --git a/fieldservice_portal/i18n/es.po b/fieldservice_portal/i18n/es.po new file mode 100644 index 0000000000..dd53ecee94 --- /dev/null +++ b/fieldservice_portal/i18n/es.po @@ -0,0 +1,207 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fieldservice_portal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-10-30 08:08+0000\n" +"PO-Revision-Date: 2024-10-30 08:08+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: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "" +"
\n" +" Arrival On Site:" +msgstr "" +"
\n" +" Llegada al lugar:" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "" +"
\n" +" City:" +msgstr "" +"
\n" +" Ciudad:" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "" +"
\n" +" Country:" +msgstr "" +"
\n" +" País:" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "" +"
\n" +" Work Type:" +msgstr "" +"
\n" +" Tipo de trabajo:" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "N/A" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "Location:" +msgstr "Ubicación:" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "Stage:" +msgstr "Etapa:" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "All" +msgstr "Todo" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +msgid "Description" +msgstr "Descripción" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_home_fsm_order +msgid "FSM Orders" +msgstr "Pedidos del servicios de campo" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "History" +msgstr "Historial" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +#, python-format +msgid "Location" +msgstr "UBicación" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +#, python-format +msgid "Name" +msgstr "Nombre" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Newest" +msgstr "El más nuevo" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "None" +msgstr "Ninguno" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "Not started yet" +msgstr "Todavía no ha empezado" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Open" +msgstr "Abierto" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Search in All" +msgstr "Busca en todo" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Search in Description" +msgstr "Busca en descripción" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Search in Location Numbers" +msgstr "Busca en numeros de ubicación" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Search in WO Number" +msgstr "Buscar en número de orden de trabajo" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +#, python-format +msgid "Stage" +msgstr "Etapa" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +msgid "There are no Work Orders in your account." +msgstr "No hay órdenes de trabajo en su cuenta." + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Ticket" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +#, python-format +msgid "Type" +msgstr "Tipo" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_home_fsm_order +msgid "View your fieldservice orders" +msgstr "Consulta sus pedidos de servicio de campo" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +msgid "Work Orders" +msgstr "Órdenes de trabajo" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +msgid "Work Orders for" +msgstr "Órdenes de trabajo para" diff --git a/fieldservice_portal/i18n/fieldservice_portal.pot b/fieldservice_portal/i18n/fieldservice_portal.pot new file mode 100644 index 0000000000..fa9ce4de06 --- /dev/null +++ b/fieldservice_portal/i18n/fieldservice_portal.pot @@ -0,0 +1,192 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fieldservice_portal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \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: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "" +"
\n" +" Arrival On Site:" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "" +"
\n" +" City:" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "" +"
\n" +" Country:" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "" +"
\n" +" Work Type:" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "N/A" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "Location:" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "Stage:" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "All" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +msgid "Description" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_home_fsm_order +msgid "FSM Orders" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "History" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +#, python-format +msgid "Location" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +#, python-format +msgid "Name" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Newest" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "None" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_fieldservice_order_page +msgid "Not started yet" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Open" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Search in All" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Search in Description" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Search in Location Numbers" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Search in WO Number" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +#, python-format +msgid "Stage" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +msgid "There are no Work Orders in your account." +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#, python-format +msgid "Ticket" +msgstr "" + +#. module: fieldservice_portal +#. odoo-python +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: code:addons/fieldservice_portal/controllers/fsm_order_portal.py:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +#, python-format +msgid "Type" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +msgid "Work Orders" +msgstr "" + +#. module: fieldservice_portal +#: model_terms:ir.ui.view,arch_db:fieldservice_portal.portal_my_fsm_orders +msgid "Work Orders for" +msgstr "" diff --git a/fieldservice_portal/pyproject.toml b/fieldservice_portal/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/fieldservice_portal/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/fieldservice_portal/readme/CONTRIBUTORS.md b/fieldservice_portal/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..f7b0824f78 --- /dev/null +++ b/fieldservice_portal/readme/CONTRIBUTORS.md @@ -0,0 +1,6 @@ +- [PyTech SRL](https://www.pytech.it): + + > - Alessio Renda \<\> +- [APSL-Nagarro](): + + > - Antoni Marroig \<\> \ No newline at end of file diff --git a/fieldservice_portal/readme/DESCRIPTION.md b/fieldservice_portal/readme/DESCRIPTION.md new file mode 100644 index 0000000000..13440bbbf7 --- /dev/null +++ b/fieldservice_portal/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +Bridge module between fieldservice and portal that allows portal users +to monitor work orders related to their locations. diff --git a/fieldservice_portal/security/ir.model.access.csv b/fieldservice_portal/security/ir.model.access.csv new file mode 100644 index 0000000000..4161fb7bcf --- /dev/null +++ b/fieldservice_portal/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_fsm_order_portal,fieldservice.order.portal,fieldservice.model_fsm_order,base.group_portal,1,0,0,0 +access_fsm_stage_portal,fieldservice.stage.portal,fieldservice.model_fsm_stage,base.group_portal,1,0,0,0 +access_fsm_order_type_portal,fieldservice.order.type.portal,fieldservice.model_fsm_order_type,base.group_portal,1,0,0,0 diff --git a/fieldservice_portal/security/portal_security.xml b/fieldservice_portal/security/portal_security.xml new file mode 100644 index 0000000000..9e3dea0ccf --- /dev/null +++ b/fieldservice_portal/security/portal_security.xml @@ -0,0 +1,36 @@ + + + + + Portal Personal Orders + + ['|','|', + ('location_id.owner_id', 'child_of', [user.commercial_partner_id.id]), + ('location_id.contact_id', 'child_of', [user.commercial_partner_id.id]), + ('message_partner_ids','child_of',[user.commercial_partner_id.id])] + + + + + Portal Personal Locations + + ['|','|', + ('owner_id', 'child_of', [user.commercial_partner_id.id]), + ('contact_id', 'child_of', [user.commercial_partner_id.id]), + ('message_partner_ids','child_of',[user.commercial_partner_id.id])] + + + + + Portal Personal Locations + + ['&', + ('service_location_id', '!=', False), + '|','|', + ('service_location_id.owner_id', 'child_of', [user.commercial_partner_id.id]), + ('service_location_id.contact_id', 'child_of', [user.commercial_partner_id.id]), + ('service_location_id.message_partner_ids','child_of', [user.commercial_partner_id.id])] + + + + diff --git a/fieldservice_portal/static/description/icon.png b/fieldservice_portal/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/fieldservice_portal/static/description/icon.png differ diff --git a/fieldservice_portal/static/description/index.html b/fieldservice_portal/static/description/index.html new file mode 100644 index 0000000000..b6be2c021d --- /dev/null +++ b/fieldservice_portal/static/description/index.html @@ -0,0 +1,439 @@ + + + + + +Field Service - Portal + + + +
+

Field Service - Portal

+ + +

Beta License: AGPL-3 OCA/field-service Translate me on Weblate Try me on Runboat

+

Bridge module between fieldservice and portal that allows portal users +to monitor work orders related to their locations.

+

Table of contents

+ +
+

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

+
    +
  • PyTech SRL
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainers:

+

aleuffre renda-dev

+

This module is part of the OCA/field-service project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/fieldservice_portal/static/src/img/fsmorder.svg b/fieldservice_portal/static/src/img/fsmorder.svg new file mode 100644 index 0000000000..3804aa8ddd --- /dev/null +++ b/fieldservice_portal/static/src/img/fsmorder.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/fieldservice_portal/static/src/js/fsm_order_portal.esm.js b/fieldservice_portal/static/src/js/fsm_order_portal.esm.js new file mode 100644 index 0000000000..a2be1e621c --- /dev/null +++ b/fieldservice_portal/static/src/js/fsm_order_portal.esm.js @@ -0,0 +1,14 @@ +/** @odoo-module */ + +// Force dependencies +import "portal.portal"; +import publicWidget from "web.public.widget"; + +publicWidget.registry.PortalHomeCounters.include({ + /** + * @override + */ + _getCountersAlwaysDisplayed() { + return this._super(...arguments).concat(["fsm_order_count"]); + }, +}); diff --git a/fieldservice_portal/tests/__init__.py b/fieldservice_portal/tests/__init__.py new file mode 100644 index 0000000000..8307da4c18 --- /dev/null +++ b/fieldservice_portal/tests/__init__.py @@ -0,0 +1 @@ +from . import test_portal diff --git a/fieldservice_portal/tests/test_portal.py b/fieldservice_portal/tests/test_portal.py new file mode 100644 index 0000000000..dfa3881861 --- /dev/null +++ b/fieldservice_portal/tests/test_portal.py @@ -0,0 +1,171 @@ +import json + +from odoo.exceptions import AccessError +from odoo.http import Request +from odoo.tests.common import HttpCase, TransactionCase, tagged + + +@tagged("post_install", "-at_install") +class TestUsersHttp(HttpCase, TransactionCase): + def test_fsm_order_portal(self): + # Accessing work order of the portal user through route APIs available + login = "portal" + self.authenticate(login, login) + response = self.url_open( + "/my/fsm_orders", + data={ + "validation": login, + "password": login, + "csrf_token": Request.csrf_token(self), + }, + ) + + # Check successful response from API + self.assertEqual(response.status_code, 200) + + login = "demo" + self.authenticate(login, login) + response = self.url_open( + "/my/fsm_orders", + data={ + "validation": login, + "password": login, + "csrf_token": Request.csrf_token(self), + }, + ) + + # Check Forbidden response from API + self.assertEqual(response.status_code, 403) + + def test_fsm_order_access(self): + order_id = self.env["fsm.order"].search([])[0].id + login = "portal" + self.authenticate(login, login) + response = self.url_open( + "/my/fsm_order/" + str(order_id), + data={ + "validation": login, + "password": login, + "csrf_token": Request.csrf_token(self), + }, + ) + self.assertEqual(response.status_code, 200) + + def test_fsm_order_access_denied(self): + # create a Res Partner to be converted to FSM Location/Person + test_loc_partner = self.env["res.partner"].create( + {"name": "Test Loc Partner", "phone": "ABC", "email": "tlp@email.com"} + ) + # create FSM Location and assign it to different user other than Portal User + test_location = self.env["fsm.location"].create( + { + "name": "Test Location No Portal User", + "phone": "123", + "email": "tp@email.com", + "customer_id": test_loc_partner.id, + "partner_id": test_loc_partner.id, + "owner_id": test_loc_partner.id, + } + ) + order = self.env["fsm.order"].create( + { + "location_id": test_location.id, + } + ) + + # Trying to access fsm_order which is not + # assigned to Portal User to check access error + login = "portal" + self.authenticate(login, login) + self.url_open( + "/my/fsm_order/" + str(order.id), + data={ + "validation": login, + "password": login, + "csrf_token": Request.csrf_token(self), + }, + ) + self.assertRaises( + AccessError, msg="Access Denied by record rules for operation: read" + ) + + def test_fsm_order_kw_usage(self): + order_id = self.env["fsm.order"].search([])[0].id + # Trying to access fsm_order url + # with query parameters + login = "portal" + self.authenticate(login, login) + response = self.url_open( + "/my/fsm_order/" + str(order_id) + "?success='success'", + data={ + "validation": login, + "password": login, + "csrf_token": Request.csrf_token(self), + }, + ) + self.assertEqual(response.status_code, 200) + + def test_fsm_no_fsm_order_present(self): + # Trying to filter fsm_orders based on filter + login = "portal" + self.authenticate(login, login) + response = self.url_open( + "/my/fsm_orders?groupby=none&filterby=Completed&page=1&search_in=&search=", + data={ + "validation": login, + "password": login, + "csrf_token": Request.csrf_token(self), + }, + ) + self.assertEqual(response.status_code, 200) + self.assertNotIn("", response.text) + self.assertIn("

There are no Work Orders in your account.

", response.text) + + def test_fsm_order_filter_usage(self): + # Trying to filter fsm_orders based on filter, group and sort + login = "portal" + self.authenticate(login, login) + response = self.url_open( + "/my/fsm_orders?groupby=stage_id&filterby=New&sortby=location", + data={ + "validation": login, + "password": login, + "csrf_token": Request.csrf_token(self), + }, + ) + self.assertEqual(response.status_code, 200) + self.assertIn("", response.text) + self.assertIn("Demo Order", response.text) + + def test_fsm_orders_portal_home(self): + login = "portal" + self.authenticate(login, login) + response = self.url_open( + "/my/home", + data={ + "validation": login, + "password": login, + "csrf_token": Request.csrf_token(self), + }, + ) + self.assertEqual(response.status_code, 200) + self.assertIn("FSM Orders", response.text) + + def test_fsm_orders_count(self): + login = "portal" + self.authenticate(login, login) + response = self.url_open( + "/my/counters", + data=json.dumps( + { + "validation": login, + "password": login, + "csrf_token": Request.csrf_token(self), + "params": { + "counters": "fsm_order_count", + }, + } + ).encode(), + headers={"Content-Type": "application/json"}, + ).json() + self.assertEqual(response["result"]["fsm_order_count"], 1) diff --git a/fieldservice_portal/views/fsm_order_template.xml b/fieldservice_portal/views/fsm_order_template.xml new file mode 100644 index 0000000000..00a88e50d9 --- /dev/null +++ b/fieldservice_portal/views/fsm_order_template.xml @@ -0,0 +1,148 @@ + + + + + + + diff --git a/fieldservice_portal/views/portal_template.xml b/fieldservice_portal/views/portal_template.xml new file mode 100644 index 0000000000..b525a1eaa6 --- /dev/null +++ b/fieldservice_portal/views/portal_template.xml @@ -0,0 +1,46 @@ + + + + + +