From 8b2b994d88b44f1e88d5a306f0328662952de640 Mon Sep 17 00:00:00 2001 From: Valentin Vinagre Urteaga Date: Fri, 26 Nov 2021 17:12:52 +0100 Subject: [PATCH 1/7] [ADD] crm_salesperson_planner [UPD] Update crm_salesperson_planner.pot [UPD] README.rst --- crm_salesperson_planner/README.rst | 95 ++ crm_salesperson_planner/__init__.py | 5 + crm_salesperson_planner/__manifest__.py | 27 + .../data/crm_salesperson_planner_sequence.xml | 17 + crm_salesperson_planner/data/ir_cron_data.xml | 15 + .../i18n/crm_salesperson_planner.pot | 1070 ++++++++++++++++ crm_salesperson_planner/i18n/es.po | 1081 +++++++++++++++++ crm_salesperson_planner/models/__init__.py | 9 + .../models/calendar_event.py | 54 + crm_salesperson_planner/models/crm_lead.py | 16 + .../models/crm_salesperson_planner_visit.py | 189 +++ ..._salesperson_planner_visit_close_reason.py | 19 + .../crm_salesperson_planner_visit_template.py | 224 ++++ crm_salesperson_planner/models/res_partner.py | 33 + crm_salesperson_planner/readme/CONFIGURE.rst | 3 + .../readme/CONTRIBUTORS.rst | 4 + .../readme/DESCRIPTION.rst | 3 + crm_salesperson_planner/readme/USAGE.rst | 6 + .../crm_salesperson_planner_security.xml | 55 + .../security/ir.model.access.csv | 5 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 443 +++++++ crm_salesperson_planner/tests/__init__.py | 5 + .../test_crm_salesperson_planner_visit.py | 147 +++ ..._crm_salesperson_planner_visit_template.py | 185 +++ crm_salesperson_planner/views/crm_lead.xml | 19 + .../views/crm_salesperson_planner_menu.xml | 49 + ...erson_planner_visit_close_reason_views.xml | 60 + ...lesperson_planner_visit_template_views.xml | 179 +++ .../crm_salesperson_planner_visit_views.xml | 299 +++++ crm_salesperson_planner/views/res_partner.xml | 28 + crm_salesperson_planner/wizards/__init__.py | 5 + ...crm_salesperson_planner_visit_close_wiz.py | 65 + ...lesperson_planner_visit_close_wiz_view.xml | 60 + ...lesperson_planner_visit_template_create.py | 40 + ...esperson_planner_visit_template_create.xml | 42 + 36 files changed, 4556 insertions(+) create mode 100644 crm_salesperson_planner/README.rst create mode 100644 crm_salesperson_planner/__init__.py create mode 100644 crm_salesperson_planner/__manifest__.py create mode 100644 crm_salesperson_planner/data/crm_salesperson_planner_sequence.xml create mode 100644 crm_salesperson_planner/data/ir_cron_data.xml create mode 100644 crm_salesperson_planner/i18n/crm_salesperson_planner.pot create mode 100644 crm_salesperson_planner/i18n/es.po create mode 100644 crm_salesperson_planner/models/__init__.py create mode 100644 crm_salesperson_planner/models/calendar_event.py create mode 100644 crm_salesperson_planner/models/crm_lead.py create mode 100644 crm_salesperson_planner/models/crm_salesperson_planner_visit.py create mode 100644 crm_salesperson_planner/models/crm_salesperson_planner_visit_close_reason.py create mode 100644 crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py create mode 100644 crm_salesperson_planner/models/res_partner.py create mode 100644 crm_salesperson_planner/readme/CONFIGURE.rst create mode 100644 crm_salesperson_planner/readme/CONTRIBUTORS.rst create mode 100644 crm_salesperson_planner/readme/DESCRIPTION.rst create mode 100644 crm_salesperson_planner/readme/USAGE.rst create mode 100644 crm_salesperson_planner/security/crm_salesperson_planner_security.xml create mode 100644 crm_salesperson_planner/security/ir.model.access.csv create mode 100644 crm_salesperson_planner/static/description/icon.png create mode 100644 crm_salesperson_planner/static/description/index.html create mode 100644 crm_salesperson_planner/tests/__init__.py create mode 100644 crm_salesperson_planner/tests/test_crm_salesperson_planner_visit.py create mode 100644 crm_salesperson_planner/tests/test_crm_salesperson_planner_visit_template.py create mode 100644 crm_salesperson_planner/views/crm_lead.xml create mode 100644 crm_salesperson_planner/views/crm_salesperson_planner_menu.xml create mode 100644 crm_salesperson_planner/views/crm_salesperson_planner_visit_close_reason_views.xml create mode 100644 crm_salesperson_planner/views/crm_salesperson_planner_visit_template_views.xml create mode 100644 crm_salesperson_planner/views/crm_salesperson_planner_visit_views.xml create mode 100644 crm_salesperson_planner/views/res_partner.xml create mode 100644 crm_salesperson_planner/wizards/__init__.py create mode 100644 crm_salesperson_planner/wizards/crm_salesperson_planner_visit_close_wiz.py create mode 100644 crm_salesperson_planner/wizards/crm_salesperson_planner_visit_close_wiz_view.xml create mode 100644 crm_salesperson_planner/wizards/crm_salesperson_planner_visit_template_create.py create mode 100644 crm_salesperson_planner/wizards/crm_salesperson_planner_visit_template_create.xml diff --git a/crm_salesperson_planner/README.rst b/crm_salesperson_planner/README.rst new file mode 100644 index 00000000000..21e6f64c17e --- /dev/null +++ b/crm_salesperson_planner/README.rst @@ -0,0 +1,95 @@ +======================= +Crm Salesperson Planner +======================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fcrm-lightgray.png?logo=github + :target: https://github.com/OCA/crm/tree/13.0/crm_salesperson_planner + :alt: OCA/crm +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/crm-13-0/crm-13-0-crm_salesperson_planner + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/111/13.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This application allows you to track and schedule salespeople visits to your customers, allowing you to determine which opportunities are going to be dealt on each visit. Visits create an all day event in calendar, and they can be easily rescheduled. +Visits can be automatically created from a template, in which it is possible to select the frequency of visits, as well as the start and end dates. The last visit can also be calculated by selecting the total number of repetitions. +This module creates a cron that generates visits from templates, but an option to create them manually is available from the template form view when the template is validated. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure this module, you need to: + +* Go to new menu **CRM > Configuration > Salesperson Planner > Close Reasons** and create a close reason for 'Cancel' and 'Incident' types. + +Usage +===== + +Go to new menu **CRM > Salesperson Planner > My Visits or All Visits** and create a new visit. +or +Go to **CRM > Salesperson Planner > Visit Templates** and create a new recurring template for create periodical visits. In this case, it is necessary to select a start date. The date of the last repetition can be calculated by selection the total number of repetitions or an end date. +There are two options available to reschedule visits that is already validated: +* Change the date from the visit. +* Change the date straight from the event automatically created in the calendar. + +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Sygel Technology + +Contributors +~~~~~~~~~~~~ + +* `Sygel `__: + + * Valentin Vinagre + * Manuel Regidor + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/crm `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/crm_salesperson_planner/__init__.py b/crm_salesperson_planner/__init__.py new file mode 100644 index 00000000000..120d03e7590 --- /dev/null +++ b/crm_salesperson_planner/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from . import models +from . import wizards diff --git a/crm_salesperson_planner/__manifest__.py b/crm_salesperson_planner/__manifest__.py new file mode 100644 index 00000000000..57b894682d6 --- /dev/null +++ b/crm_salesperson_planner/__manifest__.py @@ -0,0 +1,27 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +{ + "name": "Crm Salesperson Planner", + "version": "13.0.1.0.0", + "development_status": "Beta", + "category": "Customer Relationship Management", + "author": "Sygel Technology," "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/crm", + "license": "AGPL-3", + "depends": ["crm", "calendar"], + "data": [ + "data/crm_salesperson_planner_sequence.xml", + "wizards/crm_salesperson_planner_visit_close_wiz_view.xml", + "wizards/crm_salesperson_planner_visit_template_create.xml", + "views/crm_salesperson_planner_visit_views.xml", + "views/crm_salesperson_planner_visit_close_reason_views.xml", + "views/crm_salesperson_planner_visit_template_views.xml", + "views/crm_salesperson_planner_menu.xml", + "views/res_partner.xml", + "views/crm_lead.xml", + "data/ir_cron_data.xml", + "security/crm_salesperson_planner_security.xml", + "security/ir.model.access.csv", + ], + "installable": True, +} diff --git a/crm_salesperson_planner/data/crm_salesperson_planner_sequence.xml b/crm_salesperson_planner/data/crm_salesperson_planner_sequence.xml new file mode 100644 index 00000000000..4c21eda4c04 --- /dev/null +++ b/crm_salesperson_planner/data/crm_salesperson_planner_sequence.xml @@ -0,0 +1,17 @@ + + + + + Salesperson Planner visit Code + salesperson.planner.visit + + SPPV/ + + + Salesperson Planner visit Template Code + salesperson.planner.visit.template + + SPPVT/ + + diff --git a/crm_salesperson_planner/data/ir_cron_data.xml b/crm_salesperson_planner/data/ir_cron_data.xml new file mode 100644 index 00000000000..c9bd2385f89 --- /dev/null +++ b/crm_salesperson_planner/data/ir_cron_data.xml @@ -0,0 +1,15 @@ + + + + + CRM: Create salesperson visits + 1 + days + -1 + + + model._cron_create_visits(days=7) + code + + diff --git a/crm_salesperson_planner/i18n/crm_salesperson_planner.pot b/crm_salesperson_planner/i18n/crm_salesperson_planner.pot new file mode 100644 index 00000000000..e552dccafa2 --- /dev/null +++ b/crm_salesperson_planner/i18n/crm_salesperson_planner.pot @@ -0,0 +1,1070 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * crm_salesperson_planner +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.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: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_needaction +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__active +msgid "Active" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__activity_ids +msgid "Activities" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_state +msgid "Activity State" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Add a description..." +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__allday +msgid "All Day" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_salesperson_planner_visits +msgid "All Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__allow_reschedule +msgid "Allow Reschedule" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_attachment_count +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__is_attendee +msgid "Attendee" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__attendee_status +msgid "Attendee Status" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__auto_validate +msgid "Auto Validate" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__byday +msgid "By day" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.actions.server,name:crm_salesperson_planner.ir_cron_create_visits_ir_actions_server +#: model:ir.cron,cron_name:crm_salesperson_planner.ir_cron_create_visits +#: model:ir.cron,name:crm_salesperson_planner.ir_cron_create_visits +msgid "CRM: Create salesperson visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_calendar_event +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__calendar_event_id +msgid "Calendar Event" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit_close_reason__close_type__cancel +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_wiz_view_form +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_create_view_form +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Cancel" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit__state__cancel +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit_template__state__cancel +msgid "Cancelled" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Close Info" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_action +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__close_reason_id +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_form_view +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_wiz_view_form +msgid "Close Reason" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__close_reason_image +msgid "Close Reason Image" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__close_reason_notes +msgid "Close Reason Notes" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_close_wiz_action +#: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_config_salesperson_planner_close_reason +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_tree_view +msgid "Close Reasons" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__company_id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__company_id +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Company" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Confirm" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_res_partner +msgid "Contact" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_create_view_form +msgid "Create" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_template_create_action +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_create_view_form +msgid "Create Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.actions.act_window,help:crm_salesperson_planner.crm_salesperson_planner_visit_template_action +msgid "Create and plan commercial visit templates" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__create_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__create_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__create_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__create_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__create_uid +msgid "Created by" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__create_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__create_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__create_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__create_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__create_date +msgid "Created on" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_crm_salesperson_planner_visit_template +msgid "Crm Salesperson Planner Visit Template" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__partner_id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__partner_ids +msgid "Customer" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__display_start +msgid "Date" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__day +msgid "Date of month" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__date_to +msgid "Date to" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Day of Month" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__description +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__name +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__description +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Description" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__display_name +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__display_name +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__display_name +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__display_name +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__display_name +msgid "Display Name" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__res_id +msgid "Document ID" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__res_model_id +msgid "Document Model" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__res_model +msgid "Document Model Name" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit_template__state__done +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Done" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit__state__draft +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit_template__state__draft +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Draft" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__duration +msgid "Duration" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__stop_date +msgid "End Date" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__stop_datetime +msgid "End Datetime" +msgstr "" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/calendar_event.py:0 +#, python-format +msgid "" +"Event %s is related to salesperson visit %s. Cancel it to delete this " +"event.\n" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__display_time +msgid "Event Time" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_follower_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_channel_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_channel_ids +msgid "Followers (Channels)" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_partner_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__fr +msgid "Fri" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Future Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_crm_salesperson_planner_visit_close_wiz +msgid "Get Close Reason" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Group By" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__id +msgid "ID" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_needaction +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_unread +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_needaction +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_unread +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_error +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_sms_error +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_error +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_sms_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__active +msgid "" +"If the active field is set to false, it will allow you to hide the event " +"alarm information without removing it." +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__image +msgid "Image" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit_template__state__in-progress +msgid "In Progress" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit__state__incident +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit_close_reason__close_type__incident +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Incident" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Internal Notes" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_is_follower +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__is_highlighted +msgid "Is the Event Highlighted" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit____last_update +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason____last_update +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz____last_update +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template____last_update +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create____last_update +msgid "Last Modified on" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__write_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__write_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__write_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__write_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__write_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__write_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__write_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__write_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__write_date +msgid "Last Updated on" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__last_visit_date +msgid "Last Visit Date" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Late Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_crm_lead +msgid "Lead/Opportunity" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__rrule_type +msgid "Let the event automatically repeat at that interval" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__location +msgid "Location" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__location +msgid "Location of Event" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_main_attachment_id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Manually Create Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_error +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_ids +msgid "Messages" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__partner_mobile +msgid "Mobile" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__mo +msgid "Mon" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_salesperson_planner_my_visits +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "My Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__new_date +msgid "New Date" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_summary +msgid "Next Activity Summary" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_type_id +msgid "Next Activity Type" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__notes +msgid "Notes" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_needaction_counter +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__visit_ids_count +msgid "Number of Sales Person Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_res_partner__salesperson_planner_visit_count +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_res_users__salesperson_planner_visit_count +msgid "Number of Salesperson Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_error_counter +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_needaction_counter +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_error_counter +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_unread_counter +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_unread_counter +msgid "Number of unread messages" +msgstr "" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py:0 +#, python-format +msgid "Only one customer is allowed" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__opportunity_ids +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Opportunities" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__opportunity_id +msgid "Opportunity" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__month_by +msgid "Option" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__attendee_ids +msgid "Participant" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Partner" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__partner_id +msgid "Partner-related data of the user" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__partner_phone +msgid "Phone" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__phonecall_id +msgid "Phonecall" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__privacy +msgid "Privacy" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__reason_id +msgid "Reason" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.actions.act_window,help:crm_salesperson_planner.all_crm_salesperson_planner_visit_action +msgid "Record and track all sales commercial visits." +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.actions.act_window,help:crm_salesperson_planner.my_crm_salesperson_planner_visit_action +msgid "Record and track my sales commercial visits." +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.actions.act_window,help:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_action +msgid "Record reason for close commercial visits." +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__rrule_type +msgid "Recurrence" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__end_type +msgid "Recurrence Termination" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrency +msgid "Recurrent" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrent_id +msgid "Recurrent ID" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrent_id_date +msgid "Recurrent ID date" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrency +msgid "Recurrent Meeting" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__rrule +msgid "Recurrent Rule" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__alarm_ids +msgid "Reminders" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__count +msgid "Repeat" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__interval +msgid "Repeat Every" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__final_date +msgid "Repeat Until" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__interval +msgid "Repeat every (Days/Week/Month/Year)" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__count +msgid "Repeat x times" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__require_image +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__require_image +msgid "Require Image" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__reschedule +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__reschedule +msgid "Reschedule" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__partner_id +msgid "Responsible" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_user_id +msgid "Responsible User" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_sms_error +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_sms_error +msgid "SMS Delivery error" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.partner_view_crm_salesperson_planner +msgid "Sales Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_crm_salesperson_planner_visit_close_reason +msgid "SalesPerson Planner Visit Close Reason" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__user_id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__user_id +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Salesperson" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_config_salesperson_planner +#: model:ir.ui.menu,name:crm_salesperson_planner.menu_salesperson_planner +msgid "Salesperson Planner" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_crm_salesperson_planner_visit +msgid "Salesperson Planner Visit" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_calendar_event__salesperson_planner_visit_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__salesperson_planner_visit_ids +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_lead_crm_salesperson_planner +msgid "Salesperson Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__sa +msgid "Sat" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Search Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Send to Draft" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__sequence +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__new_sequence +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__sequence +msgid "Sequence" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__show_as +msgid "Show Time as" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start +msgid "Start" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start_date +msgid "Start Date" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start_datetime +msgid "Start DateTime" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start +msgid "Start date of an event, without time for full days events" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "State" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__state +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__state +msgid "Status" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__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: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__stop +msgid "Stop" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__stop +msgid "Stop date of an event, without time for full days events" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_wiz_view_form +msgid "Submit" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__su +msgid "Sun" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__categ_ids +msgid "Tags" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "The" +msgstr "" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_close_wiz.py:0 +#, python-format +msgid "The close reason type haven't a function." +msgstr "" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_template_create.py:0 +#, python-format +msgid "The date can't be earlier than today" +msgstr "" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit.py:0 +#, python-format +msgid "The visit must be in cancelled, incident or visited state" +msgstr "" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit.py:0 +#, python-format +msgid "The visit must be in confirmed state" +msgstr "" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit.py:0 +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit.py:0 +#, python-format +msgid "The visit must be in draft or validated state" +msgstr "" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit.py:0 +#, python-format +msgid "The visit must be in draft state" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.constraint,message:crm_salesperson_planner.constraint_crm_salesperson_planner_visit_crm_salesperson_planner_visit_name +msgid "The visit number must be unique!" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.constraint,message:crm_salesperson_planner.constraint_crm_salesperson_planner_visit_template_crm_salesperson_planner_visit_template_name +msgid "The visit template number must be unique!" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__th +msgid "Thu" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__event_tz +msgid "Timezone" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Today Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__tu +msgid "Tue" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__close_type +msgid "Type" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_unread +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_unread +msgid "Unread Messages" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_unread_counter +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_unread_counter +msgid "Unread Messages Counter" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Until" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__sequence +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__new_sequence +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__sequence +msgid "Used to order Visits in the different views" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Validate" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit__state__confirm +msgid "Validated" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Visit" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__name +msgid "Visit Number" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__visit_template_id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__visit_ids +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Visit Template" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__name +msgid "Visit Template Number" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_template_action +#: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_salesperson_planner_visit_template +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_tree_view +msgid "Visit Templates" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Visit by Date" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit__state__done +msgid "Visited" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.actions.act_window,name:crm_salesperson_planner.all_crm_salesperson_planner_visit_action +#: model:ir.actions.act_window,name:crm_salesperson_planner.my_crm_salesperson_planner_visit_action +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_lead__crm_salesperson_planner_visit_ids +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_tree_view +msgid "Visits" +msgstr "" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit.py:0 +#, python-format +msgid "Visits must be in cancelled state" +msgstr "" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Visits that are assigned to me" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__website_message_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__website_message_ids +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__we +msgid "Wed" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__week_list +msgid "Weekday" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_crm_salesperson_planner_visit_template_create +msgid "crm salesperson planner visit template create" +msgstr "" diff --git a/crm_salesperson_planner/i18n/es.po b/crm_salesperson_planner/i18n/es.po new file mode 100644 index 00000000000..65062585b5d --- /dev/null +++ b/crm_salesperson_planner/i18n/es.po @@ -0,0 +1,1081 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * crm_salesperson_planner +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-01-12 14:00+0000\n" +"PO-Revision-Date: 2022-01-12 14:00+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_needaction +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_needaction +msgid "Action Needed" +msgstr "Acción requerida" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__active +msgid "Active" +msgstr "Activo" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__activity_ids +msgid "Activities" +msgstr "Actividades" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "Decoración de Actividad de Excepción" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_state +msgid "Activity State" +msgstr "Estado de la Actividad" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Add a description..." +msgstr "Agregar descripción…" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__allday +msgid "All Day" +msgstr "Todo el día" + +#. module: crm_salesperson_planner +#: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_salesperson_planner_visits +msgid "All Visits" +msgstr "Todas las visitas" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__allow_reschedule +msgid "Allow Reschedule" +msgstr "Permitir reprogramación" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_attachment_count +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_attachment_count +msgid "Attachment Count" +msgstr "Número de Adjuntos" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__is_attendee +msgid "Attendee" +msgstr "Asistente" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__attendee_status +msgid "Attendee Status" +msgstr "Estado de asistente" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__auto_validate +msgid "Auto Validate" +msgstr "Auto validar" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__byday +msgid "By day" +msgstr "Por día" + +#. module: crm_salesperson_planner +#: model:ir.actions.server,name:crm_salesperson_planner.ir_cron_create_visits_ir_actions_server +#: model:ir.cron,cron_name:crm_salesperson_planner.ir_cron_create_visits +#: model:ir.cron,name:crm_salesperson_planner.ir_cron_create_visits +msgid "CRM: Create salesperson visits" +msgstr "CRM: Crear visitas comerciales" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_calendar_event +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__calendar_event_id +msgid "Calendar Event" +msgstr "Evento de calendario" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit_close_reason__close_type__cancel +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_wiz_view_form +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_create_view_form +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Cancel" +msgstr "Cancelar" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit__state__cancel +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit_template__state__cancel +msgid "Cancelled" +msgstr "Cancelado" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Close Info" +msgstr "Información de cierre" + +#. module: crm_salesperson_planner +#: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_action +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__close_reason_id +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_form_view +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_wiz_view_form +msgid "Close Reason" +msgstr "Motivo de cierre" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__close_reason_image +msgid "Close Reason Image" +msgstr "Imagen de motivo de cierre" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__close_reason_notes +msgid "Close Reason Notes" +msgstr "Notas de motivo de cierre" + +#. module: crm_salesperson_planner +#: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_close_wiz_action +#: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_config_salesperson_planner_close_reason +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_tree_view +msgid "Close Reasons" +msgstr "Motivos de cierre" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__company_id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__company_id +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Company" +msgstr "Compañía" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Confirm" +msgstr "Confirmar" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_res_partner +msgid "Contact" +msgstr "Contacto" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_create_view_form +msgid "Create" +msgstr "Crear" + +#. module: crm_salesperson_planner +#: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_template_create_action +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_create_view_form +msgid "Create Visits" +msgstr "Crear visitas" + +#. module: crm_salesperson_planner +#: model_terms:ir.actions.act_window,help:crm_salesperson_planner.crm_salesperson_planner_visit_template_action +msgid "Create and plan commercial visit templates" +msgstr "Crear y planificar plantillas de visitas comerciales" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__create_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__create_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__create_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__create_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__create_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__create_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__create_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__create_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_crm_salesperson_planner_visit_template +msgid "Crm Salesperson Planner Visit Template" +msgstr "Crm Salesperson Planner Visit Template" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__partner_id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__partner_ids +msgid "Customer" +msgstr "Cliente" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__display_start +msgid "Date" +msgstr "Fecha" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__day +msgid "Date of month" +msgstr "Fecha del mes" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__date_to +msgid "Date to" +msgstr "Fecha hasta" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Day of Month" +msgstr "Día del mes" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__description +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__name +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__description +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Description" +msgstr "Descripción" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__display_name +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__display_name +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__display_name +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__display_name +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__res_id +msgid "Document ID" +msgstr "ID del documento" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__res_model_id +msgid "Document Model" +msgstr "Modelo del documento" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__res_model +msgid "Document Model Name" +msgstr "Nombre del modelo del documento" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit_template__state__done +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Done" +msgstr "Hecho" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit__state__draft +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit_template__state__draft +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Draft" +msgstr "Borrador" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__duration +msgid "Duration" +msgstr "Duración" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__stop_date +msgid "End Date" +msgstr "Fecha de finalización" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__stop_datetime +msgid "End Datetime" +msgstr "Fecha y hora de finalización" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/calendar_event.py:0 +#, python-format +msgid "" +"Event %s is related to salesperson visit %s. Cancel it to delete this " +"event.\n" +msgstr "" +"El evento %s está relacionado con la visita comercial %s. Cancelela para " +"eliminar este evento.\n" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__display_time +msgid "Event Time" +msgstr "Hora del evento" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_follower_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_follower_ids +msgid "Followers" +msgstr "Seguidores" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_channel_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_channel_ids +msgid "Followers (Channels)" +msgstr "Seguidores (Canales)" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_partner_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_partner_ids +msgid "Followers (Partners)" +msgstr "Seguidores (Socios)" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__fr +msgid "Fri" +msgstr "Viernes" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Future Visits" +msgstr "Visitas futuras" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_crm_salesperson_planner_visit_close_wiz +msgid "Get Close Reason" +msgstr "Motivo de cierre" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Group By" +msgstr "Agrupar por" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__id +msgid "ID" +msgstr "ID" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_exception_icon +msgid "Icon" +msgstr "Icono" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "Icono para indicar una actividad de excepción." + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_needaction +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_unread +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_needaction +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_unread +msgid "If checked, new messages require your attention." +msgstr "Si está marcado, hay nuevos mensajes que requieren su atención." + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_error +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_sms_error +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_error +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_sms_error +msgid "If checked, some messages have a delivery error." +msgstr "Si se encuentra marcado, algunos mensajes tienen error de envío." + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__active +msgid "" +"If the active field is set to false, it will allow you to hide the event " +"alarm information without removing it." +msgstr "" +"Si el campo activo es falso, le permitirá ocultar la notificación de aviso " +"del evento sin eliminarlo." + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__image +msgid "Image" +msgstr "Imagen" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit_template__state__in-progress +msgid "In Progress" +msgstr "En progreso" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit__state__incident +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit_close_reason__close_type__incident +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Incident" +msgstr "Incidencia" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Internal Notes" +msgstr "Notas internas" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_is_follower +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_is_follower +msgid "Is Follower" +msgstr "És seguidor" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__is_highlighted +msgid "Is the Event Highlighted" +msgstr "Es el Evento Resaltado" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit____last_update +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason____last_update +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz____last_update +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template____last_update +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__write_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__write_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__write_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__write_uid +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__write_uid +msgid "Last Updated by" +msgstr "Última modificación el" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__write_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__write_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__write_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__write_date +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template_create__write_date +msgid "Last Updated on" +msgstr "Última modificación el" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__last_visit_date +msgid "Last Visit Date" +msgstr "Fecha última visita" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Late Visits" +msgstr "Visitas tardías" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_crm_lead +msgid "Lead/Opportunity" +msgstr "Iniciativa/Oportunidad" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__rrule_type +msgid "Let the event automatically repeat at that interval" +msgstr "Permite que el evento se repita automáticamente en ese intervalo" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__location +msgid "Location" +msgstr "Ubicación" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__location +msgid "Location of Event" +msgstr "Ubicación del evento" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_main_attachment_id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_main_attachment_id +msgid "Main Attachment" +msgstr "Adjunto principal" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Manually Create Visits" +msgstr "Creación manual de visitas" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_error +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_error +msgid "Message Delivery error" +msgstr "Mensaje de error de entrega" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_ids +msgid "Messages" +msgstr "Mensajes" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__partner_mobile +msgid "Mobile" +msgstr "Móvil" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__mo +msgid "Mon" +msgstr "Lunes" + +#. module: crm_salesperson_planner +#: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_salesperson_planner_my_visits +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "My Visits" +msgstr "Mis visitas" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__new_date +msgid "New Date" +msgstr "Nueva fecha" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "Siguiente plazo de actividad" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_summary +msgid "Next Activity Summary" +msgstr "Resumen de la siguiente actividad" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_type_id +msgid "Next Activity Type" +msgstr "Tipo de la siguiente actividad" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__notes +msgid "Notes" +msgstr "Notas" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_needaction_counter +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_needaction_counter +msgid "Number of Actions" +msgstr "Número de acciones" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__visit_ids_count +msgid "Number of Sales Person Visits" +msgstr "Número de visitas comerciales" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_res_partner__salesperson_planner_visit_count +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_res_users__salesperson_planner_visit_count +msgid "Number of Salesperson Visits" +msgstr "Número de visitas comerciales" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_error_counter +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_error_counter +msgid "Number of errors" +msgstr "Número de errores" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_needaction_counter +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "Número de mensajes que requieren una acción" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_error_counter +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "Número de mensajes con error de envío" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_unread_counter +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_unread_counter +msgid "Number of unread messages" +msgstr "Número de mensajes no leidos" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py:0 +#, python-format +msgid "Only one customer is allowed" +msgstr "Solo se permite un cliente" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__opportunity_ids +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Opportunities" +msgstr "Oportunidades" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__opportunity_id +msgid "Opportunity" +msgstr "Oportunidad" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__month_by +msgid "Option" +msgstr "Opción" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__attendee_ids +msgid "Participant" +msgstr "Participante" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Partner" +msgstr "Cliente" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__partner_id +msgid "Partner-related data of the user" +msgstr "Datos del usuario relativos a la empresa" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__partner_phone +msgid "Phone" +msgstr "Teléfono" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__phonecall_id +#, fuzzy +msgid "Phonecall" +msgstr "Teléfono" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__privacy +msgid "Privacy" +msgstr "Privacitat" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__reason_id +msgid "Reason" +msgstr "Razón" + +#. module: crm_salesperson_planner +#: model_terms:ir.actions.act_window,help:crm_salesperson_planner.all_crm_salesperson_planner_visit_action +msgid "Record and track all sales commercial visits." +msgstr "Registre y rastree todas las visitas comerciales." + +#. module: crm_salesperson_planner +#: model_terms:ir.actions.act_window,help:crm_salesperson_planner.my_crm_salesperson_planner_visit_action +msgid "Record and track my sales commercial visits." +msgstr "Registrar y rastrear mis visitas comerciales." + +#. module: crm_salesperson_planner +#: model_terms:ir.actions.act_window,help:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_action +msgid "Record reason for close commercial visits." +msgstr "Registrar el motivo de cierre de las visitas comerciales." + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__rrule_type +msgid "Recurrence" +msgstr "Recurrencia" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__end_type +msgid "Recurrence Termination" +msgstr "Finalizar recurrencia" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrency +msgid "Recurrent" +msgstr "Recurrente" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrent_id +msgid "Recurrent ID" +msgstr "ID recurrencia" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrent_id_date +msgid "Recurrent ID date" +msgstr "Fecha ID recurrencia" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrency +msgid "Recurrent Meeting" +msgstr "Reunión periódica" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__rrule +msgid "Recurrent Rule" +msgstr "Regla recurrente" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__alarm_ids +msgid "Reminders" +msgstr "Recordatorios" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__count +msgid "Repeat" +msgstr "Repetir" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__interval +msgid "Repeat Every" +msgstr "Repite cada" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__final_date +msgid "Repeat Until" +msgstr "Repetir hasta" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__interval +msgid "Repeat every (Days/Week/Month/Year)" +msgstr "Repetir cada (días/semana/mes/año)" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__count +msgid "Repeat x times" +msgstr "Repetir x veces" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__require_image +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__require_image +msgid "Require Image" +msgstr "Requiere una imagen" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__reschedule +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__reschedule +msgid "Reschedule" +msgstr "Reprogramar" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__partner_id +msgid "Responsible" +msgstr "Responsable" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_user_id +msgid "Responsible User" +msgstr "Usuario responsable" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_sms_error +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_sms_error +msgid "SMS Delivery error" +msgstr "SMS de error en la entrega" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.partner_view_crm_salesperson_planner +msgid "Sales Visits" +msgstr "Visitas de comerciales" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_crm_salesperson_planner_visit_close_reason +msgid "SalesPerson Planner Visit Close Reason" +msgstr "Crm Salesperson Planner Visit Template" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__user_id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__user_id +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Salesperson" +msgstr "Comercial" + +#. module: crm_salesperson_planner +#: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_config_salesperson_planner +#: model:ir.ui.menu,name:crm_salesperson_planner.menu_salesperson_planner +msgid "Salesperson Planner" +msgstr "Planificador comercial" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_crm_salesperson_planner_visit +msgid "Salesperson Planner Visit" +msgstr "Planificador de visitas comerciales" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_calendar_event__salesperson_planner_visit_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__salesperson_planner_visit_ids +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_lead_crm_salesperson_planner +msgid "Salesperson Visits" +msgstr "Visitas comerciales" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__sa +msgid "Sat" +msgstr "Sabado" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Search Visits" +msgstr "Búsqueda de visitas" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Send to Draft" +msgstr "Enviar a Borrador" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__sequence +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__new_sequence +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__sequence +msgid "Sequence" +msgstr "Secuencia" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__show_as +msgid "Show Time as" +msgstr "Mostrar hora como" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start +msgid "Start" +msgstr "Iniciar" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start_date +msgid "Start Date" +msgstr "Fecha de inicio" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start_datetime +msgid "Start DateTime" +msgstr "Fecha y hora de inicio" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start +msgid "Start date of an event, without time for full days events" +msgstr "Fecha de inicio del evento, sin horas para eventos de día completo" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "State" +msgstr "Estado" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__state +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__state +msgid "Status" +msgstr "Estado" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__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 "" +"Estado basado en las actividades \n" +"Retraso: Fecha de vencimiento ya es pasado \n" +"Hoy: Fecha actividad es hoy \n" +"Planificado: Actividades futuras." + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__stop +msgid "Stop" +msgstr "Parar" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__stop +msgid "Stop date of an event, without time for full days events" +msgstr "Fecha fin del evento, sin horas para eventos de día completo" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_wiz_view_form +msgid "Submit" +msgstr "Enviar" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__su +msgid "Sun" +msgstr "Domingo" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__categ_ids +msgid "Tags" +msgstr "Etiquetas" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "The" +msgstr "The" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_close_wiz.py:0 +#, python-format +msgid "The close reason type haven't a function." +msgstr "El tipo de motivo de cierre no tiene una función." + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_template_create.py:0 +#, python-format +msgid "The date can't be earlier than today" +msgstr "La fecha no puede ser anterior a hoy" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit.py:0 +#, python-format +msgid "The visit must be in cancelled, incident or visited state" +msgstr "La visita debe estar en estado cancelado, incidente o visitado" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit.py:0 +#, python-format +msgid "The visit must be in confirmed state" +msgstr "La visita debe estar en estado confirmado" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit.py:0 +#, python-format +msgid "The visit must be in draft or validated state" +msgstr "La visita debe estar en estado borrador o validado" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit.py:0 +#, python-format +msgid "The visit must be in draft state" +msgstr "La visita debe estar en estado de borrador" + +#. module: crm_salesperson_planner +#: model:ir.model.constraint,message:crm_salesperson_planner.constraint_crm_salesperson_planner_visit_crm_salesperson_planner_visit_name +msgid "The visit number must be unique!" +msgstr "¡El número de visita debe ser único!" + +#. module: crm_salesperson_planner +#: model:ir.model.constraint,message:crm_salesperson_planner.constraint_crm_salesperson_planner_visit_template_crm_salesperson_planner_visit_template_name +msgid "The visit template number must be unique!" +msgstr "¡El número de plantilla de visita debe ser único!" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__th +msgid "Thu" +msgstr "Jueves" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__event_tz +msgid "Timezone" +msgstr "Zona horaria" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Today Visits" +msgstr "Visitas de hoy" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__tu +msgid "Tue" +msgstr "Martes" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__close_type +msgid "Type" +msgstr "Tipo" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "Tipo de la actividad de excepción registrada." + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_unread +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_unread +msgid "Unread Messages" +msgstr "Número de mensajes no leídos" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_unread_counter +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_unread_counter +msgid "Unread Messages Counter" +msgstr "Nº de mensajes sin leer" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Until" +msgstr "Hasta" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__sequence +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__new_sequence +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__sequence +msgid "Used to order Visits in the different views" +msgstr "Utilizado para ordenar Visitas en las diferentes vistas" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Validate" +msgstr "Validar" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit__state__confirm +msgid "Validated" +msgstr "Validado" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view +msgid "Visit" +msgstr "Visita" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__name +msgid "Visit Number" +msgstr "Número de visita" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__visit_template_id +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__visit_ids +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view +msgid "Visit Template" +msgstr "Plantilla de visita" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__name +msgid "Visit Template Number" +msgstr "Número de plantilla de visita" + +#. module: crm_salesperson_planner +#: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_template_action +#: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_salesperson_planner_visit_template +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_tree_view +msgid "Visit Templates" +msgstr "Plantillas de visitas" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Visit by Date" +msgstr "Visita por fecha" + +#. module: crm_salesperson_planner +#: model:ir.model.fields.selection,name:crm_salesperson_planner.selection__crm_salesperson_planner_visit__state__done +msgid "Visited" +msgstr "Visitado" + +#. module: crm_salesperson_planner +#: model:ir.actions.act_window,name:crm_salesperson_planner.all_crm_salesperson_planner_visit_action +#: model:ir.actions.act_window,name:crm_salesperson_planner.my_crm_salesperson_planner_visit_action +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_lead__crm_salesperson_planner_visit_ids +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_tree_view +msgid "Visits" +msgstr "Visitas" + +#. module: crm_salesperson_planner +#: code:addons/crm_salesperson_planner/models/crm_salesperson_planner_visit.py:0 +#, fuzzy, python-format +msgid "Visits must be in cancelled state" +msgstr "La visita debe estar en estado cancelado" + +#. module: crm_salesperson_planner +#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter +msgid "Visits that are assigned to me" +msgstr "Visitas asignadas a mi" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__website_message_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__website_message_ids +msgid "Website Messages" +msgstr "Mensajes del sitio web" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__website_message_ids +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__website_message_ids +msgid "Website communication history" +msgstr "Historial de comunicación del sitio web" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__we +msgid "Wed" +msgstr "Miercoles" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__week_list +msgid "Weekday" +msgstr "Día de la semana" + +#. module: crm_salesperson_planner +#: model:ir.model,name:crm_salesperson_planner.model_crm_salesperson_planner_visit_template_create +msgid "crm salesperson planner visit template create" +msgstr "crm salesperson planner visit template create" diff --git a/crm_salesperson_planner/models/__init__.py b/crm_salesperson_planner/models/__init__.py new file mode 100644 index 00000000000..1a34f478297 --- /dev/null +++ b/crm_salesperson_planner/models/__init__.py @@ -0,0 +1,9 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from . import crm_salesperson_planner_visit +from . import crm_salesperson_planner_visit_template +from . import crm_salesperson_planner_visit_close_reason +from . import res_partner +from . import crm_lead +from . import calendar_event diff --git a/crm_salesperson_planner/models/calendar_event.py b/crm_salesperson_planner/models/calendar_event.py new file mode 100644 index 00000000000..87fdf603056 --- /dev/null +++ b/crm_salesperson_planner/models/calendar_event.py @@ -0,0 +1,54 @@ +# Copyright 2021 Sygel - Manuel Regidor +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import _, fields, models +from odoo.exceptions import ValidationError + + +class CalendarEvent(models.Model): + _inherit = "calendar.event" + + salesperson_planner_visit_ids = fields.One2many( + string="Salesperson Visits", + comodel_name="crm.salesperson.planner.visit", + inverse_name="calendar_event_id", + ) + + def write(self, values): + if values.get("start") or values.get("user_id"): + salesperson_visit_events = self.filtered( + lambda a: a.res_model == "crm.salesperson.planner.visit" + ) + if salesperson_visit_events: + new_vals = {} + if values.get("start"): + new_vals["date"] = values.get("start") + if values.get("user_id"): + new_vals["user_id"] = values.get("user_id") + user_id = self.env["res.users"].browse(values.get("user_id")) + if user_id: + partner_ids = self.partner_ids.filtered( + lambda a: a != self.user_id.partner_id + ).ids + partner_ids.append(user_id.partner_id.id) + values["partner_ids"] = [(6, 0, partner_ids)] + salesperson_visit_events.mapped( + "salesperson_planner_visit_ids" + ).with_context(bypass_update_event=True).write(new_vals) + return super(CalendarEvent, self).write(values) + + def unlink(self, can_be_deleted=True): + if not self.env.context.get("bypass_cancel_visit"): + salesperson_visit_events = self.filtered( + lambda a: a.res_model == "crm.salesperson.planner.visit" + and a.salesperson_planner_visit_ids + ) + if salesperson_visit_events: + error_msg = "" + for event in salesperson_visit_events: + error_msg += _( + "Event %s is related to salesperson visit %s. " + "Cancel it to delete this event.\n" + ) % (event.name, event.salesperson_planner_visit_ids[0].name) + raise ValidationError(error_msg) + return super(CalendarEvent, self).unlink(can_be_deleted) diff --git a/crm_salesperson_planner/models/crm_lead.py b/crm_salesperson_planner/models/crm_lead.py new file mode 100644 index 00000000000..2737e575bb5 --- /dev/null +++ b/crm_salesperson_planner/models/crm_lead.py @@ -0,0 +1,16 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import fields, models + + +class CrmLead(models.Model): + _inherit = "crm.lead" + + crm_salesperson_planner_visit_ids = fields.Many2many( + comodel_name="crm.salesperson.planner.visit", + relation="crm_salesperson_planner_visit_crm_lead_rel", + string="Visits", + copy=False, + domain="[('partner_id', 'child_of', partner_id)]", + ) diff --git a/crm_salesperson_planner/models/crm_salesperson_planner_visit.py b/crm_salesperson_planner/models/crm_salesperson_planner_visit.py new file mode 100644 index 00000000000..97c8f61886a --- /dev/null +++ b/crm_salesperson_planner/models/crm_salesperson_planner_visit.py @@ -0,0 +1,189 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# Copyright 2021 Sygel - Manuel Regidor +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class CrmSalespersonPlannerVisit(models.Model): + _name = "crm.salesperson.planner.visit" + _description = "Salesperson Planner Visit" + _order = "date desc,sequence" + _inherit = ["mail.thread", "mail.activity.mixin"] + + name = fields.Char( + string="Visit Number", required=True, default="/", readonly=True, copy=False, + ) + partner_id = fields.Many2one( + comodel_name="res.partner", string="Customer", required=True, + ) + partner_phone = fields.Char(string="Phone", related="partner_id.phone") + partner_mobile = fields.Char(string="Mobile", related="partner_id.mobile") + date = fields.Date(string="Date", default=fields.Date.context_today, required=True,) + sequence = fields.Integer( + string="Sequence", + help="Used to order Visits in the different views", + default=20, + ) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.company, + ) + user_id = fields.Many2one( + comodel_name="res.users", + string="Salesperson", + index=True, + tracking=True, + default=lambda self: self.env.user, + domain=lambda self: [ + ("groups_id", "in", self.env.ref("sales_team.group_sale_salesman").id) + ], + ) + opportunity_ids = fields.Many2many( + comodel_name="crm.lead", + relation="crm_salesperson_planner_visit_crm_lead_rel", + string="Opportunities", + copy=False, + domain="[('type', '=', 'opportunity'), ('partner_id', 'child_of', partner_id)]", + ) + description = fields.Html(string="Description") + state = fields.Selection( + string="Status", + required=True, + readonly=True, + copy=False, + tracking=True, + selection=[ + ("draft", "Draft"), + ("confirm", "Validated"), + ("done", "Visited"), + ("cancel", "Cancelled"), + ("incident", "Incident"), + ], + default="draft", + ) + close_reason_id = fields.Many2one( + comodel_name="crm.salesperson.planner.visit.close.reason", string="Close Reason" + ) + close_reason_image = fields.Image( + string="Close Reason Image", max_width=1024, max_height=1024, attachment=True + ) + close_reason_notes = fields.Text(string="Close Reason Notes") + visit_template_id = fields.Many2one( + comodel_name="crm.salesperson.planner.visit.template", string="Visit Template" + ) + calendar_event_id = fields.Many2one( + comodel_name="calendar.event", string="Calendar Event" + ) + + _sql_constraints = [ + ( + "crm_salesperson_planner_visit_name", + "UNIQUE (name)", + "The visit number must be unique!", + ), + ] + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if vals.get("name", "/") == "/": + vals["name"] = self.env["ir.sequence"].next_by_code( + "salesperson.planner.visit" + ) + return super().create(vals_list) + + def action_draft(self): + if self.state not in ["cancel", "incident", "done"]: + raise ValidationError( + _("The visit must be in cancelled, incident or visited state") + ) + if self.calendar_event_id: + self.calendar_event_id.with_context(bypass_cancel_visit=True).unlink() + self.write({"state": "draft"}) + + def action_confirm(self): + if self.filtered(lambda a: not a.state == "draft"): + raise ValidationError(_("The visit must be in draft state")) + events = self.create_calendar_event() + if events: + self.browse(events.mapped("res_id")).write({"state": "confirm"}) + + def action_done(self): + if not self.state == "confirm": + raise ValidationError(_("The visit must be in confirmed state")) + self.write({"state": "done"}) + + def action_cancel(self, reason_id, image=None, notes=None): + if self.state not in ["draft", "confirm"]: + raise ValidationError(_("The visit must be in draft or validated state")) + if self.calendar_event_id: + self.calendar_event_id.with_context(bypass_cancel_visit=True).unlink() + self.write( + { + "state": "cancel", + "close_reason_id": reason_id.id, + "close_reason_image": image, + "close_reason_notes": notes, + } + ) + + def create_calendar_event(self): + events = self.env["calendar.event"] + model_id = self.env.ref( + "crm_salesperson_planner.model_crm_salesperson_planner_visit" + ).id + for sel in self: + event_vals = { + "name": sel.name, + "partner_ids": [(6, 0, [sel.partner_id.id, sel.user_id.partner_id.id])], + "user_id": sel.user_id.id, + "start_date": sel.date, + "stop_date": sel.date, + "start": sel.date, + "stop": sel.date, + "allday": True, + "res_model_id": model_id, + "res_id": sel.id, + } + event = self.env["calendar.event"].create(event_vals) + if event: + event.activity_ids.unlink() + event.write({"res_model": "crm.salesperson.planner.visit"}) + sel.write({"calendar_event_id": event.id}) + events += event + return events + + def action_incident(self, reason_id, image=None, notes=None): + if self.state not in ["draft", "confirm"]: + raise ValidationError(_("The visit must be in draft or validated state")) + self.write( + { + "state": "incident", + "close_reason_id": reason_id.id, + "close_reason_image": image, + "close_reason_notes": notes, + } + ) + + def unlink(self): + if any(sel.state not in ["draft", "cancel"] for sel in self): + raise ValidationError(_("Visits must be in cancelled state")) + return super(CrmSalespersonPlannerVisit, self).unlink() + + def write(self, values): + ret_val = super(CrmSalespersonPlannerVisit, self).write(values) + if (values.get("date") or values.get("user_id")) and not self.env.context.get( + "bypass_update_event" + ): + new_vals = {} + for sel in self.filtered(lambda a: a.calendar_event_id): + if values.get("date"): + new_vals["start"] = values.get("date") + new_vals["stop"] = values.get("date") + if values.get("user_id"): + new_vals["user_id"] = values.get("user_id") + sel.calendar_event_id.write(new_vals) + return ret_val diff --git a/crm_salesperson_planner/models/crm_salesperson_planner_visit_close_reason.py b/crm_salesperson_planner/models/crm_salesperson_planner_visit_close_reason.py new file mode 100644 index 00000000000..a8d119ebd94 --- /dev/null +++ b/crm_salesperson_planner/models/crm_salesperson_planner_visit_close_reason.py @@ -0,0 +1,19 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import fields, models + + +class CrmSalespersonPlannerVisitCloseReason(models.Model): + _name = "crm.salesperson.planner.visit.close.reason" + _description = "SalesPerson Planner Visit Close Reason" + + name = fields.Char(string="Description", required=True, translate=True) + close_type = fields.Selection( + selection=[("cancel", "Cancel"), ("incident", "Incident")], + string="Type", + required=True, + default="cancel", + ) + require_image = fields.Boolean(string="Require Image", default=False) + reschedule = fields.Boolean(string="Reschedule", default=False) diff --git a/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py b/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py new file mode 100644 index 00000000000..7ed1ebb801f --- /dev/null +++ b/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py @@ -0,0 +1,224 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# Copyright 2021 Sygel - Manuel Regidor +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from dateutil.relativedelta import relativedelta + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class CrmSalespersonPlannerVisitTemplate(models.Model): + _name = "crm.salesperson.planner.visit.template" + _description = "Crm Salesperson Planner Visit Template" + _inherit = "calendar.event" + + name = fields.Char( + string="Visit Template Number", default="/", readonly=True, copy=False, + ) + partner_ids = fields.Many2many( + string="Customer", + relation="salesperson_planner_res_partner_rel", + default=False, + required=True, + ) + sequence = fields.Integer( + string="Sequence", + help="Used to order Visits in the different views", + default=20, + ) + company_id = fields.Many2one( + comodel_name="res.company", + string="Company", + default=lambda self: self.env.company, + ) + user_id = fields.Many2one( + string="Salesperson", + tracking=True, + default=lambda self: self.env.user, + domain=lambda self: [ + ("groups_id", "in", self.env.ref("sales_team.group_sale_salesman").id) + ], + ) + categ_ids = fields.Many2many(relation="visit_category_rel",) + alarm_ids = fields.Many2many(relation="visit_calendar_event_rel") + state = fields.Selection( + string="Status", + required=True, + copy=False, + tracking=True, + selection=[ + ("draft", "Draft"), + ("in-progress", "In Progress"), + ("done", "Done"), + ("cancel", "Cancelled"), + ], + default="draft", + ) + visit_ids = fields.One2many( + comodel_name="crm.salesperson.planner.visit", + inverse_name="visit_template_id", + string="Visit Template", + ) + visit_ids_count = fields.Integer( + string="Number of Sales Person Visits", compute="_compute_visit_ids_count" + ) + auto_validate = fields.Boolean(string="Auto Validate", default=True) + rrule_type = fields.Selection(default="daily", required=True,) + last_visit_date = fields.Date( + string="Last Visit Date", compute="_compute_last_visit_date", store=True + ) + final_date = fields.Date(string="Repeat Until") + allday = fields.Boolean(default=True) + recurrency = fields.Boolean(default=True) + + _sql_constraints = [ + ( + "crm_salesperson_planner_visit_template_name", + "UNIQUE (name)", + "The visit template number must be unique!", + ), + ] + + def _compute_visit_ids_count(self): + visit_data = self.env["crm.salesperson.planner.visit"].read_group( + [("visit_template_id", "in", self.ids)], + ["visit_template_id"], + ["visit_template_id"], + ) + mapped_data = { + m["visit_template_id"][0]: m["visit_template_id_count"] for m in visit_data + } + for sel in self: + sel.visit_ids_count = mapped_data.get(sel.id, 0) + + @api.depends("visit_ids.date") + def _compute_last_visit_date(self): + for sel in self.filtered(lambda x: x.visit_ids): + sel.last_visit_date = sel.visit_ids.sorted(lambda x: x.date)[-1].date + self.filtered(lambda x: not x.visit_ids).write({"last_visit_date": False}) + + # overwrite + # default _search method gives an error when trying to access + # instances of the model + def _search( + self, + args, + offset=0, + limit=None, + order=None, + count=False, + access_rights_uid=None, + ): + return super(models.Model, self)._search(args) + + @api.constrains("partner_ids") + def _constrains_partner_ids(self): + for sel in self: + if len(sel.partner_ids) > 1: + raise ValidationError(_("Only one customer is allowed")) + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if vals.get("name", "/") == "/": + vals["name"] = self.env["ir.sequence"].next_by_code( + "salesperson.planner.visit.template" + ) + return super().create(vals_list) + + # overwrite + # Calling _update_cron from default write funciont is not + # necessary in this case + def write(self, vals): + return super(models.Model, self).write(vals) + + # overwrite + # default unlink method gives an error when trying to access + # instances of the model + def unlink(self, can_be_deleted=True): + return super(models.Model, self).unlink() + + def action_view_salesperson_planner_visit(self): + action = self.env.ref( + "crm_salesperson_planner.all_crm_salesperson_planner_visit_action" + ).read()[0] + action["domain"] = [("id", "=", self.visit_ids.ids)] + action["context"] = { + "default_partner_id": self.partner_id.id, + "default_visit_template_id": self.id, + "default_description": self.description, + } + return action + + def action_validate(self): + self.write({"state": "in-progress"}) + + def action_cancel(self): + self.write({"state": "cancel"}) + + def action_draft(self): + self.write({"state": "draft"}) + + def filter_dates(self, recurrences, days="7"): + last_date = recurrences[-1].date() + reference_date = self.start_date + visit_date = fields.Datetime.from_string(reference_date) + last_visit_date = False + if not self.last_visit_date: + last_visit_date = visit_date + else: + last_visit_date = fields.Datetime.from_string( + self.last_visit_date + relativedelta(days=1) + ) + max_date = fields.Date.today() + relativedelta( + days=days, hours=23, minutes=59, seconds=59 + ) + recurrences = list( + filter( + lambda a: a.replace(tzinfo=None) <= max_date + and a.replace(tzinfo=None) > last_visit_date, + recurrences, + ) + ) + return recurrences, last_date + + def _create_visits(self, days=7): + visits_vals = [] + for sel in self: + recurrences = self._get_recurrent_date_by_event() + days, last_date = sel.filter_dates(recurrences, days) + for day in days: + visits_vals.append( + { + "partner_id": sel.partner_ids[0].id, + "date": day.date(), + "sequence": sel.sequence, + "user_id": sel.user_id.id, + "description": sel.description, + "company_id": sel.company_id.id, + "visit_template_id": sel.id, + } + ) + if days and days[-1].date() >= last_date: + sel.write({"state": "done"}) + return visits_vals + + def create_visits(self, days=7): + for sel in self: + visit_vals = sel._create_visits(days=days) + visits = self.env["crm.salesperson.planner.visit"].create(visit_vals) + if visits and sel.auto_validate: + visits.action_confirm() + + def _cron_create_visits(self, days=7): + templates = self.search([("state", "=", "in-progress")]) + templates.create_visits(days=days) + + # overwrite + def get_recurrent_ids(self, domain, order=None): + return self.search([domain], order=order) + + # overwrite + def create_attendees(self): + return [] diff --git a/crm_salesperson_planner/models/res_partner.py b/crm_salesperson_planner/models/res_partner.py new file mode 100644 index 00000000000..7e8e09a77b3 --- /dev/null +++ b/crm_salesperson_planner/models/res_partner.py @@ -0,0 +1,33 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + salesperson_planner_visit_count = fields.Integer( + string="Number of Salesperson Visits", + compute="_compute_salesperson_planner_visit_count", + ) + + def _compute_salesperson_planner_visit_count(self): + partners = self | self.mapped("child_ids") + partner_data = self.env["crm.salesperson.planner.visit"].read_group( + [("partner_id", "in", partners.ids)], ["partner_id"], ["partner_id"] + ) + mapped_data = {m["partner_id"][0]: m["partner_id_count"] for m in partner_data} + for partner in self: + visit_count = mapped_data.get(partner.id, 0) + for child in partner.child_ids: + visit_count += mapped_data.get(child.id, 0) + partner.salesperson_planner_visit_count = visit_count + + def action_view_salesperson_planner_visit(self): + action = self.env.ref( + "crm_salesperson_planner.all_crm_salesperson_planner_visit_action" + ).read()[0] + operator = "child_of" if self.is_company else "=" + action["domain"] = [("partner_id", operator, self.id)] + return action diff --git a/crm_salesperson_planner/readme/CONFIGURE.rst b/crm_salesperson_planner/readme/CONFIGURE.rst new file mode 100644 index 00000000000..b21e85f5692 --- /dev/null +++ b/crm_salesperson_planner/readme/CONFIGURE.rst @@ -0,0 +1,3 @@ +To configure this module, you need to: + +* Go to new menu **CRM > Configuration > Salesperson Planner > Close Reasons** and create a close reason for 'Cancel' and 'Incident' types. diff --git a/crm_salesperson_planner/readme/CONTRIBUTORS.rst b/crm_salesperson_planner/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..35f658858eb --- /dev/null +++ b/crm_salesperson_planner/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `Sygel `__: + + * Valentin Vinagre + * Manuel Regidor diff --git a/crm_salesperson_planner/readme/DESCRIPTION.rst b/crm_salesperson_planner/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..7f40eb8e8cc --- /dev/null +++ b/crm_salesperson_planner/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +This application allows you to track and schedule salespeople visits to your customers, allowing you to determine which opportunities are going to be dealt on each visit. Visits create an all day event in calendar, and they can be easily rescheduled. +Visits can be automatically created from a template, in which it is possible to select the frequency of visits, as well as the start and end dates. The last visit can also be calculated by selecting the total number of repetitions. +This module creates a cron that generates visits from templates, but an option to create them manually is available from the template form view when the template is validated. diff --git a/crm_salesperson_planner/readme/USAGE.rst b/crm_salesperson_planner/readme/USAGE.rst new file mode 100644 index 00000000000..cc440b32e2e --- /dev/null +++ b/crm_salesperson_planner/readme/USAGE.rst @@ -0,0 +1,6 @@ +Go to new menu **CRM > Salesperson Planner > My Visits or All Visits** and create a new visit. +or +Go to **CRM > Salesperson Planner > Visit Templates** and create a new recurring template for create periodical visits. In this case, it is necessary to select a start date. The date of the last repetition can be calculated by selection the total number of repetitions or an end date. +There are two options available to reschedule visits that is already validated: +* Change the date from the visit. +* Change the date straight from the event automatically created in the calendar. diff --git a/crm_salesperson_planner/security/crm_salesperson_planner_security.xml b/crm_salesperson_planner/security/crm_salesperson_planner_security.xml new file mode 100644 index 00000000000..6881e80bb30 --- /dev/null +++ b/crm_salesperson_planner/security/crm_salesperson_planner_security.xml @@ -0,0 +1,55 @@ + + + + + CRM Salesperson planner visit multi-company + + + ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] + + + CRM Salesperson planner visit template multi-company + + + ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] + + + Personal Salesperson Planner Visit + + ['|',('user_id','=',user.id),('user_id','=',False)] + + + + All Salesperson Planner Visit + + [(1,'=',1)] + + + + Personal Salesperson Planner Visit Template + + ['|',('user_id','=',user.id),('user_id','=',False)] + + + + All Salesperson Planner Visit Template + + [(1,'=',1)] + + + diff --git a/crm_salesperson_planner/security/ir.model.access.csv b/crm_salesperson_planner/security/ir.model.access.csv new file mode 100644 index 00000000000..764c085ab20 --- /dev/null +++ b/crm_salesperson_planner/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_crm_salesperson_planner_visit_user,crm.salesperson.planner.visit.user,model_crm_salesperson_planner_visit,sales_team.group_sale_salesman,1,1,1,1 +access_crm_salesperson_planner_visit_close_reason_user,crm.salesperson.planner.visit.close.reason.user,model_crm_salesperson_planner_visit_close_reason,sales_team.group_sale_salesman,1,0,0,0 +access_crm_salesperson_planner_visit_close_reason_manager,crm.salesperson.planner.visit.close.reason.manager,model_crm_salesperson_planner_visit_close_reason,sales_team.group_sale_manager,1,1,1,1 +access_crm_salesperson_planner_visit_template_user,crm.salesperson.planner.visit.template.user,model_crm_salesperson_planner_visit_template,sales_team.group_sale_salesman,1,1,1,1 diff --git a/crm_salesperson_planner/static/description/icon.png b/crm_salesperson_planner/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 zcmW++2RxMjAAjx~&dlBk9S+%}OXg)AGE&Cb*&}d0jUxM@u(PQx^-s)697TX`ehR4?GS^qbkof1cslKgkU)h65qZ9Oc=ml_0temigYLJfnz{IDzUf>bGs4N!v3=Z3jMq&A#7%rM5eQ#dc?k~! zVpnB`o+K7|Al`Q_U;eD$B zfJtP*jH`siUq~{KE)`jP2|#TUEFGRryE2`i0**z#*^6~AI|YzIWy$Cu#CSLW3q=GA z6`?GZymC;dCPk~rBS%eCb`5OLr;RUZ;D`}um=H)BfVIq%7VhiMr)_#G0N#zrNH|__ zc+blN2UAB0=617@>_u;MPHN;P;N#YoE=)R#i$k_`UAA>WWCcEVMh~L_ zj--gtp&|K1#58Yz*AHCTMziU1Jzt_jG0I@qAOHsk$2}yTmVkBp_eHuY$A9)>P6o~I z%aQ?!(GqeQ-Y+b0I(m9pwgi(IIZZzsbMv+9w{PFtd_<_(LA~0H(xz{=FhLB@(1&qHA5EJw1>>=%q2f&^X>IQ{!GJ4e9U z&KlB)z(84HmNgm2hg2C0>WM{E(DdPr+EeU_N@57;PC2&DmGFW_9kP&%?X4}+xWi)( z;)z%wI5>D4a*5XwD)P--sPkoY(a~WBw;E~AW`Yue4kFa^LM3X`8x|}ZUeMnqr}>kH zG%WWW>3ml$Yez?i%)2pbKPI7?5o?hydokgQyZsNEr{a|mLdt;X2TX(#B1j35xPnPW z*bMSSOauW>o;*=kO8ojw91VX!qoOQb)zHJ!odWB}d+*K?#sY_jqPdg{Sm2HdYzdEx zOGVPhVRTGPtv0o}RfVP;Nd(|CB)I;*t&QO8h zFfekr30S!-LHmV_Su-W+rEwYXJ^;6&3|L$mMC8*bQptyOo9;>Qb9Q9`ySe3%V$A*9 zeKEe+b0{#KWGp$F+tga)0RtI)nhMa-K@JS}2krK~n8vJ=Ngm?R!9G<~RyuU0d?nz# z-5EK$o(!F?hmX*2Yt6+coY`6jGbb7tF#6nHA zuKk=GGJ;ZwON1iAfG$E#Y7MnZVmrY|j0eVI(DN_MNFJmyZ|;w4tf@=CCDZ#5N_0K= z$;R~bbk?}TpfDjfB&aiQ$VA}s?P}xPERJG{kxk5~R`iRS(SK5d+Xs9swCozZISbnS zk!)I0>t=A<-^z(cmSFz3=jZ23u13X><0b)P)^1T_))Kr`e!-pb#q&J*Q`p+B6la%C zuVl&0duN<;uOsB3%T9Fp8t{ED108<+W(nOZd?gDnfNBC3>M8WE61$So|P zVvqH0SNtDTcsUdzaMDpT=Ty0pDHHNL@Z0w$Y`XO z2M-_r1S+GaH%pz#Uy0*w$Vdl=X=rQXEzO}d6J^R6zjM1u&c9vYLvLp?W7w(?np9x1 zE_0JSAJCPB%i7p*Wvg)pn5T`8k3-uR?*NT|J`eS#_#54p>!p(mLDvmc-3o0mX*mp_ zN*AeS<>#^-{S%W<*mz^!X$w_2dHWpcJ6^j64qFBft-o}o_Vx80o0>}Du;>kLts;$8 zC`7q$QI(dKYG`Wa8#wl@V4jVWBRGQ@1dr-hstpQL)Tl+aqVpGpbSfN>5i&QMXfiZ> zaA?T1VGe?rpQ@;+pkrVdd{klI&jVS@I5_iz!=UMpTsa~mBga?1r}aRBm1WS;TT*s0f0lY=JBl66Upy)-k4J}lh=P^8(SXk~0xW=T9v*B|gzIhN z>qsO7dFd~mgxAy4V?&)=5ieYq?zi?ZEoj)&2o)RLy=@hbCRcfT5jigwtQGE{L*8<@Yd{zg;CsL5mvzfDY}P-wos_6PfprFVaeqNE%h zKZhLtcQld;ZD+>=nqN~>GvROfueSzJD&BE*}XfU|H&(FssBqY=hPCt`d zH?@s2>I(|;fcW&YM6#V#!kUIP8$Nkdh0A(bEVj``-AAyYgwY~jB zT|I7Bf@%;7aL7Wf4dZ%VqF$eiaC38OV6oy3Z#TER2G+fOCd9Iaoy6aLYbPTN{XRPz z;U!V|vBf%H!}52L2gH_+j;`bTcQRXB+y9onc^wLm5wi3-Be}U>k_u>2Eg$=k!(l@I zcCg+flakT2Nej3i0yn+g+}%NYb?ta;R?(g5SnwsQ49U8Wng8d|{B+lyRcEDvR3+`O{zfmrmvFrL6acVP%yG98X zo&+VBg@px@i)%o?dG(`T;n*$S5*rnyiR#=wW}}GsAcfyQpE|>a{=$Hjg=-*_K;UtD z#z-)AXwSRY?OPefw^iI+ z)AXz#PfEjlwTes|_{sB?4(O@fg0AJ^g8gP}ex9Ucf*@_^J(s_5jJV}c)s$`Myn|Kd z$6>}#q^n{4vN@+Os$m7KV+`}c%4)4pv@06af4-x5#wj!KKb%caK{A&Y#Rfs z-po?Dcb1({W=6FKIUirH&(yg=*6aLCekcKwyfK^JN5{wcA3nhO(o}SK#!CINhI`-I z1)6&n7O&ZmyFMuNwvEic#IiOAwNkR=u5it{B9n2sAJV5pNhar=j5`*N!Na;c7g!l$ z3aYBqUkqqTJ=Re-;)s!EOeij=7SQZ3Hq}ZRds%IM*PtM$wV z@;rlc*NRK7i3y5BETSKuumEN`Xu_8GP1Ri=OKQ$@I^ko8>H6)4rjiG5{VBM>B|%`&&s^)jS|-_95&yc=GqjNo{zFkw%%HHhS~e=s zD#sfS+-?*t|J!+ozP6KvtOl!R)@@-z24}`9{QaVLD^9VCSR2b`b!KC#o;Ki<+wXB6 zx3&O0LOWcg4&rv4QG0)4yb}7BFSEg~=IR5#ZRj8kg}dS7_V&^%#Do==#`u zpy6{ox?jWuR(;pg+f@mT>#HGWHAJRRDDDv~@(IDw&R>9643kK#HN`!1vBJHnC+RM&yIh8{gG2q zA%e*U3|N0XSRa~oX-3EAneep)@{h2vvd3Xvy$7og(sayr@95+e6~Xvi1tUqnIxoIH zVWo*OwYElb#uyW{Imam6f2rGbjR!Y3`#gPqkv57dB6K^wRGxc9B(t|aYDGS=m$&S!NmCtrMMaUg(c zc2qC=2Z`EEFMW-me5B)24AqF*bV5Dr-M5ig(l-WPS%CgaPzs6p_gnCIvTJ=Y<6!gT zVt@AfYCzjjsMEGi=rDQHo0yc;HqoRNnNFeWZgcm?f;cp(6CNylj36DoL(?TS7eU#+ z7&mfr#y))+CJOXQKUMZ7QIdS9@#-}7y2K1{8)cCt0~-X0O!O?Qx#E4Og+;A2SjalQ zs7r?qn0H044=sDN$SRG$arw~n=+T_DNdSrarmu)V6@|?1-ZB#hRn`uilTGPJ@fqEy zGt(f0B+^JDP&f=r{#Y_wi#AVDf-y!RIXU^0jXsFpf>=Ji*TeqSY!H~AMbJdCGLhC) zn7Rx+sXw6uYj;WRYrLd^5IZq@6JI1C^YkgnedZEYy<&4(z%Q$5yv#Boo{AH8n$a zhb4Y3PWdr269&?V%uI$xMcUrMzl=;w<_nm*qr=c3Rl@i5wWB;e-`t7D&c-mcQl7x! zZWB`UGcw=Y2=}~wzrfLx=uet<;m3~=8I~ZRuzvMQUQdr+yTV|ATf1Uuomr__nDf=X zZ3WYJtHp_ri(}SQAPjv+Y+0=fH4krOP@S&=zZ-t1jW1o@}z;xk8 z(Nz1co&El^HK^NrhVHa-_;&88vTU>_J33=%{if;BEY*J#1n59=07jrGQ#IP>@u#3A z;!q+E1Rj3ZJ+!4bq9F8PXJ@yMgZL;>&gYA0%_Kbi8?S=XGM~dnQZQ!yBSgcZhY96H zrWnU;k)qy`rX&&xlDyA%(a1Hhi5CWkmg(`Gb%m(HKi-7Z!LKGRP_B8@`7&hdDy5n= z`OIxqxiVfX@OX1p(mQu>0Ai*v_cTMiw4qRt3~NBvr9oBy0)r>w3p~V0SCm=An6@3n)>@z!|o-$HvDK z|3D2ZMJkLE5loMKl6R^ez@Zz%S$&mbeoqH5`Bb){Ei21q&VP)hWS2tjShfFtGE+$z zzCR$P#uktu+#!w)cX!lWN1XU%K-r=s{|j?)Akf@q#3b#{6cZCuJ~gCxuMXRmI$nGtnH+-h z+GEi!*X=AP<|fG`1>MBdTb?28JYc=fGvAi2I<$B(rs$;eoJCyR6_bc~p!XR@O-+sD z=eH`-ye})I5ic1eL~TDmtfJ|8`0VJ*Yr=hNCd)G1p2MMz4C3^Mj?7;!w|Ly%JqmuW zlIEW^Ft%z?*|fpXda>Jr^1noFZEwFgVV%|*XhH@acv8rdGxeEX{M$(vG{Zw+x(ei@ zmfXb22}8-?Fi`vo-YVrTH*C?a8%M=Hv9MqVH7H^J$KsD?>!SFZ;ZsvnHr_gn=7acz z#W?0eCdVhVMWN12VV^$>WlQ?f;P^{(&pYTops|btm6aj>_Uz+hqpGwB)vWp0Cf5y< zft8-je~nn?W11plq}N)4A{l8I7$!ks_x$PXW-2XaRFswX_BnF{R#6YIwMhAgd5F9X zGmwdadS6(a^fjHtXg8=l?Rc0Sm%hk6E9!5cLVloEy4eh(=FwgP`)~I^5~pBEWo+F6 zSf2ncyMurJN91#cJTy_u8Y}@%!bq1RkGC~-bV@SXRd4F{R-*V`bS+6;W5vZ(&+I<9$;-V|eNfLa5n-6% z2(}&uGRF;p92eS*sE*oR$@pexaqr*meB)VhmIg@h{uzkk$9~qh#cHhw#>O%)b@+(| z^IQgqzuj~Sk(J;swEM-3TrJAPCq9k^^^`q{IItKBRXYe}e0Tdr=Huf7da3$l4PdpwWDop%^}n;dD#K4s#DYA8SHZ z&1!riV4W4R7R#C))JH1~axJ)RYnM$$lIR%6fIVA@zV{XVyx}C+a-Dt8Y9M)^KU0+H zR4IUb2CJ{Hg>CuaXtD50jB(_Tcx=Z$^WYu2u5kubqmwp%drJ6 z?Fo40g!Qd<-l=TQxqHEOuPX0;^z7iX?Ke^a%XT<13TA^5`4Xcw6D@Ur&VT&CUe0d} z1GjOVF1^L@>O)l@?bD~$wzgf(nxX1OGD8fEV?TdJcZc2KoUe|oP1#=$$7ee|xbY)A zDZq+cuTpc(fFdj^=!;{k03C69lMQ(|>uhRfRu%+!k&YOi-3|1QKB z z?n?eq1XP>p-IM$Z^C;2L3itnbJZAip*Zo0aw2bs8@(s^~*8T9go!%dHcAz2lM;`yp zD=7&xjFV$S&5uDaiScyD?B-i1ze`+CoRtz`Wn+Zl&#s4&}MO{@N!ufrzjG$B79)Y2d3tBk&)TxUTw@QS0TEL_?njX|@vq?Uz(nBFK5Pq7*xj#u*R&i|?7+6# z+|r_n#SW&LXhtheZdah{ZVoqwyT{D>MC3nkFF#N)xLi{p7J1jXlmVeb;cP5?e(=f# zuT7fvjSbjS781v?7{)-X3*?>tq?)Yd)~|1{BDS(pqC zC}~H#WXlkUW*H5CDOo<)#x7%RY)A;ShGhI5s*#cRDA8YgqG(HeKDx+#(ZQ?386dv! zlXCO)w91~Vw4AmOcATuV653fa9R$fyK8ul%rG z-wfS zihugoZyr38Im?Zuh6@RcF~t1anQu7>#lPpb#}4cOA!EM11`%f*07RqOVkmX{p~KJ9 z^zP;K#|)$`^Rb{rnHGH{~>1(fawV0*Z#)}M`m8-?ZJV<+e}s9wE# z)l&az?w^5{)`S(%MRzxdNqrs1n*-=jS^_jqE*5XDrA0+VE`5^*p3CuM<&dZEeCjoz zR;uu_H9ZPZV|fQq`Cyw4nscrVwi!fE6ciMmX$!_hN7uF;jjKG)d2@aC4ropY)8etW=xJvni)8eHi`H$%#zn^WJ5NLc-rqk|u&&4Z6fD_m&JfSI1Bvb?b<*n&sfl0^t z=HnmRl`XrFvMKB%9}>PaA`m-fK6a0(8=qPkWS5bb4=v?XcWi&hRY?O5HdulRi4?fN zlsJ*N-0Qw+Yic@s0(2uy%F@ib;GjXt01Fmx5XbRo6+n|pP(&nodMoap^z{~q ziEeaUT@Mxe3vJSfI6?uLND(CNr=#^W<1b}jzW58bIfyWTDle$mmS(|x-0|2UlX+9k zQ^EX7Nw}?EzVoBfT(-LT|=9N@^hcn-_p&sqG z&*oVs2JSU+N4ZD`FhCAWaS;>|wH2G*Id|?pa#@>tyxX`+4HyIArWDvVrX)2WAOQff z0qyHu&-S@i^MS-+j--!pr4fPBj~_8({~e1bfcl0wI1kaoN>mJL6KUPQm5N7lB(ui1 zE-o%kq)&djzWJ}ob<-GfDlkB;F31j-VHKvQUGQ3sp`CwyGJk_i!y^sD0fqC@$9|jO zOqN!r!8-p==F@ZVP=U$qSpY(gQ0)59P1&t@y?5rvg<}E+GB}26NYPp4f2YFQrQtot5mn3wu_qprZ=>Ig-$ zbW26Ws~IgY>}^5w`vTB(G`PTZaDiGBo5o(tp)qli|NeV( z@H_=R8V39rt5J5YB2Ky?4eJJ#b`_iBe2ot~6%7mLt5t8Vwi^Jy7|jWXqa3amOIoRb zOr}WVFP--DsS`1WpN%~)t3R!arKF^Q$e12KEqU36AWwnCBICpH4XCsfnyrHr>$I$4 z!DpKX$OKLWarN7nv@!uIA+~RNO)l$$w}p(;b>mx8pwYvu;dD_unryX_NhT8*Tj>BTrTTL&!?O+%Rv;b?B??gSzdp?6Uug9{ zd@V08Z$BdI?fpoCS$)t4mg4rT8Q_I}h`0d-vYZ^|dOB*Q^S|xqTV*vIg?@fVFSmMpaw0qtTRbx} z({Pg?#{2`sc9)M5N$*N|4;^t$+QP?#mov zGVC@I*lBVrOU-%2y!7%)fAKjpEFsgQc4{amtiHb95KQEwvf<(3T<9-Zm$xIew#P22 zc2Ix|App^>v6(3L_MCU0d3W##AB0M~3D00EWoKZqsJYT(#@w$Y_H7G22M~ApVFTRHMI_3be)Lkn#0F*V8Pq zc}`Cjy$bE;FJ6H7p=0y#R>`}-m4(0F>%@P|?7fx{=R^uFdISRnZ2W_xQhD{YuR3t< z{6yxu=4~JkeA;|(J6_nv#>Nvs&FuLA&PW^he@t(UwFFE8)|a!R{`E`K`i^ZnyE4$k z;(749Ix|oi$c3QbEJ3b~D_kQsPz~fIUKym($a_7dJ?o+40*OLl^{=&oq$<#Q(yyrp z{J-FAniyAw9tPbe&IhQ|a`DqFTVQGQ&Gq3!C2==4x{6EJwiPZ8zub-iXoUtkJiG{} zPaR&}_fn8_z~(=;5lD-aPWD3z8PZS@AaUiomF!G8I}Mf>e~0g#BelA-5#`cj;O5>N Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/crm_salesperson_planner/static/description/index.html b/crm_salesperson_planner/static/description/index.html new file mode 100644 index 00000000000..3b53184f1ce --- /dev/null +++ b/crm_salesperson_planner/static/description/index.html @@ -0,0 +1,443 @@ + + + + + + +Crm Salesperson Planner + + + +
+

