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()