From 9b2d034969acfd2bdf853ed3b7b62dc5ea6beabb Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Tue, 18 Aug 2026 15:20:22 +0800 Subject: [PATCH 01/14] feat(spp_attendance): import module from openspp-modules Verbatim copy of spp_attendance from openspp-modules @ 5a1afb71b. Adaptation and fixes follow in separate commits. --- spp_attendance/README.rst | 162 ++ spp_attendance/README.rst.bak | 210 ++ spp_attendance/__init__.py | 3 + spp_attendance/__manifest__.py | 34 + spp_attendance/controllers/__init__.py | 1 + spp_attendance/controllers/controllers.py | 708 +++++++ .../data/ir_config_parameter_data.xml | 59 + spp_attendance/i18n/lo.po | 1831 +++++++++++++++++ spp_attendance/i18n/spp_attendance.pot | 1826 ++++++++++++++++ spp_attendance/models/__init__.py | 7 + .../attendance_api_client_credentials.py | 94 + spp_attendance/models/attendance_list.py | 59 + spp_attendance/models/attendance_location.py | 10 + .../models/attendance_subscriber.py | 189 ++ spp_attendance/models/attendance_type.py | 12 + spp_attendance/models/registry_config.py | 120 ++ spp_attendance/models/res_partner.py | 11 + spp_attendance/pyproject.toml | 3 + spp_attendance/readme/DESCRIPTION.md | 57 + spp_attendance/security/ir.model.access.csv | 14 + spp_attendance/security/privileges.xml | 15 + spp_attendance/security/security.xml | 34 + .../static/description/icon-transparent.png | Bin 0 -> 12567 bytes spp_attendance/static/description/icon.png | Bin 0 -> 15480 bytes spp_attendance/static/description/index.html | 541 +++++ .../static/src/img/icons/attendance.png | Bin 0 -> 19148 bytes .../views/attendance_api_client_views.xml | 76 + .../views/attendance_location_views.xml | 45 + .../views/attendance_type_views.xml | 45 + spp_attendance/views/attendance_views.xml | 98 + spp_attendance/views/main_views.xml | 13 + spp_attendance/views/res_config_view.xml | 77 + spp_attendance/wizard/__init__.py | 1 + spp_attendance/wizard/import_attendance.py | 319 +++ .../wizard/import_attendance_view.xml | 43 + 35 files changed, 6717 insertions(+) create mode 100644 spp_attendance/README.rst create mode 100644 spp_attendance/README.rst.bak create mode 100644 spp_attendance/__init__.py create mode 100644 spp_attendance/__manifest__.py create mode 100644 spp_attendance/controllers/__init__.py create mode 100644 spp_attendance/controllers/controllers.py create mode 100644 spp_attendance/data/ir_config_parameter_data.xml create mode 100644 spp_attendance/i18n/lo.po create mode 100644 spp_attendance/i18n/spp_attendance.pot create mode 100644 spp_attendance/models/__init__.py create mode 100644 spp_attendance/models/attendance_api_client_credentials.py create mode 100644 spp_attendance/models/attendance_list.py create mode 100644 spp_attendance/models/attendance_location.py create mode 100644 spp_attendance/models/attendance_subscriber.py create mode 100644 spp_attendance/models/attendance_type.py create mode 100644 spp_attendance/models/registry_config.py create mode 100644 spp_attendance/models/res_partner.py create mode 100644 spp_attendance/pyproject.toml create mode 100644 spp_attendance/readme/DESCRIPTION.md create mode 100644 spp_attendance/security/ir.model.access.csv create mode 100644 spp_attendance/security/privileges.xml create mode 100644 spp_attendance/security/security.xml create mode 100644 spp_attendance/static/description/icon-transparent.png create mode 100644 spp_attendance/static/description/icon.png create mode 100644 spp_attendance/static/description/index.html create mode 100644 spp_attendance/static/src/img/icons/attendance.png create mode 100644 spp_attendance/views/attendance_api_client_views.xml create mode 100644 spp_attendance/views/attendance_location_views.xml create mode 100644 spp_attendance/views/attendance_type_views.xml create mode 100644 spp_attendance/views/attendance_views.xml create mode 100644 spp_attendance/views/main_views.xml create mode 100644 spp_attendance/views/res_config_view.xml create mode 100644 spp_attendance/wizard/__init__.py create mode 100644 spp_attendance/wizard/import_attendance.py create mode 100644 spp_attendance/wizard/import_attendance_view.xml diff --git a/spp_attendance/README.rst b/spp_attendance/README.rst new file mode 100644 index 000000000..495be7854 --- /dev/null +++ b/spp_attendance/README.rst @@ -0,0 +1,162 @@ +================== +OpenSPP Attendance +================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:f14cdc55cd485da151349989e21bdafb68dcad327eef90ac39756a284a0d3779 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/license-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-OpenSPP%2Fopenspp--modules-lightgray.png?logo=github + :target: https://github.com/OpenSPP/openspp-modules/tree/19.0/spp_attendance + :alt: OpenSPP/openspp-modules + +|badge1| |badge2| |badge3| + +Tracks participant attendance for social protection program activities. +Records presence/absence with date, time, location, and activity type. +Provides OAuth-secured API endpoints for external systems to submit +attendance data and import participants from external registries. + +Key Capabilities +~~~~~~~~~~~~~~~~ + +- Record attendance with date, time, type, location, and category + (present/absent) +- Query attendance history via ``get_attendance_list()`` with filtering + by date range, type, and location +- Import participants from external registries via configurable field + mapping +- Generate OAuth client credentials for external API access +- Enforce configurable uniqueness constraints (date/time/type/location) + to prevent duplicate records + +Key Models +~~~~~~~~~~ + ++----------------------------------+----------------------------------+ +| Model | Description | ++==================================+==================================+ +| ``spp.attendance.subscriber`` | Participant registry, inherits | +| | res.partner | ++----------------------------------+----------------------------------+ +| ``spp.attendance.list`` | Attendance record with date, | +| | time, type, location | ++----------------------------------+----------------------------------+ +| ``spp.attendance.type`` | Configurable attendance event | +| | types | ++----------------------------------+----------------------------------+ +| ``spp.attendance.location`` | Configurable attendance | +| | locations | ++----------------------------------+----------------------------------+ +| ``spp.at | OAuth credentials for external | +| tendance.api.client.credential`` | API clients | ++----------------------------------+----------------------------------+ +| ``spp.import.attendance.wizard`` | Wizard for importing from | +| | external registries | ++----------------------------------+----------------------------------+ + +Configuration +~~~~~~~~~~~~~ + +After installing: + +1. Navigate to **Settings > SPP Attendance Settings** to configure: + + - Uniqueness constraints (date/time/type/location) + - Server URL and API endpoints + - Field mappings for import (personal information, identifiers, + contact details) + +2. Create attendance types: **Attendance > Configuration > Attendance + Type** +3. Create locations: **Attendance > Configuration > Attendance + Location** +4. Generate OAuth credentials: **Settings > Attendance API Client + Credentials** + +UI Location +~~~~~~~~~~~ + +- **Main Menu**: Attendance > Subscriber +- **Configuration**: Attendance > Configuration (Attendance Type, + Attendance Location) +- **API Credentials**: Settings > Attendance API Client Credentials +- **Settings**: Settings > SPP Attendance Settings +- **Subscriber Form**: Contains "Attendance" and "Person Information" + tabs + +Security +~~~~~~~~ + +=========================================== ========= +Group Access +=========================================== ========= +``spp_attendance.group_attendance_viewer`` Read +``spp_attendance.group_attendance_manager`` Full CRUD +=========================================== ========= + +Extension Points +~~~~~~~~~~~~~~~~ + +- Override ``get_attendance_list()`` on ``spp.attendance.subscriber`` to + customize attendance queries +- Inherit ``spp.attendance.list`` to add domain-specific attendance + metadata +- Extend ``_import_attendance()`` in wizard to customize import logic + +Dependencies +~~~~~~~~~~~~ + +``base``, ``spp_oauth``, ``spp_security`` + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + +**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 +------- + +* OpenSPP.org + +Maintainers +----------- + +.. |maintainer-reichie020212| image:: https://github.com/reichie020212.png?size=40px + :target: https://github.com/reichie020212 + :alt: reichie020212 + +Current maintainer: + +|maintainer-reichie020212| + +This module is part of the `OpenSPP/openspp-modules `_ project on GitHub. + +You are welcome to contribute. diff --git a/spp_attendance/README.rst.bak b/spp_attendance/README.rst.bak new file mode 100644 index 000000000..ffd5f4ebe --- /dev/null +++ b/spp_attendance/README.rst.bak @@ -0,0 +1,210 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +================== +OpenSPP Attendance +================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:cb4da5e0378f0750196e4c5fe0e0ff0318fdd8ff97c454823835c029720b31f8 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/license-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-OpenSPP%2Fopenspp--modules-lightgray.png?logo=github + :target: https://github.com/OpenSPP/openspp-modules/tree/19.0/spp_attendance + :alt: OpenSPP/openspp-modules + +|badge1| |badge2| |badge3| + +Attendance +---------- + +The OpenSPP Attendance module (sppattendance) provides a comprehensive +system for managing and tracking participant attendance across various +social protection programs and farmer registries. It enables +organizations to accurately record who attended specific events, when, +where, and for what purpose, ensuring accountability and informing +program delivery. + +Purpose +~~~~~~~ + +This module streamlines the process of tracking participant engagement +and presence, critical for program effectiveness and compliance. It +accomplishes the following key capabilities: + +- **Record Participant Attendance:** Accurately logs the presence or + absence of individuals at specific program events, such as training + sessions, benefit distributions, or community meetings. This ensures + precise data for monitoring and evaluation. +- **Manage Program Participants:** Creates and maintains a registry of + individuals participating in social protection programs, linking their + attendance records to their core profile information. This centralizes + participant data for easier management. +- **Categorize Attendance Events:** Defines and categorizes different + types of attendance (e.g., "Cash Transfer Distribution," "Health + Education Workshop") and specific locations where these events occur. + This provides granular insights into program activities. +- **Integrate with External Systems:** Facilitates the secure import of + participant data from external registries and allows external + applications to submit or query attendance records via a robust API. + This supports data synchronization across the OpenSPP ecosystem. +- **Monitor Participant Engagement:** Provides tools to view an + individual's attendance history and aggregated attendance data, + offering valuable insights into participation patterns and program + reach. This helps in assessing program impact and identifying areas + for improvement. + +Dependencies and Integration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The sppattendance module integrates seamlessly with other core OpenSPP +components and extends foundational Odoo functionalities: + +- **Base (base):** As a core module, ``spp_attendance`` relies on the + standard Odoo ``base`` module for fundamental system functionalities, + data models, and user interface elements. +- **OpenSPP API: Oauth (spp_oauth):** ``spp_attendance`` leverages the + ``spp_oauth`` module to provide secure API access for external + applications. This ensures that any attendance data submitted or + queried by external systems is authenticated and authorized, + protecting sensitive participant information. +- **Partner Management (res.partner):** The module extends Odoo's core + ``res.partner`` model to manage program participants (subscribers). + Each attendance subscriber is linked to a ``res.partner`` record, + allowing ``spp_attendance`` to utilize existing contact information + while adding specific details relevant to program participation, such + as a unique person identifier and attendance history. + +This module serves as a foundational component for other +program-specific modules that require accurate participant attendance +records for their operations, such as benefit disbursement, case +management, or training program management. + +Additional Functionality +~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``spp_attendance`` module offers a range of features designed to +provide flexible and robust attendance management: + +Participant (Subscriber) Management +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Users can create and manage detailed profiles for program participants, +known as subscribers. Each subscriber is uniquely identified and linked +to a core OpenSPP contact, ensuring consistency. The module +automatically manages the underlying contact record when a subscriber is +created or updated, including details like full name, additional names, +gender, email, and phone numbers. + +Flexible Attendance Recording +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The module enables recording of attendance events with specific details. +For each record, users specify the attendance date, time, and category +(either "Present" or "Absent"). Additional context can be added through +a description and an external URL, useful for linking to external +resources or evidence of attendance. + +- **Configurable Uniqueness:** System administrators can configure rules + to prevent duplicate attendance entries for a single participant. This + includes options to ensure uniqueness based on combinations of date, + time, attendance type, and location, preventing data entry errors and + ensuring data integrity. +- **Attendance History:** Users can quickly retrieve a comprehensive + attendance history for any participant, filtering by date ranges, + attendance type, or location, providing a clear overview of their + engagement. + +Custom Attendance Types and Locations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To accommodate diverse program needs, the module allows for the +definition of custom attendance types and locations. + +- **Attendance Types:** Organizations can define various types of + attendance events (e.g., "Training Session," "Cash Distribution," + "Health Screening"). Each type can have a name and description, + allowing for clear classification of activities. +- **Attendance Locations:** Users can establish a list of specific + locations where attendance events occur (e.g., "District Office A," + "Community Hall B"). This helps in geographic tracking and reporting + of program activities. + +External System Integration +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``spp_attendance`` module supports integration with external systems +for both data import and API-driven attendance management. + +- **Secure API Access:** Organizations can generate unique Client ID and + Client Secret credentials for external applications. These credentials + enable secure, authenticated access to the ``spp_attendance`` API, + allowing external systems (e.g., mobile attendance capture apps) to + submit new attendance records or query existing ones. +- **Registry Data Import:** A dedicated wizard facilitates the import of + participant data from external registries. This feature includes + configurable mapping of fields (such as personal information, + identifiers, and contact details) to ensure accurate and efficient + synchronization of participant records into OpenSPP. + +Conclusion +~~~~~~~~~~ + +The OpenSPP Attendance module is essential for effectively managing and +monitoring participant engagement within social protection programs, +providing robust tools for recording, categorizing, and integrating +attendance data across the OpenSPP platform. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**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 +------- + +* OpenSPP.org + +Maintainers +----------- + +.. |maintainer-reichie020212| image:: https://github.com/reichie020212.png?size=40px + :target: https://github.com/reichie020212 + :alt: reichie020212 + +Current maintainer: + +|maintainer-reichie020212| + +This module is part of the `OpenSPP/openspp-modules `_ project on GitHub. + +You are welcome to contribute. diff --git a/spp_attendance/__init__.py b/spp_attendance/__init__.py new file mode 100644 index 000000000..e4f4917ae --- /dev/null +++ b/spp_attendance/__init__.py @@ -0,0 +1,3 @@ +from . import controllers +from . import models +from . import wizard diff --git a/spp_attendance/__manifest__.py b/spp_attendance/__manifest__.py new file mode 100644 index 000000000..eee8b2b09 --- /dev/null +++ b/spp_attendance/__manifest__.py @@ -0,0 +1,34 @@ +# pylint: disable=pointless-statement +{ + "name": "OpenSPP Attendance", + "category": "OpenSPP", + "version": "19.0.1.3.1", + "summary": "This module accurately tracks participant attendance for social protection program activities, capturing essential details like date, time, location, and activity type. It integrates attendance records to inform conditional benefit eligibility and leverages spp_oauth to secure API endpoints for external data submission.", + "sequence": 1, + "author": "OpenSPP.org", + "website": "https://github.com/OpenSPP/openspp-modules", + "license": "LGPL-3", + "development_status": "Alpha", + "maintainers": ["reichie020212"], + "depends": ["base", "spp_oauth", "spp_security"], + "external_dependencies": {"python": ["pytz"]}, + "data": [ + "data/ir_config_parameter_data.xml", + "security/privileges.xml", + "security/security.xml", + "security/ir.model.access.csv", + "views/main_views.xml", + "views/attendance_views.xml", + "views/attendance_api_client_views.xml", + "views/res_config_view.xml", + "views/attendance_type_views.xml", + "views/attendance_location_views.xml", + "wizard/import_attendance_view.xml", + ], + "assets": {}, + "demo": [], + "images": [], + "application": True, + "installable": True, + "auto_install": False, +} diff --git a/spp_attendance/controllers/__init__.py b/spp_attendance/controllers/__init__.py new file mode 100644 index 000000000..e046e49fb --- /dev/null +++ b/spp_attendance/controllers/__init__.py @@ -0,0 +1 @@ +from . import controllers diff --git a/spp_attendance/controllers/controllers.py b/spp_attendance/controllers/controllers.py new file mode 100644 index 000000000..25c625b20 --- /dev/null +++ b/spp_attendance/controllers/controllers.py @@ -0,0 +1,708 @@ +import json +import logging +from datetime import datetime + +import werkzeug.wrappers + +from odoo.http import Controller, request, route +from odoo.osv import expression +from odoo.tools import date_utils + +from odoo.addons.spp_oauth.tools import OpenSPPOAuthJWTException, verify_and_decode_signature + +_logger = logging.getLogger(__name__) + + +class SppAttendanceController(Controller): + def response_wrapper(self, status, data): + return werkzeug.wrappers.Response( + status=status, + content_type="application/json; charset=utf-8", + response=json.dumps(data, default=date_utils.json_default) if data else None, + ) + + def error_wrapper(self, code, message): + error = {"error": {"code": code, "message": message}} + return self.response_wrapper(code, error) + + def get_auth_header(self, headers, raise_exception=False): + auth_header = headers.get("Authorization") or headers.get("authorization") + if not auth_header or not auth_header.startswith("Bearer "): + if raise_exception: + error = { + "error": "Unauthorized", + "error_description": "Your token could not be authenticated.", + } + return self.response_wrapper(401, error) + return auth_header + + def verify_auth_header(self): + auth_header = self.get_auth_header(request.httprequest.headers, raise_exception=True) + + access_token = auth_header.replace("Bearer ", "").replace("\\n", "").encode("utf-8") + try: + verify_and_decode_signature(request.env, access_token) + except OpenSPPOAuthJWTException: + return False + + return True + + def check_required_fields(self, data, required_fields=None): + if not required_fields: + return [] + + missing_required_fields = [] + for field in required_fields: + if field not in data: + missing_required_fields.append(field) + return missing_required_fields + + def validate_page_and_limit(self, page, limit): + try: + page = int(page) + limit = int(limit) + except ValueError: + return "Page and limit must be integers." + + if page < 1 or limit < 1: + return "Page and limit must be positive integers." + + return None + + def validate_date(self, from_date, to_date): + if not all([from_date, to_date]): + return None + + try: + from_date = datetime.strptime(from_date, "%Y-%m-%d") + to_date = datetime.strptime(to_date, "%Y-%m-%d") + except ValueError: + return "FromDate and ToDate must be in the format YYYY-MM-DD." + + if from_date > to_date: + return "FromDate must be less than or equal to ToDate." + + return None + + def validate_request_header_and_body(self): + if not self.verify_auth_header(): + return self.error_wrapper(401, "Unauthorized") + + req = request + data = req.httprequest.data or "{}" + try: + data = json.loads(data) + except json.decoder.JSONDecodeError: + return self.error_wrapper(400, "data must be in JSON format.") + + return None + + def validate_entity(self, model_name, model_id, model_label): + if model_id: + try: + model_id = int(model_id) + except ValueError: + return self.error_wrapper(400, f"{model_label} must be an integer.") + + entity = request.env[model_name].sudo().search([("id", "=", model_id)], limit=1) + if not entity: + model_ids = request.env[model_name].sudo().search([]) + error_message = f"{model_label} does not exist." + if model_ids: + choices = ", ".join([f"{model.id} for {model.name}" for model in model_ids]) + error_message = f"{model_label} does not exist. Available {model_label}s: {choices}." + return self.error_wrapper(400, error_message) + return None + + def validate_attendance_type(self, attendance_type): + return self.validate_entity("spp.attendance.type", attendance_type, "Attendance Type") + + def validate_attendance_location(self, attendance_location): + return self.validate_entity("spp.attendance.location", attendance_location, "Attendance Location") + + @route( + "/auth/token", + type="http", + auth="none", + methods=["POST"], + csrf=False, + ) + def auth_get_access_token(self, **kwargs): + req = request + data = req.httprequest.data or "{}" + try: + data = json.loads(data) + except json.decoder.JSONDecodeError: + return self.response_wrapper( + 400, + { + "error": "Bad Request", + "error_description": "data must be in JSON format.", + }, + ) + + client_id = data.get("client_id", "") + client_secret = data.get("client_secret", "") + + if not all([client_id, client_secret]): + error = { + "error": "Bad Request", + "error_description": ("client_id and client_secret are required."), + } + return self.response_wrapper(400, error) + + client = ( + req.env["spp.attendance.api.client.credential"] + .sudo() + .search( + [("client_id", "=", client_id), ("client_secret", "=", client_secret)], + limit=1, + ) + ) + + if not client: + error = { + "error": "Unauthorized", + "error_description": "Invalid client id or secret.", + } + return self.response_wrapper(401, error) + + access_token = client.generate_access_token() + + data = { + "access_token": access_token, + "token_type": "Bearer", + } + return self.response_wrapper(200, data) + + @route( + "/attendances", + type="http", + auth="none", + methods=["POST"], + csrf=False, + ) + def create_attendance_list(self, ignore_unique=None, **kwargs): + if error := self.validate_request_header_and_body(): + return error + + req = request + data = req.httprequest.data or "{}" + data = json.loads(data) + + if ignore_unique: + ignore_unique = ignore_unique.lower() in ["true", "1", "t", "y", "yes"] + + if missing_required_fields := self.check_required_fields( + data, ["records", "submitted_by", "submitted_datetime"] + ): + return self.error_wrapper(400, f"Missing required fields: {', '.join(missing_required_fields)}") + + current_date = datetime.now().strftime("%Y-%m-%d") + submitted_datetime = data.get("submitted_datetime", current_date) + submitted_by = data.get("submitted_by") + submission_source = data.get("submission_source", "") + + attendance_list_data = [] + person_id_list = [] + + ALLOWED_CATEGORIES = req.env["spp.attendance.list"].ALLOWED_CATEGORIES + + for person_data in data["records"]: + if missing_required_fields := self.check_required_fields(person_data, ["time_card", "person_id"]): + return self.error_wrapper(400, f"Missing required fields: {', '.join(missing_required_fields)}") + + person_id = person_data.get("person_id") + subscriber_id = ( + req.env["spp.attendance.subscriber"].sudo().search([("person_identifier", "=", person_id)], limit=1) + ) + if not subscriber_id: + return self.error_wrapper(400, "person_id does not exist.") + + time_cards = person_data.get("time_card") + for time_card in time_cards: + missing_required_fields = self.check_required_fields(time_card, ["date_time"]) + if missing_required_fields: + return self.error_wrapper( + 400, f"Missing required fields for time_card: {', '.join(missing_required_fields)}" + ) + + attendance_datetime = time_card.get("date_time") + attendance_datetime = datetime.strptime(attendance_datetime, "%Y-%m-%d %H:%M:%S") + + attendance_date = str(attendance_datetime.date()) + attendance_time = str(attendance_datetime.time()) + + category = time_card.get("attendance_category", "present") + category = category.lower() + if category not in ALLOWED_CATEGORIES: + return self.error_wrapper( + 400, f"Invalid category. Allowed categories are {', '.join(ALLOWED_CATEGORIES)}." + ) + + attendance_type = time_card.get("attendance_type", False) or False + if result := self.validate_attendance_type(attendance_type): + return result + attendance_type = int(attendance_type) + + attendance_location = time_card.get("attendance_location", False) or False + if result := self.validate_attendance_location(attendance_location): + return result + attendance_location = int(attendance_location) + + attendance_description = time_card.get("attendance_description", "") + attendance_external_url = time_card.get("attendance_external_url", "") + attendane_list_vals = { + "subscriber_id": subscriber_id.id, + "attendance_date": attendance_date, + "attendance_time": attendance_time, + "attendance_type_id": attendance_type, + "attendance_category": category, + "attendance_location_id": attendance_location, + "attendance_description": attendance_description, + "attendance_external_url": attendance_external_url, + "submitted_by": submitted_by, + "submitted_datetime": submitted_datetime, + "submission_source": submission_source, + } + + new_attendance_list_ids = req.env["spp.attendance.list"].sudo().new(attendane_list_vals) + if person_id not in person_id_list: + person_id_list.append(person_id) + if not new_attendance_list_ids.check_uniqueness(): + if ignore_unique: + continue + return self.error_wrapper( + 400, "An attendance record is already exists. Please check the date, time, type, location." + ) + + attendance_list_data.append(attendane_list_vals) + + req.env["spp.attendance.list"].sudo().create(attendance_list_data) + + return self.response_wrapper( + 200, {"message": "Attendance list created successfully.", "person_ids": person_id_list} + ) + + def get_time_card_vals(self, time_card): + ALLOWED_CATEGORIES = request.env["spp.attendance.list"].ALLOWED_CATEGORIES + vals = {} + if "date_time" in time_card: + attendance_datetime = time_card.get("date_time") + attendance_datetime = datetime.strptime(attendance_datetime, "%Y-%m-%d %H:%M:%S") + attendance_date = str(attendance_datetime.date()) + attendance_time = str(attendance_datetime.time()) + + vals["attendance_date"] = attendance_date + vals["attendance_time"] = attendance_time + + if "attendance_type" in time_card: + attendance_type = time_card.get("attendance_type") + if result := self.validate_attendance_type(attendance_type): + return result + attendance_type = int(attendance_type) + vals["attendance_type_id"] = attendance_type + + if "attendance_location" in time_card: + attendance_location = time_card.get("attendance_location") + if result := self.validate_attendance_location(attendance_location): + return result + attendance_location = int(attendance_location) + vals["attendance_location_id"] = attendance_location + + if "attendance_description" in time_card: + attendance_description = time_card.get("attendance_description") + vals["attendance_description"] = attendance_description + + if "attendance_external_url" in time_card: + attendance_external_url = time_card.get("attendance_external_url") + vals["attendance_external_url"] = attendance_external_url + + if "attendance_category" in time_card: + category = time_card["attendance_category"] + category = category.lower() + if category not in ALLOWED_CATEGORIES: + return self.error_wrapper( + 400, f"Invalid category. Allowed categories are {', '.join(ALLOWED_CATEGORIES)}." + ) + vals["attendance_category"] = category + return vals + + @route( + "/attendances", + type="http", + auth="none", + methods=["PUT"], + csrf=False, + ) + def update_attendance_list(self, **kwargs): + if error := self.validate_request_header_and_body(): + return error + + req = request + data = req.httprequest.data or "{}" + data = json.loads(data) + + if missing_required_fields := self.check_required_fields( + data, ["records", "submitted_by", "submitted_datetime"] + ): + return self.error_wrapper(400, f"Missing required fields: {', '.join(missing_required_fields)}") + + current_date = datetime.now().strftime("%Y-%m-%d") + submitted_datetime = data.get("submitted_datetime", current_date) + submitted_by = data.get("submitted_by") + submission_source = data.get("submission_source", "") + + for attendance_data in data["records"]: + if missing_required_fields := self.check_required_fields(attendance_data, ["time_card", "id"]): + return self.error_wrapper( + 400, f"Missing required fields for records: {', '.join(missing_required_fields)}" + ) + + attendance_id = attendance_data.get("id") + if not isinstance(attendance_id, int): + return self.error_wrapper(400, "id must be an integer.") + + attendance_list_id = req.env["spp.attendance.list"].sudo().search([("id", "=", attendance_id)], limit=1) + + if not attendance_list_id: + return self.error_wrapper(400, f"Attendance ID {attendance_id} does not exist.") + + time_card = attendance_data.get("time_card") + if not isinstance(time_card, dict): + return self.error_wrapper(400, "time_card must be an object.") + + vals = self.get_time_card_vals(time_card) + if not isinstance(vals, dict): + # it returns an error if it is not a dict: return the vals + return vals + + if vals: + vals["submitted_by"] = submitted_by + vals["submitted_datetime"] = submitted_datetime + vals["submission_source"] = submission_source + attendance_list_id.write(vals) + + return self.response_wrapper(200, {"message": "Attendance list updated successfully."}) + + @route( + "/attendances", + type="http", + auth="none", + methods=["DELETE"], + csrf=False, + ) + def delete_attendance_list(self, ids=None, **kwargs): + if error := self.validate_request_header_and_body(): + return error + + if not ids: + ids = [] + else: + try: + ids = [int(item) for item in ids.split(",")] + except ValueError: + _logger.warning("Invalid ids parameter format: expected comma-separated integers") + return self.error_wrapper(400, "ids must be a list of integers.") + + req = request + attendance_list_ids = req.env["spp.attendance.list"].sudo().search([("id", "in", ids)]) + + if not attendance_list_ids: + return self.error_wrapper(400, "Attendance list does not exist.") + + attendance_list_ids.unlink() + return self.response_wrapper(200, {"message": "Attendance list deleted successfully."}) + + @route( + "/attendance/", + type="http", + auth="none", + methods=["GET"], + csrf=False, + ) + def attendance_list_person(self, person_identifier, page=1, limit=30, **kwargs): + if error := self.validate_request_header_and_body(): + return error + + req = request + data = req.httprequest.data or "{}" + data = json.loads(data) + + subscriber_id = ( + req.env["spp.attendance.subscriber"].sudo().search([("person_identifier", "=", person_identifier)], limit=1) + ) + + if not subscriber_id: + return self.error_wrapper(400, f"PersonID {person_identifier} does not exist.") + + if page_limit_error_message := self.validate_page_and_limit(page, limit): + return self.error_wrapper(400, page_limit_error_message) + + page = int(page) + limit = int(limit) + offset = (page - 1) * limit + + from_date = kwargs.get("from_date", None) + to_date = kwargs.get("to_date", None) + + if from_date and to_date: + if date_error_message := self.validate_date(from_date, to_date): + return self.error_wrapper(400, date_error_message) + else: + from_date = datetime.strptime(from_date, "%Y-%m-%d") + to_date = datetime.strptime(to_date, "%Y-%m-%d") + + attendance_type = kwargs.get("attendance_type", False) or False + if result := self.validate_attendance_type(attendance_type): + return result + attendance_type = int(attendance_type) + + attendance_location = kwargs.get("attendance_location", False) or False + if result := self.validate_attendance_location(attendance_location): + return result + attendance_location = int(attendance_location) + + total_attendance, attendance_record = subscriber_id.get_attendance_list( + from_date=from_date, + to_date=to_date, + attendance_type_id=attendance_type, + attendance_location_id=attendance_location, + offset=offset, + limit=limit, + ) + attendance_record["pagination"] = { + "page": page, + "limit": limit, + "total_records": total_attendance, + "total_pages": (total_attendance + limit - 1) // limit, + } + + return self.response_wrapper( + 200, + attendance_record, + ) + + @route( + "/attendances", + type="http", + auth="none", + methods=["GET"], + csrf=False, + ) + def attendance_list(self, page=1, limit=30, last_id=None, **kwargs): + if error := self.validate_request_header_and_body(): + return error + + req = request + data = req.httprequest.data or "{}" + data = json.loads(data) + + if page_limit_error_message := self.validate_page_and_limit(page, limit): + return self.error_wrapper(400, page_limit_error_message) + + page = int(page) + limit = int(limit) + + # Use cursor-based pagination if last_id provided, otherwise fall back to offset + if last_id: + try: + last_id = int(last_id) + except ValueError: + return self.error_wrapper(400, "last_id must be an integer.") + + from_date = kwargs.get("from_date", None) + to_date = kwargs.get("to_date", None) + + if from_date and to_date: + if date_error_message := self.validate_date(from_date, to_date): + return self.error_wrapper(400, date_error_message) + else: + from_date = datetime.strptime(from_date, "%Y-%m-%d") + to_date = datetime.strptime(to_date, "%Y-%m-%d") + + attendance_type = kwargs.get("attendance_type", False) or False + if result := self.validate_attendance_type(attendance_type): + return result + attendance_type = int(attendance_type) + + attendance_location = kwargs.get("attendance_location", False) or False + if result := self.validate_attendance_location(attendance_location): + return result + attendance_location = int(attendance_location) + + domain = [] + + person_identifier_list = data.get("person_ids", []) + if person_identifier_list: + domain.append(("person_identifier", "in", person_identifier_list)) + + # Apply cursor-based pagination + if last_id is not None: + domain = expression.AND([domain, [("id", ">", last_id)]]) + + subscriber_model = request.env["spp.attendance.subscriber"].sudo() + subscriber_ids = subscriber_model.search(domain, limit=limit, order="id") + total_records = subscriber_model.search_count(domain) + + records = [] + for subscriber_id in subscriber_ids: + total_attendance, attendance_record = subscriber_id.get_attendance_list( + from_date=from_date, + to_date=to_date, + attendance_type_id=attendance_type, + attendance_location_id=attendance_location, + ) + records.append(attendance_record) + + response_data = { + "records": records, + "pagination": { + "page": page, + "limit": limit, + "total_records": total_records, + "total_pages": (total_records + limit - 1) // limit, + }, + } + + return self.response_wrapper( + 200, + response_data, + ) + + @route( + "/attendance/types", + type="http", + auth="none", + methods=["GET"], + csrf=False, + ) + def get_attendance_types(self): + if error := self.validate_request_header_and_body(): + return error + + req = request + data = req.httprequest.data or "{}" + data = json.loads(data) + + attendance_type_ids = req.env["spp.attendance.type"].sudo().search([]) + + return self.response_wrapper( + 200, + { + "records": [ + { + "id": attendance_type.id, + "name": attendance_type.name, + "description": attendance_type.description, + } + for attendance_type in attendance_type_ids + ], + }, + ) + + @route( + "/attendance/locations", + type="http", + auth="none", + methods=["GET"], + csrf=False, + ) + def get_attendance_locations(self): + if error := self.validate_request_header_and_body(): + return error + + req = request + data = req.httprequest.data or "{}" + data = json.loads(data) + + attendance_location_ids = req.env["spp.attendance.location"].sudo().search([]) + + return self.response_wrapper( + 200, + { + "records": [ + { + "id": attendance_location.id, + "name": attendance_location.name, + "description": attendance_location.description, + } + for attendance_location in attendance_location_ids + ], + }, + ) + + @route( + "/subscribers", + type="http", + auth="none", + methods=["GET"], + csrf=False, + ) + def get_subscriber_list_information(self, page=1, limit=30, last_id=None, **kwargs): + if error := self.validate_request_header_and_body(): + return error + + if page_limit_error_message := self.validate_page_and_limit(page, limit): + return self.error_wrapper(400, page_limit_error_message) + + page = int(page) + limit = int(limit) + + # Use cursor-based pagination if last_id provided + if last_id: + try: + last_id = int(last_id) + except ValueError: + return self.error_wrapper(400, "last_id must be an integer.") + + domain = [] + + # Apply cursor-based pagination + if last_id is not None: + domain = expression.AND([domain, [("id", ">", last_id)]]) + + subscriber_model = request.env["spp.attendance.subscriber"].sudo() + subscriber_ids = subscriber_model.search(domain, limit=limit, order="id") + total_records = subscriber_model.search_count(domain) + + records = subscriber_ids.get_attendance_info_list() + + response_data = { + "records": records, + "pagination": { + "page": page, + "limit": limit, + "total_records": total_records, + "total_pages": (total_records + limit - 1) // limit, + }, + } + + return self.response_wrapper( + 200, + response_data, + ) + + @route( + "/subscriber/", + type="http", + auth="none", + methods=["GET"], + csrf=False, + ) + def get_subscriber_information(self, person_identifier, **kwargs): + if error := self.validate_request_header_and_body(): + return error + subscriber_id = ( + request.env["spp.attendance.subscriber"] + .sudo() + .search([("person_identifier", "=", person_identifier)], limit=1) + ) + + if not subscriber_id: + return self.error_wrapper(400, f"PersonID {person_identifier} does not exist.") + + return self.response_wrapper( + 200, + subscriber_id.get_attendance_subscriber_info(), + ) diff --git a/spp_attendance/data/ir_config_parameter_data.xml b/spp_attendance/data/ir_config_parameter_data.xml new file mode 100644 index 000000000..4a875631f --- /dev/null +++ b/spp_attendance/data/ir_config_parameter_data.xml @@ -0,0 +1,59 @@ + + + + + + + + spp_attendance.attendance_auth_endpoint + /oauth2/client/token + + + + spp_attendance.access_token_mapping + access_token + + + + spp_attendance.attendance_import_endpoint + /registry/sync/search + + + + spp_attendance.personal_information_mapping + message.search_response.0.data.reg_records + + + + spp_attendance.person_identifier_mapping + identifier.0.identifier + + + + spp_attendance.family_name_mapping + familyName + + + + spp_attendance.given_name_mapping + givenName + + + + spp_attendance.email_mapping + email + + + + spp_attendance.phone_mapping + phoneNumbers.0.phone + + + + spp_attendance.gender_mapping + sex + + diff --git a/spp_attendance/i18n/lo.po b/spp_attendance/i18n/lo.po new file mode 100644 index 000000000..f5c135a7a --- /dev/null +++ b/spp_attendance/i18n/lo.po @@ -0,0 +1,1831 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * spp_attendance +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"Language: lo\n" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__event_count +msgid "# Events" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__meeting_count +msgid "# Meetings" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "%(name)s is not set." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__type +msgid "" +"- Contact: Use this to organize the contact details of employees of a given " +"company (e.g. CEO, CFO, ...).\n" +"- Invoice Address: Preferred address for all invoices. Selected by default " +"when you invoice an order that belongs to this company.\n" +"- Delivery Address: Preferred address for all deliveries. Selected by " +"default when you deliver an order that belongs to this company.\n" +"- Other: Other address for the company (e.g. subsidiary, ...)" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.constraint,message:spp_attendance.constraint_spp_attendance_subscriber_partner_id_uniq +msgid "A subscriber with the same partner already exists." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.constraint,message:spp_attendance.constraint_spp_attendance_subscriber_person_identifier_uniq +msgid "A subscriber with the same person identifier already exists." +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/models/attendance_list.py:0 +#: model:ir.model.fields.selection,name:spp_attendance.selection__spp_attendance_list__attendance_category__absent +#, python-format +msgid "Absent" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__access_token_mapping +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Access Token Mapping" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_account_payable_id +msgid "Account Payable" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_account_receivable_id +msgid "Account Receivable" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__active +msgid "Active" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__active_lang_count +msgid "Active Lang Count" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_ids +msgid "Activities" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_state +msgid "Activity State" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_partner__addl_name +#: model:ir.model.fields,field_description:spp_attendance.field_res_users__addl_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__addl_name +msgid "Additional Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__additional_info +msgid "Additional info" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.attendance_subscriber_form +msgid "Addl. Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__type +msgid "Address Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__lang +msgid "" +"All the emails and documents sent to this contact will be translated in this " +"language." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: spp_attendance +#: model:ir.module.category,name:spp_attendance.attendance_module_management +#: model:ir.ui.menu,name:spp_attendance.menu_root +#: model_terms:ir.ui.view,arch_db:spp_attendance.attendance_subscriber_form +msgid "Attendance" +msgstr "" + +#. module: spp_attendance +#: model:ir.ui.menu,name:spp_attendance.menu_attendance_api_client_credentiale +msgid "Attendance API Client Credentials" +msgstr "" + +#. module: spp_attendance +#: model:ir.actions.act_window,name:spp_attendance.spp_attendance_client_credential_window +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_form +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_view_credentials +msgid "Attendance Client Credentials" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Attendance Data Uniqueness" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.attendance_subscriber_form +msgid "Attendance Information" +msgstr "" + +#. module: spp_attendance +#: model:ir.model,name:spp_attendance.model_spp_attendance_list +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__attendance_list_ids +msgid "Attendance List" +msgstr "" + +#. module: spp_attendance +#: model:ir.actions.act_window,name:spp_attendance.attendance_location_action_window +#: model:ir.model,name:spp_attendance.model_spp_attendance_location +#: model:ir.ui.menu,name:spp_attendance.attendance_location_menu +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Attendance Location" +msgstr "" + +#. module: spp_attendance +#: model:res.groups,name:spp_attendance.attendance_manager +msgid "Attendance Manager" +msgstr "" + +#. module: spp_attendance +#: model:ir.actions.act_window,name:spp_attendance.attendance_subscriber_action_window +#: model:ir.model,name:spp_attendance.model_spp_attendance_subscriber +msgid "Attendance Subscriber" +msgstr "" + +#. module: spp_attendance +#: model:ir.actions.act_window,name:spp_attendance.attendance_type_action_windown +#: model:ir.model,name:spp_attendance.model_spp_attendance_type +#: model:ir.ui.menu,name:spp_attendance.attendance_type_menu +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Attendance Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.constraint,message:spp_attendance.constraint_spp_attendance_type_name_uniq +msgid "Attendance Type must be unique!" +msgstr "" + +#. module: spp_attendance +#: model:res.groups,name:spp_attendance.attendance_viewer +msgid "Attendance Viewer" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__attendance_auth_endpoint +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Auth Endpoint" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__auth_header +msgid "Auth Headers" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Auth Headers must be in JSON format" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__auth_type +msgid "Auth Type" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.import_attendance_wizard +msgid "Authentication" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Authentication failed. Reason: %s." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__avatar_1920 +msgid "Avatar" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__avatar_1024 +msgid "Avatar 1024" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__avatar_128 +msgid "Avatar 128" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__avatar_256 +msgid "Avatar 256" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__avatar_512 +msgid "Avatar 512" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__bank_account_count +msgid "Bank" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__bank_ids +msgid "Banks" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__barcode +msgid "Barcode" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields.selection,name:spp_attendance.selection__spp_import_attendance_wizard__auth_type__basic +msgid "Basic" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields.selection,name:spp_attendance.selection__spp_import_attendance_wizard__auth_type__bearer +msgid "Bearer" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__is_blacklisted +msgid "Blacklist" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__mobile_blacklisted +msgid "Blacklisted Phone Is Mobile" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__phone_blacklisted +msgid "Blacklisted Phone is Phone" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_bounce +msgid "Bounce" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__can_publish +msgid "Can Publish" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.import_attendance_wizard +msgid "Cancel" +msgstr "" + +#. module: spp_attendance +#: model:ir.actions.act_window,name:spp_attendance.action_change_request_cancel_wizard +msgid "Cancel Change Request" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_category +msgid "Category" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__channel_ids +msgid "Channels" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__is_company +msgid "Check if the contact is a company, otherwise it is a person" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__employee +msgid "Check this box if this contact is an Employee." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__city +msgid "City" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__client_id +msgid "Client" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_form +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_view_credentials +msgid "Client ID" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/models/attendance_api_client_credentials.py:0 +#, python-format +msgid "Client ID and Client Secret is already showed once." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.constraint,message:spp_attendance.constraint_spp_attendance_api_client_credential_client_id_uniq +msgid "Client ID must be unique!" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__name +msgid "Client Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.constraint,message:spp_attendance.constraint_spp_attendance_api_client_credential_name_uniq +msgid "Client Name must be unique!" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__client_secret +msgid "Client Secret" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.constraint,message:spp_attendance.constraint_spp_attendance_api_client_credential_client_secret_uniq +msgid "Client Secret must be unique !" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__peppol_eas +msgid "" +"Code used to identify the Endpoint for BIS Billing 3.0 and its derivatives.\n" +" List available at https://docs.peppol.eu/poacc/billing/3.0/" +"codelist/eas/" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__color +msgid "Color Index" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__commercial_partner_id +msgid "Commercial Entity" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__ref_company_ids +msgid "Companies that refers to partner" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__company_id +msgid "Company" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__company_registry +msgid "Company ID" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__company_name +msgid "Company Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__commercial_company_name +msgid "Company Name Entity" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__company_type +msgid "Company Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__partner_gid +msgid "Company database ID" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__contact_address +msgid "Complete Address" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__complete_name +msgid "Complete Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model,name:spp_attendance.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: spp_attendance +#: model:ir.ui.menu,name:spp_attendance.attendance_configuration_menu_root +msgid "Configuration" +msgstr "" + +#. module: spp_attendance +#: model:ir.model,name:spp_attendance.model_res_partner +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__child_ids +msgid "Contact" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__message_bounce +msgid "Counter of the number of bounced emails for this contact" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__country_id +msgid "Country" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__country_code +msgid "Country Code" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.actions.act_window,help:spp_attendance.spp_attendance_client_credential_window +msgid "Create and manage the Attendance API Client Credentials." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.actions.act_window,help:spp_attendance.attendance_location_action_window +msgid "Create and manage the attendance location." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.actions.act_window,help:spp_attendance.attendance_type_action_windown +msgid "Create and manage the attendance type." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.actions.act_window,help:spp_attendance.attendance_subscriber_action_window +msgid "Create and manage the attendance." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__create_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__create_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__create_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__create_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__create_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__create_uid +msgid "Created by" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__create_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__create_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__create_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__create_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__create_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__create_date +msgid "Created on" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__credit_limit +msgid "Credit Limit" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__credit_to_invoice +msgid "Credit To Invoice" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__credit_limit +msgid "Credit limit specific to this partner." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__currency_id +msgid "Currency" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_stock_customer +msgid "Customer Location" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_payment_term_id +msgid "Customer Payment Terms" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__customer_rank +msgid "Customer Rank" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Data before the index is not a list" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__date +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Date" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__days_sales_outstanding +msgid "Days Sales Outstanding (DSO)" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__trust +msgid "Degree of trust you have in this debtor" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_description +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__description +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__description +msgid "Description" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__display_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__display_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__display_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__display_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__display_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__display_name +msgid "Display Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__duplicated_bank_account_partners_count +msgid "Duplicated Bank Account Partners Count" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__partner_share +msgid "" +"Either customer (not a user), either shared user. Indicated the current " +"partner is a customer without access or with a limited access created for " +"sharing data." +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Element %s not found in data" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__email_mapping +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__email +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Email" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__employee +msgid "Employee" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Endpoing of the API to Authenticate." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Endpoint of the API to import attendance." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_external_url +msgid "External URL" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__family_name_mapping +#: model:ir.model.fields,field_description:spp_attendance.field_res_partner__family_name +#: model:ir.model.fields,field_description:spp_attendance.field_res_users__family_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__family_name +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Family Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__phone_sanitized +msgid "" +"Field used to store sanitized phone number. Helps speeding up searches and " +"comparisons." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__fiscal_country_codes +msgid "Fiscal Country Codes" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_account_position_id +msgid "Fiscal Position" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__ubl_cii_format +msgid "Format" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__email_formatted +msgid "Format email address \"Name \"" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__email_formatted +msgid "Formatted Email" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__partner_name +msgid "Full Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__gender_mapping +#: model:ir.model.fields,field_description:spp_attendance.field_res_partner__gender_char +#: model:ir.model.fields,field_description:spp_attendance.field_res_users__gender_char +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__gender_char +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Gender" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__partner_latitude +msgid "Geo Latitude" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__partner_longitude +msgid "Geo Longitude" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__given_name_mapping +#: model:ir.model.fields,field_description:spp_attendance.field_res_partner__given_name +#: model:ir.model.fields,field_description:spp_attendance.field_res_users__given_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__given_name +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Given Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__has_message +msgid "Has Message" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__has_unreconciled_entries +msgid "Has Unreconciled Entries" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__id +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__id +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__id +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__id +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__id +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__id +msgid "ID" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__im_status +msgid "IM Status" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_partner__identifier +#: model:ir.model.fields,field_description:spp_attendance.field_res_users__identifier +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__identifier +msgid "Identifier" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__message_needaction +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__message_has_error +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__message_has_sms_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__is_blacklisted +msgid "" +"If the email address is on the blacklist, the contact won't receive mass " +"mailing anymore, from any list" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__phone_sanitized_blacklisted +msgid "" +"If the sanitized phone number is on the blacklist, the contact won't receive " +"mass mailing sms anymore, from any list" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__image_1920 +msgid "Image" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__image_1024 +msgid "Image 1024" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__image_128 +msgid "Image 128" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__image_256 +msgid "Image 256" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__image_512 +msgid "Image 512" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#: model_terms:ir.ui.view,arch_db:spp_attendance.import_attendance_wizard +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +#, python-format +msgid "Import" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/models/registry_config.py:0 +#, python-format +msgid "Import Attendance" +msgstr "" + +#. module: spp_attendance +#: model:ir.model,name:spp_attendance.model_spp_import_attendance_wizard +msgid "Import Attendance Wizard" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__attendance_import_endpoint +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Import Endpoint" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__import_header +msgid "Import Headers" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Import Headers must be in JSON format" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Import failed. Reason: %s." +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "" +"Import successfully. \n" +" Pagination information: %s" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Imported %s persons." +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Index out of range" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__mobile_blacklisted +msgid "" +"Indicates if a blacklisted sanitized phone number is a mobile number. Helps " +"distinguish which number is blacklisted when there is both a " +"mobile and phone field in a model." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__phone_blacklisted +msgid "" +"Indicates if a blacklisted sanitized phone number is a phone number. Helps " +"distinguish which number is blacklisted when there is both a " +"mobile and phone field in a model." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__industry_id +msgid "Industry" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__contact_address_inline +msgid "Inlined Complete Address" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__invoice_warn +msgid "Invoice" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__invoice_ids +msgid "Invoices" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__is_public +msgid "Is Public" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__is_published +msgid "Is Published" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__is_company +msgid "Is a Company" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__function +msgid "Job Position" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__journal_item_count +msgid "Journal Items" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__lang +msgid "Language" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__write_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__write_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__write_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__write_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__write_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__write_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__write_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__write_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__write_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__write_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__write_date +msgid "Last Updated on" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__calendar_last_notif_ack +msgid "Last notification marked as read from base Calendar" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__last_time_entries_checked +msgid "" +"Last time the invoices & payments matching was performed for this partner. " +"It is set either if there's not at least an unreconciled debit and an " +"unreconciled credit or if you click the \"Done\" button." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__last_time_entries_checked +msgid "Latest Invoices & Payments Matching Date" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__limit +msgid "Limit" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_location_id +msgid "Location" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_view_credentials +msgid "" +"Make sure to copy your client id and client secret now. You won't be able to " +"see it again!" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for email." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for family name." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for gender." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for given name." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for person identifier." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for personal informations. Last element must be a list." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for phone." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mappings" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__meeting_ids +msgid "Meetings" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__invoice_warn_msg +msgid "Message for Invoice" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__picking_warn_msg +msgid "Message for Stock Picking" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_ids +msgid "Messages" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__mobile +msgid "Mobile" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__name +msgid "Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_calendar_event_id +msgid "Next Activity Calendar Event" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_summary +msgid "Next Activity Summary" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_type_id +msgid "Next Activity Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__email_normalized +msgid "Normalized Email" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/models/attendance_api_client_credentials.py:0 +#, python-format +msgid "Not allowed to export on this model." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__comment +msgid "Notes" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_view_credentials +msgid "OK" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__page +msgid "Page" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__parent_name +msgid "Parent name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__partner_id +msgid "Partner" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__contract_ids +msgid "Partner Contracts" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__use_partner_credit_limit +msgid "Partner Limit" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__same_company_registry_partner_id +msgid "Partner with same Company Registry" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__same_vat_partner_id +msgid "Partner with same Tax ID" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__debit_limit +msgid "Payable Limit" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__payment_token_count +msgid "Payment Token Count" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__payment_token_ids +msgid "Payment Tokens" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__peppol_endpoint +msgid "Peppol Endpoint" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__peppol_eas +msgid "Peppol e-address (EAS)" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__person_identifier_mapping +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__person_identifier +#: model_terms:ir.ui.view,arch_db:spp_attendance.attendance_subscriber_form +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Person Identifier" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.attendance_subscriber_form +msgid "Person Information" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__personal_information_mapping +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Personal Information" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Personal Information Mapping must point to a list." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__phone_mapping +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__phone +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Phone" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__phone_sanitized_blacklisted +msgid "Phone Blacklisted" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__phone_mobile_search +msgid "Phone/Mobile" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Please fill in the following required fields: %s, " +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__pos_order_ids +msgid "Pos Order" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__pos_order_count +msgid "Pos Order Count" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/models/attendance_list.py:0 +#: model:ir.model.fields.selection,name:spp_attendance.selection__spp_attendance_list__attendance_category__present +#, python-format +msgid "Present" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_product_pricelist +msgid "Pricelist" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__raw_body +msgid "Raw Body for Authentication" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__raw_body_for_import +msgid "Raw Body for Import" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__ref +msgid "Reference" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__parent_id +msgid "Related Company" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_user_id +msgid "Responsible User" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__website_id +msgid "Restrict publishing to this website." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_has_sms_error +msgid "SMS Delivery error" +msgstr "" + +#. module: spp_attendance +#: model:ir.model,name:spp_attendance.model_spp_attendance_api_client_credential +msgid "SPP Attendance Client Credential" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "SPP Attendance Settings" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__user_id +msgid "Salesperson" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__phone_sanitized +msgid "Sanitized Number" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__invoice_warn +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__picking_warn +msgid "" +"Selecting the \"Warning\" option will notify user with the message, " +"Selecting \"Blocking Message\" will throw an exception with the message and " +"block the flow. The Message has to be written in the next field." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__self +msgid "Self" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__server_url +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Server URL" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Server URL of the API." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__partner_share +msgid "Share Partner" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_form +msgid "Show" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__show_credit_limit +msgid "Show Credit Limit" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__signup_expiration +msgid "Signup Expiration" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__signup_token +msgid "Signup Token" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__signup_type +msgid "Signup Token Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__signup_valid +msgid "Signup Token is Valid" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__signup_url +msgid "Signup URL" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__starred_message_ids +msgid "Starred Message" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__state_id +msgid "State" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__static_map_url +msgid "Static Map Url" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__static_map_url_is_valid +msgid "Static Map Url Is Valid" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__picking_warn +msgid "Stock Picking" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__street +msgid "Street" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__street2 +msgid "Street2" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__submission_source +msgid "Submission Source" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__submitted_by +msgid "Submitted By" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__submitted_datetime +msgid "Submitted Datetime" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.attendance_subscriber_form +msgid "Submitted Information" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__subscriber_id +#: model:ir.ui.menu,name:spp_attendance.attendance_subscriber_menu +msgid "Subscriber" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__supplier_rank +msgid "Supplier Rank" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__category_id +msgid "Tags" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__vat +msgid "Tax ID" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__country_code +msgid "" +"The ISO country code in two chars. \n" +"You can use this field for quick search." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__vat +msgid "" +"The Tax Identification Number. Values here will be validated based on the " +"country format. You can use '/' to indicate that the partner is not subject " +"to tax." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_account_position_id +msgid "" +"The fiscal position determines the taxes/accounts used for this contact." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__website_url +msgid "The full URL to access the document through the website." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__user_id +msgid "The internal user in charge of this contact." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__pos_order_count +msgid "The number of point of sales orders related to this customer" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__has_unreconciled_entries +msgid "" +"The partner has at least one unreconciled debit and credit since last time " +"the invoices & payments matching was performed." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "" +"The path to the access token in the response. leave blank if response is a " +"string." +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#: model:ir.model.fields,help:spp_attendance.field_spp_import_attendance_wizard__raw_body +#, python-format +msgid "The raw body to be sent to the auth URL. Must be in JSON format." +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#: model:ir.model.fields,help:spp_attendance.field_spp_import_attendance_wizard__raw_body_for_import +#, python-format +msgid "The raw body to be sent to the import URL. Must be in JSON format." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__company_registry +msgid "" +"The registry number of the company. Use it if it is different from the Tax " +"ID. It must be unique across all partners of a same country" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_stock_customer +msgid "" +"The stock location used as destination when sending goods to this contact." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_stock_supplier +msgid "" +"The stock location used as source when receiving goods from this contact." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_account_payable_id +msgid "" +"This account will be used instead of the default one as the payable account " +"for the current partner" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_account_receivable_id +msgid "" +"This account will be used instead of the default one as the receivable " +"account for the current partner" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__email_normalized +msgid "" +"This field is used to search on email address as the primary email field can " +"contain more than strictly an email address." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_supplier_payment_term_id +msgid "" +"This payment term will be used instead of the default one for purchase " +"orders and vendor bills" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_payment_term_id +msgid "" +"This payment term will be used instead of the default one for sales orders " +"and customer invoices" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_product_pricelist +msgid "" +"This pricelist will be used, instead of the default one, for sales to the " +"current partner" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_time +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Time" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__tz +msgid "Timezone" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__tz_offset +msgid "Timezone offset" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__title +msgid "Title" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__total_invoiced +msgid "Total Invoiced" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__debit +msgid "Total Payable" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__credit +msgid "Total Receivable" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__credit +msgid "Total amount this customer owes you." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__debit +msgid "Total amount you have to pay to this vendor." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_type_id +msgid "Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "URL and Auth Settings" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Unable to connect to the server. Please check your internet connection." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__date_unique +msgid "Unique Date" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__location_unique +msgid "Unique Location" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__time_unique +msgid "Unique Time" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__type_unique +msgid "Unique Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__peppol_endpoint +msgid "" +"Unique identifier used by the BIS Billing 3.0 and its derivatives, also " +"known as 'Endpoint ID'." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__barcode +msgid "Use a barcode to identify this contact." +msgstr "" + +#. module: spp_attendance +#: model:ir.module.category,description:spp_attendance.attendance_module_management +msgid "User access level for Attendance" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__user_ids +msgid "Users" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_stock_supplier +msgid "Vendor Location" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_supplier_payment_term_id +msgid "Vendor Payment Terms" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__show_button_clicked +msgid "Viewed?" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__website_published +msgid "Visible on current website" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__visitor_ids +msgid "Visitors" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__website_id +msgid "Website" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__website +msgid "Website Link" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__website_url +msgid "Website URL" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__tz +msgid "" +"When printing documents and exporting/importing data, time values are " +"computed according to this timezone.\n" +"If the timezone is not set, UTC (Coordinated Universal Time) is used.\n" +"Anywhere else, time values are computed according to the time offset of your " +"web client." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__zip +msgid "Zip" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__days_sales_outstanding +msgid "" +"[(Total Receivable/Total Revenue) * number of days since the first invoice] " +"for this customer" +msgstr "" diff --git a/spp_attendance/i18n/spp_attendance.pot b/spp_attendance/i18n/spp_attendance.pot new file mode 100644 index 000000000..23c68593a --- /dev/null +++ b/spp_attendance/i18n/spp_attendance.pot @@ -0,0 +1,1826 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * spp_attendance +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.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: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__event_count +msgid "# Events" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__meeting_count +msgid "# Meetings" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "%(name)s is not set." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__type +msgid "" +"- Contact: Use this to organize the contact details of employees of a given company (e.g. CEO, CFO, ...).\n" +"- Invoice Address: Preferred address for all invoices. Selected by default when you invoice an order that belongs to this company.\n" +"- Delivery Address: Preferred address for all deliveries. Selected by default when you deliver an order that belongs to this company.\n" +"- Other: Other address for the company (e.g. subsidiary, ...)" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.constraint,message:spp_attendance.constraint_spp_attendance_subscriber_partner_id_uniq +msgid "A subscriber with the same partner already exists." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.constraint,message:spp_attendance.constraint_spp_attendance_subscriber_person_identifier_uniq +msgid "A subscriber with the same person identifier already exists." +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/models/attendance_list.py:0 +#: model:ir.model.fields.selection,name:spp_attendance.selection__spp_attendance_list__attendance_category__absent +#, python-format +msgid "Absent" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__access_token_mapping +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Access Token Mapping" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_account_payable_id +msgid "Account Payable" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_account_receivable_id +msgid "Account Receivable" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__active +msgid "Active" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__active_lang_count +msgid "Active Lang Count" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_ids +msgid "Activities" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_state +msgid "Activity State" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_partner__addl_name +#: model:ir.model.fields,field_description:spp_attendance.field_res_users__addl_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__addl_name +msgid "Additional Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__additional_info +msgid "Additional info" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.attendance_subscriber_form +msgid "Addl. Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__type +msgid "Address Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__lang +msgid "" +"All the emails and documents sent to this contact will be translated in this" +" language." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: spp_attendance +#: model:ir.module.category,name:spp_attendance.attendance_module_management +#: model:ir.ui.menu,name:spp_attendance.menu_root +#: model_terms:ir.ui.view,arch_db:spp_attendance.attendance_subscriber_form +msgid "Attendance" +msgstr "" + +#. module: spp_attendance +#: model:ir.ui.menu,name:spp_attendance.menu_attendance_api_client_credentiale +msgid "Attendance API Client Credentials" +msgstr "" + +#. module: spp_attendance +#: model:ir.actions.act_window,name:spp_attendance.spp_attendance_client_credential_window +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_form +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_view_credentials +msgid "Attendance Client Credentials" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Attendance Data Uniqueness" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.attendance_subscriber_form +msgid "Attendance Information" +msgstr "" + +#. module: spp_attendance +#: model:ir.model,name:spp_attendance.model_spp_attendance_list +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__attendance_list_ids +msgid "Attendance List" +msgstr "" + +#. module: spp_attendance +#: model:ir.actions.act_window,name:spp_attendance.attendance_location_action_window +#: model:ir.model,name:spp_attendance.model_spp_attendance_location +#: model:ir.ui.menu,name:spp_attendance.attendance_location_menu +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Attendance Location" +msgstr "" + +#. module: spp_attendance +#: model:res.groups,name:spp_attendance.attendance_manager +msgid "Attendance Manager" +msgstr "" + +#. module: spp_attendance +#: model:ir.actions.act_window,name:spp_attendance.attendance_subscriber_action_window +#: model:ir.model,name:spp_attendance.model_spp_attendance_subscriber +msgid "Attendance Subscriber" +msgstr "" + +#. module: spp_attendance +#: model:ir.actions.act_window,name:spp_attendance.attendance_type_action_windown +#: model:ir.model,name:spp_attendance.model_spp_attendance_type +#: model:ir.ui.menu,name:spp_attendance.attendance_type_menu +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Attendance Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.constraint,message:spp_attendance.constraint_spp_attendance_type_name_uniq +msgid "Attendance Type must be unique!" +msgstr "" + +#. module: spp_attendance +#: model:res.groups,name:spp_attendance.attendance_viewer +msgid "Attendance Viewer" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__attendance_auth_endpoint +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Auth Endpoint" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__auth_header +msgid "Auth Headers" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Auth Headers must be in JSON format" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__auth_type +msgid "Auth Type" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.import_attendance_wizard +msgid "Authentication" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Authentication failed. Reason: %s." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__avatar_1920 +msgid "Avatar" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__avatar_1024 +msgid "Avatar 1024" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__avatar_128 +msgid "Avatar 128" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__avatar_256 +msgid "Avatar 256" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__avatar_512 +msgid "Avatar 512" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__bank_account_count +msgid "Bank" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__bank_ids +msgid "Banks" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__barcode +msgid "Barcode" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields.selection,name:spp_attendance.selection__spp_import_attendance_wizard__auth_type__basic +msgid "Basic" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields.selection,name:spp_attendance.selection__spp_import_attendance_wizard__auth_type__bearer +msgid "Bearer" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__is_blacklisted +msgid "Blacklist" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__mobile_blacklisted +msgid "Blacklisted Phone Is Mobile" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__phone_blacklisted +msgid "Blacklisted Phone is Phone" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_bounce +msgid "Bounce" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__can_publish +msgid "Can Publish" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.import_attendance_wizard +msgid "Cancel" +msgstr "" + +#. module: spp_attendance +#: model:ir.actions.act_window,name:spp_attendance.action_change_request_cancel_wizard +msgid "Cancel Change Request" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_category +msgid "Category" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__channel_ids +msgid "Channels" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__is_company +msgid "Check if the contact is a company, otherwise it is a person" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__employee +msgid "Check this box if this contact is an Employee." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__city +msgid "City" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__client_id +msgid "Client" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_form +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_view_credentials +msgid "Client ID" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/models/attendance_api_client_credentials.py:0 +#, python-format +msgid "Client ID and Client Secret is already showed once." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.constraint,message:spp_attendance.constraint_spp_attendance_api_client_credential_client_id_uniq +msgid "Client ID must be unique!" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__name +msgid "Client Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.constraint,message:spp_attendance.constraint_spp_attendance_api_client_credential_name_uniq +msgid "Client Name must be unique!" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__client_secret +msgid "Client Secret" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.constraint,message:spp_attendance.constraint_spp_attendance_api_client_credential_client_secret_uniq +msgid "Client Secret must be unique !" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__peppol_eas +msgid "" +"Code used to identify the Endpoint for BIS Billing 3.0 and its derivatives.\n" +" List available at https://docs.peppol.eu/poacc/billing/3.0/codelist/eas/" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__color +msgid "Color Index" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__commercial_partner_id +msgid "Commercial Entity" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__ref_company_ids +msgid "Companies that refers to partner" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__company_id +msgid "Company" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__company_registry +msgid "Company ID" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__company_name +msgid "Company Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__commercial_company_name +msgid "Company Name Entity" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__company_type +msgid "Company Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__partner_gid +msgid "Company database ID" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__contact_address +msgid "Complete Address" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__complete_name +msgid "Complete Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model,name:spp_attendance.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: spp_attendance +#: model:ir.ui.menu,name:spp_attendance.attendance_configuration_menu_root +msgid "Configuration" +msgstr "" + +#. module: spp_attendance +#: model:ir.model,name:spp_attendance.model_res_partner +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__child_ids +msgid "Contact" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__message_bounce +msgid "Counter of the number of bounced emails for this contact" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__country_id +msgid "Country" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__country_code +msgid "Country Code" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.actions.act_window,help:spp_attendance.spp_attendance_client_credential_window +msgid "Create and manage the Attendance API Client Credentials." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.actions.act_window,help:spp_attendance.attendance_location_action_window +msgid "Create and manage the attendance location." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.actions.act_window,help:spp_attendance.attendance_type_action_windown +msgid "Create and manage the attendance type." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.actions.act_window,help:spp_attendance.attendance_subscriber_action_window +msgid "Create and manage the attendance." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__create_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__create_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__create_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__create_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__create_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__create_uid +msgid "Created by" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__create_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__create_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__create_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__create_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__create_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__create_date +msgid "Created on" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__credit_limit +msgid "Credit Limit" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__credit_to_invoice +msgid "Credit To Invoice" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__credit_limit +msgid "Credit limit specific to this partner." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__currency_id +msgid "Currency" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_stock_customer +msgid "Customer Location" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_payment_term_id +msgid "Customer Payment Terms" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__customer_rank +msgid "Customer Rank" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Data before the index is not a list" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__date +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Date" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__days_sales_outstanding +msgid "Days Sales Outstanding (DSO)" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__trust +msgid "Degree of trust you have in this debtor" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_description +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__description +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__description +msgid "Description" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__display_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__display_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__display_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__display_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__display_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__display_name +msgid "Display Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__duplicated_bank_account_partners_count +msgid "Duplicated Bank Account Partners Count" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__partner_share +msgid "" +"Either customer (not a user), either shared user. Indicated the current " +"partner is a customer without access or with a limited access created for " +"sharing data." +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Element %s not found in data" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__email_mapping +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__email +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Email" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__employee +msgid "Employee" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Endpoing of the API to Authenticate." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Endpoint of the API to import attendance." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_external_url +msgid "External URL" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__family_name_mapping +#: model:ir.model.fields,field_description:spp_attendance.field_res_partner__family_name +#: model:ir.model.fields,field_description:spp_attendance.field_res_users__family_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__family_name +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Family Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__phone_sanitized +msgid "" +"Field used to store sanitized phone number. Helps speeding up searches and " +"comparisons." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__fiscal_country_codes +msgid "Fiscal Country Codes" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_account_position_id +msgid "Fiscal Position" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__ubl_cii_format +msgid "Format" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__email_formatted +msgid "Format email address \"Name \"" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__email_formatted +msgid "Formatted Email" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__partner_name +msgid "Full Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__gender_mapping +#: model:ir.model.fields,field_description:spp_attendance.field_res_partner__gender_char +#: model:ir.model.fields,field_description:spp_attendance.field_res_users__gender_char +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__gender_char +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Gender" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__partner_latitude +msgid "Geo Latitude" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__partner_longitude +msgid "Geo Longitude" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__given_name_mapping +#: model:ir.model.fields,field_description:spp_attendance.field_res_partner__given_name +#: model:ir.model.fields,field_description:spp_attendance.field_res_users__given_name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__given_name +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Given Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__has_message +msgid "Has Message" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__has_unreconciled_entries +msgid "Has Unreconciled Entries" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__id +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__id +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__id +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__id +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__id +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__id +msgid "ID" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__im_status +msgid "IM Status" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_partner__identifier +#: model:ir.model.fields,field_description:spp_attendance.field_res_users__identifier +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__identifier +msgid "Identifier" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__message_needaction +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__message_has_error +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__message_has_sms_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__is_blacklisted +msgid "" +"If the email address is on the blacklist, the contact won't receive mass " +"mailing anymore, from any list" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__phone_sanitized_blacklisted +msgid "" +"If the sanitized phone number is on the blacklist, the contact won't receive" +" mass mailing sms anymore, from any list" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__image_1920 +msgid "Image" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__image_1024 +msgid "Image 1024" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__image_128 +msgid "Image 128" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__image_256 +msgid "Image 256" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__image_512 +msgid "Image 512" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#: model_terms:ir.ui.view,arch_db:spp_attendance.import_attendance_wizard +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +#, python-format +msgid "Import" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/models/registry_config.py:0 +#, python-format +msgid "Import Attendance" +msgstr "" + +#. module: spp_attendance +#: model:ir.model,name:spp_attendance.model_spp_import_attendance_wizard +msgid "Import Attendance Wizard" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__attendance_import_endpoint +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Import Endpoint" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__import_header +msgid "Import Headers" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Import Headers must be in JSON format" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Import failed. Reason: %s." +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "" +"Import successfully. \n" +" Pagination information: %s" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Imported %s persons." +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Index out of range" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__mobile_blacklisted +msgid "" +"Indicates if a blacklisted sanitized phone number is a mobile number. Helps " +"distinguish which number is blacklisted when there is both a " +"mobile and phone field in a model." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__phone_blacklisted +msgid "" +"Indicates if a blacklisted sanitized phone number is a phone number. Helps " +"distinguish which number is blacklisted when there is both a " +"mobile and phone field in a model." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__industry_id +msgid "Industry" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__contact_address_inline +msgid "Inlined Complete Address" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__invoice_warn +msgid "Invoice" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__invoice_ids +msgid "Invoices" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__is_public +msgid "Is Public" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__is_published +msgid "Is Published" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__is_company +msgid "Is a Company" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__function +msgid "Job Position" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__journal_item_count +msgid "Journal Items" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__lang +msgid "Language" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__write_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__write_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__write_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__write_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__write_uid +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__write_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__write_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__write_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__write_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__write_date +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__write_date +msgid "Last Updated on" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__calendar_last_notif_ack +msgid "Last notification marked as read from base Calendar" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__last_time_entries_checked +msgid "" +"Last time the invoices & payments matching was performed for this partner. " +"It is set either if there's not at least an unreconciled debit and an " +"unreconciled credit or if you click the \"Done\" button." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__last_time_entries_checked +msgid "Latest Invoices & Payments Matching Date" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__limit +msgid "Limit" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_location_id +msgid "Location" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_view_credentials +msgid "" +"Make sure to copy your client id and client secret now. You won't be able to" +" see it again!" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for email." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for family name." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for gender." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for given name." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for person identifier." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for personal informations. Last element must be a list." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mapping for phone." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Mappings" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__meeting_ids +msgid "Meetings" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__invoice_warn_msg +msgid "Message for Invoice" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__picking_warn_msg +msgid "Message for Stock Picking" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_ids +msgid "Messages" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__mobile +msgid "Mobile" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_location__name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__name +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_type__name +msgid "Name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_calendar_event_id +msgid "Next Activity Calendar Event" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_summary +msgid "Next Activity Summary" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_type_id +msgid "Next Activity Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__email_normalized +msgid "Normalized Email" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/models/attendance_api_client_credentials.py:0 +#, python-format +msgid "Not allowed to export on this model." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__comment +msgid "Notes" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_view_credentials +msgid "OK" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__page +msgid "Page" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__parent_name +msgid "Parent name" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__partner_id +msgid "Partner" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__contract_ids +msgid "Partner Contracts" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__use_partner_credit_limit +msgid "Partner Limit" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__same_company_registry_partner_id +msgid "Partner with same Company Registry" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__same_vat_partner_id +msgid "Partner with same Tax ID" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__debit_limit +msgid "Payable Limit" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__payment_token_count +msgid "Payment Token Count" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__payment_token_ids +msgid "Payment Tokens" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__peppol_endpoint +msgid "Peppol Endpoint" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__peppol_eas +msgid "Peppol e-address (EAS)" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__person_identifier_mapping +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__person_identifier +#: model_terms:ir.ui.view,arch_db:spp_attendance.attendance_subscriber_form +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Person Identifier" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.attendance_subscriber_form +msgid "Person Information" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__personal_information_mapping +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Personal Information" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Personal Information Mapping must point to a list." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__phone_mapping +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__phone +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Phone" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__phone_sanitized_blacklisted +msgid "Phone Blacklisted" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__phone_mobile_search +msgid "Phone/Mobile" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "Please fill in the following required fields: %s, " +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__pos_order_ids +msgid "Pos Order" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__pos_order_count +msgid "Pos Order Count" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/models/attendance_list.py:0 +#: model:ir.model.fields.selection,name:spp_attendance.selection__spp_attendance_list__attendance_category__present +#, python-format +msgid "Present" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_product_pricelist +msgid "Pricelist" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__raw_body +msgid "Raw Body for Authentication" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_import_attendance_wizard__raw_body_for_import +msgid "Raw Body for Import" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__ref +msgid "Reference" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__parent_id +msgid "Related Company" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__activity_user_id +msgid "Responsible User" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__website_id +msgid "Restrict publishing to this website." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__message_has_sms_error +msgid "SMS Delivery error" +msgstr "" + +#. module: spp_attendance +#: model:ir.model,name:spp_attendance.model_spp_attendance_api_client_credential +msgid "SPP Attendance Client Credential" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "SPP Attendance Settings" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__user_id +msgid "Salesperson" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__phone_sanitized +msgid "Sanitized Number" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__invoice_warn +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__picking_warn +msgid "" +"Selecting the \"Warning\" option will notify user with the message, " +"Selecting \"Blocking Message\" will throw an exception with the message and " +"block the flow. The Message has to be written in the next field." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__self +msgid "Self" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__server_url +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Server URL" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Server URL of the API." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__partner_share +msgid "Share Partner" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_client_credential_form +msgid "Show" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__show_credit_limit +msgid "Show Credit Limit" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__signup_expiration +msgid "Signup Expiration" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__signup_token +msgid "Signup Token" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__signup_type +msgid "Signup Token Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__signup_valid +msgid "Signup Token is Valid" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__signup_url +msgid "Signup URL" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__starred_message_ids +msgid "Starred Message" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__state_id +msgid "State" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__static_map_url +msgid "Static Map Url" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__static_map_url_is_valid +msgid "Static Map Url Is Valid" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__picking_warn +msgid "Stock Picking" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__street +msgid "Street" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__street2 +msgid "Street2" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__submission_source +msgid "Submission Source" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__submitted_by +msgid "Submitted By" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__submitted_datetime +msgid "Submitted Datetime" +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.attendance_subscriber_form +msgid "Submitted Information" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__subscriber_id +#: model:ir.ui.menu,name:spp_attendance.attendance_subscriber_menu +msgid "Subscriber" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__supplier_rank +msgid "Supplier Rank" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__category_id +msgid "Tags" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__vat +msgid "Tax ID" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__country_code +msgid "" +"The ISO country code in two chars. \n" +"You can use this field for quick search." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__vat +msgid "" +"The Tax Identification Number. Values here will be validated based on the " +"country format. You can use '/' to indicate that the partner is not subject " +"to tax." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_account_position_id +msgid "" +"The fiscal position determines the taxes/accounts used for this contact." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__website_url +msgid "The full URL to access the document through the website." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__user_id +msgid "The internal user in charge of this contact." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__pos_order_count +msgid "The number of point of sales orders related to this customer" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__has_unreconciled_entries +msgid "" +"The partner has at least one unreconciled debit and credit since last time " +"the invoices & payments matching was performed." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "" +"The path to the access token in the response. leave blank if response is a " +"string." +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#: model:ir.model.fields,help:spp_attendance.field_spp_import_attendance_wizard__raw_body +#, python-format +msgid "The raw body to be sent to the auth URL. Must be in JSON format." +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#: model:ir.model.fields,help:spp_attendance.field_spp_import_attendance_wizard__raw_body_for_import +#, python-format +msgid "The raw body to be sent to the import URL. Must be in JSON format." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__company_registry +msgid "" +"The registry number of the company. Use it if it is different from the Tax " +"ID. It must be unique across all partners of a same country" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_stock_customer +msgid "" +"The stock location used as destination when sending goods to this contact." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_stock_supplier +msgid "" +"The stock location used as source when receiving goods from this contact." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_account_payable_id +msgid "" +"This account will be used instead of the default one as the payable account " +"for the current partner" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_account_receivable_id +msgid "" +"This account will be used instead of the default one as the receivable " +"account for the current partner" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__email_normalized +msgid "" +"This field is used to search on email address as the primary email field can" +" contain more than strictly an email address." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_supplier_payment_term_id +msgid "" +"This payment term will be used instead of the default one for purchase " +"orders and vendor bills" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_payment_term_id +msgid "" +"This payment term will be used instead of the default one for sales orders " +"and customer invoices" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__property_product_pricelist +msgid "" +"This pricelist will be used, instead of the default one, for sales to the " +"current partner" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_time +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "Time" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__tz +msgid "Timezone" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__tz_offset +msgid "Timezone offset" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__title +msgid "Title" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__total_invoiced +msgid "Total Invoiced" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__debit +msgid "Total Payable" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__credit +msgid "Total Receivable" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__credit +msgid "Total amount this customer owes you." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__debit +msgid "Total amount you have to pay to this vendor." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_list__attendance_type_id +msgid "Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. module: spp_attendance +#: model_terms:ir.ui.view,arch_db:spp_attendance.spp_attendance_res_config_settings_view_form +msgid "URL and Auth Settings" +msgstr "" + +#. module: spp_attendance +#. odoo-python +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#: code:addons/spp_attendance/wizard/import_attendance.py:0 +#, python-format +msgid "" +"Unable to connect to the server. Please check your internet connection." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__date_unique +msgid "Unique Date" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__location_unique +msgid "Unique Location" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__time_unique +msgid "Unique Time" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_res_config_settings__type_unique +msgid "Unique Type" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__peppol_endpoint +msgid "" +"Unique identifier used by the BIS Billing 3.0 and its derivatives, also " +"known as 'Endpoint ID'." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__barcode +msgid "Use a barcode to identify this contact." +msgstr "" + +#. module: spp_attendance +#: model:ir.module.category,description:spp_attendance.attendance_module_management +msgid "User access level for Attendance" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__user_ids +msgid "Users" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_stock_supplier +msgid "Vendor Location" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__property_supplier_payment_term_id +msgid "Vendor Payment Terms" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_api_client_credential__show_button_clicked +msgid "Viewed?" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__website_published +msgid "Visible on current website" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__visitor_ids +msgid "Visitors" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__website_id +msgid "Website" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__website +msgid "Website Link" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__website_url +msgid "Website URL" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__tz +msgid "" +"When printing documents and exporting/importing data, time values are computed according to this timezone.\n" +"If the timezone is not set, UTC (Coordinated Universal Time) is used.\n" +"Anywhere else, time values are computed according to the time offset of your web client." +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,field_description:spp_attendance.field_spp_attendance_subscriber__zip +msgid "Zip" +msgstr "" + +#. module: spp_attendance +#: model:ir.model.fields,help:spp_attendance.field_spp_attendance_subscriber__days_sales_outstanding +msgid "" +"[(Total Receivable/Total Revenue) * number of days since the first invoice] " +"for this customer" +msgstr "" diff --git a/spp_attendance/models/__init__.py b/spp_attendance/models/__init__.py new file mode 100644 index 000000000..81f811d76 --- /dev/null +++ b/spp_attendance/models/__init__.py @@ -0,0 +1,7 @@ +from . import attendance_subscriber +from . import attendance_list +from . import attendance_api_client_credentials +from . import attendance_type +from . import registry_config +from . import attendance_location +from . import res_partner diff --git a/spp_attendance/models/attendance_api_client_credentials.py b/spp_attendance/models/attendance_api_client_credentials.py new file mode 100644 index 000000000..cd7147e73 --- /dev/null +++ b/spp_attendance/models/attendance_api_client_credentials.py @@ -0,0 +1,94 @@ +import calendar +import os +import uuid +from datetime import datetime, timedelta + +from odoo import _, api, fields, models +from odoo.exceptions import UserError + +from odoo.addons.spp_oauth.tools import calculate_signature + +TOKEN_EXPIRATION_MIN = os.getenv("ATTENDANCE_TOKEN_EXPIRATION_MIN", 10) + + +class AttendanceApiClientCredential(models.Model): + _name = "spp.attendance.api.client.credential" + _description = "SPP Attendance Client Credential" + + @api.model + def _generate_client_id(self): + client_id = str(uuid.uuid4()) + while self.search_count([("client_id", "=", client_id)]): + client_id = str(uuid.uuid4()) + + return f"c-id-{client_id}" + + @api.model + def _generate_client_secret(self): + client_secret = str(uuid.uuid4()) + while self.search_count([("client_secret", "=", client_secret)]): + client_secret = str(uuid.uuid4()) + + return f"c-secret-{client_secret}" + + name = fields.Char("Client Name", required=True) + + # bearer fields + client_id = fields.Char(required=True, readonly=True, default=_generate_client_id) + client_secret = fields.Char(required=True, readonly=True, default=_generate_client_secret) + + show_button_clicked = fields.Boolean("Viewed?") + + _name_uniq = models.Constraint("unique(name)", "Client Name must be unique!") + _client_id_uniq = models.Constraint("unique(client_id)", "Client ID must be unique!") + _client_secret_uniq = models.Constraint("unique(client_secret)", "Client Secret must be unique !") + + ALLOW_EXPORT = False + + @api.model + def generate_access_token(self): + today = datetime.today() + expiry_datetime = today + timedelta(minutes=TOKEN_EXPIRATION_MIN) + + header = {"alg": "RS256", "typ": "JWT"} + payload = { + "iat": calendar.timegm(today.timetuple()), + "exp": calendar.timegm(expiry_datetime.timetuple()), + "iss": "openspp:auth-service", + } + + return calculate_signature(self.env, header, payload) + + def show_credentials(self): + self.ensure_one() + + if not self.show_button_clicked: + action = self.env[self._name].get_formview_action() + form_id = self.env.ref("spp_attendance.view_spp_attendance_api_client_credential_form").id + + action.update( + { + "views": [(form_id, "form")], + "target": "new", + "type": "ir.actions.act_window", + "view_type": "form", + "view_mode": "form", + "res_id": self.id, + } + ) + + self.update( + { + "show_button_clicked": True, + } + ) + else: + raise UserError(_("Client ID and Client Secret is already showed once.")) + + return action + + def export_data(self, fields_to_export): + if not self.ALLOW_EXPORT: + raise UserError(_("Not allowed to export on this model.")) + + return super().export_data(fields_to_export) diff --git a/spp_attendance/models/attendance_list.py b/spp_attendance/models/attendance_list.py new file mode 100644 index 000000000..f0bebfe16 --- /dev/null +++ b/spp_attendance/models/attendance_list.py @@ -0,0 +1,59 @@ +from odoo import _, fields, models + + +class Attendance(models.Model): + _name = "spp.attendance.list" + _description = "Attendance List" + + PRESENT = "present" + ABSENT = "absent" + ALLOWED_CATEGORIES = [PRESENT, ABSENT] + + subscriber_id = fields.Many2one("spp.attendance.subscriber", required=True, readonly=True) + attendance_date = fields.Date(required=True, default=lambda self: fields.Date.today(), string="Date") + attendance_time = fields.Char(required=True, string="Time", default="00:00:00") + + attendance_type_id = fields.Many2one("spp.attendance.type", string="Type") + attendance_location_id = fields.Many2one("spp.attendance.location", string="Location") + attendance_description = fields.Text(string="Description") + attendance_external_url = fields.Char(string="External URL") + + submitted_by = fields.Char(required=True) + submitted_datetime = fields.Char( + required=True, default=lambda self: fields.Datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ) + submission_source = fields.Char() + attendance_category = fields.Selection( + selection=[(PRESENT, _("Present")), (ABSENT, _("Absent"))], + string="Category", + default=PRESENT, + required=True, + ) + + def get_unique_domain(self): + self.ensure_one() + date_unique = self.env["ir.config_parameter"].sudo().get_param("spp_attendance.date_unique") + time_unique = self.env["ir.config_parameter"].sudo().get_param("spp_attendance.time_unique") + type_unique = self.env["ir.config_parameter"].sudo().get_param("spp_attendance.type_unique") + location_unique = self.env["ir.config_parameter"].sudo().get_param("spp_attendance.location_unique") + + domain = [] + if date_unique: + domain.append(("attendance_date", "=", self.attendance_date)) + if time_unique: + domain.append(("attendance_time", "=", self.attendance_time)) + if type_unique: + domain.append(("attendance_type_id", "=", self.attendance_type_id.id)) + if location_unique: + domain.append(("attendance_location_id", "=", self.attendance_location_id.id)) + return domain + + def check_uniqueness(self, domain=None): + for rec in self: + domain = rec.get_unique_domain() + if domain: + new_domain = domain + [("subscriber_id", "=", rec.subscriber_id.id)] + count = self.search_count(new_domain) + if count > 0: + return False + return True diff --git a/spp_attendance/models/attendance_location.py b/spp_attendance/models/attendance_location.py new file mode 100644 index 000000000..c04c46cff --- /dev/null +++ b/spp_attendance/models/attendance_location.py @@ -0,0 +1,10 @@ +from odoo import fields, models + + +class SPPAttendanceLocation(models.Model): + _name = "spp.attendance.location" + _description = "Attendance Location" + _order = "name ASC" + + name = fields.Char(required=True) + description = fields.Char() diff --git a/spp_attendance/models/attendance_subscriber.py b/spp_attendance/models/attendance_subscriber.py new file mode 100644 index 000000000..76847ff48 --- /dev/null +++ b/spp_attendance/models/attendance_subscriber.py @@ -0,0 +1,189 @@ +from odoo import api, fields, models + + +class AttendanceSubscriber(models.Model): + _name = "spp.attendance.subscriber" + _inherits = { + "res.partner": "partner_id", + } + _description = "Attendance Subscriber" + + name = fields.Char(related="partner_id.name", inherited=True) + active = fields.Boolean(default=True) + partner_id = fields.Many2one("res.partner", required=True, ondelete="restrict", readonly=True) + attendance_list_ids = fields.One2many("spp.attendance.list", "subscriber_id") + person_identifier = fields.Char(required=True, inverse="_inverse_person_identifier", store=True) + + partner_name = fields.Char(compute="_compute_partner_name", string="Full Name") + family_name = fields.Char(related="partner_id.family_name", inherited=True, required=True, readonly=False) + given_name = fields.Char(related="partner_id.given_name", inherited=True, required=True, readonly=False) + addl_name = fields.Char(related="partner_id.addl_name", inherited=True, readonly=False) + email = fields.Char( + related="partner_id.email", inherited=True, compute="_compute_partner", inverse="_inverse_partner", store=True + ) + phone = fields.Char( + related="partner_id.phone", inherited=True, compute="_compute_partner", inverse="_inverse_partner", store=True + ) + # Odoo 19 removed partner.mobile; mirror the main phone instead to keep API compatibility + mobile = fields.Char( + related="partner_id.phone", inherited=True, compute="_compute_partner", inverse="_inverse_partner", store=True + ) + gender_char = fields.Char( + string="Gender", related="partner_id.gender_char", inherited=True, readonly=False, default="Male" + ) + + _partner_id_uniq = models.Constraint("unique(partner_id)", "A subscriber with the same partner already exists.") + _person_identifier_uniq = models.Constraint( + "unique(person_identifier)", + "A subscriber with the same person identifier already exists.", + ) + + def _inverse_person_identifier(self): + for record in self: + record.partner_id.write({"identifier": record.person_identifier}) + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if "partner_id" not in vals: + partner_name = f"{vals.get('family_name')}, {vals.get('given_name')}" + if vals.get("addl_name"): + partner_name += f" {vals.get('addl_name')}" + if partner_id := self.env["res.partner"].search( + [ + ("family_name", "=", vals.get("family_name")), + ("given_name", "=", vals.get("given_name")), + ("identifier", "=", vals.get("identifier")), + ], + limit=1, + ): + vals["partner_id"] = partner_id.id + else: + partner_id = self.env["res.partner"].create( + { + "name": partner_name, + "family_name": vals.get("family_name"), + "given_name": vals.get("given_name"), + "addl_name": vals.get("addl_name"), + "email": vals.get("email"), + "phone": vals.get("phone") or vals.get("mobile"), + "identifier": vals.get("person_identifier"), + "gender_char": vals.get("gender_char", "").title() or "Male", + } + ) + vals["partner_id"] = partner_id.id + return super().create(vals_list) + + @api.depends("partner_id.name", "partner_id.email", "partner_id.phone") + def _compute_partner(self): + for record in self: + if record.partner_id: + record.write( + { + "email": record.partner_id.email, + "phone": record.partner_id.phone, + "mobile": record.partner_id.phone, + } + ) + + def _inverse_partner(self): + for record in self: + if record.partner_id: + record.partner_id.write( + { + "name": f"{record.family_name}, {record.given_name}", + "email": record.email, + "phone": record.phone or record.mobile, + } + ) + + @api.depends("family_name", "given_name", "addl_name") + def _compute_partner_name(self): + for record in self: + if record.family_name and record.given_name: + record.partner_name = f"{record.family_name}, {record.given_name}" + if record.addl_name: + record.partner_name += f" {record.addl_name}" + else: + record.partner_name = "" + + def get_attendance_list( + self, + from_date=None, + to_date=None, + attendance_type_id=None, + attendance_location_id=None, + offset=0, + limit=None, + ): + domain = [("subscriber_id", "=", self.id)] + if from_date and to_date: + domain += [("attendance_date", ">=", from_date), ("attendance_date", "<=", to_date)] + if attendance_type_id: + domain += [("attendance_type_id", "=", attendance_type_id)] + if attendance_location_id: + domain += [("attendance_location_id", "=", attendance_location_id)] + + attendance_list_ids = self.env["spp.attendance.list"].search( + domain, offset=offset, limit=limit, order="attendance_date desc, attendance_time desc" + ) + total_attendances = self.env["spp.attendance.list"].sudo().search_count(domain) + + present_domain = domain + [("attendance_category", "=", "present")] + number_of_days_present = list( + set(self.env["spp.attendance.list"].sudo().search(present_domain).mapped("attendance_date")) + ) + + return total_attendances, { + "person_id": self.person_identifier, + "dates_present": number_of_days_present, + "number_of_days_present": len(number_of_days_present), + "attendance_list": [ + { + "id": attendance.id, + "date": attendance.attendance_date, + "time": attendance.attendance_time, + "attendance_type": { + "id": attendance.attendance_type_id.id, + "name": attendance.attendance_type_id.name, + "description": attendance.attendance_type_id.description, + } + if attendance.attendance_type_id + else {}, + "attendance_location": { + "id": attendance.attendance_location_id.id, + "name": attendance.attendance_location_id.name, + "description": attendance.attendance_location_id.description, + } + if attendance.attendance_location_id + else {}, + "attendance_description": attendance.attendance_description or "", + "attendance_external_url": attendance.attendance_external_url or "", + "submitted_by": attendance.submitted_by, + "submitted_datetime": attendance.submitted_datetime, + "submission_source": attendance.submission_source or "", + "attendance_category": attendance.attendance_category, + } + for attendance in attendance_list_ids + ], + } + + def get_attendance_info_list(self): + attendance_infos = [] + for subscriber in self: + attendance_infos.append(subscriber.get_attendance_subscriber_info()) + return attendance_infos + + def get_attendance_subscriber_info(self): + self.ensure_one() + return { + # Use the external person identifier as the public ID instead of the database ID. + "id": self.person_identifier, + "person_id": self.person_identifier, + "name": self.partner_name, + "family_name": self.family_name, + "given_name": self.given_name, + "email": self.email, + "phone": self.phone, + "mobile": self.mobile, + } diff --git a/spp_attendance/models/attendance_type.py b/spp_attendance/models/attendance_type.py new file mode 100644 index 000000000..a0fc4effd --- /dev/null +++ b/spp_attendance/models/attendance_type.py @@ -0,0 +1,12 @@ +from odoo import fields, models + + +class SPPAttendanceType(models.Model): + _name = "spp.attendance.type" + _description = "Attendance Type" + _order = "name ASC" + + name = fields.Char(required=True) + description = fields.Char() + + _name_uniq = models.Constraint("unique(name)", "Attendance Type must be unique!") diff --git a/spp_attendance/models/registry_config.py b/spp_attendance/models/registry_config.py new file mode 100644 index 000000000..131039a1d --- /dev/null +++ b/spp_attendance/models/registry_config.py @@ -0,0 +1,120 @@ +from odoo import _, api, fields, models + + +class RegistryConfig(models.TransientModel): + _inherit = "res.config.settings" + + server_url = fields.Char( + string="Server URL", + config_parameter="spp_attendance.server_url", + default=lambda self: self.env["ir.config_parameter"].sudo().get_param("web.base.url"), + ) + + attendance_auth_endpoint = fields.Char( + string="Auth Endpoint", + config_parameter="spp_attendance.attendance_auth_endpoint", + default="/oauth2/client/token", + ) + access_token_mapping = fields.Char( + string="Access Token Mapping", + config_parameter="spp_attendance.access_token_mapping", + default="access_token", + ) + attendance_import_endpoint = fields.Char( + string="Import Endpoint", + config_parameter="spp_attendance.attendance_import_endpoint", + default="/registry/sync/search", + ) + + personal_information_mapping = fields.Char( + string="Personal Information", + config_parameter="spp_attendance.personal_information_mapping", + default="message.search_response.0.data.reg_records", + ) + person_identifier_mapping = fields.Char( + string="Person Identifier", + config_parameter="spp_attendance.person_identifier_mapping", + default="identifier.0.identifier", + ) + family_name_mapping = fields.Char( + string="Family Name", + config_parameter="spp_attendance.family_name_mapping", + default="familyName", + ) + given_name_mapping = fields.Char( + string="Given Name", + config_parameter="spp_attendance.given_name_mapping", + default="givenName", + ) + email_mapping = fields.Char( + string="Email", + config_parameter="spp_attendance.email_mapping", + default="email", + ) + phone_mapping = fields.Char( + string="Phone", + config_parameter="spp_attendance.phone_mapping", + default="phoneNumbers.0.phone", + ) + gender_mapping = fields.Char( + string="Gender", + config_parameter="spp_attendance.gender_mapping", + default="sex", + ) + + is_date_unique = fields.Boolean( + string="Unique Date", + config_parameter="spp_attendance.date_unique", + default=False, + inverse="_onchange_unique_fields", + ) + is_time_unique = fields.Boolean( + string="Unique Time", + config_parameter="spp_attendance.time_unique", + default=False, + inverse="_onchange_unique_fields", + ) + is_type_unique = fields.Boolean( + string="Unique Type", + config_parameter="spp_attendance.type_unique", + default=False, + inverse="_onchange_unique_fields", + ) + is_location_unique = fields.Boolean( + string="Unique Location", + config_parameter="spp_attendance.location_unique", + default=False, + ) + + def action_import(self): + self.ensure_one() + + form_id = self.env.ref("spp_attendance.import_attendance_wizard").id + action = { + "name": _("Import Attendance"), + "type": "ir.actions.act_window", + "view_mode": "form", + "view_id": form_id, + "view_type": "form", + "res_model": "spp.import.attendance.wizard", + "target": "new", + } + return action + + @api.onchange("is_date_unique", "is_time_unique", "is_type_unique") + def _onchange_unique_fields(self): + if not self.is_date_unique: + self.is_time_unique = False + self.is_type_unique = False + self.is_location_unique = False + if not self.is_time_unique: + self.is_type_unique = False + self.is_location_unique = False + if not self.is_type_unique: + self.is_location_unique = False + + @api.model + def set_server_url(self): + if not self.env["ir.config_parameter"].sudo().get_param("spp_attendance.server_url"): + web_base_url = self.env["ir.config_parameter"].sudo().get_param("web.base.url") + self.env["ir.config_parameter"].sudo().set_param("spp_attendance.server_url", web_base_url) diff --git a/spp_attendance/models/res_partner.py b/spp_attendance/models/res_partner.py new file mode 100644 index 000000000..32d288bb8 --- /dev/null +++ b/spp_attendance/models/res_partner.py @@ -0,0 +1,11 @@ +from odoo import fields, models + + +class ResPartnerInherit(models.Model): + _inherit = "res.partner" + + family_name = fields.Char(translate=False) + given_name = fields.Char(translate=False) + addl_name = fields.Char(translate=False, string="Additional Name") + identifier = fields.Char() + gender_char = fields.Char(translate=False, default="Male", string="Gender") diff --git a/spp_attendance/pyproject.toml b/spp_attendance/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/spp_attendance/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/spp_attendance/readme/DESCRIPTION.md b/spp_attendance/readme/DESCRIPTION.md new file mode 100644 index 000000000..0a4530f54 --- /dev/null +++ b/spp_attendance/readme/DESCRIPTION.md @@ -0,0 +1,57 @@ +Tracks participant attendance for social protection program activities. Records presence/absence with date, time, location, and activity type. Provides OAuth-secured API endpoints for external systems to submit attendance data and import participants from external registries. + +### Key Capabilities + +- Record attendance with date, time, type, location, and category (present/absent) +- Query attendance history via `get_attendance_list()` with filtering by date range, type, and location +- Import participants from external registries via configurable field mapping +- Generate OAuth client credentials for external API access +- Enforce configurable uniqueness constraints (date/time/type/location) to prevent duplicate records + +### Key Models + +| Model | Description | +| -------------------------------------- | -------------------------------------------------- | +| `spp.attendance.subscriber` | Participant registry, inherits res.partner | +| `spp.attendance.list` | Attendance record with date, time, type, location | +| `spp.attendance.type` | Configurable attendance event types | +| `spp.attendance.location` | Configurable attendance locations | +| `spp.attendance.api.client.credential` | OAuth credentials for external API clients | +| `spp.import.attendance.wizard` | Wizard for importing from external registries | + +### Configuration + +After installing: + +1. Navigate to **Settings > SPP Attendance Settings** to configure: + - Uniqueness constraints (date/time/type/location) + - Server URL and API endpoints + - Field mappings for import (personal information, identifiers, contact details) +2. Create attendance types: **Attendance > Configuration > Attendance Type** +3. Create locations: **Attendance > Configuration > Attendance Location** +4. Generate OAuth credentials: **Settings > Attendance API Client Credentials** + +### UI Location + +- **Main Menu**: Attendance > Subscriber +- **Configuration**: Attendance > Configuration (Attendance Type, Attendance Location) +- **API Credentials**: Settings > Attendance API Client Credentials +- **Settings**: Settings > SPP Attendance Settings +- **Subscriber Form**: Contains "Attendance" and "Person Information" tabs + +### Security + +| Group | Access | +| ---------------------------------------- | --------- | +| `spp_attendance.group_attendance_viewer` | Read | +| `spp_attendance.group_attendance_manager` | Full CRUD | + +### Extension Points + +- Override `get_attendance_list()` on `spp.attendance.subscriber` to customize attendance queries +- Inherit `spp.attendance.list` to add domain-specific attendance metadata +- Extend `_import_attendance()` in wizard to customize import logic + +### Dependencies + +`base`, `spp_oauth`, `spp_security` diff --git a/spp_attendance/security/ir.model.access.csv b/spp_attendance/security/ir.model.access.csv new file mode 100644 index 000000000..9df25e30a --- /dev/null +++ b/spp_attendance/security/ir.model.access.csv @@ -0,0 +1,14 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink + +access_spp_attendance_subscriber_manager,Attendance Subscriber Manager,model_spp_attendance_subscriber,spp_attendance.group_attendance_manager,1,1,1,1 +access_spp_attendance_list_manager,Attendance List Manager,model_spp_attendance_list,spp_attendance.group_attendance_manager,1,1,1,1 +access_spp_attendance_api_client_credential_manager,Attendance API Client Manager,model_spp_attendance_api_client_credential,spp_attendance.group_attendance_manager,1,1,1,1 +access_spp_import_attendance_wizard_manager,Import Attendance Wizard Manager,model_spp_import_attendance_wizard,spp_attendance.group_attendance_manager,1,1,1,1 +access_spp_attendance_type_manager,Attendance Type Manager,model_spp_attendance_type,spp_attendance.group_attendance_manager,1,1,1,1 +access_spp_attendance_location_manager,Attendance Location Manager,model_spp_attendance_location,spp_attendance.group_attendance_manager,1,1,1,1 + +access_spp_attendance_subscriber_viewer,Attendance Subscriber Viewer,model_spp_attendance_subscriber,spp_attendance.group_attendance_viewer,1,0,0,0 +access_spp_attendance_list_viewer,Attendance List Viewer,model_spp_attendance_list,spp_attendance.group_attendance_viewer,1,0,0,0 +access_spp_attendance_api_client_credential_viewer,Attendance API Client Viewer,model_spp_attendance_api_client_credential,spp_attendance.group_attendance_viewer,1,0,0,0 +access_spp_attendance_type_viewer,Attendance Type Viewer,model_spp_attendance_type,spp_attendance.group_attendance_viewer,1,0,0,0 +access_spp_attendance_location_viewer,Attendance Location Viewer,model_spp_attendance_location,spp_attendance.group_attendance_viewer,1,0,0,0 diff --git a/spp_attendance/security/privileges.xml b/spp_attendance/security/privileges.xml new file mode 100644 index 000000000..5db8bb3ff --- /dev/null +++ b/spp_attendance/security/privileges.xml @@ -0,0 +1,15 @@ + + + + + Viewer + + 10 + + + + Manager + + 20 + + diff --git a/spp_attendance/security/security.xml b/spp_attendance/security/security.xml new file mode 100644 index 000000000..319f829ef --- /dev/null +++ b/spp_attendance/security/security.xml @@ -0,0 +1,34 @@ + + + + + Viewer + + Can view attendance records. + + + + Manager + + Full attendance management including all CRUD operations. + + + + + + + + + + + Attendance Viewer (Deprecated) + Deprecated: Use group_attendance_viewer instead. + + + + + Attendance Manager (Deprecated) + Deprecated: Use group_attendance_manager instead. + + + diff --git a/spp_attendance/static/description/icon-transparent.png b/spp_attendance/static/description/icon-transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..35f8fec263588314689c920efc4b6728b407daaa GIT binary patch literal 12567 zcmW+-b66#B8$Q|AW^J`;v$sxev+XuxvyH7bZ?9d&I=lQSGq%(qxksmE0ENqsa=TIC*`@5*; zlZ@L_`{Xb|K5+g!T~BvDVye!4*Q3X9c!~4i!s6O*km%@}$Iack7k;fEj2m%dbS@g+ z&=(0&=8uP-x0Zo=3!)It?=-Zk9Pq4;w^+LagDlrZJ6O15xaY^oL=bSz;<)GDlr$uL6X)^k3|pxhu2( z;_tdO%DJ4jTUyBdzT$xUzXTq*Mdd^n8tz8T7ZRRi57vC$Zv>MUEP4wpRL0 zY~SS+buafxe|G8TvkS=E4my#ndzs#k96VRZL_BmH9%~Hc|NA6bAm*Nire4mUl&2d2 z>UMGd#-BiDQI%(923rv?=0>kWjD+9ZAv9Jzf77qJHcRST^@=%Qt!u=j@^4d?Tkss- zU5`VFrEG7c!5cXm5>)>2;wujVpiHWM;V%zYEnn zt{>bHYhNrG_lst7{4I$IN{efQ=;!t!5EFQ`Y%Rl%rR3!~XGQpFSPX`5eeZe3Oj>yf zxL0S$JbqB}QK?@2EY#`BMLLb136|1Gf%s9awql4-YM|Oz z>a>59{l&87VSl#6y^V1grlEyNVMwf!6ruszAaUXjgMt_7Ws?(?LzT{+J&=(RqR0k6t)rkqb z=6ZE9`|NcR`%Z387?`YBCLvYoFh5*2e*z)+Y2sXfHRfeB2>NOCfrtBI3$_cqgB3momFOvQ*KE z&q^#v$pl`u%p5!>y-+V1J!r?bi=NnVeq<{Qe^)ZV6TDYFiJlpC3)TJ{}7kC1|(f(x%6%=_QO(vTj(KKhu0ic)h0 zTB(3@c)p;iR=v4tHV}}(Qz;K{gCgT3o|$yG*xZW+6+it`di?T;NCb;9p!IKl0@y<{&@8*(dG zJ7=~SeleZo{^PgBKKA7wX^xmLUTavWkKEz)<$QUrg&XUJ%p78hIa%mCH&;;we@08D zT%eN(cMsjihvEYZz~rDSYHjU>tTx-b^6CU~h+9usoWn@ji1CoojaRBR4-_2F?n2~? z4$HICsd3{I-T-M72lkVR!+4dwNqA7l(3}Yy#$N3~z`h*uk#De!Zt6lyx>cx*r0S{a zNwuM|l|25s=i1704&l(k8N*k$q3Ijf!FFGTCoHmMqT2UW{*!}XrnoRoaaL!GmA?^M z{9InDo!g66sdpfdQpkv8pJ!K_smq}UD+gb=G_u2OyXoUgNmv{RLZ_A;W3^D5o67Dd z@aT?=EoQhUmWeW~*D|+l$07|tDXsq<+W;I(4ICRdi&A?)GrzkAPB?>ucfTOQ#JD-# z7u%{5g}WLVp2_!@03h(SLY&)DOa%$lNoFB<06hlQZ24v5krn`=qj9d zxwR^Mf*f90DFeCveGlO+RzGX?z|-N@ym<28E-@LF`q zCtG1cf$zcH9SOD$MM8+Ht~WkxfZ21;TkxvXkWS?JOtD{3|L`L#dwk31`#||~ef$!w z{o9j%TuulmsLR;u9nEx3%0doO3gvn)y3}mqqu`ATUkj7vCW*f+H!C(w)8oIZp@r-% z*LxrN@dQ5KG{xo=%^2pXoqt^y4p!4vFpWv4__1`JHHsE94Hw<_{$sNAP*SPJs`l4Z z3|z?G?+Y!08V0EqD^h$9^=r$dQz?31f8>gV?0zyj#^X9K&tWupNNCXpFO3LrBU4y9 z8BPteLvEfu2SXy>+MzpPW0_@4k{vUGbBv$(~9jjo)Nd zM)mt2Z8o&0KVspRzwk^?J5edrBQUIJ-A>ih}QyxSEOpH;2tinG>d-JI|C(+gT;kGC9c9uN5$=MfXm9(Wk> zX$SmbTL2lPVPpbyw~m|0%PS?8aDl#QkaOG$zL&8KFCV%c1-3ArCWmG9VjTL z$!JRotdQ?yMHl(T%FKdg62;t-{$S%qrjg;)5i~L&f8eDZ30X%X33+t*D%31SXi2s- zNn+HXRj}?g*kaAQQz`P=3I|-MvKr;ugL4Dik5Ptm;vl*aWDm+3maw!@)ni!aD6G-j zl)+DOS3JU(F*08DlYhgDph+kQ&>}tAw5TUi;+76nb9w*Po?DMGv0Jp;(fV^3D?&2nd5cj$n$$I!33DJBhkwuf zn(kRuq98aA1IEiy_lCedU?7Dtxq}){mJI`KBG|Q4(}gAx;`Q)lp;I=K?Xr2ShJ*kk zrkkhB5>lDVTDuh4kFbODtb~7q-}Y~yLttDHF+8Z=x(4pN_1`Y5grUi%4n`C_J>GgQsRoi9^EW@lXv^Zz;ELGC&4y+{K8YRW}ZFS9mSRU2hmLErl znloh1nLX2Ey$BPR2XcPDE^PN+l-`^qs!JhBfLk0Cz@*r_EnaKEyE&$KcidbMYC!cI z`{QC=DDhOb6L8ReD|11TFfs%{W~TSSq)Y<|Aw*e^sYg7?hk-(T%%q(gm~@VB=x76_ z<{ER>I=ONDb9B{8;_h&P|l!vJGEn+K|biD#LQOzdT z{?eho+7+fwmFKjvFsQlFTe#T@AB4>SP4sS1JjAh@$2tZ}y>utu(y2D@>^u;v?^x6D z18!{{r3MWPz=C&@0c)ZDdGrYyixJ8&y1L|urPW_Bl0w=rI!uSLHn?Nd%k^EBfAY?V zIEOkTN*hEAL-dL0-D#+clL#6+_-g4pj5@VKP-vtl1C}8C^*Hd8Hz z%d$o5Poa`)H_^u>Z+cpom)pTvd!jL=eN6noK{bhR9U}8&RXNGdBH^z7Dud`DJ9eUp zur6n6rC)8p2}aE4_ia&=)r^d)94S3`E;nT1&qfm~$f(kDDT6$7LP?d|I89lsuaRmn zA+k0Iq;S)Zj>OvSZFE&<-D^cqvCyAj`G-P3D7`kP?LF`#|9?+?`}i4cYNR@z%b7 zH}Ny%Vot0;)l^aL&ym=YA8g1?XDHHzdrSFs0(IpZbzyCEK<%>u-CY}nut46`GL2VG z7Db}?HwJ*Tbh0hFjF27Su`^-NI@B3BY=%_Ztl?Aqvm-hToO`A_?;XW@A+FsxsXm*n zWk=$5)D3~HiMD5}wc782-R`@26~W!oMfkU#(E}m(v`$L|Vw?AN5)1MYSJv@e_gT6J zs^A51l@OB(KK7v&qO%=QL=I}2`=dYdXnfN>?H)v%@%vFI$Kz);9E!NIM@=vw5TEo` z${*bu2MIi9^B8G&=P6HL4dm!tdjubg>a#W|MIoJ$D0E;qr6w~s2GG4#MltlZeO6dG z`C{$qUUQWaRcZ0??YlQWNZr9I$5Dq$9$tfK-+jg4AJ&$Up02s3>e~;1SwVx+;XuqV z(n|x=LhWVt80$?LIDZehr(J)Up!`-bM~k?8T1Y9VP}Pd3WMX|6$>ID<5Fy(E0g_!O zD&(b(aHRa2o<+IKmM(D6zTM(;(NS}@@5pSEKo`!SZ-E$!y06;cEZYX#qLuGCr+~)O z-Yeuh1K&ldU_-9BsL`zdnxE-=UF!1H)QayX#3cQ}5p>t&L1fY?-Zg$abs=HWrmK_L z^I0?o^kXc8jVEX41Kww=Mk#R@XC19X)0FllZkT$OE5Ds=Y9GJLH21jA+B+2O2@|2C z6FW5lUcTfvtHk{G`F)WXo9^4OK3Y!9w%XcB^?dy!%{owfGJJW)!XvhwiKqOEDeTT` z`U7CSmK#OS{pugg9K)Sp)w`$urpg1-@zxu8x}9T6BX$|$trv*pRt$(&+rP*-n7pwo z7)^8vTSy-h8y^$vGeqt+7N*+!paK>PL_TK3D)3pr4H-Uk4-M>Fc8`yc(dPV}4g*W6?-)p_Jue6ZF*A5MY zlrA}chd%LXdI0cvx3U|9V|>&g4lJkJa!hT#v7!A|HHVHg=4z<9U{}@u{n@)fq1PwJyT`&eCOT@Q=6m_v!H; z3%pX-m_HSdI#DiEsx6KTA?;JE%Nl5PP1fjRAQ6{`tD zz&5uzP@vAp^^dY;TD0_K$VAD)x=#xyiUTLy&p2XKKj+>ZVGS-<0)Y12_>^AL=cZ_; z3W0jv!NB;j5UP0pn~Om$rq04}ml8NQTZxFAQ~G z(TB`(_I+fLA(=5qMpndowwoVcHE3eRwO=D;bCU7|4%Np#V%HhympjWOL{-FKl~=># zm8Bsbte00&6YG&u*mbU=x%^CBA&{U8QQ4~0UF8qdQ7rk;JAZ8~Aweehnv!|6Uus}R z0CA{$abL2SRb-_84Bi*dan$;@Rq92O| zBxpKOO)@LxG$ONqBBa_EFO%>K)uqH>xi;Ba*R+>o(0Cq@Ki&fgeFu+NF>|2Ey@|!$ z9)G4S2p_}>4(ptVKS}DX=XV#2i~n@t`Fx>oJ5L4(Utf~tPJisuz=g{RHlDFB`yNS< z!&Br7{{z|+)4@C{J|z~0+`tCDFcf#|6iAL2M7DPKe?A(mtA$~VqL$|}gs~qwLQkeamYGUMY z90ks|B}`~C0hMF*`Rt&w8<)ftUgVM)Ln9&(II!gRj~;6hOpMtGvTX68yVAtR+Kk#R zga%HfH#SP#xqXpcWC^oRxb)l)S6dE*GUq|Pwa;##?r6rY`xqg_(qluEfPr5(OhS#$ z@I;%DD#xCn^jhO-fLznFNjyvfL!JC+c*K>5vNC%>z56GWA1vRbj4@s}c6y)8{u4T+ z2N(TZ$>zhQ(<{m8nesab9ScD@bhI%Vtb(X$Bp*C4=@xFtz%q& zvN`Yk95|HS=<3>Q#5LH7rCR0dDE@{E+I*K$gu}9zsh`I6rm9+a-xzI+QcadX!Sue; zQrd_lpV8wm9>k;AQ+Vcs0dYx-F%;#e5~GiGIdQ08R10C#Ii};+t$Qa~?C1Pd@UIm> zr7rH7UZrO~Gk`xH|20v)h@VeEUGgk3X(cZ2b>s2QWj2?lo!V!QF1PRe7F%-i&aj@M z_eW-bML0Cx5eZ~KA$w^H-=gP>*nuy%oCgDdpca1dw}VR z-x18fT{x3V!OH&2OMEulPQV1|&()vaA1W3f8AF2SVoQxMCMakT*(A*0{c9mwrOr0( zNG#e^`Gj$|Dv%Saa|&;GDZZw=a(~Xo+rpePorA@-rC*JsU1Xw+J#6Dg96g~Ke`Eg0 zmkQ^Nm#brp6~8T{J1H<3bo;3KGUF541NDgL2USd7EIfbNJ#`8Z_+=i&ZnlVf3Jrdn z@6P%*ymuA|-Fn81 z0?Wut04RiznTQiM=PXvx_hp5?-FAmjC=$1BfJNe>Qqp0t-0(2WNL9LqAs}=nQ9wTC z`_M&(VR?NHzwDG$p=lapcsf@pn2l9^Eh*1<5K%*M@=r-*@Dwm-LA9Hq)?Si{{x@0# z*D0N-XNHjIaBZ+s;(Q2YWq`IiZ^^g{sTmrt3_lz7g|rK!u|I9{H5{1{VzSI`ynM>p z*xWm%&kVnZYrosZxUu5&Axg=LM8_C{EhMl@l1*OCg+8trsD%%ev&L@~Z1p|~;1r53ZYWwXc6{B% zKR}gLlYT)zSpxb!4uIL>AqS82kg`{EfnQqw_PX9`2Ux$kTbmX(|Fd zWc%=xLwwxw3f|Dv7Qj#W5|NWRl&F31s4uWl{Vd+nt67vKo|RkqyZ8ey5 z7+y2mNqA}5v!5}}+lFqjHMNKmY2L4T-%q%h;C;sc!Lu`Nyxbe=F{Dca2aAuqheJNf zG{~#UVW|N0YsBj#;`uGX3ArH*gqp2`M=<2%?g9o{LgCoi$NTp1TVsLQT1uj_k;h=b zasc)*Y%qSq9p?2X6eSiUpx;Yj8hQCh5o{%W-DUf}f#=jwykNrTk5X9Z-!zA-mmDO! z60mXDL-BOjlS?zXe;90RyeOl7QVlIaJg9a=a3()o7z1+QPdhSTl@7XBaZmU0#>T$6f8Lg8|?SxYawWC$MP_G z*%yV(xlzr-$mF8-C3Y1(Ya%@bHSqTW5fK%RVrU_BTaXBW{zIffgc51w_+^uO8wtHz2=o&M z#8UT(BsB&!ai)O4$HbbPModc2OGuq_x*Dcz?-}?-#k419wiL!fGxg)ev!(7M>kb;? z;r$mphn94xxRqoK9-*0!qn5WTeSb0cmVdtCJD>5i7b`1iE5FB&IQ13x@!PeaUpv=#F>?8JFA28J^EvF1{k&9>`73Ajh1R8HY`o7buJMco_an81Jk z*OD1S==G$*zGnOZAj3KsqJeQc5)9En465_2g`nsoEl-r^A6cSHZ&wfegKTG7JgGgO-MtqcLD zIYY=H>{z&{@}*8e+3Z{;K}n)T)70yI+4X&l@eA9Na?S=0OP(6DtMYoKH-2d#h%CQr zSDQI)H*iiHkBdZs;j?y{}!q*QrdZjp&bFS9K**yZPpZCw#><#M)XrIYHvU_j~m&Gzg-#Uth{w2`h9 z*U-^H+1^-~;8VcG**cECV1rD-RCQj5SM@sjw|fsi1^vkm zeC$4v2UC@=4r;!lfY(7ZKGO~h>UD5v>Ya;oP4{W+)7+((RdSakrq$jhCTEnc>sJVA z8R$5`6-rg4kELClbQ0S*c%Ny_*EhSAb{Tg9p$FH!h_QUTRMN}X5#!SJ)BYq{!YQG} ztgWwpt7~N$yN5k5R3LnOoZf<}{`oiZ9B=&0>xl6dE^Ldc{~ul9Wpc!k<247=F8a7md®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 + + + + +OpenSPP Attendance + + + +
+