Crm Salesperson Planner

+ + +

Beta License: AGPL-3 OCA/crm Translate me on Weblate Try me on Runbot

+

This application allows you to track and schedule salespeople visits to your customers, allowing you to determine which opportunities are going to be dealt on each visit. Visits create an all day event in calendar, and they can be easily rescheduled. +Visits can be automatically created from a template, in which it is possible to select the frequency of visits, as well as the start and end dates. The last visit can also be calculated by selecting the total number of repetitions. +This module creates a cron that generates visits from templates, but an option to create them manually is available from the template form view when the template is validated.

+

Table of contents

+ +
+

Configuration

+

To configure this module, you need to:

+
    +
  • Go to new menu CRM > Configuration > Salesperson Planner > Close Reasons and create a close reason for ‘Cancel’ and ‘Incident’ types.
  • +
+
+
+

Usage

+

Go to new menu CRM > Salesperson Planner > My Visits or All Visits and create a new visit. +or +Go to CRM > Salesperson Planner > Visit Templates and create a new recurring template for create periodical visits. In this case, it is necessary to select a start date. The date of the last repetition can be calculated by selection the total number of repetitions or an end date. +There are two options available to reschedule visits that is already validated: +* Change the date from the visit. +* Change the date straight from the event automatically created in the calendar.

