From 5bbde75ca99a844ddc643ad79bd736f466093589 Mon Sep 17 00:00:00 2001 From: younes Date: Thu, 11 Sep 2025 07:59:56 +0100 Subject: [PATCH] IMP benefit --- odex25_ensan/odex_benefit/__manifest__.py | 3 +- .../odex_benefit/data/email_temps.xml | 63 +++++++- odex25_ensan/odex_benefit/i18n/ar_001.po | 144 +++++++++++++++++- odex25_ensan/odex_benefit/models/__init__.py | 3 +- odex25_ensan/odex_benefit/models/visit.py | 99 ++++++++++-- .../odex_benefit/models/visit_survey.py | 6 + odex25_ensan/odex_benefit/views/visit.xml | 20 ++- .../odex_benefit/views/visit_survey.xml | 15 ++ .../wizards/visit_location_otp_wizard.py | 1 + 9 files changed, 329 insertions(+), 25 deletions(-) create mode 100644 odex25_ensan/odex_benefit/models/visit_survey.py create mode 100644 odex25_ensan/odex_benefit/views/visit_survey.xml diff --git a/odex25_ensan/odex_benefit/__manifest__.py b/odex25_ensan/odex_benefit/__manifest__.py index 025cc7812..11933e225 100644 --- a/odex25_ensan/odex_benefit/__manifest__.py +++ b/odex25_ensan/odex_benefit/__manifest__.py @@ -7,7 +7,7 @@ 'website': 'http://exp-sa.com', 'license': 'GPL-3', 'author': 'Expert Ltd', - 'depends': ['base', 'takaful_core', 'website', 'account', 'report_xlsx', 'sale', 'product', 'stock', 'hr', + 'depends': ['base','survey', 'takaful_core', 'website', 'account', 'report_xlsx', 'sale', 'product', 'stock', 'hr', 'purchase','web_google_maps','odex25_account_payment_fix','otp_sms_auth_custom'], 'data': [ 'security/security_view.xml', @@ -63,6 +63,7 @@ 'wizards/visit_location_otp_wizard_view.xml', 'views/benefit_vehicle_model.xml', 'wizards/visit_location_refused_wizard_view.xml', + 'views/visit_survey.xml', 'views/actions_and_menus.xml', ], 'external_dependencies': { diff --git a/odex25_ensan/odex_benefit/data/email_temps.xml b/odex25_ensan/odex_benefit/data/email_temps.xml index ace40634e..f936c52a3 100644 --- a/odex25_ensan/odex_benefit/data/email_temps.xml +++ b/odex25_ensan/odex_benefit/data/email_temps.xml @@ -265,7 +265,7 @@ - + Visit Location OTP Email @@ -280,7 +280,8 @@

Hello,

-

Your OTP for confirming visit ${object.name} is:

+

Your OTP for confirming visit ${object.name} is: +

@@ -313,8 +314,7 @@ - - + Visit Location OTP @@ -324,5 +324,60 @@ + + + Visit Location Survey SMS + + + Dear family, please complete the evaluation form for visit ${object.name}: ${object.survey_url} + + + + + Visit Location Survey Email + + Family Evaluation Survey - ${object.name} + +
+ +

Hello,

+ +

Please complete the evaluation form for your visit: + ${object.name}. +

+ + +

+ + Open Survey + +

+ +

Thank you for your cooperation. +
+ ${user.company_id.name} +

+ +
+ +

+ This is an automated message. Please do not reply. +

+
+
+
+ diff --git a/odex25_ensan/odex_benefit/i18n/ar_001.po b/odex25_ensan/odex_benefit/i18n/ar_001.po index 9653995ac..4c85b59a4 100644 --- a/odex25_ensan/odex_benefit/i18n/ar_001.po +++ b/odex25_ensan/odex_benefit/i18n/ar_001.po @@ -10977,6 +10977,7 @@ msgstr "إعدادات الاستبيان" #. module: odex_benefit #: model:ir.model.fields,field_description:odex_benefit.field_survey_setting__survey_url +#: model:ir.model.fields,field_description:odex_benefit.field_visit_location__survey_url msgid "Survey URL" msgstr "رابط استبيان الزيارة" @@ -11952,6 +11953,7 @@ msgstr "ظاهرة في الموقع الحالي" #. module: odex_benefit #: code:addons/odex_benefit/wizards/visit_location_refused_wizard.py:0 +#: model:ir.model.fields,field_description:odex_benefit.field_survey_user_input__visit_id #: model:ir.model.fields,field_description:odex_benefit.field_visit_location_otp_wizard__visit_id #: model:ir.model.fields,field_description:odex_benefit.field_visit_location_refusal_reason_wizard__visit_location_id #: model:ir.model.fields,field_description:odex_benefit.field_visit_skip_otp_wizard__visit_id @@ -15411,4 +15413,144 @@ msgid "" "The visit record %s state has been changed to %s.
Refusal " "Reason: %s
Notes: %s" msgstr "" -"تم تغيير حالة سجل الزيارة %s إلى %s.
سبب الرفض: %s
ملاحظات: %s" \ No newline at end of file +"تم تغيير حالة سجل الزيارة %s إلى %s.
سبب الرفض: %s
ملاحظات: %s" + +#. module: odex_benefit +#: model_terms:ir.ui.view,arch_db:odex_benefit.visits_form +msgid "Resend Survey Link" +msgstr "إعادة إرسال رابط الاستطلاع" + +#. module: odex_benefit +#: model_terms:ir.ui.view,arch_db:odex_benefit.visits_form +msgid "Surveys" +msgstr "استطلاعات" + +#. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_visit_location__response_id +msgid "Survey Responses" +msgstr "إجابات الاستطلاع" + +#. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_visit_location__response_count +msgid "Responses Count" +msgstr "عدد الإجابات" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/visit.py:0 +#, python-format +msgid "Family Evaluation Responses" +msgstr "جابات استطلاع الأسرة" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/visit.py:0 +#, python-format +msgid "You must send the evaluation form before closing the visit." +msgstr "يجب إرسال نموذج الاستطلاع قبل إغلاق الزيارة." + +#. module: odex_benefit +#: code:addons/odex_benefit/models/visit.py:0 +#, python-format +msgid "" +"The visit cannot be closed before the family completes the evaluation form." +msgstr "لا يمكن إغلاق الزيارة قبل أن تُكمل الأسرة نموذج الاستطلاع." + +#. module: odex_benefit +#: code:addons/odex_benefit/models/visit.py:0 +#, python-format +msgid "The family profile has no email address. Please add an email first." +msgstr "ملف الأسرة لا يحتوي على عنوان بريد إلكتروني. يرجى إضافة البريد أولاً." + +#. module: odex_benefit +#: code:addons/odex_benefit/models/visit.py:0 +#, python-format +msgid "The email template 'Visit Location Survey Email' is missing." +msgstr "قالب البريد الإلكتروني 'زيارة - استطلاع' غير موجود." + +#. module: odex_benefit +#: code:addons/odex_benefit/models/visit.py:0 +#, python-format +msgid "" +"The family profile has no mobile number. Please add a valid phone number." +msgstr "ملف الأسرة لا يحتوي على رقم جوال. يرجى إضافة رقم صالح." + +#. module: odex_benefit +#: code:addons/odex_benefit/models/visit.py:0 +#, python-format +msgid "The SMS template 'Visit Location Survey' is missing." +msgstr "قالب الرسائل القصيرة 'زيارة - استطلاع' غير موجود." + +#. module: odex_benefit +#: model:sms.template,name:odex_benefit.visit_location_survey_sms_template +msgid "Visit Location Survey SMS" +msgstr "رسالة استطلاع الزيارة" + +#. module: odex_benefit +#: model:sms.template,body:odex_benefit.visit_location_survey_sms_template +msgid "" +"\n" +" Dear family, please complete the evaluation form for visit ${object.name}: ${object.survey_url}\n" +" " +msgstr "" +"\n" +" عائلة كريمة، يرجى تعبئة نموذج الاستطلاع للزيارة ${object.name}: ${object.survey_url}\n" +" " + +#. module: odex_benefit +#: model:mail.template,body_html:odex_benefit.visit_location_survey_email_template +msgid "" +"
\n" +"\n" +"

Hello,

\n" +"\n" +"

Please complete the evaluation form for your visit:\n" +" ${object.name}.\n" +"

\n" +"\n" +" \n" +"

\n" +" \n" +" Open Survey\n" +" \n" +"

\n" +"\n" +"

Thank you for your cooperation.\n" +"
\n" +" ${user.company_id.name}\n" +"

\n" +"\n" +"
\n" +"\n" +"

\n" +" This is an automated message. Please do not reply.\n" +"

\n" +"
\n" +" " +msgstr "" +"
\n" +"\n" +"

مرحباً،

\n" +"\n" +"

يرجى تعبئة نموذج الاستطلاع الخاص بزيارتكم:\n" +" ${object.name}.\n" +"

\n" +"\n" +" \n" +"

\n" +" \n" +" فتح الاستطلاع\n" +" \n" +"

\n" +"\n" +"

نشكر لكم تعاونكم.\n" +"
\n" +" ${user.company_id.name}\n" +"

\n" +"\n" +"
\n" +"\n" +"

\n" +" هذه رسالة آلية، يرجى عدم الرد عليها.\n" +"

\n" +"
\n" +" " + diff --git a/odex25_ensan/odex_benefit/models/__init__.py b/odex25_ensan/odex_benefit/models/__init__.py index 225a10fae..738e517e8 100644 --- a/odex25_ensan/odex_benefit/models/__init__.py +++ b/odex25_ensan/odex_benefit/models/__init__.py @@ -37,4 +37,5 @@ from . import res_users from . import res_partner from . import job_settings from . import death_reason_settings -from . import benefit_vehicle_model \ No newline at end of file +from . import benefit_vehicle_model +from . import visit_survey \ No newline at end of file diff --git a/odex25_ensan/odex_benefit/models/visit.py b/odex25_ensan/odex_benefit/models/visit.py index a2322d6b5..bc247ce08 100644 --- a/odex25_ensan/odex_benefit/models/visit.py +++ b/odex25_ensan/odex_benefit/models/visit.py @@ -1,7 +1,10 @@ +from Tools.scripts.pdeps import store + from odoo import fields, models,api,_ import math, random from odoo.exceptions import UserError, ValidationError from datetime import timedelta +import werkzeug class Visit(models.Model): _name = 'visit.location' @@ -52,6 +55,25 @@ class Visit(models.Model): default=lambda self: _('New')) otp_code = fields.Char(string="OTP Code", readonly=True, copy=False) otp_generated_at = fields.Datetime(string="OTP Generated At", readonly=True, copy=False) + response_id = fields.Many2one('survey.user_input', string="Survey Responses", ondelete='restrict', copy=False) + response_count = fields.Integer(compute="_compute_response_count",store=True, string="Responses Count", copy=False) + survey_url = fields.Char(string="Survey URL") + + @api.depends('response_id') + def _compute_response_count(self): + for rec in self: + rec.response_count = self.env['survey.user_input'].search_count([('visit_id', '=', rec.id)]) + + def action_view_responses(self): + self.ensure_one() + return { + 'name': _("Family Evaluation Responses"), + 'type': 'ir.actions.act_window', + 'res_model': 'survey.user_input', + 'view_mode': 'tree,form', + 'domain': [('visit_id', '=', self.id)], + 'context': {'create': False}, + } def _expand_states(self, states, domain, order): return [key for key, val in type(self).state.selection] @@ -178,6 +200,7 @@ class Visit(models.Model): else: self.state = 'done' self.benefit_id.last_visit_date = self.visit_date + self.action_send_survey() def action_skip_otp(self): self.ensure_one() @@ -194,24 +217,68 @@ class Visit(models.Model): 'context': context, } + def action_send_survey(self): + self.ensure_one() + if self.visit_types.survey_id: + survey = self.visit_types.survey_id + + if not self.response_id: + response = survey._create_answer( + user=self.benefit_id.user_id, + partner=self.benefit_id.partner_id, + email=self.benefit_id.email, + test_entry=False, + check_attempts=True, + visit_id=self.id + ) + self.response_id = response.id + else: + response = self.response_id + + + self.survey_url = '%s%s?%s' % ( + survey.get_base_url(), + survey.get_start_url(), + # werkzeug.urls.url_encode({'answer_token': response.access_token}) + werkzeug.urls.url_encode({'answer_token': response and response.access_token or None}) + ) + + if self.benefit_id.contact_type == 'email': + if not self.benefit_id.email: + raise UserError(_("The family profile has no email address. Please add an email first.")) + template = self.env.ref('odex_benefit.visit_location_survey_email_template', False) + if not template: + raise UserError(_("The email template 'Visit Location Survey Email' is missing.")) + email_values = {"email_from": self.env.user.company_id.hr_email or self.env.user.company_id.email} + template.write({'email_to': self.benefit_id.email, + 'email_cc': self.env.user.company_id.hr_email or self.env.user.company_id.email, }) + template.with_context( + lang=self.env.user.lang, + tracking_disable=True, + survey_url=self.survey_url + ).send_mail(self.id, force_send=True, raise_exception=False, email_values=email_values) + elif self.benefit_id.contact_type == 'sms': + if not self.benefit_id.sms_phone: + raise UserError(_("The family profile has no mobile number. Please add a valid phone number.")) + sms_template = self.env.ref('odex_benefit.visit_location_survey_sms_template', False) + if not sms_template: + raise UserError(_("The SMS template 'Visit Location Survey' is missing.")) + self.with_context(tracking_disable=True,survey_url=self.survey_url)._message_sms_with_template( + template=sms_template, + put_in_queue=False, + partner_ids=self.benefit_id.partner_id.ids, + author_id=self.env.ref('base.partner_root').id + ) + def action_close(self): - survey_url = '' - survey_conf = self.env['survey.setting'].search([],limit=1) - if survey_conf: - survey_url = survey_conf.survey_url - survey_url = ' %s' % (survey_url) + self.ensure_one() + if self.visit_types.survey_id: + if not self.response_id: + raise UserError(_("You must send the evaluation form before closing the visit.")) + if self.response_id.state != 'done': + raise UserError(_("The visit cannot be closed before the family completes the evaluation form.")) self.state = 'close' - if self.benefit_id.contact_type == 'email': - body = survey_url - mail = self.env['mail.mail'].create({ - 'body_html':survey_url , - 'subject': "Visit Close", - 'email_to': self.benefit_id.email, - # 'email_cc': self.researcher_id.work_email, - }) - mail.send() - elif self.benefit_id.contact_type == 'sms': - self.benefit_id.partner_id.send_sms_notification(survey_url , self.benefit_id.sms_phone) + @api.depends("researcher_team") def get_researcher_ids(self): for rec in self: diff --git a/odex25_ensan/odex_benefit/models/visit_survey.py b/odex25_ensan/odex_benefit/models/visit_survey.py new file mode 100644 index 000000000..707e61888 --- /dev/null +++ b/odex25_ensan/odex_benefit/models/visit_survey.py @@ -0,0 +1,6 @@ +from odoo import api, fields, models, exceptions, _ + +class SurveyUserInput(models.Model): + _inherit = 'survey.user_input' + + visit_id = fields.Many2one('visit.location', string="Visit") diff --git a/odex25_ensan/odex_benefit/views/visit.xml b/odex25_ensan/odex_benefit/views/visit.xml index 237a07051..f7553346f 100644 --- a/odex25_ensan/odex_benefit/views/visit.xml +++ b/odex25_ensan/odex_benefit/views/visit.xml @@ -93,12 +93,24 @@ +

@@ -108,7 +120,8 @@ - + - + + + diff --git a/odex25_ensan/odex_benefit/views/visit_survey.xml b/odex25_ensan/odex_benefit/views/visit_survey.xml new file mode 100644 index 000000000..4b6ed519a --- /dev/null +++ b/odex25_ensan/odex_benefit/views/visit_survey.xml @@ -0,0 +1,15 @@ + + + + + survey.user_input.view.form.inherit + survey.user_input + + + + + + + + + diff --git a/odex25_ensan/odex_benefit/wizards/visit_location_otp_wizard.py b/odex25_ensan/odex_benefit/wizards/visit_location_otp_wizard.py index f92285ebe..4349ae22d 100644 --- a/odex25_ensan/odex_benefit/wizards/visit_location_otp_wizard.py +++ b/odex25_ensan/odex_benefit/wizards/visit_location_otp_wizard.py @@ -22,6 +22,7 @@ class VisitLocationOtpWizard(models.TransientModel): self.visit_id.write({'state': 'done',}) self.visit_id.benefit_id.last_visit_date = self.visit_id.visit_date + self.visit_id.action_send_survey() class VisitSkipOtpWizard(models.TransientModel): _name = 'visit.skip.otp.wizard'