OpenSPP Attendance

+ + +

Alpha License: LGPL-3 OpenSPP/openspp-modules

+

Tracks participant attendance for social protection program activities. +Records presence/absence with date, time, location, and activity type. +Provides OAuth-secured API endpoints for external systems to submit +attendance data and import participants from external registries.

+
+

Key Capabilities

+
    +
  • Record attendance with date, time, type, location, and category +(present/absent)
  • +
  • Query attendance history via get_attendance_list() with filtering +by date range, type, and location
  • +
  • Import participants from external registries via configurable field +mapping
  • +
  • Generate OAuth client credentials for external API access
  • +
  • Enforce configurable uniqueness constraints (date/time/type/location) +to prevent duplicate records
  • +
+
+
+

Key Models

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
ModelDescription
spp.attendance.subscriberParticipant registry, inherits +res.partner
spp.attendance.listAttendance record with date, +time, type, location
spp.attendance.typeConfigurable attendance event +types
spp.attendance.locationConfigurable attendance +locations
spp.at +tendance.api.client.credentialOAuth credentials for external +API clients
spp.import.attendance.wizardWizard for importing from +external registries
+
+
+

Configuration

+

After installing:

+
    +
  1. Navigate to Settings > SPP Attendance Settings to configure:
      +
    • Uniqueness constraints (date/time/type/location)
    • +
    • Server URL and API endpoints
    • +
    • Field mappings for import (personal information, identifiers, +contact details)
    • +
    +
  2. +
  3. Create attendance types: Attendance > Configuration > Attendance +Type
  4. +
  5. Create locations: Attendance > Configuration > Attendance +Location
  6. +
  7. Generate OAuth credentials: Settings > Attendance API Client +Credentials
  8. +