+
+
+

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 smashing it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Sygel Technology
  • +
+
+
+

Contributors

+
    +
  • Sygel:
      +
    • Valentin Vinagre
    • +
    • Manuel Regidor
    • +
    +
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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

+

This module is part of the OCA/crm project on GitHub.

+

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

+
+
+
+ + diff --git a/crm_salesperson_planner/tests/__init__.py b/crm_salesperson_planner/tests/__init__.py new file mode 100644 index 00000000000..69669a08800 --- /dev/null +++ b/crm_salesperson_planner/tests/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from . import test_crm_salesperson_planner_visit +from . import test_crm_salesperson_planner_visit_template diff --git a/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit.py b/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit.py new file mode 100644 index 00000000000..0f437f36854 --- /dev/null +++ b/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit.py @@ -0,0 +1,147 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from dateutil.relativedelta import relativedelta + +from odoo import fields +from odoo.tests import common + + +class TestCrmSalespersonPlannerVisit(common.SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.visit_model = cls.env["crm.salesperson.planner.visit"] + cls.partner_model = cls.env["res.partner"] + cls.close_model = cls.env["crm.salesperson.planner.visit.close.reason"] + cls.close_wiz_model = cls.env["crm.salesperson.planner.visit.close.wiz"] + cls.partner1 = cls.partner_model.create( + { + "name": "Partner Visit 1", + "email": "partner.visit.1@example.com", + "phone": "1234567890", + } + ) + cls.partner1_contact1 = cls.partner_model.create( + { + "name": "Partner Contact Visit 1", + "email": "partner.visit.1@example.com", + "phone": "1234567890", + "parent_id": cls.partner1.id, + } + ) + cls.visit1 = cls.visit_model.create({"partner_id": cls.partner1.id}) + cls.visit2 = cls.visit_model.create( + {"partner_id": cls.partner1_contact1.id, "sequence": 1} + ) + cls.cancel = cls.close_model.create( + { + "name": "Cancel", + "close_type": "cancel", + "require_image": False, + "reschedule": False, + } + ) + cls.cancel_resch = cls.close_model.create( + { + "name": "Cancel", + "close_type": "cancel", + "require_image": False, + "reschedule": True, + } + ) + cls.cancel_img = cls.close_model.create( + { + "name": "Cancel", + "close_type": "cancel", + "require_image": True, + "reschedule": False, + } + ) + cls.incident = cls.close_model.create( + { + "name": "Incident", + "close_type": "incident", + "require_image": False, + "reschedule": False, + } + ) + + def test_crm_salesperson_planner_visit(self): + self.assertNotEqual(self.visit1.name, "/") + self.assertEqual(self.visit1.state, "draft") + self.assertEqual(self.partner1.salesperson_planner_visit_count, 2) + self.assertEqual(self.partner1_contact1.salesperson_planner_visit_count, 1) + self.assertEqual(self.visit1.date, fields.Date.context_today(self.visit1)) + self.assertEqual( + self.visit_model.search( + [("partner_id", "child_of", self.partner1.id)], limit=1 + ), + self.visit2, + ) + + def config_close_wiz(self, att_close_type, vals): + additionnal_context = { + "active_model": self.visit_model, + "active_ids": self.visit1.ids, + "active_id": self.visit1.id, + "att_close_type": att_close_type, + } + close_wiz = self.close_wiz_model.with_context(**additionnal_context).create( + vals + ) + close_wiz.action_close_reason_apply() + + def test_crm_salesperson_close_wiz_cancel(self): + self.visit1.action_confirm() + self.assertEqual(self.visit1.state, "confirm") + self.config_close_wiz("close", {"reason_id": self.cancel.id, "notes": "Test"}) + self.assertEqual(self.visit1.state, "cancel") + self.assertEqual(self.visit1.close_reason_id.id, self.cancel.id) + self.assertEqual(self.visit1.close_reason_notes, "Test") + self.assertEqual( + self.visit_model.search_count( + [("partner_id", "child_of", self.partner1.id)] + ), + 2, + ) + + def test_crm_salesperson_close_wiz_cancel_resch(self): + self.visit1.action_confirm() + self.assertEqual(self.visit1.state, "confirm") + self.config_close_wiz( + "close", + { + "reason_id": self.cancel_resch.id, + "new_date": self.visit1.date + relativedelta(days=10), + "new_sequence": 40, + }, + ) + self.assertEqual(self.visit1.close_reason_id.id, self.cancel_resch.id) + self.assertEqual( + self.visit_model.search_count( + [ + ("partner_id", "=", self.partner1.id), + ("date", "=", self.visit1.date + relativedelta(days=10)), + ("sequence", "=", 40), + ("state", "=", "confirm"), + ] + ), + 1, + ) + + def test_crm_salesperson_close_wiz_cancel_img(self): + self.visit1.action_confirm() + self.assertEqual(self.visit1.state, "confirm") + detail_image = b"R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" + self.config_close_wiz( + "close", {"reason_id": self.cancel_img.id, "image": detail_image} + ) + self.assertEqual(self.visit1.close_reason_id.id, self.cancel_img.id) + self.assertEqual(self.visit1.close_reason_image, detail_image) + + def test_crm_salesperson_close_wiz_incident(self): + self.visit1.action_confirm() + self.assertEqual(self.visit1.state, "confirm") + self.config_close_wiz("incident", {"reason_id": self.incident.id}) + self.assertEqual(self.visit1.state, "incident") diff --git a/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit_template.py b/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit_template.py new file mode 100644 index 00000000000..aafe238f87d --- /dev/null +++ b/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit_template.py @@ -0,0 +1,185 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# Copyright 2021 Sygel - Manuel Regidor +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from datetime import timedelta + +from odoo import exceptions, fields +from odoo.tests import common + + +class TestCrmSalespersonPlannerVisitTemplate(common.SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.visit_template_model = cls.env["crm.salesperson.planner.visit.template"] + cls.partner_model = cls.env["res.partner"] + cls.close_reason_mode = cls.env["crm.salesperson.planner.visit.close.reason"] + cls.partner1 = cls.partner_model.create( + { + "name": "Partner Visit 1", + "email": "partner.visit.1@example.com", + "phone": "1234567890", + } + ) + cls.visit_template_base = cls.visit_template_model.create( + { + "partner_ids": [(4, cls.partner1.id)], + "start_date": fields.Date.today(), + "stop_date": fields.Date.today(), + "start": fields.Date.today(), + "stop": fields.Date.today(), + } + ) + cls.close_reason = cls.close_reason_mode.create( + {"name": "close reason", "close_type": "cancel"} + ) + + def test_01_repeat_days(self): + self.visit_template_base.write( + { + "auto_validate": False, + "interval": 1, + "rrule_type": "daily", + "end_type": "count", + "count": 10, + } + ) + self.visit_template_base.action_validate() + self.visit_template_base.create_visits(days=4) + self.assertEqual(self.visit_template_base.visit_ids_count, 5) + self.assertEqual( + len( + self.visit_template_base.visit_ids.filtered( + lambda a: a.state == "draft" + ) + ), + 5, + ) + self.assertEqual( + len( + self.visit_template_base.visit_ids.filtered( + lambda a: a.calendar_event_id.id + ) + ), + 0, + ) + self.assertEqual(self.visit_template_base.state, "in-progress") + self.visit_template_base.create_visits(days=9) + self.visit_template_base._compute_visit_ids_count() + self.assertEqual(self.visit_template_base.visit_ids_count, 10) + self.assertEqual( + len( + self.visit_template_base.visit_ids.filtered( + lambda a: a.state == "draft" + ) + ), + 10, + ) + self.assertEqual( + len( + self.visit_template_base.visit_ids.filtered( + lambda a: a.calendar_event_id.id + ) + ), + 0, + ) + self.assertEqual(self.visit_template_base.state, "done") + + def test_02_repeat_days_autovalidate(self): + self.visit_template_base.write( + { + "auto_validate": True, + "interval": 1, + "rrule_type": "daily", + "end_type": "count", + "count": 10, + } + ) + self.visit_template_base.action_validate() + self.visit_template_base.create_visits(days=4) + self.assertEqual(self.visit_template_base.visit_ids_count, 5) + self.assertEqual( + len( + self.visit_template_base.visit_ids.filtered( + lambda a: a.state == "draft" + ) + ), + 0, + ) + self.assertEqual( + len( + self.visit_template_base.visit_ids.filtered( + lambda a: a.calendar_event_id.id + ) + ), + 5, + ) + self.assertEqual(self.visit_template_base.state, "in-progress") + self.visit_template_base.create_visits(days=9) + self.visit_template_base._compute_visit_ids_count() + self.assertEqual(self.visit_template_base.visit_ids_count, 10) + self.assertEqual( + len( + self.visit_template_base.visit_ids.filtered( + lambda a: a.state == "draft" + ) + ), + 0, + ) + self.assertEqual( + len( + self.visit_template_base.visit_ids.filtered( + lambda a: a.calendar_event_id.id + ) + ), + 10, + ) + self.assertEqual(self.visit_template_base.state, "done") + + def test_03_change_visit_date(self): + visit_template = self.visit_template_base.copy() + visit_template.write( + { + "auto_validate": True, + "interval": 1, + "rrule_type": "daily", + "end_type": "count", + "count": 10, + } + ) + visit_template.create_visits(days=10) + visit_0 = visit_template.visit_ids[0] + event_id_0 = visit_0.calendar_event_id + self.assertEqual(visit_0.date, event_id_0.start_date) + visit_0.write({"date": fields.Date.today() + timedelta(days=7)}) + self.assertEqual(event_id_0.start_date, fields.Date.today() + timedelta(days=7)) + event_id_0.write( + { + "start": fields.Datetime.today() + timedelta(days=14), + "stop": fields.Datetime.today() + timedelta(days=14), + } + ) + self.assertEqual(visit_0.date, fields.Date.today() + timedelta(days=14)) + + def test_04_cancel_visit(self): + visit_template = self.visit_template_base.copy() + visit_template.write( + { + "auto_validate": True, + "interval": 1, + "rrule_type": "daily", + "end_type": "count", + "count": 10, + } + ) + visit_template.create_visits(days=10) + first_visit = visit_template.visit_ids[0] + self.assertNotEqual(first_visit.calendar_event_id.id, False) + with self.assertRaises(exceptions.ValidationError): + first_visit.unlink() + self.assertEqual(len(visit_template.visit_ids), 10) + first_visit.action_cancel(self.close_reason) + self.assertEqual(first_visit.calendar_event_id.id, False) + first_visit.unlink() + self.assertEqual(len(visit_template.visit_ids), 9) diff --git a/crm_salesperson_planner/views/crm_lead.xml b/crm_salesperson_planner/views/crm_lead.xml new file mode 100644 index 00000000000..a0561d29551 --- /dev/null +++ b/crm_salesperson_planner/views/crm_lead.xml @@ -0,0 +1,19 @@ + + + + + crm.lead.view.crm.salesperson.planner + crm.lead + + + + + + + + + + + + diff --git a/crm_salesperson_planner/views/crm_salesperson_planner_menu.xml b/crm_salesperson_planner/views/crm_salesperson_planner_menu.xml new file mode 100644 index 00000000000..a3eb6ed5828 --- /dev/null +++ b/crm_salesperson_planner/views/crm_salesperson_planner_menu.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + diff --git a/crm_salesperson_planner/views/crm_salesperson_planner_visit_close_reason_views.xml b/crm_salesperson_planner/views/crm_salesperson_planner_visit_close_reason_views.xml new file mode 100644 index 00000000000..1af2f9501db --- /dev/null +++ b/crm_salesperson_planner/views/crm_salesperson_planner_visit_close_reason_views.xml @@ -0,0 +1,60 @@ + + + + + CRM - Salesperson Planner Visit Close Reason Tree + crm.salesperson.planner.visit.close.reason + + + + + + + + + + + CRM - Salesperson Planner Visit Close Reason Form + crm.salesperson.planner.visit.close.reason + +
+ +
+

+ +

+
+ + + + + + + + + +
+
+
+
+ + Close Reason + crm.salesperson.planner.visit.close.reason + tree,form + +

+ Record reason for close commercial visits. +

+
+
+
diff --git a/crm_salesperson_planner/views/crm_salesperson_planner_visit_template_views.xml b/crm_salesperson_planner/views/crm_salesperson_planner_visit_template_views.xml new file mode 100644 index 00000000000..e86529235ae --- /dev/null +++ b/crm_salesperson_planner/views/crm_salesperson_planner_visit_template_views.xml @@ -0,0 +1,179 @@ + + + + + CRM - Salesperson Planner Visit Template Tree + crm.salesperson.planner.visit.template + + + + + + + + + + + + + + CRM - Salesperson Planner Visit Template Form + crm.salesperson.planner.visit.template + +
+
+
+ +
+ +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+
+
+ + + + + +
+
+
+
+ + Visit Templates + crm.salesperson.planner.visit.template + tree,form + +

+ Create and plan commercial visit templates +

+
+
+
diff --git a/crm_salesperson_planner/views/crm_salesperson_planner_visit_views.xml b/crm_salesperson_planner/views/crm_salesperson_planner_visit_views.xml new file mode 100644 index 00000000000..44ab01feefb --- /dev/null +++ b/crm_salesperson_planner/views/crm_salesperson_planner_visit_views.xml @@ -0,0 +1,299 @@ + + + + + CRM - Salesperson Planner Visit Tree + crm.salesperson.planner.visit + + + + + + + + + + + + + + diff --git a/crm_salesperson_planner/wizards/__init__.py b/crm_salesperson_planner/wizards/__init__.py new file mode 100644 index 00000000000..1c13423b276 --- /dev/null +++ b/crm_salesperson_planner/wizards/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from . import crm_salesperson_planner_visit_close_wiz +from . import crm_salesperson_planner_visit_template_create diff --git a/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_close_wiz.py b/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_close_wiz.py new file mode 100644 index 00000000000..83c3858dce5 --- /dev/null +++ b/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_close_wiz.py @@ -0,0 +1,65 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import _, fields, models + + +class CrmSalespersonPlannerVisitCloseWiz(models.TransientModel): + _name = "crm.salesperson.planner.visit.close.wiz" + _description = "Get Close Reason" + + def _default_new_date(self): + visits = self.env["crm.salesperson.planner.visit"].browse( + self.env.context.get("active_id") + ) + return visits.date + + def _default_new_sequence(self): + visits = self.env["crm.salesperson.planner.visit"].browse( + self.env.context.get("active_id") + ) + return visits.sequence + + reason_id = fields.Many2one( + comodel_name="crm.salesperson.planner.visit.close.reason", + string="Reason", + required=True, + ) + image = fields.Image(string="Image", max_width=1024, max_height=1024) + new_date = fields.Date( + string="New Date", default=lambda self: self._default_new_date() + ) + new_sequence = fields.Integer( + string="Sequence", + help="Used to order Visits in the different views", + default=lambda self: self._default_new_sequence(), + ) + require_image = fields.Boolean( + string="Require Image", related="reason_id.require_image" + ) + reschedule = fields.Boolean(string="Reschedule", default=True) + allow_reschedule = fields.Boolean( + string="Allow Reschedule", related="reason_id.reschedule" + ) + notes = fields.Text(string="Notes") + + def action_close_reason_apply(self): + visits = self.env["crm.salesperson.planner.visit"].browse( + self.env.context.get("active_id") + ) + visit_close_find_method_name = "action_%s" % self.reason_id.close_type + if hasattr(visits, visit_close_find_method_name): + getattr(visits, visit_close_find_method_name)( + self.reason_id, self.image, self.notes + ) + if self.allow_reschedule and self.reschedule: + visits.copy( + { + "date": self.new_date, + "sequence": self.new_sequence, + "opportunity_ids": visits.opportunity_ids.ids, + } + ).action_confirm() + else: + raise ValueError(_("The close reason type haven't a function.")) + return {"type": "ir.actions.act_window_close"} diff --git a/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_close_wiz_view.xml b/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_close_wiz_view.xml new file mode 100644 index 00000000000..71d4d9d8265 --- /dev/null +++ b/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_close_wiz_view.xml @@ -0,0 +1,60 @@ + + + + + crm.salesperson.lost.form + crm.salesperson.planner.visit.close.wiz + +
+ + + + + + + + + + +
+
+
+
+
+ + Close Reasons + ir.actions.act_window + crm.salesperson.planner.visit.close.wiz + form + + new + +
diff --git a/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_template_create.py b/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_template_create.py new file mode 100644 index 00000000000..ed1fd389b5c --- /dev/null +++ b/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_template_create.py @@ -0,0 +1,40 @@ +# Copyright 2021 Sygel - Valentin Vinagre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from datetime import timedelta + +from odoo import _, fields, models +from odoo.exceptions import ValidationError + + +class CrmSalespersonPlannerVisitTemplateCreate(models.TransientModel): + _name = "crm.salesperson.planner.visit.template.create" + _description = "crm salesperson planner visit template create" + + def _default_date_to(self): + template = self.env["crm.salesperson.planner.visit.template"].browse( + self.env.context.get("active_id") + ) + date = False + if template and template.last_visit_date: + date = template.last_visit_date + timedelta(days=7) + else: + date = fields.Date.context_today(self) + timedelta(days=7) + return date + + date_to = fields.Date( + string="Date to", default=lambda self: self._default_date_to(), required=True + ) + + def create_visits(self): + template = self.env["crm.salesperson.planner.visit.template"].browse( + self.env.context.get("active_id") + ) + days = (self.date_to - fields.Date.context_today(self)).days + if days < 0: + raise ValidationError(_("The date can't be earlier than today")) + visit_vals = template._create_visits(days=days) + visits = self.env["crm.salesperson.planner.visit"].create(visit_vals) + if visits and template.auto_validate: + visits.action_confirm() + return {"type": "ir.actions.act_window_close"} diff --git a/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_template_create.xml b/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_template_create.xml new file mode 100644 index 00000000000..5f29eb654a3 --- /dev/null +++ b/crm_salesperson_planner/wizards/crm_salesperson_planner_visit_template_create.xml @@ -0,0 +1,42 @@ + + + + + crm.salesperson.planner.visit.template.create.form + crm.salesperson.planner.visit.template.create + +
+ + + +
+
+
+
+
+ + Create Visits + ir.actions.act_window + crm.salesperson.planner.visit.template.create + form + + new + +
From ca12bee1ca8b810bde016c18210c50e2dff2792a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Wed, 23 Nov 2022 16:03:00 +0100 Subject: [PATCH 2/7] [IMP] crm_salesperson_planner: black, isort, prettier --- .../models/crm_salesperson_planner_visit.py | 16 +++++++++++++--- .../crm_salesperson_planner_visit_template.py | 14 +++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/crm_salesperson_planner/models/crm_salesperson_planner_visit.py b/crm_salesperson_planner/models/crm_salesperson_planner_visit.py index 97c8f61886a..c300051c4b1 100644 --- a/crm_salesperson_planner/models/crm_salesperson_planner_visit.py +++ b/crm_salesperson_planner/models/crm_salesperson_planner_visit.py @@ -13,14 +13,24 @@ class CrmSalespersonPlannerVisit(models.Model): _inherit = ["mail.thread", "mail.activity.mixin"] name = fields.Char( - string="Visit Number", required=True, default="/", readonly=True, copy=False, + string="Visit Number", + required=True, + default="/", + readonly=True, + copy=False, ) partner_id = fields.Many2one( - comodel_name="res.partner", string="Customer", required=True, + comodel_name="res.partner", + string="Customer", + required=True, ) partner_phone = fields.Char(string="Phone", related="partner_id.phone") partner_mobile = fields.Char(string="Mobile", related="partner_id.mobile") - date = fields.Date(string="Date", default=fields.Date.context_today, required=True,) + date = fields.Date( + string="Date", + default=fields.Date.context_today, + required=True, + ) sequence = fields.Integer( string="Sequence", help="Used to order Visits in the different views", diff --git a/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py b/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py index 7ed1ebb801f..b628d1186dd 100644 --- a/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py +++ b/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py @@ -14,7 +14,10 @@ class CrmSalespersonPlannerVisitTemplate(models.Model): _inherit = "calendar.event" name = fields.Char( - string="Visit Template Number", default="/", readonly=True, copy=False, + string="Visit Template Number", + default="/", + readonly=True, + copy=False, ) partner_ids = fields.Many2many( string="Customer", @@ -40,7 +43,9 @@ class CrmSalespersonPlannerVisitTemplate(models.Model): ("groups_id", "in", self.env.ref("sales_team.group_sale_salesman").id) ], ) - categ_ids = fields.Many2many(relation="visit_category_rel",) + categ_ids = fields.Many2many( + relation="visit_category_rel", + ) alarm_ids = fields.Many2many(relation="visit_calendar_event_rel") state = fields.Selection( string="Status", @@ -64,7 +69,10 @@ class CrmSalespersonPlannerVisitTemplate(models.Model): string="Number of Sales Person Visits", compute="_compute_visit_ids_count" ) auto_validate = fields.Boolean(string="Auto Validate", default=True) - rrule_type = fields.Selection(default="daily", required=True,) + rrule_type = fields.Selection( + default="daily", + required=True, + ) last_visit_date = fields.Date( string="Last Visit Date", compute="_compute_last_visit_date", store=True ) From bd5ae85b68a5904356a3f4b6fb720f832c0b8808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Mart=C3=ADnez?= Date: Wed, 23 Nov 2022 17:05:31 +0100 Subject: [PATCH 3/7] [MIG] crm_salesperson_planner: Migration to 15.0 TT38992 --- crm_salesperson_planner/README.rst | 10 +- crm_salesperson_planner/__manifest__.py | 2 +- .../i18n/crm_salesperson_planner.pot | 162 ++++++++++++------ .../migrations/15.0.1.0.0/pre-migration.py | 63 +++++++ .../models/calendar_event.py | 17 +- .../models/crm_salesperson_planner_visit.py | 59 ++++--- ..._salesperson_planner_visit_close_reason.py | 4 +- .../crm_salesperson_planner_visit_template.py | 143 +++++++--------- crm_salesperson_planner/models/res_partner.py | 4 +- .../security/ir.model.access.csv | 2 + .../static/description/index.html | 6 +- .../test_crm_salesperson_planner_visit.py | 4 +- ..._crm_salesperson_planner_visit_template.py | 20 +-- ...erson_planner_visit_close_reason_views.xml | 4 +- ...lesperson_planner_visit_template_views.xml | 20 +-- .../crm_salesperson_planner_visit_views.xml | 1 - ...crm_salesperson_planner_visit_close_wiz.py | 10 +- ...lesperson_planner_visit_template_create.py | 13 +- 18 files changed, 314 insertions(+), 230 deletions(-) create mode 100644 crm_salesperson_planner/migrations/15.0.1.0.0/pre-migration.py diff --git a/crm_salesperson_planner/README.rst b/crm_salesperson_planner/README.rst index 21e6f64c17e..0c053f17de1 100644 --- a/crm_salesperson_planner/README.rst +++ b/crm_salesperson_planner/README.rst @@ -14,13 +14,13 @@ Crm Salesperson Planner :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fcrm-lightgray.png?logo=github - :target: https://github.com/OCA/crm/tree/13.0/crm_salesperson_planner + :target: https://github.com/OCA/crm/tree/15.0/crm_salesperson_planner :alt: OCA/crm .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/crm-13-0/crm-13-0-crm_salesperson_planner + :target: https://translation.odoo-community.org/projects/crm-15-0/crm-15-0-crm_salesperson_planner :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/111/13.0 + :target: https://runbot.odoo-community.org/runbot/111/15.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -57,7 +57,7 @@ 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 smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -90,6 +90,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/crm `_ project on GitHub. +This module is part of the `OCA/crm `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/crm_salesperson_planner/__manifest__.py b/crm_salesperson_planner/__manifest__.py index 57b894682d6..cd935d97f43 100644 --- a/crm_salesperson_planner/__manifest__.py +++ b/crm_salesperson_planner/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) { "name": "Crm Salesperson Planner", - "version": "13.0.1.0.0", + "version": "15.0.1.0.0", "development_status": "Beta", "category": "Customer Relationship Management", "author": "Sygel Technology," "Odoo Community Association (OCA)", diff --git a/crm_salesperson_planner/i18n/crm_salesperson_planner.pot b/crm_salesperson_planner/i18n/crm_salesperson_planner.pot index e552dccafa2..7b198963938 100644 --- a/crm_salesperson_planner/i18n/crm_salesperson_planner.pot +++ b/crm_salesperson_planner/i18n/crm_salesperson_planner.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 13.0\n" +"Project-Id-Version: Odoo Server 15.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" @@ -40,6 +40,11 @@ msgstr "" msgid "Activity State" msgstr "" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + #. module: crm_salesperson_planner #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view msgid "Add a description..." @@ -66,11 +71,6 @@ msgstr "" msgid "Attachment Count" msgstr "" -#. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__is_attendee -msgid "Attendee" -msgstr "" - #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__attendee_status msgid "Attendee Status" @@ -83,7 +83,7 @@ msgstr "" #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__byday -msgid "By day" +msgid "Byday" msgstr "" #. module: crm_salesperson_planner @@ -114,13 +114,26 @@ msgstr "" msgid "Cancelled" msgstr "" +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__is_organizer_alone +msgid "" +"Check if the organizer is alone in the event, i.e. if the organizer is the only one that hasn't declined\n" +" the event (only if the organizer is not the only attendee)" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrence_update +msgid "" +"Choose what to do with other events in the recurrence. Updating All Events " +"is not allowed when dates or time is modified" +msgstr "" + #. module: crm_salesperson_planner #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view msgid "Close Info" msgstr "" #. module: crm_salesperson_planner -#: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_action #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__close_reason_id #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_form_view #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_wiz_view_form @@ -138,9 +151,9 @@ msgid "Close Reason Notes" msgstr "" #. module: crm_salesperson_planner +#: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_action #: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_close_wiz_action #: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_config_salesperson_planner_close_reason -#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_tree_view msgid "Close Reasons" msgstr "" @@ -208,7 +221,6 @@ msgstr "" #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__date -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__display_start msgid "Date" msgstr "" @@ -235,6 +247,11 @@ msgstr "" msgid "Description" msgstr "" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__display_description +msgid "Display Description" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__display_name #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__display_name @@ -282,17 +299,12 @@ msgstr "" msgid "End Date" msgstr "" -#. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__stop_datetime -msgid "End Datetime" -msgstr "" - #. module: crm_salesperson_planner #: code:addons/crm_salesperson_planner/models/calendar_event.py:0 #, python-format msgid "" -"Event %s is related to salesperson visit %s. Cancel it to delete this " -"event.\n" +"Event %(event_name)s is related to salesperson visit %(partner_name)s. " +"Cancel it to delete this event.\n" msgstr "" #. module: crm_salesperson_planner @@ -301,15 +313,14 @@ msgid "Event Time" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_follower_ids -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_follower_ids -msgid "Followers" +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__follow_recurrence +msgid "Follow Recurrence" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_channel_ids -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_channel_ids -msgid "Followers (Channels)" +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_follower_ids +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_follower_ids +msgid "Followers" msgstr "" #. module: crm_salesperson_planner @@ -319,7 +330,12 @@ msgid "Followers (Partners)" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__fr +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__fri msgid "Fri" msgstr "" @@ -338,6 +354,12 @@ msgstr "" msgid "Group By" msgstr "" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__has_message +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__has_message +msgid "Has Message" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__id #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__id @@ -380,6 +402,13 @@ msgid "" "alarm information without removing it." msgstr "" +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__show_as +msgid "" +"If the time is shown as 'busy', this event will be visible to other people with either the full information or simply 'busy' written depending on its privacy. Use this option to let other people know that you are unavailable during that period of time. \n" +" If the time is shown as 'free', this event won't be visible to other people at all. Use this option to let other people know that you are available during that period of time." +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__image msgid "Image" @@ -413,6 +442,11 @@ msgstr "" msgid "Is the Event Highlighted" msgstr "" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__is_organizer_alone +msgid "Is the Organizer Alone" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit____last_update #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason____last_update @@ -481,6 +515,11 @@ msgstr "" msgid "Manually Create Visits" msgstr "" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__videocall_location +msgid "Meeting URL" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_error #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_error @@ -499,10 +538,15 @@ msgid "Mobile" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__mo +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__mon msgid "Mon" msgstr "" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_salesperson_planner_my_visits #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter @@ -514,6 +558,11 @@ msgstr "" msgid "New Date" msgstr "" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_calendar_event_id +msgid "Next Activity Calendar Event" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_date_deadline msgid "Next Activity Deadline" @@ -534,6 +583,11 @@ msgstr "" msgid "Notes" msgstr "" +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__alarm_ids +msgid "Notifications sent to all attendees to remind of the meeting." +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_needaction_counter #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_needaction_counter @@ -612,6 +666,11 @@ msgstr "" msgid "Partner-related data of the user" msgstr "" +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__privacy +msgid "People to whom this event will be visible." +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__partner_phone msgid "Phone" @@ -653,28 +712,23 @@ msgid "Recurrence" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__end_type -msgid "Recurrence Termination" +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrence_id +msgid "Recurrence Rule" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrency -msgid "Recurrent" -msgstr "" - -#. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrent_id -msgid "Recurrent ID" +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__end_type +msgid "Recurrence Termination" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrent_id_date -msgid "Recurrent ID date" +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrence_update +msgid "Recurrence Update" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrency -msgid "Recurrent Meeting" +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrency +msgid "Recurrent" msgstr "" #. module: crm_salesperson_planner @@ -724,11 +778,6 @@ msgstr "" msgid "Reschedule" msgstr "" -#. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__partner_id -msgid "Responsible" -msgstr "" - #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_user_id msgid "Responsible User" @@ -777,10 +826,15 @@ msgid "Salesperson Visits" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__sa +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__sat msgid "Sat" msgstr "" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__partner_id +msgid "Scheduled by" +msgstr "" + #. module: crm_salesperson_planner #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter msgid "Search Visits" @@ -800,7 +854,7 @@ msgstr "" #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__show_as -msgid "Show Time as" +msgid "Show as" msgstr "" #. module: crm_salesperson_planner @@ -813,11 +867,6 @@ msgstr "" msgid "Start Date" msgstr "" -#. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start_datetime -msgid "Start DateTime" -msgstr "" - #. module: crm_salesperson_planner #: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start msgid "Start date of an event, without time for full days events" @@ -859,7 +908,7 @@ msgid "Submit" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__su +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__sun msgid "Sun" msgstr "" @@ -921,7 +970,7 @@ msgid "The visit template number must be unique!" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__th +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__thu msgid "Thu" msgstr "" @@ -936,7 +985,7 @@ msgid "Today Visits" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__tu +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__tue msgid "Tue" msgstr "" @@ -963,6 +1012,7 @@ msgid "Unread Messages Counter" msgstr "" #. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__until #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view msgid "Until" msgstr "" @@ -1009,7 +1059,6 @@ msgstr "" #. module: crm_salesperson_planner #: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_template_action #: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_salesperson_planner_visit_template -#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_tree_view msgid "Visit Templates" msgstr "" @@ -1027,7 +1076,6 @@ msgstr "" #: model:ir.actions.act_window,name:crm_salesperson_planner.all_crm_salesperson_planner_visit_action #: model:ir.actions.act_window,name:crm_salesperson_planner.my_crm_salesperson_planner_visit_action #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_lead__crm_salesperson_planner_visit_ids -#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_tree_view msgid "Visits" msgstr "" @@ -1055,12 +1103,12 @@ msgid "Website communication history" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__we +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__wed msgid "Wed" msgstr "" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__week_list +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__weekday msgid "Weekday" msgstr "" diff --git a/crm_salesperson_planner/migrations/15.0.1.0.0/pre-migration.py b/crm_salesperson_planner/migrations/15.0.1.0.0/pre-migration.py new file mode 100644 index 00000000000..af35398900a --- /dev/null +++ b/crm_salesperson_planner/migrations/15.0.1.0.0/pre-migration.py @@ -0,0 +1,63 @@ +from openupgradelib import openupgrade + +field_spec = [ + ( + "crm.salesperson.planner.visit.template", + "crm_salesperson_planner_visit_template", + "start_datetime", + "start", + ), + ( + "crm.salesperson.planner.visit.template", + "crm_salesperson_planner_visit_template", + "week_list", + "weekday", + ), + ( + "crm.salesperson.planner.visit.template", + "crm_salesperson_planner_visit_template", + "mo", + "mon", + ), + ( + "crm.salesperson.planner.visit.template", + "crm_salesperson_planner_visit_template", + "tu", + "tue", + ), + ( + "crm.salesperson.planner.visit.template", + "crm_salesperson_planner_visit_template", + "we", + "wed", + ), + ( + "crm.salesperson.planner.visit.template", + "crm_salesperson_planner_visit_template", + "th", + "thu", + ), + ( + "crm.salesperson.planner.visit.template", + "crm_salesperson_planner_visit_template", + "fr", + "fri", + ), + ( + "crm.salesperson.planner.visit.template", + "crm_salesperson_planner_visit_template", + "sa", + "sat", + ), + ( + "crm.salesperson.planner.visit.template", + "crm_salesperson_planner_visit_template", + "su", + "sun", + ), +] + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.rename_fields(env, field_spec, False) diff --git a/crm_salesperson_planner/models/calendar_event.py b/crm_salesperson_planner/models/calendar_event.py index 87fdf603056..102d7bc1f88 100644 --- a/crm_salesperson_planner/models/calendar_event.py +++ b/crm_salesperson_planner/models/calendar_event.py @@ -35,9 +35,9 @@ def write(self, values): salesperson_visit_events.mapped( "salesperson_planner_visit_ids" ).with_context(bypass_update_event=True).write(new_vals) - return super(CalendarEvent, self).write(values) + return super().write(values) - def unlink(self, can_be_deleted=True): + def unlink(self): if not self.env.context.get("bypass_cancel_visit"): salesperson_visit_events = self.filtered( lambda a: a.res_model == "crm.salesperson.planner.visit" @@ -47,8 +47,13 @@ def unlink(self, can_be_deleted=True): error_msg = "" for event in salesperson_visit_events: error_msg += _( - "Event %s is related to salesperson visit %s. " - "Cancel it to delete this event.\n" - ) % (event.name, event.salesperson_planner_visit_ids[0].name) + "Event %(event_name)s is related to salesperson visit " + "%(partner_name)s. Cancel it to delete this event.\n" + ) % { + "event_name": event.name, + "partner_name": fields.first( + event.salesperson_planner_visit_ids + ).name, + } raise ValidationError(error_msg) - return super(CalendarEvent, self).unlink(can_be_deleted) + return super().unlink() diff --git a/crm_salesperson_planner/models/crm_salesperson_planner_visit.py b/crm_salesperson_planner/models/crm_salesperson_planner_visit.py index c300051c4b1..4bd850926ef 100644 --- a/crm_salesperson_planner/models/crm_salesperson_planner_visit.py +++ b/crm_salesperson_planner/models/crm_salesperson_planner_visit.py @@ -27,12 +27,10 @@ class CrmSalespersonPlannerVisit(models.Model): partner_phone = fields.Char(string="Phone", related="partner_id.phone") partner_mobile = fields.Char(string="Mobile", related="partner_id.mobile") date = fields.Date( - string="Date", default=fields.Date.context_today, required=True, ) sequence = fields.Integer( - string="Sequence", help="Used to order Visits in the different views", default=20, ) @@ -58,7 +56,7 @@ class CrmSalespersonPlannerVisit(models.Model): copy=False, domain="[('type', '=', 'opportunity'), ('partner_id', 'child_of', partner_id)]", ) - description = fields.Html(string="Description") + description = fields.Html() state = fields.Selection( string="Status", required=True, @@ -77,10 +75,8 @@ class CrmSalespersonPlannerVisit(models.Model): close_reason_id = fields.Many2one( comodel_name="crm.salesperson.planner.visit.close.reason", string="Close Reason" ) - close_reason_image = fields.Image( - string="Close Reason Image", max_width=1024, max_height=1024, attachment=True - ) - close_reason_notes = fields.Text(string="Close Reason Notes") + close_reason_image = fields.Image(max_width=1024, max_height=1024, attachment=True) + close_reason_notes = fields.Text() visit_template_id = fields.Many2one( comodel_name="crm.salesperson.planner.visit.template", string="Visit Template" ) @@ -140,29 +136,32 @@ def action_cancel(self, reason_id, image=None, notes=None): } ) + def _prepare_calendar_event_vals(self): + return { + "name": self.name, + "partner_ids": [(6, 0, [self.partner_id.id, self.user_id.partner_id.id])], + "user_id": self.user_id.id, + "start_date": self.date, + "stop_date": self.date, + "start": self.date, + "stop": self.date, + "allday": True, + "res_model": self._name, + "res_model_id": self.env.ref( + "crm_salesperson_planner.model_crm_salesperson_planner_visit" + ).id, + "res_id": self.id, + } + def create_calendar_event(self): events = self.env["calendar.event"] - model_id = self.env.ref( - "crm_salesperson_planner.model_crm_salesperson_planner_visit" - ).id - for sel in self: - event_vals = { - "name": sel.name, - "partner_ids": [(6, 0, [sel.partner_id.id, sel.user_id.partner_id.id])], - "user_id": sel.user_id.id, - "start_date": sel.date, - "stop_date": sel.date, - "start": sel.date, - "stop": sel.date, - "allday": True, - "res_model_id": model_id, - "res_id": sel.id, - } - event = self.env["calendar.event"].create(event_vals) + for item in self: + event = self.env["calendar.event"].create( + item._prepare_calendar_event_vals() + ) if event: event.activity_ids.unlink() - event.write({"res_model": "crm.salesperson.planner.visit"}) - sel.write({"calendar_event_id": event.id}) + item.calendar_event_id = event events += event return events @@ -181,19 +180,19 @@ def action_incident(self, reason_id, image=None, notes=None): def unlink(self): if any(sel.state not in ["draft", "cancel"] for sel in self): raise ValidationError(_("Visits must be in cancelled state")) - return super(CrmSalespersonPlannerVisit, self).unlink() + return super().unlink() def write(self, values): - ret_val = super(CrmSalespersonPlannerVisit, self).write(values) + ret_val = super().write(values) if (values.get("date") or values.get("user_id")) and not self.env.context.get( "bypass_update_event" ): new_vals = {} - for sel in self.filtered(lambda a: a.calendar_event_id): + for item in self.filtered(lambda a: a.calendar_event_id): if values.get("date"): new_vals["start"] = values.get("date") new_vals["stop"] = values.get("date") if values.get("user_id"): new_vals["user_id"] = values.get("user_id") - sel.calendar_event_id.write(new_vals) + item.calendar_event_id.write(new_vals) return ret_val diff --git a/crm_salesperson_planner/models/crm_salesperson_planner_visit_close_reason.py b/crm_salesperson_planner/models/crm_salesperson_planner_visit_close_reason.py index a8d119ebd94..12adbbcb1e4 100644 --- a/crm_salesperson_planner/models/crm_salesperson_planner_visit_close_reason.py +++ b/crm_salesperson_planner/models/crm_salesperson_planner_visit_close_reason.py @@ -15,5 +15,5 @@ class CrmSalespersonPlannerVisitCloseReason(models.Model): required=True, default="cancel", ) - require_image = fields.Boolean(string="Require Image", default=False) - reschedule = fields.Boolean(string="Reschedule", default=False) + require_image = fields.Boolean(default=False) + reschedule = fields.Boolean(default=False) diff --git a/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py b/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py index b628d1186dd..1c86af5c53b 100644 --- a/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py +++ b/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py @@ -2,7 +2,7 @@ # Copyright 2021 Sygel - Manuel Regidor # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) -from dateutil.relativedelta import relativedelta +from datetime import timedelta from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -26,7 +26,6 @@ class CrmSalespersonPlannerVisitTemplate(models.Model): required=True, ) sequence = fields.Integer( - string="Sequence", help="Used to order Visits in the different views", default=20, ) @@ -68,14 +67,12 @@ class CrmSalespersonPlannerVisitTemplate(models.Model): visit_ids_count = fields.Integer( string="Number of Sales Person Visits", compute="_compute_visit_ids_count" ) - auto_validate = fields.Boolean(string="Auto Validate", default=True) + auto_validate = fields.Boolean(default=True) rrule_type = fields.Selection( default="daily", required=True, ) - last_visit_date = fields.Date( - string="Last Visit Date", compute="_compute_last_visit_date", store=True - ) + last_visit_date = fields.Date(compute="_compute_last_visit_date", store=True) final_date = fields.Date(string="Repeat Until") allday = fields.Boolean(default=True) recurrency = fields.Boolean(default=True) @@ -104,26 +101,11 @@ def _compute_visit_ids_count(self): def _compute_last_visit_date(self): for sel in self.filtered(lambda x: x.visit_ids): sel.last_visit_date = sel.visit_ids.sorted(lambda x: x.date)[-1].date - self.filtered(lambda x: not x.visit_ids).write({"last_visit_date": False}) - - # overwrite - # default _search method gives an error when trying to access - # instances of the model - def _search( - self, - args, - offset=0, - limit=None, - order=None, - count=False, - access_rights_uid=None, - ): - return super(models.Model, self)._search(args) @api.constrains("partner_ids") def _constrains_partner_ids(self): - for sel in self: - if len(sel.partner_ids) > 1: + for item in self: + if len(item.partner_ids) > 1: raise ValidationError(_("Only one customer is allowed")) @api.model_create_multi @@ -141,16 +123,10 @@ def create(self, vals_list): def write(self, vals): return super(models.Model, self).write(vals) - # overwrite - # default unlink method gives an error when trying to access - # instances of the model - def unlink(self, can_be_deleted=True): - return super(models.Model, self).unlink() - def action_view_salesperson_planner_visit(self): - action = self.env.ref( + action = self.env["ir.actions.act_window"]._for_xml_id( "crm_salesperson_planner.all_crm_salesperson_planner_visit_action" - ).read()[0] + ) action["domain"] = [("id", "=", self.visit_ids.ids)] action["context"] = { "default_partner_id": self.partner_id.id, @@ -168,65 +144,62 @@ def action_cancel(self): def action_draft(self): self.write({"state": "draft"}) - def filter_dates(self, recurrences, days="7"): - last_date = recurrences[-1].date() - reference_date = self.start_date - visit_date = fields.Datetime.from_string(reference_date) - last_visit_date = False - if not self.last_visit_date: - last_visit_date = visit_date - else: - last_visit_date = fields.Datetime.from_string( - self.last_visit_date + relativedelta(days=1) - ) - max_date = fields.Date.today() + relativedelta( - days=days, hours=23, minutes=59, seconds=59 - ) - recurrences = list( - filter( - lambda a: a.replace(tzinfo=None) <= max_date - and a.replace(tzinfo=None) > last_visit_date, - recurrences, - ) - ) - return recurrences, last_date + def _prepare_crm_salesperson_planner_visit_vals(self, dates): + return [ + { + "partner_id": ( + fields.first(self.partner_ids).id if self.partner_ids else False + ), + "date": date, + "sequence": self.sequence, + "user_id": self.user_id.id, + "description": self.description, + "company_id": self.company_id.id, + "visit_template_id": self.id, + } + for date in dates + ] + + def _get_max_date(self): + return self._increase_date(self.start_date, self.count) + + def _increase_date(self, date, value): + if self.rrule_type == "daily": + date += timedelta(days=value) + elif self.rrule_type == "weekly": + date += timedelta(weeks=value) + elif self.rrule_type == "monthly": + date += timedelta(months=value) + elif self.rrule_type == "yearly": + date += timedelta(years=value) + return date + + def _get_recurrence_dates(self, items): + dates = [] + max_date = self._get_max_date() + from_date = self._increase_date(self.last_visit_date or self.start_date, 1) + if max_date > from_date: + for _x in range(items): + if from_date <= max_date: + dates.append(from_date) + from_date = self._increase_date(from_date, 1) + return dates def _create_visits(self, days=7): - visits_vals = [] - for sel in self: - recurrences = self._get_recurrent_date_by_event() - days, last_date = sel.filter_dates(recurrences, days) - for day in days: - visits_vals.append( - { - "partner_id": sel.partner_ids[0].id, - "date": day.date(), - "sequence": sel.sequence, - "user_id": sel.user_id.id, - "description": sel.description, - "company_id": sel.company_id.id, - "visit_template_id": sel.id, - } - ) - if days and days[-1].date() >= last_date: - sel.write({"state": "done"}) - return visits_vals + return self._prepare_crm_salesperson_planner_visit_vals( + self._get_recurrence_dates(days) + ) def create_visits(self, days=7): - for sel in self: - visit_vals = sel._create_visits(days=days) - visits = self.env["crm.salesperson.planner.visit"].create(visit_vals) - if visits and sel.auto_validate: + for item in self: + visits = self.env["crm.salesperson.planner.visit"].create( + item._create_visits(days) + ) + if visits and item.auto_validate: visits.action_confirm() + if item.last_visit_date >= item._get_max_date(): + item.state = "done" def _cron_create_visits(self, days=7): templates = self.search([("state", "=", "in-progress")]) - templates.create_visits(days=days) - - # overwrite - def get_recurrent_ids(self, domain, order=None): - return self.search([domain], order=order) - - # overwrite - def create_attendees(self): - return [] + templates.create_visits(days) diff --git a/crm_salesperson_planner/models/res_partner.py b/crm_salesperson_planner/models/res_partner.py index 7e8e09a77b3..c09f897f673 100644 --- a/crm_salesperson_planner/models/res_partner.py +++ b/crm_salesperson_planner/models/res_partner.py @@ -25,9 +25,9 @@ def _compute_salesperson_planner_visit_count(self): partner.salesperson_planner_visit_count = visit_count def action_view_salesperson_planner_visit(self): - action = self.env.ref( + action = self.env["ir.actions.act_window"]._for_xml_id( "crm_salesperson_planner.all_crm_salesperson_planner_visit_action" - ).read()[0] + ) operator = "child_of" if self.is_company else "=" action["domain"] = [("partner_id", operator, self.id)] return action diff --git a/crm_salesperson_planner/security/ir.model.access.csv b/crm_salesperson_planner/security/ir.model.access.csv index 764c085ab20..ec153a4e19c 100644 --- a/crm_salesperson_planner/security/ir.model.access.csv +++ b/crm_salesperson_planner/security/ir.model.access.csv @@ -3,3 +3,5 @@ access_crm_salesperson_planner_visit_user,crm.salesperson.planner.visit.user,mod access_crm_salesperson_planner_visit_close_reason_user,crm.salesperson.planner.visit.close.reason.user,model_crm_salesperson_planner_visit_close_reason,sales_team.group_sale_salesman,1,0,0,0 access_crm_salesperson_planner_visit_close_reason_manager,crm.salesperson.planner.visit.close.reason.manager,model_crm_salesperson_planner_visit_close_reason,sales_team.group_sale_manager,1,1,1,1 access_crm_salesperson_planner_visit_template_user,crm.salesperson.planner.visit.template.user,model_crm_salesperson_planner_visit_template,sales_team.group_sale_salesman,1,1,1,1 +crm_salesperson_planner.access_crm_salesperson_planner_visit_close_wiz,access_crm_salesperson_planner_visit_close_wiz,crm_salesperson_planner.model_crm_salesperson_planner_visit_close_wiz,sales_team.group_sale_salesman,1,1,1,1 +crm_salesperson_planner.access_crm_salesperson_planner_visit_template_create,access_crm_salesperson_planner_visit_template_create,crm_salesperson_planner.model_crm_salesperson_planner_visit_template_create,sales_team.group_sale_salesman,1,1,1,1 diff --git a/crm_salesperson_planner/static/description/index.html b/crm_salesperson_planner/static/description/index.html index 3b53184f1ce..f36f851e956 100644 --- a/crm_salesperson_planner/static/description/index.html +++ b/crm_salesperson_planner/static/description/index.html @@ -367,7 +367,7 @@

Crm Salesperson Planner

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/crm Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/crm Translate me on Weblate Try me on Runbot

This application allows you to track and schedule salespeople visits to your customers, allowing you to determine which opportunities are going to be dealt on each visit. Visits create an all day event in calendar, and they can be easily rescheduled. Visits can be automatically created from a template, in which it is possible to select the frequency of visits, as well as the start and end dates. The last visit can also be calculated by selecting the total number of repetitions. This module creates a cron that generates visits from templates, but an option to create them manually is available from the template form view when the template is validated.

@@ -406,7 +406,7 @@

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 smashing it by providing a detailed and welcomed -feedback.

+feedback.

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

@@ -434,7 +434,7 @@

Maintainers

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

-

This module is part of the OCA/crm project on GitHub.

+

This module is part of the OCA/crm project on GitHub.

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

diff --git a/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit.py b/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit.py index 0f437f36854..f87b93258a5 100644 --- a/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit.py +++ b/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit.py @@ -7,7 +7,7 @@ from odoo.tests import common -class TestCrmSalespersonPlannerVisit(common.SavepointCase): +class TestCrmSalespersonPlannerVisit(common.TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() @@ -82,7 +82,7 @@ def test_crm_salesperson_planner_visit(self): def config_close_wiz(self, att_close_type, vals): additionnal_context = { - "active_model": self.visit_model, + "active_model": self.visit_model._name, "active_ids": self.visit1.ids, "active_id": self.visit1.id, "att_close_type": att_close_type, diff --git a/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit_template.py b/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit_template.py index aafe238f87d..4cf6add1e07 100644 --- a/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit_template.py +++ b/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit_template.py @@ -8,7 +8,7 @@ from odoo.tests import common -class TestCrmSalespersonPlannerVisitTemplate(common.SavepointCase): +class TestCrmSalespersonPlannerVisitTemplate(common.TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() @@ -24,7 +24,7 @@ def setUpClass(cls): ) cls.visit_template_base = cls.visit_template_model.create( { - "partner_ids": [(4, cls.partner1.id)], + "partner_ids": [(6, False, cls.partner1.ids)], "start_date": fields.Date.today(), "stop_date": fields.Date.today(), "start": fields.Date.today(), @@ -47,14 +47,14 @@ def test_01_repeat_days(self): ) self.visit_template_base.action_validate() self.visit_template_base.create_visits(days=4) - self.assertEqual(self.visit_template_base.visit_ids_count, 5) + self.assertEqual(self.visit_template_base.visit_ids_count, 4) self.assertEqual( len( self.visit_template_base.visit_ids.filtered( lambda a: a.state == "draft" ) ), - 5, + 4, ) self.assertEqual( len( @@ -98,7 +98,7 @@ def test_02_repeat_days_autovalidate(self): ) self.visit_template_base.action_validate() self.visit_template_base.create_visits(days=4) - self.assertEqual(self.visit_template_base.visit_ids_count, 5) + self.assertEqual(self.visit_template_base.visit_ids_count, 4) self.assertEqual( len( self.visit_template_base.visit_ids.filtered( @@ -113,7 +113,7 @@ def test_02_repeat_days_autovalidate(self): lambda a: a.calendar_event_id.id ) ), - 5, + 4, ) self.assertEqual(self.visit_template_base.state, "in-progress") self.visit_template_base.create_visits(days=9) @@ -149,7 +149,7 @@ def test_03_change_visit_date(self): } ) visit_template.create_visits(days=10) - visit_0 = visit_template.visit_ids[0] + visit_0 = fields.first(visit_template.visit_ids) event_id_0 = visit_0.calendar_event_id self.assertEqual(visit_0.date, event_id_0.start_date) visit_0.write({"date": fields.Date.today() + timedelta(days=7)}) @@ -174,12 +174,12 @@ def test_04_cancel_visit(self): } ) visit_template.create_visits(days=10) - first_visit = visit_template.visit_ids[0] - self.assertNotEqual(first_visit.calendar_event_id.id, False) + first_visit = fields.first(visit_template.visit_ids) + self.assertTrue(first_visit.calendar_event_id) with self.assertRaises(exceptions.ValidationError): first_visit.unlink() self.assertEqual(len(visit_template.visit_ids), 10) first_visit.action_cancel(self.close_reason) - self.assertEqual(first_visit.calendar_event_id.id, False) + self.assertFalse(first_visit.calendar_event_id) first_visit.unlink() self.assertEqual(len(visit_template.visit_ids), 9) diff --git a/crm_salesperson_planner/views/crm_salesperson_planner_visit_close_reason_views.xml b/crm_salesperson_planner/views/crm_salesperson_planner_visit_close_reason_views.xml index 1af2f9501db..b53c52b714c 100644 --- a/crm_salesperson_planner/views/crm_salesperson_planner_visit_close_reason_views.xml +++ b/crm_salesperson_planner/views/crm_salesperson_planner_visit_close_reason_views.xml @@ -9,7 +9,7 @@ CRM - Salesperson Planner Visit Close Reason Tree crm.salesperson.planner.visit.close.reason - + @@ -48,7 +48,7 @@ model="ir.actions.act_window" id="crm_salesperson_planner_visit_close_reason_action" > - Close Reason + Close Reasons crm.salesperson.planner.visit.close.reason tree,form diff --git a/crm_salesperson_planner/views/crm_salesperson_planner_visit_template_views.xml b/crm_salesperson_planner/views/crm_salesperson_planner_visit_template_views.xml index e86529235ae..d7a26b1d4e6 100644 --- a/crm_salesperson_planner/views/crm_salesperson_planner_visit_template_views.xml +++ b/crm_salesperson_planner/views/crm_salesperson_planner_visit_template_views.xml @@ -7,7 +7,7 @@ CRM - Salesperson Planner Visit Template Tree crm.salesperson.planner.visit.template - + @@ -82,7 +82,7 @@ - + - - - - - - - + + + + + + + crm.salesperson.planner.visit Date: Wed, 30 Nov 2022 14:13:37 +0100 Subject: [PATCH 4/7] [IMP] crm_salesperson_planner: Improve tests to allow inheritance TT38993 --- .../i18n/crm_salesperson_planner.pot | 2 +- crm_salesperson_planner/i18n/es.po | 213 +++++++++++++----- .../test_crm_salesperson_planner_visit.py | 4 +- 3 files changed, 155 insertions(+), 64 deletions(-) diff --git a/crm_salesperson_planner/i18n/crm_salesperson_planner.pot b/crm_salesperson_planner/i18n/crm_salesperson_planner.pot index 7b198963938..b11a76b9784 100644 --- a/crm_salesperson_planner/i18n/crm_salesperson_planner.pot +++ b/crm_salesperson_planner/i18n/crm_salesperson_planner.pot @@ -406,7 +406,7 @@ msgstr "" #: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__show_as msgid "" "If the time is shown as 'busy', this event will be visible to other people with either the full information or simply 'busy' written depending on its privacy. Use this option to let other people know that you are unavailable during that period of time. \n" -" If the time is shown as 'free', this event won't be visible to other people at all. Use this option to let other people know that you are available during that period of time." +" If the event is shown as 'free', other users know that you are available during that period of time." msgstr "" #. module: crm_salesperson_planner diff --git a/crm_salesperson_planner/i18n/es.po b/crm_salesperson_planner/i18n/es.po index 65062585b5d..f4785d9ef28 100644 --- a/crm_salesperson_planner/i18n/es.po +++ b/crm_salesperson_planner/i18n/es.po @@ -43,6 +43,11 @@ msgstr "Decoración de Actividad de Excepción" msgid "Activity State" msgstr "Estado de la Actividad" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + #. module: crm_salesperson_planner #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view msgid "Add a description..." @@ -69,11 +74,6 @@ msgstr "Permitir reprogramación" msgid "Attachment Count" msgstr "Número de Adjuntos" -#. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__is_attendee -msgid "Attendee" -msgstr "Asistente" - #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__attendee_status msgid "Attendee Status" @@ -86,8 +86,8 @@ msgstr "Auto validar" #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__byday -msgid "By day" -msgstr "Por día" +msgid "Byday" +msgstr "" #. module: crm_salesperson_planner #: model:ir.actions.server,name:crm_salesperson_planner.ir_cron_create_visits_ir_actions_server @@ -117,13 +117,27 @@ msgstr "Cancelar" msgid "Cancelled" msgstr "Cancelado" +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__is_organizer_alone +msgid "" +"Check if the organizer is alone in the event, i.e. if the organizer is the " +"only one that hasn't declined\n" +" the event (only if the organizer is not the only attendee)" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrence_update +msgid "" +"Choose what to do with other events in the recurrence. Updating All Events " +"is not allowed when dates or time is modified" +msgstr "" + #. module: crm_salesperson_planner #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_form_view msgid "Close Info" msgstr "Información de cierre" #. module: crm_salesperson_planner -#: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_action #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__close_reason_id #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_form_view #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_wiz_view_form @@ -141,9 +155,9 @@ msgid "Close Reason Notes" msgstr "Notas de motivo de cierre" #. module: crm_salesperson_planner +#: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_action #: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_close_wiz_action #: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_config_salesperson_planner_close_reason -#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_close_reason_tree_view msgid "Close Reasons" msgstr "Motivos de cierre" @@ -211,7 +225,6 @@ msgstr "Cliente" #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__date -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__display_start msgid "Date" msgstr "Fecha" @@ -238,6 +251,11 @@ msgstr "Día del mes" msgid "Description" msgstr "Descripción" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__display_description +msgid "Display Description" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__display_name #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__display_name @@ -285,38 +303,30 @@ msgstr "Duración" msgid "End Date" msgstr "Fecha de finalización" -#. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__stop_datetime -msgid "End Datetime" -msgstr "Fecha y hora de finalización" - #. module: crm_salesperson_planner #: code:addons/crm_salesperson_planner/models/calendar_event.py:0 #, python-format msgid "" -"Event %s is related to salesperson visit %s. Cancel it to delete this " -"event.\n" +"Event %(event_name)s is related to salesperson visit %(partner_name)s. " +"Cancel it to delete this event.\n" msgstr "" -"El evento %s está relacionado con la visita comercial %s. Cancelela para " -"eliminar este evento.\n" #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__display_time msgid "Event Time" msgstr "Hora del evento" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__follow_recurrence +msgid "Follow Recurrence" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_follower_ids #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_follower_ids msgid "Followers" msgstr "Seguidores" -#. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_channel_ids -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_channel_ids -msgid "Followers (Channels)" -msgstr "Seguidores (Canales)" - #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_partner_ids #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_partner_ids @@ -324,7 +334,12 @@ msgid "Followers (Partners)" msgstr "Seguidores (Socios)" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__fr +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__fri msgid "Fri" msgstr "Viernes" @@ -343,6 +358,12 @@ msgstr "Motivo de cierre" msgid "Group By" msgstr "Agrupar por" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__has_message +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__has_message +msgid "Has Message" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__id #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason__id @@ -387,6 +408,17 @@ msgstr "" "Si el campo activo es falso, le permitirá ocultar la notificación de aviso " "del evento sin eliminarlo." +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__show_as +msgid "" +"If the time is shown as 'busy', this event will be visible to other people " +"with either the full information or simply 'busy' written depending " +"on its privacy. Use this option to let other people know that you " +"are unavailable during that period of time. \n" +" If the event is shown as 'free', other users know that you are " +"available during that period of time." +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_wiz__image msgid "Image" @@ -420,6 +452,11 @@ msgstr "És seguidor" msgid "Is the Event Highlighted" msgstr "Es el Evento Resaltado" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__is_organizer_alone +msgid "Is the Organizer Alone" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit____last_update #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_close_reason____last_update @@ -488,6 +525,11 @@ msgstr "Adjunto principal" msgid "Manually Create Visits" msgstr "Creación manual de visitas" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__videocall_location +msgid "Meeting URL" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_has_error #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_has_error @@ -506,10 +548,15 @@ msgid "Mobile" msgstr "Móvil" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__mo +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__mon msgid "Mon" msgstr "Lunes" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_salesperson_planner_my_visits #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter @@ -521,6 +568,11 @@ msgstr "Mis visitas" msgid "New Date" msgstr "Nueva fecha" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_calendar_event_id +msgid "Next Activity Calendar Event" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_date_deadline msgid "Next Activity Deadline" @@ -541,6 +593,11 @@ msgstr "Tipo de la siguiente actividad" msgid "Notes" msgstr "Notas" +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__alarm_ids +msgid "Notifications sent to all attendees to remind of the meeting." +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__message_needaction_counter #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__message_needaction_counter @@ -619,6 +676,11 @@ msgstr "Cliente" msgid "Partner-related data of the user" msgstr "Datos del usuario relativos a la empresa" +#. module: crm_salesperson_planner +#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__privacy +msgid "People to whom this event will be visible." +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__partner_phone msgid "Phone" @@ -660,31 +722,26 @@ msgstr "Registrar el motivo de cierre de las visitas comerciales." msgid "Recurrence" msgstr "Recurrencia" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrence_id +msgid "Recurrence Rule" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__end_type msgid "Recurrence Termination" msgstr "Finalizar recurrencia" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrence_update +msgid "Recurrence Update" +msgstr "" + #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrency msgid "Recurrent" msgstr "Recurrente" -#. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrent_id -msgid "Recurrent ID" -msgstr "ID recurrencia" - -#. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrent_id_date -msgid "Recurrent ID date" -msgstr "Fecha ID recurrencia" - -#. module: crm_salesperson_planner -#: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__recurrency -msgid "Recurrent Meeting" -msgstr "Reunión periódica" - #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__rrule msgid "Recurrent Rule" @@ -732,11 +789,6 @@ msgstr "Requiere una imagen" msgid "Reschedule" msgstr "Reprogramar" -#. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__partner_id -msgid "Responsible" -msgstr "Responsable" - #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit__activity_user_id msgid "Responsible User" @@ -785,10 +837,15 @@ msgid "Salesperson Visits" msgstr "Visitas comerciales" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__sa +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__sat msgid "Sat" msgstr "Sabado" +#. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__partner_id +msgid "Scheduled by" +msgstr "" + #. module: crm_salesperson_planner #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_filter msgid "Search Visits" @@ -808,8 +865,8 @@ msgstr "Secuencia" #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__show_as -msgid "Show Time as" -msgstr "Mostrar hora como" +msgid "Show as" +msgstr "" #. module: crm_salesperson_planner #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start @@ -821,11 +878,6 @@ msgstr "Iniciar" msgid "Start Date" msgstr "Fecha de inicio" -#. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start_datetime -msgid "Start DateTime" -msgstr "Fecha y hora de inicio" - #. module: crm_salesperson_planner #: model:ir.model.fields,help:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__start msgid "Start date of an event, without time for full days events" @@ -871,7 +923,7 @@ msgid "Submit" msgstr "Enviar" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__su +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__sun msgid "Sun" msgstr "Domingo" @@ -932,7 +984,7 @@ msgid "The visit template number must be unique!" msgstr "¡El número de plantilla de visita debe ser único!" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__th +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__thu msgid "Thu" msgstr "Jueves" @@ -947,7 +999,7 @@ msgid "Today Visits" msgstr "Visitas de hoy" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__tu +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__tue msgid "Tue" msgstr "Martes" @@ -974,6 +1026,7 @@ msgid "Unread Messages Counter" msgstr "Nº de mensajes sin leer" #. module: crm_salesperson_planner +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__until #: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_form_view msgid "Until" msgstr "Hasta" @@ -1020,7 +1073,6 @@ msgstr "Número de plantilla de visita" #. module: crm_salesperson_planner #: model:ir.actions.act_window,name:crm_salesperson_planner.crm_salesperson_planner_visit_template_action #: model:ir.ui.menu,name:crm_salesperson_planner.menu_crm_salesperson_planner_visit_template -#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_template_tree_view msgid "Visit Templates" msgstr "Plantillas de visitas" @@ -1038,7 +1090,6 @@ msgstr "Visitado" #: model:ir.actions.act_window,name:crm_salesperson_planner.all_crm_salesperson_planner_visit_action #: model:ir.actions.act_window,name:crm_salesperson_planner.my_crm_salesperson_planner_visit_action #: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_lead__crm_salesperson_planner_visit_ids -#: model_terms:ir.ui.view,arch_db:crm_salesperson_planner.crm_salesperson_planner_visit_tree_view msgid "Visits" msgstr "Visitas" @@ -1066,12 +1117,12 @@ msgid "Website communication history" msgstr "Historial de comunicación del sitio web" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__we +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__wed msgid "Wed" msgstr "Miercoles" #. module: crm_salesperson_planner -#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__week_list +#: model:ir.model.fields,field_description:crm_salesperson_planner.field_crm_salesperson_planner_visit_template__weekday msgid "Weekday" msgstr "Día de la semana" @@ -1079,3 +1130,41 @@ msgstr "Día de la semana" #: model:ir.model,name:crm_salesperson_planner.model_crm_salesperson_planner_visit_template_create msgid "crm salesperson planner visit template create" msgstr "crm salesperson planner visit template create" + +#~ msgid "Attendee" +#~ msgstr "Asistente" + +#~ msgid "By day" +#~ msgstr "Por día" + +#~ msgid "End Datetime" +#~ msgstr "Fecha y hora de finalización" + +#, python-format +#~ msgid "" +#~ "Event %s is related to salesperson visit %s. Cancel it to delete this " +#~ "event.\n" +#~ msgstr "" +#~ "El evento %s está relacionado con la visita comercial %s. Cancelela para " +#~ "eliminar este evento.\n" + +#~ msgid "Followers (Channels)" +#~ msgstr "Seguidores (Canales)" + +#~ msgid "Recurrent ID" +#~ msgstr "ID recurrencia" + +#~ msgid "Recurrent ID date" +#~ msgstr "Fecha ID recurrencia" + +#~ msgid "Recurrent Meeting" +#~ msgstr "Reunión periódica" + +#~ msgid "Responsible" +#~ msgstr "Responsable" + +#~ msgid "Show Time as" +#~ msgstr "Mostrar hora como" + +#~ msgid "Start DateTime" +#~ msgstr "Fecha y hora de inicio" diff --git a/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit.py b/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit.py index f87b93258a5..e252429a1cc 100644 --- a/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit.py +++ b/crm_salesperson_planner/tests/test_crm_salesperson_planner_visit.py @@ -7,7 +7,7 @@ from odoo.tests import common -class TestCrmSalespersonPlannerVisit(common.TransactionCase): +class TestCrmSalespersonPlannerVisitBase(common.TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() @@ -67,6 +67,8 @@ def setUpClass(cls): } ) + +class TestCrmSalespersonPlannerVisit(TestCrmSalespersonPlannerVisitBase): def test_crm_salesperson_planner_visit(self): self.assertNotEqual(self.visit1.name, "/") self.assertEqual(self.visit1.state, "draft") From 2e88f928e20557d5e11323197dd477e3308c68ee Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Wed, 19 Apr 2023 18:47:44 +0200 Subject: [PATCH 5/7] [OU-FIX] crm_salesperson_planner: Fix migration scripts - Missing column renaming for not collapsing - Missing weekday conversion --- .../migrations/15.0.1.0.0/pre-migration.py | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/crm_salesperson_planner/migrations/15.0.1.0.0/pre-migration.py b/crm_salesperson_planner/migrations/15.0.1.0.0/pre-migration.py index af35398900a..9d99683e282 100644 --- a/crm_salesperson_planner/migrations/15.0.1.0.0/pre-migration.py +++ b/crm_salesperson_planner/migrations/15.0.1.0.0/pre-migration.py @@ -1,12 +1,15 @@ from openupgradelib import openupgrade +column_spec = { + "crm_salesperson_planner_visit_template": [ + ("start", None), + ("start_datetime", "start"), + ("stop", None), + ("stop_datetime", "stop"), + ] +} + field_spec = [ - ( - "crm.salesperson.planner.visit.template", - "crm_salesperson_planner_visit_template", - "start_datetime", - "start", - ), ( "crm.salesperson.planner.visit.template", "crm_salesperson_planner_visit_template", @@ -60,4 +63,19 @@ @openupgrade.migrate() def migrate(env, version): + openupgrade.rename_columns(env.cr, column_spec) openupgrade.rename_fields(env, field_spec, False) + openupgrade.logged_query( + env.cr, + """ + UPDATE crm_salesperson_planner_visit_template + SET weekday = CASE WHEN weekday = 'FR' THEN 'FRI' + WHEN weekday = 'MO' THEN 'MON' + WHEN weekday = 'SA' THEN 'SAT' + WHEN weekday = 'SU' THEN 'SUN' + WHEN weekday = 'TH' THEN 'THU' + WHEN weekday = 'TU' THEN 'TUE' + WHEN weekday = 'WE' THEN 'WED' + END + WHERE weekday IN ('FR', 'MO', 'SA', 'SU', 'TH', 'TU', 'WE')""", + ) From e3685cf8b5d1f7cfd575f36e2ed076b7a5749201 Mon Sep 17 00:00:00 2001 From: Gerardo Marin Parra Date: Wed, 24 May 2023 08:30:51 -0500 Subject: [PATCH 6/7] [IMP] crm_salesperson_planner: black, isort, prettier --- .../odoo/addons/crm_salesperson_planner | 1 + setup/crm_salesperson_planner/setup.py | 6 ++++++ 2 files changed, 7 insertions(+) create mode 120000 setup/crm_salesperson_planner/odoo/addons/crm_salesperson_planner create mode 100644 setup/crm_salesperson_planner/setup.py diff --git a/setup/crm_salesperson_planner/odoo/addons/crm_salesperson_planner b/setup/crm_salesperson_planner/odoo/addons/crm_salesperson_planner new file mode 120000 index 00000000000..8f9b77d069f --- /dev/null +++ b/setup/crm_salesperson_planner/odoo/addons/crm_salesperson_planner @@ -0,0 +1 @@ +../../../../crm_salesperson_planner \ No newline at end of file diff --git a/setup/crm_salesperson_planner/setup.py b/setup/crm_salesperson_planner/setup.py new file mode 100644 index 00000000000..28c57bb6403 --- /dev/null +++ b/setup/crm_salesperson_planner/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 12f57077d2a3fd2d8248983a68452f13e9b8d924 Mon Sep 17 00:00:00 2001 From: Gerardo Marin Parra Date: Wed, 24 May 2023 12:28:39 -0500 Subject: [PATCH 7/7] [MIG] crm_salesperson_planner: Migration to 16.0 --- crm_salesperson_planner/README.rst | 27 ++- crm_salesperson_planner/__manifest__.py | 2 +- .../migrations/15.0.1.0.0/pre-migration.py | 81 -------- .../crm_salesperson_planner_visit_template.py | 6 +- .../readme/CONTRIBUTORS.rst | 4 + .../static/description/index.html | 16 +- .../test_crm_salesperson_planner_visit.py | 193 +++++++++++++++++- 7 files changed, 229 insertions(+), 100 deletions(-) delete mode 100644 crm_salesperson_planner/migrations/15.0.1.0.0/pre-migration.py diff --git a/crm_salesperson_planner/README.rst b/crm_salesperson_planner/README.rst index 0c053f17de1..8c4abe6e74e 100644 --- a/crm_salesperson_planner/README.rst +++ b/crm_salesperson_planner/README.rst @@ -2,10 +2,13 @@ Crm Salesperson Planner ======================= -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:ece9cce90a55feea9d5e85078846e339a4c3fe95400e5a7e218f7624b81201c0 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -14,16 +17,16 @@ Crm Salesperson Planner :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fcrm-lightgray.png?logo=github - :target: https://github.com/OCA/crm/tree/15.0/crm_salesperson_planner + :target: https://github.com/OCA/crm/tree/16.0/crm_salesperson_planner :alt: OCA/crm .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/crm-15-0/crm-15-0-crm_salesperson_planner + :target: https://translation.odoo-community.org/projects/crm-16-0/crm-16-0-crm_salesperson_planner :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/111/15.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/crm&target_branch=16.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| This application allows you to track and schedule salespeople visits to your customers, allowing you to determine which opportunities are going to be dealt on each visit. Visits create an all day event in calendar, and they can be easily rescheduled. Visits can be automatically created from a template, in which it is possible to select the frequency of visits, as well as the start and end dates. The last visit can also be calculated by selecting the total number of repetitions. @@ -56,8 +59,8 @@ 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 smashing it by providing a detailed and welcomed -`feedback `_. +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. @@ -77,6 +80,10 @@ Contributors * Valentin Vinagre * Manuel Regidor +* `Pesol `__: + + * Gerardo Marin Parra + Maintainers ~~~~~~~~~~~ @@ -90,6 +97,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/crm `_ project on GitHub. +This module is part of the `OCA/crm `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/crm_salesperson_planner/__manifest__.py b/crm_salesperson_planner/__manifest__.py index cd935d97f43..059acecb390 100644 --- a/crm_salesperson_planner/__manifest__.py +++ b/crm_salesperson_planner/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) { "name": "Crm Salesperson Planner", - "version": "15.0.1.0.0", + "version": "16.0.1.0.0", "development_status": "Beta", "category": "Customer Relationship Management", "author": "Sygel Technology," "Odoo Community Association (OCA)", diff --git a/crm_salesperson_planner/migrations/15.0.1.0.0/pre-migration.py b/crm_salesperson_planner/migrations/15.0.1.0.0/pre-migration.py deleted file mode 100644 index 9d99683e282..00000000000 --- a/crm_salesperson_planner/migrations/15.0.1.0.0/pre-migration.py +++ /dev/null @@ -1,81 +0,0 @@ -from openupgradelib import openupgrade - -column_spec = { - "crm_salesperson_planner_visit_template": [ - ("start", None), - ("start_datetime", "start"), - ("stop", None), - ("stop_datetime", "stop"), - ] -} - -field_spec = [ - ( - "crm.salesperson.planner.visit.template", - "crm_salesperson_planner_visit_template", - "week_list", - "weekday", - ), - ( - "crm.salesperson.planner.visit.template", - "crm_salesperson_planner_visit_template", - "mo", - "mon", - ), - ( - "crm.salesperson.planner.visit.template", - "crm_salesperson_planner_visit_template", - "tu", - "tue", - ), - ( - "crm.salesperson.planner.visit.template", - "crm_salesperson_planner_visit_template", - "we", - "wed", - ), - ( - "crm.salesperson.planner.visit.template", - "crm_salesperson_planner_visit_template", - "th", - "thu", - ), - ( - "crm.salesperson.planner.visit.template", - "crm_salesperson_planner_visit_template", - "fr", - "fri", - ), - ( - "crm.salesperson.planner.visit.template", - "crm_salesperson_planner_visit_template", - "sa", - "sat", - ), - ( - "crm.salesperson.planner.visit.template", - "crm_salesperson_planner_visit_template", - "su", - "sun", - ), -] - - -@openupgrade.migrate() -def migrate(env, version): - openupgrade.rename_columns(env.cr, column_spec) - openupgrade.rename_fields(env, field_spec, False) - openupgrade.logged_query( - env.cr, - """ - UPDATE crm_salesperson_planner_visit_template - SET weekday = CASE WHEN weekday = 'FR' THEN 'FRI' - WHEN weekday = 'MO' THEN 'MON' - WHEN weekday = 'SA' THEN 'SAT' - WHEN weekday = 'SU' THEN 'SUN' - WHEN weekday = 'TH' THEN 'THU' - WHEN weekday = 'TU' THEN 'TUE' - WHEN weekday = 'WE' THEN 'WED' - END - WHERE weekday IN ('FR', 'MO', 'SA', 'SU', 'TH', 'TU', 'WE')""", - ) diff --git a/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py b/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py index 1c86af5c53b..e7a71783bfe 100644 --- a/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py +++ b/crm_salesperson_planner/models/crm_salesperson_planner_visit_template.py @@ -4,6 +4,8 @@ from datetime import timedelta +from dateutil.relativedelta import relativedelta + from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -169,9 +171,9 @@ def _increase_date(self, date, value): elif self.rrule_type == "weekly": date += timedelta(weeks=value) elif self.rrule_type == "monthly": - date += timedelta(months=value) + date += relativedelta(months=value) elif self.rrule_type == "yearly": - date += timedelta(years=value) + date += relativedelta(years=value) return date def _get_recurrence_dates(self, items): diff --git a/crm_salesperson_planner/readme/CONTRIBUTORS.rst b/crm_salesperson_planner/readme/CONTRIBUTORS.rst index 35f658858eb..790c98428d1 100644 --- a/crm_salesperson_planner/readme/CONTRIBUTORS.rst +++ b/crm_salesperson_planner/readme/CONTRIBUTORS.rst @@ -2,3 +2,7 @@ * Valentin Vinagre * Manuel Regidor + +* `Pesol `__: + + * Gerardo Marin Parra diff --git a/crm_salesperson_planner/static/description/index.html b/crm_salesperson_planner/static/description/index.html index f36f851e956..d7dd93d6c32 100644 --- a/crm_salesperson_planner/static/description/index.html +++ b/crm_salesperson_planner/static/description/index.html @@ -3,7 +3,7 @@ - + Crm Salesperson Planner