From d50e096ce01fc71e2af7e477ce6256359ced1f65 Mon Sep 17 00:00:00 2001 From: David Vidal Date: Mon, 17 Apr 2017 18:55:00 +0200 Subject: [PATCH 01/14] [ADD] event_session: New module --- event_session/README.rst | 45 ++ event_session/__init__.py | 6 + event_session/__manifest__.py | 23 + event_session/i18n/es.po | 521 ++++++++++++++++++ event_session/models/__init__.py | 5 + event_session/models/event.py | 50 ++ event_session/models/event_mail.py | 75 +++ event_session/models/event_session.py | 195 +++++++ event_session/reports/__init__.py | 3 + .../reports/report_event_registration.py | 40 ++ .../report_event_registration_view.xml | 31 ++ event_session/security/ir.model.access.csv | 3 + event_session/tests/__init__.py | 3 + event_session/tests/test_session.py | 165 ++++++ event_session/views/event_session_view.xml | 142 +++++ event_session/views/event_view.xml | 59 ++ event_session/wizards/__init__.py | 3 + event_session/wizards/wizard_event_session.py | 253 +++++++++ .../wizards/wizard_event_session_view.xml | 73 +++ 19 files changed, 1695 insertions(+) create mode 100644 event_session/README.rst create mode 100644 event_session/__init__.py create mode 100644 event_session/__manifest__.py create mode 100644 event_session/i18n/es.po create mode 100644 event_session/models/__init__.py create mode 100644 event_session/models/event.py create mode 100644 event_session/models/event_mail.py create mode 100644 event_session/models/event_session.py create mode 100644 event_session/reports/__init__.py create mode 100644 event_session/reports/report_event_registration.py create mode 100644 event_session/reports/report_event_registration_view.xml create mode 100644 event_session/security/ir.model.access.csv create mode 100644 event_session/tests/__init__.py create mode 100644 event_session/tests/test_session.py create mode 100644 event_session/views/event_session_view.xml create mode 100644 event_session/views/event_view.xml create mode 100644 event_session/wizards/__init__.py create mode 100644 event_session/wizards/wizard_event_session.py create mode 100644 event_session/wizards/wizard_event_session_view.xml diff --git a/event_session/README.rst b/event_session/README.rst new file mode 100644 index 000000000..46dde9416 --- /dev/null +++ b/event_session/README.rst @@ -0,0 +1,45 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + + +Create and assign sessions to events +==================================== + +This module allows to create sessions associated with events. + +Usage +===== + +You can either: + +* Go to Events > Sessions and create some sessions associated with an event. +* Go to an event and use the sessions wizard to create all your event sessions according to a given schedule. + +Known issues / Roadmap +====================== + +* TODO: Seats and registrations management +* TODO: Improve hours constraints + +Credits +======= + +Contributors +------------ + +* Sergio Teruel +* David Vidal + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. diff --git a/event_session/__init__.py b/event_session/__init__.py new file mode 100644 index 000000000..c4e9a8c2f --- /dev/null +++ b/event_session/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- + +from . import models +from . import wizards +from . import reports +from . import tests diff --git a/event_session/__manifest__.py b/event_session/__manifest__.py new file mode 100644 index 000000000..b8431e40c --- /dev/null +++ b/event_session/__manifest__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Event Sessions', + 'version': '10.0.1.0.0', + 'author': 'Tecnativa, ' + 'Odoo Community Association (OCA)', + "license": "AGPL-3", + 'website': 'https://odoo-community.org/', + 'category': 'Marketing', + 'summary': 'Sessions in events', + 'depends': ['event', 'event_mail'], + 'data': [ + 'security/ir.model.access.csv', + 'views/event_session_view.xml', + 'views/event_view.xml', + 'wizards/wizard_event_session_view.xml', + 'reports/report_event_registration_view.xml', + ], + 'installable': True, +} diff --git a/event_session/i18n/es.po b/event_session/i18n/es.po new file mode 100644 index 000000000..8e3f890f0 --- /dev/null +++ b/event_session/i18n/es.po @@ -0,0 +1,521 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * event_session +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-04-28 15:18+0000\n" +"PO-Revision-Date: 2017-04-28 17:40+0200\n" +"Last-Translator: David \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Language: es\n" +"X-Generator: Poedit 1.8.7.1\n" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_active +msgid "Active" +msgstr "Activo" + +#. module: event_session +#: model:ir.model,name:event_session.model_event_registration +msgid "Attendee" +msgstr "Asistente" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_registration_ids +#: model:ir.ui.view,arch_db:event_session.view_event_session_form +msgid "Attendees" +msgstr "Asistentes" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.view_event_session_form +msgid "Attendees on this session" +msgstr "Asistentes a esta sesión" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_seats_available +msgid "Available Seats" +msgstr "Plazas disponibles en sesión" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.generator_view_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_wizard_event_session_delete_existing_sessions +msgid "Check in order to delete every previous session for this event" +msgstr "Seleccionar para borrar todas las sesiones existentes en este evento." + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_wizard_event_session_fridays +msgid "Create sessions on Fridays" +msgstr "Crear sesiones los Viernes" + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_wizard_event_session_mondays +msgid "Create sessions on Mondays" +msgstr "Crear sesiones los Lunes" + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_wizard_event_session_saturdays +msgid "Create sessions on Saturdays" +msgstr "Crear sesiones los Sábados" + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_wizard_event_session_sundays +msgid "Create sessions on Sundays" +msgstr "Crear sesiones los Domingos" + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_wizard_event_session_thursdays +msgid "Create sessions on Thursdays" +msgstr "Create sessions on Jueves" + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_wizard_event_session_tuesdays +msgid "Create sessions on Tuesdays" +msgstr "Create sessions on Martes" + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_wizard_event_session_wednesdays +msgid "Create sessions on Wednesdays" +msgstr "Create sessions on Miércoles" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_create_uid +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_create_uid +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_hours_create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_create_date +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_create_date +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_hours_create_date +msgid "Created on" +msgstr "Creado el" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.generator_view_form +msgid "Dates and event data" +msgstr "Datos de evento y fechas" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_delete_existing_sessions +msgid "Delete existing sessions" +msgstr "Borrar sesiones existentes" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_display_name +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_display_name +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_hours_display_name +msgid "Display Name" +msgstr "Nombre a mostrar" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.view_event_session_form +msgid "Email Schedule" +msgstr "Programación de Correo Electrónico" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_event_date_end +msgid "End Date" +msgstr "Fecha finalización" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_end_time +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_hours_end_time +msgid "End time" +msgstr "Hora de fin" + +#. module: event_session +#: code:addons/event_session/models/event_session.py:184 +#, python-format +msgid "Ending and starting time can't be the same!" +msgstr "La hora de comienzo y de fin no puede ser la misma" + +#. module: event_session +#: model:ir.model,name:event_session.model_event_event +#: model:ir.model.fields,field_description:event_session.field_event_session_event_id +#: model:ir.ui.view,arch_db:event_session.view_session_search +msgid "Event" +msgstr "Evento" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.view_event_session_form +msgid "Event Session" +msgstr "Sesión de evento" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.view_session_search +msgid "Event Sesssion" +msgstr "Sesión de evento" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_event_id +msgid "Event id" +msgstr "Evento" + +#. module: event_session +#: model:ir.model,name:event_session.model_event_session +msgid "Event session" +msgstr "Sessión de evento" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_fridays +msgid "Fridays" +msgstr "Viernes" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_seats_available_pc +msgid "Full %" +msgstr "% de aforo" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.session_view_event_form +msgid "Generate Sessions" +msgstr "Generar sesiones" + +#. module: event_session +#: model:ir.actions.act_window,name:event_session.act_wizard_event_session +msgid "Generate Sessions Wizard" +msgstr "Asistente para generar sesiones" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.generator_view_form +msgid "Generate sessions" +msgstr "Generar sesiones" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.view_session_search +msgid "Group By" +msgstr "Agrupar por" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_session_hour_ids +#: model:ir.ui.view,arch_db:event_session.generator_view_form +msgid "Hours" +msgstr "Horas" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_id +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_hours_id +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_id +msgid "ID" +msgstr "ID" + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_wizard_event_session_name +msgid "It will be generated according to given parameters" +msgstr "Se generarán de acuerdo con los parámetros dados" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session___last_update +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session___last_update +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_hours___last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_write_uid +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_hours_write_uid +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_write_date +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_hours_write_date +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: event_session +#: selection:event.session,seats_availability:0 +msgid "Limited" +msgstr "Limitados" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_event_mail_ids +msgid "Mail Schedule" +msgstr "Programación de correo" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_event_mail_template_id +msgid "Mail Template" +msgstr "Plantilla de programación de correo" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_seats_availability +msgid "Maximum Attendees" +msgstr "Asistentes máximos" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_seats_max +msgid "Maximum seats" +msgstr "Plazas máximas de sesión" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_seats_min +msgid "Minimum seats" +msgstr "Plazas mínimas" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_mondays +msgid "Mondays" +msgstr "Lunes" + +#. module: event_session +#: code:addons/event_session/models/event_session.py:166 +#, python-format +msgid "No more available seats for this session." +msgstr "No hay más plazas disponibles para esta sesión." + +#. module: event_session +#: code:addons/event_session/models/event.py:50 +#, python-format +msgid "No more seats available for this event." +msgstr "No hay más plazas disponibles para esta sesión." + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_seats_expected +msgid "Number of Expected Attendees" +msgstr "Número previsto de asistentes" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_seats_used +msgid "Number of Participants" +msgstr "Número de asistentes" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.view_event_session_form +msgid "Origin" +msgstr "Origen" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.generator_view_form +msgid "Other options" +msgstr "Otras opciones" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.view_event_session_form +msgid "Partner" +msgstr "Empresa" + +#. module: event_session +#: model:ir.model,name:event_session.model_event_mail_registration +msgid "Registration Mail Scheduler" +msgstr "Registro de Programador de Correo Electrónico" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.view_event_session_form +msgid "Registrations" +msgstr "Registros" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_seats_reserved +msgid "Reserved Seats" +msgstr "Plazas reservadas" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_saturdays +msgid "Saturdays" +msgstr "Sábados" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.generator_view_form +msgid "Schedule" +msgstr "Programar" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_mail_scheduler_template_session_id_4282 +#: model:ir.model.fields,field_description:event_session.field_event_mail_session_id +#: model:ir.model.fields,field_description:event_session.field_event_registration_session_id +#: model:ir.model.fields,field_description:event_session.field_event_session_name +#: model:ir.model.fields,field_description:event_session.field_report_event_registration_session_id +#: model:ir.ui.view,arch_db:event_session.view_event_session_calendar +#: model:ir.ui.view,arch_db:event_session.view_report_event_registration_search +#: model:ir.ui.view,arch_db:event_session.view_session_search +msgid "Session" +msgstr "Sesión" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_date +msgid "Session date" +msgstr "Fecha de sesión" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_date_end +msgid "Session date end" +msgstr "Fecha de fin de sesión" + +#. module: event_session +#: code:addons/event_session/models/event_session.py:175 +#, python-format +msgid "Session date is out of this event dates range" +msgstr "La fecha de la sesión está fuera del rango de fechas de este evento" + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_event_session_end_time +msgid "Session end time" +msgstr "Fecha de fin" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_name +msgid "Session info" +msgstr "Información de la sesión" + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_event_session_start_time +msgid "Session start time" +msgstr "Hora de comienzo de la sesión" + +#. module: event_session +#: model:ir.actions.act_window,name:event_session.act_event_session_event_form +#: model:ir.actions.act_window,name:event_session.act_event_session_form +#: model:ir.model.fields,field_description:event_session.field_event_event_session_ids +#: model:ir.ui.menu,name:event_session.event_session_menu +#: model:ir.ui.view,arch_db:event_session.view_event_form +#: model:ir.ui.view,arch_db:event_session.view_event_session_tree +msgid "Sessions" +msgstr "Sesiones" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.view_event_form +msgid "Sessions availables for this event" +msgstr "Sesiones disponibles para este evento" + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_wizard_event_session_event_date_begin +msgid "Set it up in the event configurationSessions will be generated from this date" +msgstr "Establécela en la configuración del evento. Se generarán sesiones a partir de esta fecha." + +#. module: event_session +#: model:ir.model.fields,help:event_session.field_wizard_event_session_event_date_end +#: model:ir.model.fields,help:event_session.field_wizard_event_session_event_date_tz +msgid "Set it up in the event configurationSessions will be generated up to this date" +msgstr "Establécela en la configuración del evento. Se generarán sesiones a hasta esta fecha." + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_event_date_begin +msgid "Start Date" +msgstr "Fecha de inicio" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_start_time +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_hours_start_time +msgid "Start time" +msgstr "Hora de incio" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.view_event_session_form +msgid "State" +msgstr "Estado" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_sundays +msgid "Sundays" +msgstr "Domingos" + +#. module: event_session +#: code:addons/event_session/wizards/wizard_event_session.py:86 +#: code:addons/event_session/wizards/wizard_event_session.py:91 +#, python-format +msgid "There are overlapping hours!" +msgstr "¡Hay horarios superpuestos!" + +#. module: event_session +#: code:addons/event_session/wizards/wizard_event_session.py:233 +#, python-format +msgid "There are sessions with no duration!" +msgstr "¡Hay horarios con duración nula!" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_thursdays +msgid "Thursdays" +msgstr "Jueves" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_event_date_tz +msgid "Timezone" +msgstr "Zona horaria" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_event_sessions_count +#: model:ir.model.fields,field_description:event_session.field_event_registration_event_sessions_count +msgid "Total event sessions" +msgstr "Sesiones de evento totales" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_tuesdays +msgid "Tuesdays" +msgstr "Martes" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_event_session_seats_unconfirmed +msgid "Unconfirmed Seat Reservations" +msgstr "Reservas de plazas no confirmadas" + +#. module: event_session +#: selection:event.session,seats_availability:0 +msgid "Unlimited" +msgstr "Ilimitados" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_wednesdays +msgid "Wednesdays" +msgstr "Miércoles" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.generator_view_form +msgid "Weekdays" +msgstr "Días de la semana" + +#. module: event_session +#: model:ir.model.fields,field_description:event_session.field_wizard_event_session_hours_wizard_event_session_id +msgid "Wizard event session id" +msgstr "Wizard event session id" + +#. module: event_session +#: code:addons/event_session/wizards/wizard_event_session.py:210 +#, python-format +msgid "You must select at least one weekday" +msgstr "Debes seleccionar al menos un día de la semana" + +#. module: event_session +#: code:addons/event_session/wizards/wizard_event_session.py:241 +#, python-format +msgid "You've entered invalid hours!" +msgstr "¡Las horas introducidas son erróneas!" + +#. module: event_session +#: model:ir.model,name:event_session.model_event_mail +msgid "event.mail" +msgstr "event.mail" + +#. module: event_session +#: model:ir.ui.view,arch_db:event_session.generator_view_form +msgid "or" +msgstr "o" + +#. module: event_session +#: model:ir.model,name:event_session.model_report_event_registration +msgid "report.event.registration" +msgstr "report.event.registration" + +#. module: event_session +#: model:ir.model,name:event_session.model_wizard_event_session +msgid "wizard.event.session" +msgstr "wizard.event.session" + +#. module: event_session +#: model:ir.model,name:event_session.model_wizard_event_session_hours +msgid "wizard.event.session.hours" +msgstr "wizard.event.session.hours" diff --git a/event_session/models/__init__.py b/event_session/models/__init__.py new file mode 100644 index 000000000..d01dffd0a --- /dev/null +++ b/event_session/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +from . import event_session +from . import event +from . import event_mail diff --git a/event_session/models/event.py b/event_session/models/event.py new file mode 100644 index 000000000..d8fcb83fc --- /dev/null +++ b/event_session/models/event.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class EventEvent(models.Model): + _inherit = 'event.event' + + session_ids = fields.One2many( + comodel_name='event.session', + inverse_name='event_id', + string='Sessions', + ) + sessions_count = fields.Integer( + compute='_compute_sessions_count', + string='Total event sessions', + ) + + @api.multi + def _compute_sessions_count(self): + for event in self: + event.sessions_count = len(event.session_ids) + + +class EventRegistration(models.Model): + _inherit = 'event.registration' + + event_sessions_count = fields.Integer( + related='event_id.sessions_count', + readonly=True, + ) + session_id = fields.Many2one( + comodel_name='event.session', + string='Session', + ondelete='set null', + ) + + @api.multi + @api.constrains('event_id', 'session_id', 'state') + def _check_seats_limit(self): + for registration in self: + if (registration.session_id.seats_availability == 'limited' and + self.session_id.seats_max and + self.session_id.seats_available < + (1 if self.state == 'draft' else 0)): + raise ValidationError( + _('No more seats available for this event.')) diff --git a/event_session/models/event_mail.py b/event_session/models/event_mail.py new file mode 100644 index 000000000..3364f9eda --- /dev/null +++ b/event_session/models/event_mail.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import datetime +from odoo import api, fields, models, tools +import logging + +_logger = logging.getLogger(__name__) + +try: + from odoo.addons.event.models.event_mail import _INTERVALS +except ImportError: + _logger.debug('Can not import events module.') + + +class EventMailScheduler(models.Model): + _inherit = 'event.mail' + + session_id = fields.Many2one( + comodel_name='event.session', + string='Session', + ondelete='cascade', + ) + + @api.multi + def _compute_done(self): + super(EventMailScheduler, self)._compute_done() + for event_mail in self: + if (event_mail.session_id and + event_mail.interval_type not in + ['before_event', 'after_event']): + event_mail.done = ( + True if event_mail.event_id.sessions_count > 0 and + not event_mail.session_id else + len(event_mail.mail_registration_ids) == len( + event_mail.session_id.registration_ids) and + all(line.mail_sent for line in + event_mail.mail_registration_ids) + ) + + @api.multi + def _compute_scheduled_date(self): + super(EventMailScheduler, self)._compute_scheduled_date() + for event_mail in self: + if event_mail.event_id.state in ['confirm', 'done'] and \ + event_mail.session_id: + if event_mail.interval_type == 'before_event': + date, sign = event_mail.session_id.date, -1 + else: + date, sign = event_mail.session_id.date_end, 1 + event_mail.scheduled_date = datetime.strptime( + date, tools.DEFAULT_SERVER_DATETIME_FORMAT) + _INTERVALS[ + event_mail.interval_unit](sign * event_mail.interval_nbr) + + +class EventMailRegistration(models.Model): + _inherit = 'event.mail.registration' + + @api.multi + @api.depends('registration_id', 'scheduler_id.interval_unit', + 'scheduler_id.interval_type') + def _compute_scheduled_date(self): + super(EventMailRegistration, self)._compute_scheduled_date() + for event_mail_reg in self: + if (event_mail_reg.registration_id and + event_mail_reg.registration_id.session_id): + date_open = event_mail_reg.registration_id.session_id.date + date_open_datetime = date_open and datetime.strptime( + date_open, tools.DEFAULT_SERVER_DATETIME_FORMAT + ) or fields.datetime.now() + event_mail_reg.scheduled_date = ( + date_open_datetime + + _INTERVALS[event_mail_reg.scheduler_id.interval_unit]( + event_mail_reg.scheduler_id.interval_nbr)) diff --git a/event_session/models/event_session.py b/event_session/models/event_session.py new file mode 100644 index 000000000..caff1c608 --- /dev/null +++ b/event_session/models/event_session.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class EventSession(models.Model): + _name = 'event.session' + _description = 'Event session' + + name = fields.Char( + string='Session', + required=True, + ) + active = fields.Boolean( + default=True, + ) + event_id = fields.Many2one( + comodel_name='event.event', + string='Event', + ) + seats_min = fields.Integer( + string='Minimum seats', + ) + seats_max = fields.Integer( + string="Maximum seats", + ) + seats_availability = fields.Selection( + [('limited', 'Limited'), ('unlimited', 'Unlimited')], + 'Maximum Attendees', required=True, default='unlimited', + ) + seats_reserved = fields.Integer( + string='Reserved Seats', store=True, readonly=True, + compute='_compute_seats', + ) + seats_available = fields.Integer( + oldname='register_avail', string='Available Seats', + store=True, readonly=True, compute='_compute_seats') + seats_unconfirmed = fields.Integer( + oldname='register_prospect', string='Unconfirmed Seat Reservations', + store=True, readonly=True, compute='_compute_seats') + seats_used = fields.Integer( + oldname='register_attended', string='Number of Participants', + store=True, readonly=True, compute='_compute_seats') + seats_expected = fields.Integer( + string='Number of Expected Attendees', + readonly=True, compute='_compute_seats') + date = fields.Datetime( + string="Session date", + required=True, + ) + date_end = fields.Datetime( + string="Session date end", + required=True, + ) + start_time = fields.Float( + required=True, + help="Session start time", + ) + end_time = fields.Float( + required=True, + help="Session end time", + ) + registration_ids = fields.One2many( + comodel_name='event.registration', + inverse_name='session_id', + string='Attendees', + state={'done': [('readonly', True)]}, + ) + event_mail_ids = fields.One2many( + comodel_name='event.mail', + inverse_name='session_id', + string='Mail Schedule', + copy=True) + + @api.model + def _set_session_mail_ids(self, event_id): + return [(0, 0, { + 'event_id': event_id, + 'interval_unit': 'now', + 'interval_type': 'after_sub', + 'template_id': self.env.ref('event.event_subscription').id + }), (0, 0, { + 'event_id': event_id, + 'interval_nbr': 2, + 'interval_unit': 'days', + 'interval_type': 'before_event', + 'template_id': self.env.ref('event.event_reminder').id + }), (0, 0, { + 'event_id': event_id, + 'interval_nbr': 15, + 'interval_unit': 'days', + 'interval_type': 'before_event', + 'template_id': self.env.ref('event.event_reminder').id + })] + + @api.multi + def name_get(self): + """Redefine the name_get method to show the event name with the event + session. + """ + res = [] + for item in self: + res.append((item.id, "[%s] %s" % (item.event_id.name, item.name))) + return res + + @api.model + def create(self, vals): + if 'event_mail_ids' not in vals: + vals.update({ + 'event_mail_ids': self._set_session_mail_ids(vals['event_id']) + }) + return super(EventSession, self).create(vals) + + @api.multi + @api.depends('seats_max', 'registration_ids.state') + def _compute_seats(self): + """Determine reserved, available, reserved but unconfirmed and used + seats by session. + """ + # initialize fields to 0 + for session in self: + session.seats_unconfirmed = session.seats_reserved = \ + session.seats_used = session.seats_available = 0 + # aggregate registrations by event session and by state + if self.ids: + state_field = { + 'draft': 'seats_unconfirmed', + 'open': 'seats_reserved', + 'done': 'seats_used', + } + query = """ + SELECT session_id, state, count(session_id) + FROM event_registration + WHERE session_id IN %s AND state IN ('draft', 'open', 'done') + GROUP BY session_id, state """ + self._cr.execute(query, (tuple(self.ids),)) + for session_id, state, num in self._cr.fetchall(): + session = self.browse(session_id) + session[state_field[state]] += num + # compute seats_available + for session in self: + if session.seats_max > 0: + session.seats_available = session.seats_max - ( + session.seats_reserved + session.seats_used) + session.seats_expected = ( + session.seats_unconfirmed + session.seats_reserved + + session.seats_used) + + @api.onchange('event_id') + def onchage_event_selection(self): + self.seats_min = self.event_id.seats_min + self.seats_max = self.event_id.seats_max + self.seats_availability = self.event_id.seats_availability + self.date = self.event_id.date_begin_located + + @api.multi + @api.constrains('seats_max', 'seats_available') + def _check_seats_limit(self): + for session in self: + if (session.seats_availability == 'limited' and + session.seats_max and session.seats_available < 0): + raise ValidationError( + _('No more available seats for this session.')) + + @api.multi + @api.constrains('date') + def _check_out_of_event_date_range(self): + for session in self: + if self.event_id.date_end_located < session.date or \ + session.date < self.event_id.date_begin_located: + raise ValidationError( + _("Session date is out of this event dates range") + ) + + @api.multi + @api.constrains('start_time', 'end_time') + def _check_zero_duration(self): + for session in self: + if session.end_time == session.start_time: + raise ValidationError( + _("Ending and starting time can't be the same!") + ) + + @api.multi + def button_open_registration(self): + """Opens session registrations""" + self.ensure_one() + action = self.env.ref( + 'event.act_event_registration_from_event').read()[0] + action['domain'] = [('id', 'in', self.registration_ids.ids)] + action['context'] = {} + return action diff --git a/event_session/reports/__init__.py b/event_session/reports/__init__.py new file mode 100644 index 000000000..d544480a3 --- /dev/null +++ b/event_session/reports/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import report_event_registration diff --git a/event_session/reports/report_event_registration.py b/event_session/reports/report_event_registration.py new file mode 100644 index 000000000..d0005e96d --- /dev/null +++ b/event_session/reports/report_event_registration.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# © 2016 Sergio Teruel +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models, fields + + +class ReportEventRegistration(models.Model): + """Events Analysis""" + _inherit = "report.event.registration" + + session_id = fields.Many2one( + comodel_name='event.session', + string='Session', + required=True, + readonly=True, + ) + session_seats_max = fields.Integer( + string="Maximum session seats", readonly=True, group_operator="avg") + session_seats_available = fields.Integer( + string='Available session Seats', readonly=True, group_operator="avg") + + def _select(self): + select_str = super(ReportEventRegistration, self)._select() + return select_str + """ + , MIN(r.session_id) AS session_id, + MIN(es.seats_max) AS session_seats_max, + MIN(es.seats_available) AS session_seats_available + """ + + def _from(self): + from_str = super(ReportEventRegistration, self)._from() + from_str += """ + LEFT JOIN event_session es ON r.session_id = es.id + """ + return from_str + + def _group_by(self): + group_by_str = super(ReportEventRegistration, self)._group_by() + return group_by_str + ", r.session_id" diff --git a/event_session/reports/report_event_registration_view.xml b/event_session/reports/report_event_registration_view.xml new file mode 100644 index 000000000..3dcc08389 --- /dev/null +++ b/event_session/reports/report_event_registration_view.xml @@ -0,0 +1,31 @@ + + + + + + report.event.registration + + + + + + + + + + + + report.event.registration + + + + + + + + + + + + diff --git a/event_session/security/ir.model.access.csv b/event_session/security/ir.model.access.csv new file mode 100644 index 000000000..26b364c5d --- /dev/null +++ b/event_session/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_event_session_user,event.session.user,event_session.model_event_session,event.group_event_user,1,0,0,0 +access_event_session_admin,event.session.admin,event_session.model_event_session,event.group_event_manager,1,1,1,1 diff --git a/event_session/tests/__init__.py b/event_session/tests/__init__.py new file mode 100644 index 000000000..e7f04fd77 --- /dev/null +++ b/event_session/tests/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import test_session diff --git a/event_session/tests/test_session.py b/event_session/tests/test_session.py new file mode 100644 index 000000000..1cc3436e8 --- /dev/null +++ b/event_session/tests/test_session.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Tecnativa - David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl-3.0). + +from odoo.tests import common +from odoo.exceptions import ValidationError +from datetime import datetime, timedelta + + +class EventSession(common.SavepointCase): + + @classmethod + def setUpClass(cls): + super(EventSession, cls).setUpClass() + cls.event = cls.env['event.event'].create({ + 'name': 'Test event', + 'date_begin': datetime.today(), + 'date_end': datetime.today() + timedelta(days=7), + 'seats_availability': 'limited', + 'seats_max': '5', + 'seats_min': '1', + }) + cls.session = cls.env['event.session'].create({ + 'name': 'Test session', + 'date': datetime.today() + timedelta(days=1), + 'date_end': datetime.today() + timedelta(days=1), + 'event_id': cls.event.id, + 'start_time': 20.0, + 'end_time': 21.5, + 'seats_availability': cls.event.seats_availability, + 'seats_max': cls.event.seats_max, + 'seats_min': cls.event.seats_min, + }) + cls.attendee = cls.env['event.registration'].create({ + 'name': 'Test attendee', + 'event_id': cls.event.id, + 'session_id': cls.session.id, + }) + cls.wizard = cls.env['wizard.event.session'].create({ + 'event_id': cls.event.id, + 'mondays': True, + 'tuesdays': True, + 'wednesdays': True, + 'thursdays': True, + 'fridays': True, + 'sundays': True, + 'saturdays': True, + 'delete_existing_sessions': False, + 'session_hour_ids': [ + (0, 0, {'start_time': 20.0, 'end_time': 21.0}), + ], + }) + cls.template = cls.env['event.mail.template'].create({ + 'name': 'Template test 01', + 'scheduler_template_ids': [(0, 0, { + 'interval_nbr': 15, + 'interval_unit': 'days', + 'interval_type': 'before_event', + 'template_id': cls.env.ref('event.event_reminder').id})], + }) + + def test_session_methods(self): + """ Session methods """ + self.assertEqual( + # name_get method + self.session.name_get()[0][1], + '[' + self.event.name + '] ' + self.session.name + ) + with self.assertRaises(ValidationError), self.cr.savepoint(): + # out of range begining date + self.session.update({ + 'date': datetime.strptime( + self.event.date_begin, '%Y-%m-%d %H:%M:%S' + ) - timedelta(days=1), + }) + with self.assertRaises(ValidationError), self.cr.savepoint(): + # out of range begining date + self.session.update({ + 'date': datetime.strptime( + self.event.date_end, '%Y-%m-%d %H:%M:%S' + ) + timedelta(days=1), + }) + with self.assertRaises(ValidationError), self.cr.savepoint(): + # zero duration + self.session.update({ + 'start_time': 20.0, + 'end_time': 20.0, + }) + # registrations button + res = self.session.button_open_registration() + attendees = self.env['event.registration'].search([ + ['session_id', '=', self.session.id] + ]) + self.assertEqual( + res['domain'], + [('id', 'in', attendees.ids)] + ) + # assign mail templates + self.session._set_session_mail_ids(self.event.id) + self.assertEqual(len(self.session.event_mail_ids), 3) + self.session._set_session_mail_ids(self.template) + self.assertEqual(len(self.session.event_mail_ids), 3) + + def test_session_seats(self): + """ Session seat """ + self.assertEqual( + self.event.seats_available, + self.session.seats_available) + self.assertEqual( + self.event.seats_unconfirmed, + self.session.seats_unconfirmed + ) + self.assertEqual( + self.event.seats_used, + self.session.seats_used + ) + with self.assertRaises(ValidationError), self.cr.savepoint(): + # check limit regs + for i in range(int(self.session.seats_available)+1): + self.env['event.registration'].create({ + 'name': 'Test Attendee', + 'event_id': self.event.id, + 'session_id': self.session.id, + }) + + def test_wizard(self): + """Test Session Generation Wizard""" + self.wizard.action_generate_sessions() + # delete previous sessions + self.wizard.update({'delete_existing_sessions': True}) + self.wizard.update({'event_mail_template_id': self.template}) + self.wizard.action_generate_sessions() + sessions = self.env['event.session'].search([ + ['event_id', '=', self.event.id] + ]) + for session in sessions: + self.assertTrue(session.event_mail_ids) + self.assertEqual(session.seats_max, self.event.seats_max) + self.assertEqual(session.seats_availability, + self.event.seats_availability) + with self.assertRaises(ValidationError), self.cr.savepoint(): + # session duration = 0 + self.wizard.update({'session_hour_ids': [ + (0, 0, {'start_time': 20.0, 'end_time': 20.0}), + ], + }) + with self.assertRaises(ValidationError), self.cr.savepoint(): + # schedules overlap + self.wizard.update({'session_hour_ids': [ + (0, 0, {'start_time': 20.0, 'end_time': 21.0}), + (0, 0, {'start_time': 20.5, 'end_time': 21.5}), + ], + }) + with self.assertRaises(ValidationError), self.cr.savepoint(): + # weekday not set + self.wizard.update({ + 'mondays': False, + 'tuesdays': False, + 'wednesdays': False, + 'thursdays': False, + 'fridays': False, + 'sundays': False, + 'saturdays': False, + }) + self.wizard.action_generate_sessions() diff --git a/event_session/views/event_session_view.xml b/event_session/views/event_session_view.xml new file mode 100644 index 000000000..9ce2bba16 --- /dev/null +++ b/event_session/views/event_session_view.xml @@ -0,0 +1,142 @@ + + + + + event.session.search + event.session + + + + + + + + + + + + + event.session.form + event.session + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + event.session.tree + event.session + + + + + + + + + + + + + + + + + + + event.session.calendar + event.session + + + + + + + + + + + event.session + form + Sessions + tree,form,calendar + + + + + + +
diff --git a/event_session/views/event_view.xml b/event_session/views/event_view.xml new file mode 100644 index 000000000..b1abcea9e --- /dev/null +++ b/event_session/views/event_view.xml @@ -0,0 +1,59 @@ + + + + + event.registration + + + + + + + + + + + event.registration + + + + + + + + + + + event.session + form + Sessions + tree,form,calendar + {'search_default_event_id': active_id, 'default_event_id': active_id} + + + + event.event + + + + + + + + + + + + + diff --git a/event_session/wizards/__init__.py b/event_session/wizards/__init__.py new file mode 100644 index 000000000..f23e87e25 --- /dev/null +++ b/event_session/wizards/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import wizard_event_session diff --git a/event_session/wizards/wizard_event_session.py b/event_session/wizards/wizard_event_session.py new file mode 100644 index 000000000..55c6447c2 --- /dev/null +++ b/event_session/wizards/wizard_event_session.py @@ -0,0 +1,253 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError +from datetime import datetime, timedelta +from time import strftime, strptime +from locale import setlocale, LC_ALL + + +class WizardEventSession(models.TransientModel): + _name = "wizard.event.session" + + name = fields.Char( + "Session info", + required=True, + help="It will be generated according to given parameters", + default='/', + ) + event_id = fields.Many2one( + comodel_name="event.event", + readonly=True, + default=lambda self: self.env.context["active_id"], + required=True, + ) + event_date_begin = fields.Datetime( + related="event_id.date_begin", + readonly=True, + help="Set it up in the event configuration" + "Sessions will be generated from this date", + ) + event_date_end = fields.Datetime( + related="event_id.date_end", + readonly=True, + help="Set it up in the event configuration" + "Sessions will be generated up to this date", + ) + event_date_tz = fields.Selection( + related="event_id.date_tz", + readonly=True, + help="Set it up in the event configuration" + "Sessions will be generated up to this date", + ) + mondays = fields.Boolean( + help="Create sessions on Mondays", + ) + tuesdays = fields.Boolean( + help="Create sessions on Tuesdays", + ) + wednesdays = fields.Boolean( + help="Create sessions on Wednesdays", + ) + thursdays = fields.Boolean( + help="Create sessions on Thursdays", + ) + fridays = fields.Boolean( + help="Create sessions on Fridays", + ) + saturdays = fields.Boolean( + help="Create sessions on Saturdays", + ) + sundays = fields.Boolean( + help="Create sessions on Sundays", + ) + delete_existing_sessions = fields.Boolean( + help="Check in order to delete every previous session for this event" + ) + session_hour_ids = fields.One2many( + comodel_name='wizard.event.session.hours', + inverse_name='wizard_event_session_id', + string='Hours', + ) + event_mail_template_id = fields.Many2one( + comodel_name='event.mail.template', + string='Mail Schedule Template', + ) + + @api.multi + @api.constrains('session_hour_ids') + def _avoid_overlapping_hours(self): + for hour_a in self.session_hour_ids: + for hour_b in self.session_hour_ids: + if hour_a != hour_b: + if hour_a.start_time == hour_b.start_time: + raise ValidationError( + _("There are overlapping hours!") + ) + elif hour_b.start_time < \ + hour_a.start_time < hour_b.end_time: + raise ValidationError( + _("There are overlapping hours!") + ) + + @api.multi + def weekdays(self): + return (self.mondays, + self.tuesdays, + self.wednesdays, + self.thursdays, + self.fridays, + self.saturdays, + self.sundays) + + @api.multi + def datetime_fields(self): + """Fields converted to Python's Datetime-based objects.""" + result = { + "event_start": fields.Datetime.from_string(self.event_date_begin), + "event_end": fields.Datetime.from_string(self.event_date_end), + "day_delta": timedelta(days=1), + } + return result + + @api.multi + def existing_sessions(self, date): + """Return existing sessions that match some criteria.""" + # Todo: Improve match + return self.env["event.session"].search( + [("event_id", "=", self.event_id.id), + ("date", "=", date), + ("start_time", "=", self.start_time)], + ) + + def _get_session_mail_template(self, mail_template): + vals = [(6, 0, [])] + if isinstance(mail_template, int): + mail_template = self.env['event.mail.template'].browse( + mail_template) + for scheduler in mail_template.scheduler_template_ids: + vals.append((0, 0, { + 'event_id': self.event_id.id, + 'interval_nbr': scheduler.interval_nbr, + 'interval_unit': scheduler.interval_unit, + 'interval_type': scheduler.interval_type, + 'template_id': scheduler.template_id.id, + })) + return vals + + @api.multi + def create_session(self, **values): + """Create a new session record with the provided values.""" + setlocale(LC_ALL, locale=(self.env.lang, 'UTF-8')) + data = { + "name": "{} {} - {}".format( + strftime('%A %d/%m/%y', + strptime(values["date"], "%Y-%m-%d %H:%M:%S")), + "%02d:%02d" % divmod(values["start_time"]*60, 60), + "%02d:%02d" % divmod(values["end_time"]*60, 60), + ).capitalize(), + "event_id": self.event_id.id, + "date_end": '%s : %s' % ( + strftime('%Y-%m-%d', strptime( + values["date"], "%Y-%m-%d %H:%M:%S")), + "%02d:%02d" % divmod(values["end_time"]*60, 60)), + "start_time": values["start_time"], + "end_time": values["end_time"], + "seats_min": self.event_id.seats_min, + "seats_max": self.event_id.seats_max, + "seats_availability": self.event_id.seats_availability, + } + mail_template = (self.event_mail_template_id or + self.event_id._default_event_mail_template_id()) + if mail_template: + data['event_mail_ids'] = self._get_session_mail_template( + mail_template) + else: + data['event_mail_ids'] = [] + + data.update(values) + return self.env["event.session"].create(data) + + @api.multi + def generate_sessions(self): + self.ensure_one() + counter = 0 + dt = self.datetime_fields() + weekdays = self.weekdays() + current = dt["event_start"] + while current <= dt["event_end"]: + for hour in self.session_hour_ids: + tm = hour.time_fields() + current_start = datetime.combine( + current.date(), + tm["start_time"].time() + ) + if (current_start >= dt["event_start"] and + weekdays[current.weekday()]): + current_end = datetime.combine( + current.date(), + tm["end_time"].time() + ) + if current_end <= dt["event_end"]: + current_start = \ + fields.Datetime.to_string(current_start) + # TODO: Check that no session exists with this data + self.create_session( + date=current_start, + start_time=hour.start_time, + end_time=hour.end_time, + ) + counter += 1 + # Next day + current += dt["day_delta"] + + @api.multi + def action_generate_sessions(self): + """Here's where magic is triggered""" + weekdays = self.weekdays() + if not any(weekdays): + raise ValidationError(_("You must select at least one weekday")) + if self.delete_existing_sessions: + self.event_id.session_ids.unlink() + self.generate_sessions() + + +class WizardEventSessionHours(models.TransientModel): + _name = "wizard.event.session.hours" + + wizard_event_session_id = fields.Many2one( + comodel_name='wizard.event.session' + ) + start_time = fields.Float(required=True) + end_time = fields.Float(required=True) + + # Todo: manage multiday sessions + + @api.multi + @api.constrains('start_time', 'end_time') + def _check_zero_duration(self): + for hour in self: + if hour.start_time == hour.end_time: + raise ValidationError( + _("There are sessions with no duration!") + ) + + @api.multi + @api.constrains('start_time', 'end_time') + def _check_hour_validity(self): + for hour in self: + if hour.start_time > 23.99 or hour.end_time > 23.99: + raise ValidationError( + _("You've entered invalid hours!") + ) + + @api.multi + def time_fields(self): + """Format hours""" + result = { + "start_time": datetime.min + timedelta(hours=self.start_time), + "end_time": datetime.min + timedelta(hours=self.end_time), + } + return result diff --git a/event_session/wizards/wizard_event_session_view.xml b/event_session/wizards/wizard_event_session_view.xml new file mode 100644 index 000000000..4a48085e3 --- /dev/null +++ b/event_session/wizards/wizard_event_session_view.xml @@ -0,0 +1,73 @@ + + + + + + Generate Sessions Wizard + wizard.event.session + form + form + new + + + + event.event + + + + - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - @@ -100,22 +43,6 @@ - - - - - - - - diff --git a/event_session/views/event_view.xml b/event_session/views/event_view.xml index b1abcea9e..6f7410519 100644 --- a/event_session/views/event_view.xml +++ b/event_session/views/event_view.xml @@ -1,31 +1,6 @@ - - event.registration - - - - - - - - - - - event.registration - - - - - - - - event.session @@ -48,11 +23,6 @@ - - - - diff --git a/event_session/wizards/__init__.py b/event_session/wizards/__init__.py index f23e87e25..24f0ce1dc 100644 --- a/event_session/wizards/__init__.py +++ b/event_session/wizards/__init__.py @@ -1,3 +1,2 @@ -# -*- coding: utf-8 -*- - +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from . import wizard_event_session diff --git a/event_session/wizards/wizard_event_session.py b/event_session/wizards/wizard_event_session.py index bdabd697a..067f9d6c8 100644 --- a/event_session/wizards/wizard_event_session.py +++ b/event_session/wizards/wizard_event_session.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Copyright 2017 David Vidal # Copyright 2017 Tecnativa - Pedro M. Baeza -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -70,10 +70,6 @@ class WizardEventSession(models.TransientModel): inverse_name='wizard_event_session_id', string='Hours', ) - event_mail_template_id = fields.Many2one( - comodel_name='event.mail.template', - string='Mail Schedule Template', - ) @api.multi @api.constrains('session_hour_ids') @@ -119,20 +115,7 @@ def _prepare_session_values(self, date_begin, date_end): "event_id": self.event_id.id, "date_begin": fields.Datetime.to_string(date_begin), "date_end": fields.Datetime.to_string(date_end), - "seats_min": self.event_id.seats_min, - "seats_max": self.event_id.seats_max, - "seats_availability": self.event_id.seats_availability, } - mail_template = ( - self.event_mail_template_id or - self.env['ir.values'].get_default( - 'event.config.settings', 'event_mail_template_id')) - - if mail_template: - template_values = \ - self.env['event.session']._session_mails_from_template( - self.event_id.id, mail_template) - vals['event_mail_ids'] = template_values return vals @api.multi diff --git a/event_session/wizards/wizard_event_session_view.xml b/event_session/wizards/wizard_event_session_view.xml index 4a48085e3..4a7dd1f2f 100644 --- a/event_session/wizards/wizard_event_session_view.xml +++ b/event_session/wizards/wizard_event_session_view.xml @@ -55,7 +55,6 @@ -