+
+
+

UI Location

+
    +
  • Main Menu: Attendance > Subscriber
  • +
  • Configuration: Attendance > Configuration (Attendance Type, +Attendance Location)
  • +
  • API Credentials: Settings > Attendance API Client Credentials
  • +
  • Settings: Settings > SPP Attendance Settings
  • +
  • Subscriber Form: Contains “Attendance” and “Person Information” +tabs
  • +
+
+
+

Security

+ ++++ + + + + + + + + + + + + + +
GroupAccess
spp_attendance.group_attendance_viewerRead
spp_attendance.group_attendance_managerFull CRUD
+
+
+

Extension Points

+
    +
  • Override get_attendance_list() on spp.attendance.subscriber to +customize attendance queries
  • +
  • Inherit spp.attendance.list to add domain-specific attendance +metadata
  • +
  • Extend _import_attendance() in wizard to customize import logic
  • +
+
+
+

Dependencies

+

base, spp_oauth, spp_security

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production.

+
+

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

+
    +
  • OpenSPP.org
  • +
+
+
+

Maintainers

+

Current maintainer:

+

reichie020212

+

This module is part of the OpenSPP/openspp-modules project on GitHub.

+

You are welcome to contribute.

+
+
+
+
+ + diff --git a/spp_attendance/static/src/img/icons/attendance.png b/spp_attendance/static/src/img/icons/attendance.png new file mode 100644 index 0000000000000000000000000000000000000000..d39f4bc9590261a113264a2481de4bf61ddebf53 GIT binary patch literal 19148 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 literal 0 HcmV?d00001 diff --git a/spp_attendance/views/attendance_api_client_views.xml b/spp_attendance/views/attendance_api_client_views.xml new file mode 100644 index 000000000..888b7402f --- /dev/null +++ b/spp_attendance/views/attendance_api_client_views.xml @@ -0,0 +1,76 @@ + + + + spp_attendance_client_credential_tree + spp.attendance.api.client.credential + + + + + + + + + + spp_attendance_client_credential_form + spp.attendance.api.client.credential + +
+ + + + + + + + + + +