# This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. from datetime import datetime, timedelta, time from trytond.model import Workflow, ModelView, ModelSQL, DeactivableMixin, fields from trytond.pyson import Eval, Or, Bool, Not from trytond.transaction import Transaction from trytond.pool import Pool, PoolMeta from trytond.wizard import Wizard, StateView, Button, StateReport from trytond.report import Report from .exceptions import MessageUserError from trytond.modules.dash.dash import DashAppBase STATES = { 'readonly': (Eval('state') != 'draft'), } NEW_MEDIA = new_sel = [ ('', ''), ('sede', 'Sede'), ('web', 'Web'), ('supersalud', 'Supersalud'), ('chat', 'Chat'), ('email', 'Email'), ('write', 'Write'), ('perzonalized', 'Perzonalized'), ('social_media', 'Social Media'), ('phone', 'Phone'), ] CLASSIFICATION = new_sel = [ ('', ''), ('reclamo_riesgo_vital', 'Riesgo Vital'), ('reclamo_riesgo_priorizado', 'Riesgo Priorizado'), ('reclamo_riesgo_simple', 'Riesgo Simple'), # ('riesgo_vida', 'Riesgo de Vida'), # ('regular', 'Regular'), ] SEXUAL_DIVERSITY = [ ('N.A', 'N.A'), ('heteresexual', 'HETEROSEXUAL'), ('gay', 'GAY'), ('lesbiana', 'LESBIANA'), ('travesti', 'TRAVESTI'), ('bisexual', 'BISEXUAL'), ('lgbti', 'LGBTI'), ] ETHNICAL_GROUP = [ ('N.A', 'N.A'), ('afrocolombiano', 'AFROCOLOMBIANO'), ('palenquero', 'PALENQUERO'), ('indigena', 'INDÍGENA'), ('raizal', 'RAIZAL'), ('rom_gitano', 'ROM O GITANO'), ('mulato', 'MULATO'), ] OPTION_SELECT = [ ('', ''), ('N.A', 'N.A'), ('yes', 'Yes'), ('no', 'No'), ] # pending PENDING = [ (None, ''), ('medicamentos', 'Medicamentos: recibido del medicamento, firma del usuario y/o familiar '), ('consultas', 'Consultas (generales y especializadas): historia clínica'), ('ayudas_diagnosticas', 'Ayudas diagnósticas: resultado de la ayuda diagnóstica'), ('cirugias', 'Cirugías o procedimientos: historia clínica'), ('urgencias, hospitalización y remisiones', 'Urgencias, hospitalización y remisiones: historia clínica o epicrisis'), ('trámites administrativos (autorizaciones)', 'Trámites administrativos (autorizaciones): autorización'), ('novedades', 'Novedades en la base de datos nacional (HOSVITAL): pantallazo legible de HOSVITAL'), ('exclusiones', 'Exclusiones: listado de las exclusiones'), ('n.a', 'N.A'), ('otro', 'OTRO') ] DENIAL = [ ('', ''), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10','10'), ('11','11'), ('12','12'), ] DENIAL_DICT = { '1': 'Tratamientos de infertilidad.' + 'Entiéndase como los tratamientos' + 'y exámenes cuyo fin único y' + 'esencial sea el embarazo y' + 'la procreación.', '2': 'Tratamientos considerados estéticos,' + 'cosméticos o suntuarios no encaminados' + 'a la restitución de la funcionalidad' + 'perdida por enfermedad o la grave' + 'afectación estética por trauma o cirugía' + 'mayor.', '3': 'Todos los tratamientos quirúrgicos y' + 'medicamentos considerados experimentales' + 'o los no autorizados por las sociedades' + 'científicas debidamente reconocidas en el' + 'país, así serealicen y suministren por' + 'fuera del territorio Nacional.', '4': 'Se excluyen expresamente todos los' + 'tratamientos médico-quirúrgicos realizados' + 'en el exterior.', '5': 'Se excluyen todos los medicamentos no' + 'autorizados por el INVIMA o el ente regulador' + 'correspondiente.', '6': 'Se excluyen tecnologías en salud sobre las' + 'cuales no exista evidencia científica, de' + 'seguridad o costo efectividad o que tengan' + 'alertas de seguridad o falta de efectividad que' + 'recomienden su retiro del mercado, de acuerdo' + 'con la normatividad vigente.', '7': 'Prestaciones de salud en instituciones no' + 'habilitadas para tal fin dentro del sistema de' + 'salud.', '8': 'No se suministrarán artículos suntuarios,' + 'cosméticos, complementos vitamínicos (excepto' + 'los relacionados con los Programas de Promoción y' + 'Prevención) líquidos para lentes de contacto,' + 'tratamientos capilares, champús, jabones, enjuagues' + 'bucales, cremas dentales, cepillo y seda dental y' + 'demás elementos de aseo; leches, cremas hidratantes,' + 'anti solares, drogas para la memoria, edulcorantes' + 'o sustitutos de la sal, anorexígenos, anti-solares y' + 'cremas hidratantes serán cubiertas cuando sean' + 'necesarios para el tratamiento de la patología integral' + 'del paciente.', '9': 'No se reconocerán servicios por fuera del ámbito de' + 'la salud salvo algunos servicios complementarios y' + 'necesarios para el adecuado acceso a los servicios' + 'como el caso deltransporte.', '10': 'Calzado Ortopédico.', '11': 'Los pañales de niños y adultos y las toallas higiénicas.', '12': 'Todo lo que no está explícitamente excluido se' + 'considera incluido.' } class ReceiverService(ModelSQL, ModelView): 'Receicer Service' __name__ = 'crm.receiver_service' _rec_name = 'name' name = fields.Char('Name', required=True) emails = fields.One2Many('crm.receiver_service.email', 'receiver', 'Emails') def get_rec_name(self, name): return self.name @classmethod def search_rec_name(cls, name, clause): if clause[1].startswith('!') or clause[1].startswith('not '): bool_op = 'AND' else: bool_op = 'OR' return [ bool_op, ('name',) + tuple(clause[1:]), ] class ReceiverEmail(ModelSQL, ModelView): 'Receiver - Emails' __name__ = 'crm.receiver_service.email' receiver = fields.Many2One('crm.receiver_service', 'Receiver', required=True) email = fields.Char('Email') class ListSpecialty(DeactivableMixin, ModelSQL, ModelView): 'List Specialty' __name__ = 'list.specialty' name = fields.Char('Name', required=True) code = fields.Char('Code', required=True) @classmethod def __setup__(cls): super(ListSpecialty, cls).__setup__() class HealthProvider(ModelSQL, ModelView): 'Health Provider' __name__ = 'crm.health_provider' _rec_name = 'party' name = fields.Function(fields.Char('Name'), 'get_rec_name') party = fields.Many2One('party.party', 'Party') city = fields.Many2One('party.city_code', 'City') asset = fields.Boolean('Asset', help="Check this if the provider is active!") def get_rec_name(self, name): return self.party.name @classmethod def search_rec_name(cls, name, clause): if clause[1].startswith('!') or clause[1].startswith('not '): bool_op = 'AND' else: bool_op = 'OR' return [bool_op, ('party.name',) + tuple(clause[1:]), ] class CustomerService(metaclass=PoolMeta): __name__ = 'crm.customer_service' receiver = fields.Many2One('crm.receiver_service', 'Receiver', states={ 'required': (Eval('state') == 'open'), }) region = fields.Many2One('crm.region_fiduprevisora', 'Region') department_region = fields.Many2One('party.department_code', 'Department', domain=[ ('region', '=', Eval('region')) ]) city_region = fields.Many2One('party.city_code', 'City', depends=['party'], # domain=[ # ('department', '=', Eval('department_region')) # ] ) other_city = fields.Char('Other City', states={ 'required': (Not(Bool(Eval('city_region')))), 'invisible': (Bool(Eval('city_region'))), }) health_provider = fields.Many2One('crm.health_provider', 'Health Provider', domain=[ ('city', '=', Eval('city_region')), ('asset', '=', True) ]) id_document = fields.Char('Document') email = fields.Char('Email') sended_mail = fields.Boolean('Sended Email') attach_customer_1 = fields.Char('Attach Customer Web...', readonly=True) attach_customer_2 = fields.Char('Attach Customer Web...', readonly=True) attachments = fields.One2Many('crm.fiduprevisora.attachments', 'service', 'Attachments') others_receivers = fields.One2Many('crm.customer_receiver', 'customer_service', 'Receivers') others_receivers_string = fields.Function(fields.Char('Others Receicers'), 'get_others_receivers_string') classification = fields.Selection(CLASSIFICATION, 'Classification', states={ 'required': (Eval('media') == 'supersalud'), 'invisible': (Eval('media') != 'supersalud'), }) support = fields.Selection([ ('ok', 'OK'), ('pendiente', 'Pendiente') ], 'Soporte', states={'required': True} ) # pending = fields.Selection(PENDING, 'Pending', # states={'invisible': # (Eval('support') != 'ok') # }) pending = fields.MultiSelection(PENDING, 'Pending', states={ 'invisible': (Eval('support') != 'ok') }) other = fields.Text('Other') # states={ # 'invisible': ~Eval('invoice_types', []), # }, translate=True, depends=['pending'],) detailed_especific_1 = fields.Many2One('list.specialty', 'Detailed Especific 1', states={'required': True}) detailed_especific_2 = fields.Many2One('list.specialty', 'Detailed Especific 2') detailed_especific_3 = fields.Many2One('list.specialty', 'Detailed Especific 3') ethnical_group = fields.Selection(ETHNICAL_GROUP, 'Ethnical Group', states={'required': False}) sexual_diversity = fields.Selection(SEXUAL_DIVERSITY, 'Sexual Diversity', states={'required': False}) displaced = fields.Selection(OPTION_SELECT, 'Displaced', states={'required': False}) victim_of_violence = fields.Selection(OPTION_SELECT, 'Victim Of Violence', states={'required': False}) disabled_person = fields.Selection(OPTION_SELECT, 'Disabled Person', states={'required': False}) pregnant_woman = fields.Selection(OPTION_SELECT, 'Pregnan Woman', states={'required': False}) homeless = fields.Selection(OPTION_SELECT, 'Homeless', states={'required': False}) deprived_of_freedom = fields.Selection(OPTION_SELECT, 'Deprived Of Freedom', states={'required': False}) nursing_mother = fields.Selection(OPTION_SELECT, 'Nursing Mother', states={'required': False}) older_adult = fields.Selection(OPTION_SELECT, 'Older Adult', states={'required': False}) older_adult_string = older_adult.translated('older_adult') current_risk = fields.Function(fields.Char('Current Risk'), 'get_current_risk', searcher='search_current_risk') age = fields.Function(fields.Integer('Age'), 'get_age') media_string = older_adult.translated('media') # petotionary data petionary = fields.Char('petionary') petionary_phone = fields.Char('Phone') petionary_mail = fields.Char('Mail') petionary_address = fields.Char('Address') court_decision = fields.Boolean('Court Decision') denial = fields.Selection(DENIAL, 'denial') denial_text = fields.Function(fields.Text('Denial Text'), 'get_denial_text') @classmethod def __setup__(cls): super(CustomerService, cls).__setup__() cls.party.required = True cls.media.states = { 'readonly': (Eval('state') == 'cancelled'), } cls.media.selection = NEW_MEDIA cls._buttons.update({ 'send_email': { 'invisible': Or( Eval('state') != 'open', Bool(Eval('sended_mail')), )}, }) cls.effective_date.states = { 'readonly': True, } @classmethod @ModelView.button def send_email(cls, records): for service in records: if not service.response: service.get_message('Debe existir una respuesta.') config = Pool().get('crm.configuration')(1) template = config.response_mail_template if not template: service.get_message( 'No se ha definido una plantilla para el envío del correo.') _attachments = [] if service.attachments: for att in service.attachments: q = str(att.attach_response)[1:10] ext_ = service.get_extension(q) _attachments.append({ 'attachment': att.attach_response, 'file_name': 'Respuesta', 'extension': ext_, }) service.send_email_customer(template, _attachments) service.send_emails_receivers(template, _attachments) @staticmethod def default_region(): Configuration = Pool().get('crm.configuration') config = Configuration(1) if config and config.region_defect: return config.region_defect.id @fields.depends('party', 'address', 'phone') def on_change_party(self): super(CustomerService, self).on_change_party() if self.party: self.email = self.party.email self.phone = self.party.phone self.address = self.party.street if self.party.city_attention: self.city_region = self.party.city_attention.id self.department_region = self.party.city_attention.department.id @fields.depends('effective_date', 'response') def on_change_with_effective_date(self): if self.response and not self.effective_date: return datetime.now() else: return self.effective_date @classmethod def create(cls, vlist): services = super(CustomerService, cls).create(vlist) for service in services: if not service.department_region and service.city_region: cls.write([service], {'department_region': service.city_region.department}) return services @classmethod @ModelView.button @Workflow.transition('open') def open(cls, services): if not services: return res = cls.validate_service(services) service = services[0] if res['status'] == 'success': super(CustomerService, cls).open(services) if service.email or service.party.email: service.send_email_notification( service.email or service.party.email ) service.send_emails_department() else: cls.write([service], {'state': 'cancelled', 'notes': res['msg']}) if service.media == 'web': res['customer'] = service.party.name res['media'] = 'web' res['city_region'] = { 'id': service.party.city_attention.id, 'name': service.party.city_attention.name, } return res @classmethod def validate_service(cls, services): res = { 'status': 'success', 'msg': 'Requerimiento enviado satifactoriamente !!', } if not services: return res service = services[0] if service.party and service.party.affiliation_state == 'retirado': message = 'El proceso no se pudo completar, el usuario: ' + service.party.name + ' Se encuentra en estado retirado' res['status'] = 'error' res['msg'] = message if service.media != 'web': service.get_message(message) return res if service.media == 'web': Attachment = Pool().get('ir.attachment') attachments = Attachment.search([ ('resource', '=', 'crm.customer_service,' + str(service.id)) ]) attach = attachments or service.attach_customer_1 or service.attach_customer_2 or None if service.case.required_attach and not attach: message = 'El proceso no se pudo completar, por favor adjuntar: \n' + \ service.case.attach_description res['status'] = 'info' res['msg'] = message if service.media != 'web': service.get_message(message) return res date_ = service.cs_date - timedelta(days=30) services_ = cls.search([ ('id', '!=', service.id), ('cs_date', '>=', date_), ('case', '=', service.case.id), ('party.id_number', '=', service.party.id_number), ('state', '=', 'open'), ]) if services_: message = 'El proceso no se pudo completar, ya existe un PQR para el usuario:' + \ services_[0].party.name + ' con el caso: ' + services_[0].case.name + ' en los últimos 30 días; Caso numero '+ \ str(services_[0].number) res['status'] = 'error' res['msg'] = message if service.media != 'web': service.get_message(message) return res def get_denial_text(self, name): if self.denial: return DENIAL_DICT[self.denial] def get_age(self, name): if self.party: return str(self.party.age) def get_others_receivers_string(self, name): string_ = '' for r in self.others_receivers: if r.receiver: string_ += r.receiver.name + ' /n' return string_ def get_extension(self, string_): ext_ = '' if string_.find('PDF') > 0: ext_ = 'PDF' elif string_.find('PNG') > 0: ext_ = 'PNG' elif string_.find('JPEG') > 0: ext_ = 'JPEG' elif string_.find('JPG') > 0: ext_ = 'JPG' elif string_.find('DOC') > 0: ext_ = 'DOC' elif string_.find('ODS') > 0: ext_ = 'ODS' elif string_.find('ODT') > 0: ext_ = 'ODT' return ext_ def get_current_risk(self, name=None): if self.cs_date: days_diference = datetime.now() - self.cs_date if self.media == 'supersalud': if self.classification == 'reclamo_riesgo_vital' and self.state == 'open': # return 'Higher' return 'Alto' if self.classification == 'reclamo_riesgo_priorizado' and self.state == 'open': if days_diference.days == 0: # return 'Medium' return 'Medio' if days_diference.days >= 1: # return 'Higher' return 'Alto' if self.classification == 'reclamo_riesgo_simple' and self.state == 'open': if days_diference.days >= 2: # return 'Higher' return 'Alto' if days_diference.days >= 1: # return 'Medium' return 'Medio' # return 'low' return 'Bajo' @classmethod def search_current_risk(cls, name, clause): cursor = Transaction().connection.cursor() if clause[2] == 'higher': query = f"""select id from crm_customer_service where (state='open' AND media='supersalud') AND ((classification='reclamo_riesgo_vital') OR (DATE_PART('day', CURRENT_DATE - cs_date) > 0 AND classification='reclamo_riesgo_priorizado') OR (DATE_PART('day', CURRENT_DATE - cs_date) > 1 AND classification='reclamo_riesgo_simple'));""" elif clause[2] == 'medium': query = f"""select id from crm_customer_service where (state='open' AND media='supersalud') AND ((DATE_PART('day', CURRENT_DATE - cs_date)=0 AND classification='reclamo_riesgo_priorizado') OR (DATE_PART('day', CURRENT_DATE - cs_date)=1 AND classification='reclamo_riesgo_simple'));""" elif clause[2] == 'low': query = f"""select id from crm_customer_service where (state='open' AND media='supersalud') AND ((DATE_PART('day', CURRENT_DATE - cs_date)=0 AND classification='reclamo_riesgo_simple'));""" cursor.execute(query) result = cursor.fetchall() ids = [tupla[0] for tupla in result] return [('id', 'in', ids)] def send_email_customer(self, template, attachments=[]): if self.department_region and self.department_region.emails: dmt_email = self.department_region.emails[0].email template.from_email = dmt_email template.subject = template.subject + self.company.party.name if attachments: response = self._send_mails( template, self, self.email or self.party.email, True, attachments) else: response = self._send_mails( template, self, self.email or self.party.email, False, []) if response.status_code == 202: self.write([self], {'sended_mail': True}) else: self.get_message('Error de Envío.') def send_email_notification(self, email): pool = Pool() Template = pool.get('email.template') config = pool.get('crm.configuration')(1) template = config.notification_mail_template if template: # attach_dict = {} template.subject = template.subject + self.company.party.name response = Template.send(template, self, email, attach=False, attachments=[]) if not response or response.status_code != 202: self.write({self}, {'notes': 'Fallo envio al correo: ' + email}) def send_emails_department(self): if self.department_region: for email in self.department_region.emails: self.send_email_notification(email.email) def send_emails_receivers(self, template, attachments=[]): attach = False if attachments: attach = True template.subject = template.subject + self.company.party.name if self.department_region and self.department_region.emails: dmt_email = self.department_region.emails[0].email template.from_email = dmt_email if self.receiver: for email in self.receiver.emails: response = self._send_mails(template, self, email.email, attach, attachments) if response.status_code != 202: self.write({self}, {'notes': 'Fallo envio al correo: ' + email}) for rec in self.others_receivers: for email in rec.receiver.emails: response = self._send_mails(template, self, email.email, attach, attachments) if response.status_code != 202: self.write({self}, {'notes': 'Fallo envio al correo: ' + email}) def _send_mails(self, template, crm, email, attach, attachments): Template = Pool().get('email.template') response = Template.send(template, crm, email, attach=attach, attachments=attachments) return response def get_message(self, message): raise MessageUserError('message', message) @classmethod def create_service(cls, args): pool = Pool() Date = pool.get('ir.date') Party = pool.get('party.party') today = Date.today() date_ = today - timedelta(days=30) dt = datetime.combine(date_, datetime.min.time()) res = { 'status': 'success', } services = cls.search([ ('party', '=', args['party']), ('cs_date', '>=', dt), ('state', '=', 'open'), ('case', '=', args['case']), ]) services = None special_population = args.pop('special_population') if args.get('special_population') else [] fields_ = [ 'sexual_diversity', 'displaced', 'victim_of_violence', 'disabled_person', 'pregnant_woman', 'homeless', 'deprived_of_freedom', 'nursing_mother', 'older_adult', ] for f in fields_: if f == 'sexual_diversity': if 'lgbti' in special_population: args[f] = 'lgbti' else: args[f] = 'N.A' elif f in special_population: args[f] = 'yes' else: args[f] = 'N.A' if not services: args['cs_date'] = datetime.now() args['media'] = 'web' args['customer'] = Party(args['party']).name args['company'] = 1 # fix me this value must be variable args['receiver'] = 8 record, = cls.create([args]) response = cls.open([record]) if response: return response # print(record.state, record.number, 'valida estado y numero') res['record'] = {'id': record.id} res['msg'] = 'Requerimiento enviado satifactoriamente, toma nota del numero de radicado para posterior consulta de estado ' + record.number else: service = services[0] message = 'El proceso no se pudo completar, ya existe un PQR para el usuario:' + \ service.party.name + ' con el caso: ' + service.case.name + ' en los últimos 30 días ' res['status'] = 'error' res['msg'] = message return res class FiduprevisoraAttachments(ModelSQL, ModelView): 'Fiduprevisora - Attachments' __name__ = 'crm.fiduprevisora.attachments' service = fields.Many2One('crm.customer_service', 'Service', required=True) attach_response = fields.Binary('Attach Response', required=True) class FiduprevisoraReportStart(ModelView): 'Fiduprevisora Report Start' __name__ = 'crm_fiduprevisora.fiduprevisora_report.start' company = fields.Many2One('company.company', 'Company', required=True) start_date = fields.Date('Start Date') end_date = fields.Date('End Date') party = fields.Many2One('party.party', 'Party') region = fields.Many2One('crm.region_fiduprevisora', 'Region') receiver_service = fields.Many2One('crm.receiver_service', 'Receiver') department_region = fields.Many2One('party.department_code', 'Department', domain=[ ('region', '=', Eval('region')) ]) city_region = fields.Many2One('party.city_code', 'City', domain=[ ('department', '=', Eval('department_region')) ]) @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_region(): Configuration = Pool().get('crm.configuration') config = Configuration(1) if config and config.region_defect: return config.region_defect.id @staticmethod def default_end(): Date = Pool().get('ir.date') return Date.today() class FiduprevisoraReportWizard(Wizard): 'Fiduprevisora Report Wizard' __name__ = 'crm_fiduprevisora.fiduprevisora_report.wizard' start = StateView( 'crm_fiduprevisora.fiduprevisora_report.start', 'crm_fiduprevisora.fiduprevisora_report_start_view_form', [ Button('Cancel', 'end', 'tryton-cancel'), Button('Print', 'print_', 'tryton-ok', default=True), ]) print_ = StateReport('crm_fiduprevisora.fiduprevisora_report') def do_print_(self, action): party_id = self.start.party.id if self.start.party else None region_id = self.start.region.id if self.start.region else None department_id = self.start.department_region.id if self.start.department_region else None city_id = self.start.city_region.id if self.start.city_region else None receiver_service_id = self.start.receiver_service.id if self.start.receiver_service else None data = { 'company': self.start.company.id, 'start_date': self.start.start_date, 'end_date': self.start.end_date, 'party': party_id, 'region': region_id, 'department': department_id, 'city': city_id, 'receiver_service': receiver_service_id, } return action, data def transition_print_(self): return 'end' class FiduprevisoraReport(Report): 'Fiduprevisora Report' __name__ = 'crm_fiduprevisora.fiduprevisora_report' @classmethod def get_context(cls, records, header, data): report_context = super(FiduprevisoraReport, cls).get_context(records, header, data) pool = Pool() Company = pool.get('company.company') Service = pool.get('crm.customer_service') company = Company(data['company']) start_date = data['start_date'] end_date = data['end_date'] party = data['party'] region = data['region'] receiver_service = data['receiver_service'] department_region = data['department'] city_region = data['city'] records = {} dom_service = [ ('company', '=', data['company']), ('state', '!=', 'cancelled'), ] if start_date: start_date = datetime.combine(start_date, time(0,0)) dom_service.append( ('cs_date', '>=', start_date), ) if end_date: end_date = datetime.combine(end_date, time(23,59)) dom_service.append( ('cs_date', '<=', end_date), ) if party: dom_service.append( ('party', '=', party), ) if region: dom_service.append( ('region', '=', region), ) if receiver_service: dom_service.append( ('receiver', '=', receiver_service), ) if department_region: dom_service.append( ('department_region', '=', department_region), ) if city_region: dom_service.append( ('city_region', '=', city_region), ) services = Service.search(dom_service) report_context['records'] = services report_context['start_date'] = start_date report_context['end_date'] = end_date report_context['company'] = company.party.name return report_context class CustomerReceiver(ModelSQL, ModelView): 'Customer Receiver' __name__ = 'crm.customer_receiver' customer_service = fields.Many2One('crm.customer_service', 'Customer Service', required=True) receiver = fields.Many2One('crm.receiver_service', 'Receiver', required=True) media = fields.Selection(NEW_MEDIA, 'Media', required=True) class AppCustomerFiduprevisora(DashAppBase): 'App Customer Fiduprevisora' __name__ = 'dash.app.crm_fiduprevisora'