404 lines
18 KiB
Python
404 lines
18 KiB
Python
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'
|
|
_inherit = ['mail.thread', 'mail.activity.mixin']
|
|
_order = 'create_date desc'
|
|
|
|
@api.model
|
|
def _default_researcher_team(self):
|
|
researcher_team = self.env['committees.line'].search([('employee_id', 'in', [self.env.user.employee_id.id])],
|
|
limit=1)
|
|
return researcher_team.id
|
|
|
|
benefit_type = fields.Selection([
|
|
('benefit', 'Benefit'),
|
|
('family', 'Family'),
|
|
], string='Type', default="benefit")
|
|
benefit_id = fields.Many2one(
|
|
'grant.benefit', string='Family file', domain="[('state', '=', 'second_approve')]")
|
|
benefit_name = fields.Char(related="benefit_id.name")
|
|
benefit_code = fields.Char(related="benefit_id.code")
|
|
sms_phone = fields.Char(string="Contact Phone", related="benefit_id.sms_phone")
|
|
researcher_team = fields.Many2one("committees.line", string="Researcher Team", related="benefit_id.researcher_id")
|
|
researcher_ids = fields.Many2many("hr.employee", string="Researcher", compute="get_researcher_ids", readonly=False)
|
|
visit_date = fields.Datetime(string='Visit Date', tracking=True)
|
|
description = fields.Char(string='Description')
|
|
message = fields.Text(string='Message')
|
|
visit_objective = fields.Selection([
|
|
('inform_visit', 'Inform Visit'),
|
|
('objective_visit', 'Objective Visit'),
|
|
], string='Visit Objective')
|
|
visit_types = fields.Many2one(
|
|
'visits.types',
|
|
string='Visits Types',
|
|
ondelete='restrict'
|
|
)
|
|
visit_types_creation_method = fields.Selection(related='visit_types.creation_method')
|
|
evaluation = fields.Selection(
|
|
[('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6')],
|
|
string='Evaluation')
|
|
state = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('contact', 'Contact'),
|
|
('schedule_a_visit', 'Schedule a visit'),
|
|
('pending', 'Pending'),
|
|
('done', 'Done'),
|
|
('close', 'Close'),
|
|
('cancel', 'Cancel'),
|
|
], string='State', default="draft", tracking=True, group_expand='_expand_states')
|
|
color = fields.Integer('Color Index', default=0)
|
|
family_id = fields.Many2one('benefit.family')
|
|
reason = fields.Text(string='Reason/Justification')
|
|
name = fields.Char(string='Reference', required=True, copy=False, readonly=True, index=True,
|
|
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", copy=False)
|
|
response_count = fields.Integer(compute="_compute_response_count", store=True, string="Responses Count", copy=False)
|
|
survey_url = fields.Char(string="Survey URL")
|
|
|
|
|
|
def action_postpone(self):
|
|
"""Open wizard to postpone"""
|
|
context = dict(self.env.context or {})
|
|
context['target_state'] = "pending"
|
|
context['active_id'] = self.id
|
|
return {
|
|
'name': _('Postpone Visit'),
|
|
'view_mode': 'form',
|
|
'view_type': 'form',
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'visit.location.refusal.reason.wizard',
|
|
'view_id': self.env.ref('odex_benefit.view_visit_location_refusal_reason_wizard_form').id,
|
|
'target': 'new',
|
|
'context': context,
|
|
}
|
|
|
|
def action_reschedule(self):
|
|
for rec in self:
|
|
rec.state = "contact"
|
|
|
|
def action_create_new_visit(self):
|
|
for rec in self:
|
|
new_visit = self.create({
|
|
'creation_type': rec.creation_type,
|
|
'visit_types': rec.visit_types.id,
|
|
'benefit_id': rec.benefit_id.id,
|
|
'researcher_ids': [(6, 0, rec.researcher_ids.ids)],
|
|
'visit_date': fields.Datetime.now(),
|
|
'state': 'draft',
|
|
})
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'visit.location',
|
|
'view_mode': 'form',
|
|
'res_id': new_visit.id,
|
|
}
|
|
|
|
@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]
|
|
|
|
def unlink(self):
|
|
for order in self:
|
|
if order.state not in ['draft'] or order.visit_types.creation_method == 'automatic':
|
|
raise UserError(_('You cannot delete this record'))
|
|
return super(Visit, self).unlink()
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
# If the 'name' field is 'New', generate a new sequence number
|
|
if vals.get('name', _('New')) == _('New'):
|
|
vals['name'] = self.env['ir.sequence'].next_by_code('visit.location.sequence') or _('New')
|
|
return super(Visit, self).create(vals)
|
|
|
|
def assign_sequence_to_records(self):
|
|
sequence = self.env['ir.sequence']
|
|
sorted_records = self.sorted(lambda r: r.create_date)
|
|
|
|
for record in sorted_records:
|
|
if not record.name or record.name == _('New'):
|
|
record.name = sequence.next_by_code('visit.location.sequence')
|
|
|
|
def get_researchers_email(self):
|
|
email_ids = ''
|
|
for rec in self.researcher_ids:
|
|
if email_ids:
|
|
email_ids = email_ids + ',' + str(rec.work_email)
|
|
else:
|
|
email_ids = str(rec.work_email)
|
|
return email_ids
|
|
|
|
def action_draft(self):
|
|
self.state = 'draft'
|
|
|
|
def action_contact(self):
|
|
self.state = 'contact'
|
|
if self.benefit_id.contact_type == 'email':
|
|
template = self.env.ref('odex_benefit.schedule_a_visit_email_template', False)
|
|
if not template:
|
|
return
|
|
template.with_context(lang=self.env.user.lang).send_mail(self.id, force_send=True,
|
|
raise_exception=False)
|
|
elif self.benefit_id.contact_type == 'sms':
|
|
self.benefit_id.partner_id.send_sms_notification(self.message, self.benefit_id.sms_phone)
|
|
|
|
def action_schedule_a_visit(self):
|
|
self.state = 'schedule_a_visit'
|
|
|
|
def action_cancel(self):
|
|
context = dict(self.env.context or {})
|
|
context['target_state'] = "cancel"
|
|
context['active_id'] = self.id
|
|
return {
|
|
'name': _('Refuse Reason Wizard'),
|
|
'view_mode': 'form',
|
|
'view_type': 'form',
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'visit.location.refusal.reason.wizard',
|
|
'view_id': self.env.ref('odex_benefit.view_visit_location_refusal_reason_wizard_form').id,
|
|
'target': 'new',
|
|
'context': context,
|
|
}
|
|
|
|
def action_done(self):
|
|
if self.visit_types.otp_verification:
|
|
otp_validity = self.visit_types.otp_validity_minutes or 5
|
|
expired = (
|
|
not self.otp_code
|
|
or not self.otp_generated_at
|
|
or fields.Datetime.now() > self.otp_generated_at + timedelta(minutes=otp_validity)
|
|
)
|
|
|
|
if expired:
|
|
self.otp_code = self.generateOTP()
|
|
self.otp_generated_at = fields.Datetime.now()
|
|
|
|
if self.benefit_id.contact_type == 'email':
|
|
if not self.benefit_id.email:
|
|
raise UserError(
|
|
_("The family profile has no email address. OTP cannot be sent. Please add an email first."))
|
|
template = self.env.ref('odex_benefit.visit_location_otp_email_template', False)
|
|
if not template:
|
|
raise UserError(
|
|
_("The email template 'Visit Location OTP Email' is missing. Please contact your administrator."))
|
|
template.write({'email_to': self.benefit_id.email,
|
|
'email_cc': self.env.user.company_id.hr_email or self.env.user.company_id.email, })
|
|
email_values = {"email_from": self.env.user.company_id.hr_email or self.env.user.company_id.email,
|
|
'res_id': None}
|
|
template.with_context({'lang': self.env.user.lang}).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 before sending OTP."))
|
|
sms_template_id = self.env.ref('odex_benefit.visit_location_otp_sms_template')
|
|
if not sms_template_id:
|
|
raise UserError(
|
|
_("The SMS template 'Visit Location OTP' is missing. Please contact your administrator."))
|
|
|
|
sms_body = sms_template_id._render_template(sms_template_id.body,self._name,[self.id],post_process=True)[self.id]
|
|
sms_values = {
|
|
'number': self.benefit_id.sms_phone,
|
|
'body': sms_body,
|
|
'partner_id': self.benefit_id.partner_id.id,
|
|
'state': 'outgoing',
|
|
}
|
|
self.env['sms.sms'].sudo().create(sms_values).send()
|
|
|
|
# don't delete this code
|
|
# bot = self.env.ref('base.partner_root').id
|
|
# self.with_context(tracking_disable=True)._message_sms_with_template(
|
|
# template=sms_template_id,
|
|
# put_in_queue=False,
|
|
# partner_ids=self.benefit_id.partner_id.ids,
|
|
# author_id=bot
|
|
# )
|
|
# message = _("Your verification code is %s. It is valid for %s minutes.") % (
|
|
# self.otp_code,
|
|
# self.visit_types.otp_validity_minutes or 5
|
|
# )
|
|
# self.benefit_id.user_id.send_sms_to_user(message, self.benefit_id.sms_phone)
|
|
|
|
context = dict(self.env.context or {})
|
|
context['active_id'] = self.id
|
|
return {
|
|
'name': _('Verify OTP'),
|
|
'view_mode': 'form',
|
|
'view_type': 'form',
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'visit.location.otp.wizard',
|
|
'view_id': self.env.ref('odex_benefit.visit_location_otp_wizard_form').id,
|
|
'target': 'new',
|
|
'context': context,
|
|
}
|
|
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()
|
|
context = dict(self.env.context or {})
|
|
context['active_id'] = self.id
|
|
return {
|
|
'name': _('Skip OTP Verification'),
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'visit.skip.otp.wizard',
|
|
'view_mode': 'form',
|
|
'view_type': 'form',
|
|
'view_id': self.env.ref('odex_benefit.view_visit_skip_otp_wizard_form').id,
|
|
'target': 'new',
|
|
'context': context,
|
|
}
|
|
|
|
def action_send_survey(self):
|
|
self.ensure_one()
|
|
if self.visit_types.survey_id:
|
|
survey = self.visit_types.survey_id
|
|
|
|
if self.response_id:
|
|
response = self.response_id
|
|
if response.survey_id.id != survey.id or response.state != 'done':
|
|
response.sudo().unlink()
|
|
self.response_id = False
|
|
|
|
if not self.response_id:
|
|
response = survey.sudo()._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})
|
|
)
|
|
link_tracker = self.env['link.tracker'].sudo().create({
|
|
'url': self.survey_url,
|
|
'title': 'Survey Link',
|
|
'campaign_id': False,
|
|
'medium_id': False,
|
|
'source_id': False,
|
|
})
|
|
self.survey_url = link_tracker.short_url
|
|
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,"res_id": None}
|
|
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,
|
|
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."))
|
|
|
|
sms_body = sms_template._render_template(sms_template.body, self._name, [self.id], post_process=True)[self.id]
|
|
sms_values = {
|
|
'number': self.benefit_id.sms_phone,
|
|
'body': sms_body,
|
|
'partner_id': self.benefit_id.partner_id.id,
|
|
'state': 'outgoing',
|
|
}
|
|
self.env['sms.sms'].sudo().create(sms_values).send()
|
|
# 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):
|
|
# 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'
|
|
|
|
@api.depends("researcher_team")
|
|
def get_researcher_ids(self):
|
|
for rec in self:
|
|
rec.researcher_ids = rec.researcher_team.employee_id
|
|
|
|
def send_visit_date_email(self):
|
|
template = self.env.ref('odex_benefit.visit_date_email', False)
|
|
if not template:
|
|
return
|
|
template.with_context(lang=self.env.user.lang).send_mail(self.id, force_send=True, raise_exception=False)
|
|
|
|
# function to generate OTP
|
|
def generateOTP(self):
|
|
digits = "0123456789"
|
|
OTP = ""
|
|
|
|
# length of password can be changed
|
|
# by changing value in range
|
|
for i in range(4):
|
|
OTP += digits[math.floor(random.random() * 10)]
|
|
|
|
return OTP
|
|
|
|
def geo_localize(self):
|
|
for visit in self:
|
|
if visit.benefit_id:
|
|
url = "http://maps.google.com/maps/search/?api=1&query=%s,%s" % (
|
|
visit.benefit_id.lat, visit.benefit_id.lon),
|
|
return {
|
|
'type': 'ir.actions.act_url',
|
|
'target': 'new',
|
|
'url': url
|
|
}
|
|
|
|
def get_url_local(self):
|
|
for visit in self:
|
|
if visit.benefit_id:
|
|
url = "http://maps.google.com/maps/search/?api=1&query=%s,%s" % (
|
|
visit.benefit_id.lat, visit.benefit_id.lon)
|
|
return url
|
|
|
|
|
|
class MemberEducationStatus(models.Model):
|
|
_name = 'member.education.status'
|
|
|
|
name = fields.Char()
|