from odoo import models, fields, api,_ from odoo.exceptions import UserError, ValidationError,Warning from psycopg2 import sql, DatabaseError from werkzeug import utils import json import logging import xlwt from io import BytesIO import base64 import csv import os,secrets _logger = logging.getLogger(__name__) # MASS_MAILING_BUSINESS_MODELS = [ # 'crm.lead', # 'event.registration', # 'hr.applicant', # 'res.partner', # 'event.track', # 'sale.order', # 'mailing.list', # 'mailing.contact' # ] class opendons_operation(models.Model): _name = 'opendons.operation' _description = 'opération marketing : mailing, emailing evenements' _inherit = ['mail.thread'] _check_company_auto = True #_order = "__last_update desc" code=fields.Char(string='Code',required=True, translate=True,track_visibility='always') name=fields.Char(string='Name',required=True, translate=True,copy=False,default='campagne',track_visibility='always') partner_count = fields.Integer(string="count",readonly=True) exported_date=fields.Date(string='Exported Date',track_visibility='always', readonly=True) #end_date=fields.Date(string='Date end',required=True, translate=True,track_visibility='always') partner_ids = fields.Many2many('res.partner', 'partner_operation_rel', 'partner_id', 'operation_id', string='partners') #contacts exclus de l'opération company_id = fields.Many2one( "res.company", string="Company", required=True, states={"exported": [("readonly", True)]}, default=lambda self: self.env.company ) partner_excl_ids=fields.Many2many('res.partner', 'partner_excl_operation_rel', 'partner_id', 'operation_id', string='excluded partners') chanel=fields.Selection([ ('mail', 'Mailing'), ('email', 'E-mailing'), ('event', 'Event'), ('face_to_face', 'Face to face')], string='Chanel',required=True, translate=True,track_visibility='always') user_id = fields.Many2one('res.users', string='Author', default=lambda self: self.env.uid) cost = fields.Monetary( string='Cost', currency_field='currency_id') @api.model def _default_currency(self): company = self.env['res.company']._company_default_get( 'opendons.operation') return company.currency_id currency_id = fields.Many2one( 'res.currency', string='Currency', required=True, track_visibility='onchange', ondelete='restrict', default=_default_currency ) number_of_sending = fields.Integer( compute='_compute_number_of_sending', string="# of sending", readonly=True ) segment_ids = fields.One2many( 'opendons.segment', 'operation_id', string='Segments', readonly=True ) segment_count = fields.Integer( string="# of segment", readonly=True ) state = fields.Selection([ ('draft', 'Draft'), ('validated', 'Validated'), ('exported', 'Exported')], string='State', readonly=True, copy=False, default='draft', index=True, track_visibility='onchange' ) csv_export = fields.Binary('csv export', filters='.csv', readonly=True) document_fname=fields.Char() def validate(self): for operation in self: vals = {'state': 'validated'} operation.write(vals) return def validated2draft(self): '''from Done state to Cancel state''' for operation in self: operation.state = 'draft' def validated2exported(self): '''from Done state to Cancel state''' for operation in self: operation.state = 'exported' def exported2validated(self): '''from Done state to Cancel state''' for operation in self: operation.state = 'validated' def csv_export_operation(self): tmstp=secrets.token_hex(16) csv_filename='/tmp/export_'+tmstp+'.csv' with open(csv_filename, mode='w') as file: writer = csv.writer(file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) if self.chanel=='email': writer.writerow(['id','key','name','firstname','email','operation','segment']) if self.chanel=='mail': writer.writerow(['id','key','name','firstname','title','street','street2','city','cp','country_name','operation','segment']) #sélections des contacts res=self.select_partner_operation() segments=self.env['opendons.segment'].search(['&',('operation_id','=',self.id),('exclusion','=',False)]) #extraction des contacts sélectionnés sur chaque segment for seg in segments: if seg.partner_ids: for p in seg.partner_ids: #raise UserError(p) id=p.id key=p.key name=p.name firstname=p.firstname if p.firstname else '' street=p.street if p.street else '' street2=p.street2 if p.street2 else '' city=p.city if p.city else '' cp=p.zip if p.zip else '' country_name=p.country_id.name if p.country_id else '' title=p.title.name if p.title else '' operation=int(self.id) segment=int(seg.id) if self.chanel=='email': writer.writerow([id,key,name,firstname,email,operation,segment]) if self.chanel=='mail': writer.writerow([id,key,name,firstname,title,street,street2,city,cp,country_name,operation,segment]) with open(csv_filename, 'r', encoding="utf-8") as f2: # file encode and store in a variable ‘data’ data = str.encode(f2.read(), 'utf-8') self.csv_export=base64.encodestring(data) self.document_fname='operation'+str(self.id)+'.csv' self.exported_date=fields.Date.context_today(self) os.unlink(csv_filename) #raise Warning('Traitement terminé') return def select_partner_operation(self): # suppression selections précédemment calculées sur l'opération self.partner_ids=False self.partner_excl_ids=False #suppression des selections précédemment calculées sur sur les segments segments=self.env['opendons.segment'].search(['&',('operation_id','=',self.id),('exclusion','=',False)]) for seg in segments: seg.partner_ids=False segexcl=self.env['opendons.segment'].search(['&',('operation_id','=',self.id),('exclusion','=',True)]) segments=self.env['opendons.segment'].search(['&',('operation_id','=',self.id),('exclusion','=',False)],order='sequence asc') i=1 for sg in segments: if i==1: mailing_domain='["&","!",'+segexcl.mailing_domain[1:-1]+','+ sg.mailing_domain[1:-1]+']' #["&","!",["id","=",1],["id","=",2]] #["&","!",[["id","=",1]],[["id","=",1]]] #raise UserError(str(mailing_domain)) #try: partners=self.env['res.partner'].search(eval(mailing_domain)) #except: # raise UserError(mailing_domain) #ajouts des contact à l'opération self.partner_ids=partners #ajouts des contacts au segment sg.partner_ids=partners #sg_1=sg else: #on doit exclure tous les contacts qui pourraient être dans les segments précédent sg_up=self.env['opendons.segment'].search(['&',('operation_id','=',self.id),('sequence','<',int(sg.sequence))]) if len(sg_up)>1: str_domain='"&",' j=1 for s in sg_up: if j==1: str_domain=str_domain + s.mailing_domain[1:-1] else: str_domain= '"&",' + str_domain & s.mailing_domain[1:-1] j=j+1 str_domain=str_domain+',' else: str_domain='' #mailing_domain='["&","!","&",'+segexcl.mailing_domain[1:-1]+','+ sg_1.mailing_domain[1:-1]+',' +sg.mailing_domain[1:-1]+']' mailing_domain='["&","!",'+str_domain+ segexcl.mailing_domain[1:-1]+',' +sg.mailing_domain[1:-1]+']' #raise UserError(str_domain) partners=self.env['res.partner'].search(eval(mailing_domain)) #ajouts des contact à l'opération self.partner_ids=partners #ajouts des contacts au segment sg.partner_ids=partners sg_1=sg i=i+1 self.exported_date=fields.Date.context_today(self, readonly=True) return True @api.model def create(self,vals): op=self.env['opendons.operation'].search(['&',('name','=',vals['name']),('company_id','=',int(self.env.user.company_id))]) if "company_id" in vals: self = self.with_company(vals["company_id"]) if op : raise Warning(_('name already exist')) res=super(opendons_operation, self).create(vals) #création du segment d'exclusion vals2={} vals2['operation_id']=res.id vals2['sequence']=1 vals2['name']='Exclusion' vals2['exclusion']=True res2=self.env['opendons.segment'].create(vals2) return res def export_partners(self): partners = self.env['res.partner'].sudo().search([]) stream = BytesIO() book = xlwt.Workbook(encoding='utf-8') sheet = book.add_sheet(u'{}'.format(u'My Sheet')) row = 1 cell = 0 for p in partners: sheet.write(row, cell, p.name) row += 1 book.save(stream) self.excel_report = base64.encodestring(stream.getvalue()) self.document_fname='operation'+seld.id+'.csv' return def copy(self,default_values=None): vals={} vals['name']='copy of ' + self.name vals['chanel']=self.chanel vals['user_id']=self._uid vals['state']='draft' dup_operation=super(opendons_operation, self).create(vals) #on duplique les segment de l'opération for segment_id in self.segment_ids: #raise Warning(segment_id.id) seg=self.env['opendons.segment'].search([('id','=',int(segment_id.id))]) if seg: vals2={} vals2['operation_id']=dup_operation.id vals2['name']=seg.name vals2['mailing_domain']=seg.mailing_domain #dup_segment=super(opendons_segment,self).create(vals2) dup_segment=self.env['opendons.segment'].create(vals2) dup_operation.write({'segment_ids':[(4,dup_segment.id)]}) for ensemble_id in seg.ensemble_ids: ens=self.env['opendons.ensemble'].search([('id','=',int(ensemble_id.id))]) if ens: vals3={} vals3['segment_id']=dup_segment.id vals3['name']=ens.name vals3['mailing_domain']=ens.mailing_domain #dup_segment=super(opendons_segment,self).create(vals2) dup_ensemble=self.env['opendons.ensemble'].create(vals3) dup_segment.write({'ensemble_ids':[(4,dup_ensemble.id)]}) for request_id in ens.request_ids: req=self.env['opendons.request'].search([('id','=',int(request_id.id))]) if req: vals4={} vals4['ensemble_id']=dup_ensemble.id vals4['name']=req.name vals4['mailing_domain']=req.mailing_domain #dup_segment=super(opendons_segment,self).create(vals2) dup_request=self.env['opendons.request'].create(vals4) dup_ensemble.write({'request_ids':[(4,dup_request.id)]}) #rtn=super(opendons_operation,self).copy(default=default_values) return dup_operation class opendons_segment(models.Model): _name = 'opendons.segment' _description = 'operation marketing segment : a segment is a part of contacts selected for an operation ' _inherit = ['mail.thread'] sequence = fields.Integer(string="sequence", default=10) #code=fields.Char(string='Code',required=True, translate=True,track_visibility='always') name=fields.Char(string='Name',required=True, translate=True,track_visibility='always') exclusion=fields.Boolean(String='segment d\'exclusion',default=False,readonly=True) logical_operator = fields.Selection([('union','union'),('inter','intersection')],'Type',default='union') cost = fields.Monetary( string='Cost', currency_field='currency_id') partner_ids = fields.Many2many('res.partner', 'partner_segment_rel', 'partner_id', 'segment_id', string='partners') partner_count = fields.Integer(string="partners count", compute='_count_partner_segment',readonly=True) mailing_domain = fields.Char(string='partners selection') @api.model def _default_currency(self): company = self.env['res.company']._company_default_get( 'opendons.segment') return company.currency_id currency_id = fields.Many2one( 'res.currency', string='Currency', required=True, track_visibility='onchange', ondelete='restrict', default=_default_currency ) operation_id = fields.Many2one( 'opendons.operation', String='Operation', index=True, readonly=True, track_visibility='onchange', ondelete='cascade' ) ensemble_ids = fields.One2many( 'opendons.ensemble', 'segment_id', string='Ensemble', required=True, track_visibility='onchange') ensemble_count = fields.Integer( compute='_compute_ensemble_count', string="# of ensemble", readonly=True ) chanel=fields.Selection([ ('mail', 'Mailing'), ('email', 'E-mailing'), ('event', 'Event'), ('face_to_face', 'Face to face')], string='Chanel',required=True, translate=True,track_visibility='always') @api.depends('mailing_domain') def _count_partner_segment(self): for rec in self: if rec.mailing_domain : rec.partner_count=self.env['res.partner'].search_count(eval(rec.mailing_domain)) else: rec.partner_count=0 def _compute_ensemble_count(self): # The current user may not have access rights for donations for segment in self: try: segment.ensemble_count = len(segment.ensemble_ids) except Exception: segment.ensemble_count = 0 @api.model def create(self,vals): res=super(opendons_segment, self).create(vals) #segment=self.env['opendons.segment'].search([('id','=',int(res.id))]) operation=self.env['opendons.operation'].search([('id','=',int(res.operation_id))]) #_logger.error('segment.id='+str(res.operation_id)) __last_update=fields.Date.context_today(self) operation.write( { 'segment_count':len(operation.segment_ids), '__last_update':__last_update } ) return res def write(self,vals): res=super(opendons_segment, self).write(vals) mailing_domain=False for val in vals: if val=='logical_operator': mailing_domain=self.env['opendons.ensemble'].update_segment_domain(self) if mailing_domain: vals['mailing_domain']=mailing_domain res=super(opendons_segment, self).write(vals) operation=self.env['opendons.operation'].search([('id','=',int(self.operation_id))]) __last_update=fields.Date.context_today(self) operation.write({'__last_update':__last_update}) def copy(self,default_values=None): vals={} vals['name']='copy of ' + self.name vals['operation_id']=int(self.operation_id) vals['mailing_domain']=self.mailing_domain dup_segment=super(opendons_segment, self).create(vals) #on duplique les ensembles du segment for ensemble_id in self.ensemble_ids: #raise Warning(segment_id.id) ens=self.env['opendons.ensemble'].search([('id','=',int(ensemble_id.id))]) if ens: vals3={} vals3['segment_id']=dup_segment.id vals3['name']=ens.name vals3['mailing_domain']=ens.mailing_domain #dup_segment=super(opendons_segment,self).create(vals2) dup_ensemble=self.env['opendons.ensemble'].create(vals3) dup_segment.write({'ensemble_ids':[(4,dup_ensemble.id)]}) for request_id in ens.request_ids: req=self.env['opendons.request'].search([('id','=',int(request_id.id))]) if req: vals4={} vals4['ensemble_id']=dup_ensemble.id vals4['name']=req.name vals4['mailing_domain']=req.mailing_domain #dup_segment=super(opendons_segment,self).create(vals2) dup_request=self.env['opendons.request'].create(vals4) dup_ensemble.write({'request_ids':[(4,dup_request.id)]}) #rtn=super(opendons_operation,self).copy(default=default_values) return dup_segment class opendons_ensemble(models.Model): _name = 'opendons.ensemble' _description = 'operation marketing ensemble : an ensemble is a part of contacts selected for an segment ' _inherit = ['mail.thread'] name=fields.Char(string='Name',required=True, translate=True,track_visibility='always') logical_operator = fields.Selection([('union','union'),('inter','intersection')],'Type',default='union') mailing_domain = fields.Char(string='partners selection') partner_count = fields.Integer(string="partners count",compute='_count_partner_ensemble',readonly=True) #partner_ids = fields.Many2many('res.partner', 'partner_segment_rel', 'partner_id', 'segment_id', string='partners') sequence = fields.Integer(string="sequence", default=10) segment_id = fields.Many2one( 'opendons.segment','Segment', index=True, readonly=True, track_visibility='onchange', ondelete='cascade') request_ids = fields.One2many( 'opendons.request', 'ensemble_id', string='Request', required=True, track_visibility='onchange') request_count = fields.Integer( compute='_compute_request_count', string="# of request", readonly=True ) csv_export = fields.Binary('csv export', filters='.csv', readonly=True) document_fname=fields.Char() @api.depends('mailing_domain') def _count_partner_ensemble(self): for rec in self: if rec.mailing_domain : rec.partner_count=self.env['res.partner'].search_count(eval(rec.mailing_domain)) else: rec.partner_count=0 def csv_export_ensemble(self): with open('export.csv', mode='w') as file: writer = csv.writer(file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) # create a row contains heading of each column writer.writerow(['name', 'email']) # fetch products and write respective data. partners = self.env['res.partner'].sudo().search([]) for p in partners: name=p.name email=p.email writer.writerow([name,email]) with open('export.csv', 'r', encoding="utf-8") as f2: # file encode and store in a variable ‘data’ data = str.encode(f2.read(), 'utf-8') self.csv_export=base64.encodestring(data) self.document_fname='contacts.csv' return def _compute_request_count(self): # The current user may not have access rights for donations for ensemble in self: try: ensemble.request_count = len(ensemble.request_ids) except Exception: ensemble.request_count = 0 def update_segment_domain(self,segment): logical_operator=segment.logical_operator if logical_operator=='union': str_operator='"|",' if logical_operator=='inter': str_operator='"&",' mailing_domain='' if len(segment.ensemble_ids)>1: i=1 for ens in segment.ensemble_ids: if i==1 and ens.mailing_domain!=False: mailing_domain=ens.mailing_domain[1:-1] if i>1 : if ens.mailing_domain!=False: mailing_domain=str_operator +mailing_domain+","+ens.mailing_domain[1:-1] i+=1 mailing_domain="["+mailing_domain+"]" if len(segment.ensemble_ids)==1: for ens in segment.ensemble_ids: mailing_domain=ens.mailing_domain return mailing_domain @api.model def create(self,vals): res=super(opendons_ensemble, self).create(vals) segment=self.env['opendons.segment'].search([('id','=',int(res.segment_id))]) #_logger.error('segment.id='+str(res.operation_id)) segment_domain=self.update_segment_domain(segment) segment.write( { 'ensemble_count':len(segment.ensemble_ids), 'mailing_domain':segment_domain, }) operation=self.env['opendons.operation'].search([('id','=',int(segment.operation_id))]) __last_update=fields.Date.context_today(self) operation.write({'__last_update':__last_update}) return res def write(self,vals): res=super(opendons_ensemble, self).write(vals) mailing_domain=False for val in vals: if val=='logical_operator': mailing_domain=self.env['opendons.request'].update_ensemble_domain(self) if mailing_domain: vals['mailing_domain']=mailing_domain segment=self.env['opendons.segment'].search([('id','=',int(self.segment_id))]) segment_domain=self.update_segment_domain(segment) segment.write({ 'ensemble_count':len(segment.ensemble_ids), 'mailing_domain':segment_domain }) segment=self.env['opendons.segment'].search([('id','=',int(self.segment_id))]) operation=self.env['opendons.operation'].search([('id','=',int(segment.operation_id))]) __last_update=fields.Date.context_today(self) operation.write({'__last_update':__last_update}) res=super(opendons_ensemble, self).write(vals) def copy(self,default_values=None): vals={} vals['name']='copy of ' + self.name vals['segment_id']=int(self.segment_id) vals['mailing_domain']=self.mailing_domain dup_ensemble=super(opendons_ensemble, self).create(vals) #on duplique les requêtes de l'ensemble for request_id in self.request_ids: #raise Warning(segment_id.id) req=self.env['opendons.request'].search([('id','=',int(request_id.id))]) if req: vals3={} vals3['ensemble_id']=dup_ensemble.id vals3['name']=req.name vals3['mailing_domain']=req.mailing_domain #dup_segment=super(opendons_segment,self).create(vals2) dup_request=self.env['opendons.request'].create(vals3) dup_ensemble.write({'request_ids':[(4,dup_request.id)]}) #rtn=super(opendons_operation,self).copy(default=default_values) return dup_ensemble class opendons_request(models.Model): _name = 'opendons.request' _description = 'operation marketing request : a request is a part of contacts selected for an ensemble ' _inherit = ['mail.thread'] sequence = fields.Integer(string="sequence", default=10) name=fields.Char(string='Name',required=True, translate=True,track_visibility='always') partner_count = fields.Integer(string="partners count", compute='_count_partner_request', readonly=True) partner_ids = fields.Many2many('res.partner', 'partner_request_rel', 'partner_id', 'request_id', string='partners') ensemble_id = fields.Many2one( 'opendons.ensemble','Ensemble', index=True, readonly=True, track_visibility='onchange', ondelete='cascade') mailing_domain = fields.Char( string='Domain', readonly=False, store=True) @api.depends('mailing_domain') def _count_partner_request(self): for rec in self: if rec.mailing_domain : rec.partner_count=self.env['res.partner'].search_count(eval(rec.mailing_domain)) else: rec.partner_count=0 def count_partners_ensemble(self,ensemble): #on boucle sur toutes les requêtes de l'ensemble pour compter les contacts result=0 for req in ensemble.request_ids: result+=req.partner_count return result def update_ensemble_domain(self,ensemble): #on boucle sur toutes les requêtes de l'ensemble logical_operator=ensemble.logical_operator if logical_operator=='union': str_operator='"|",' if logical_operator=='inter': str_operator='"&",' mailing_domain='' if len(ensemble.request_ids)>1: i=1 for req in ensemble.request_ids: if i==1: mailing_domain=req.mailing_domain[1:-1] if i>1 : mailing_domain=str_operator +mailing_domain+","+req.mailing_domain[1:-1] i+=1 mailing_domain="["+mailing_domain+"]" #si une seule requête pour l'ensemble if len(ensemble.request_ids)==1: for req in ensemble.request_ids: mailing_domain=req.mailing_domain return mailing_domain def count_partners_segment(self,segment): #on boucle sur toutes les requêtes de l'ensemble pour compter les contacts result=0 for ensemble in segment.ensemble_ids: result+=ensemble.partner_count return result def count_partners_operation(self,operation): #on boucle sur toutes les requêtes de l'ensemble pour compter les contacts result=0 for segment in operation.segment_ids: result+=segment.partner_count return result def refresh_partners_count(self): for r in self: r.partner_count=self.env['res.partner'].search_count(eval(r.mailing_domain)) return True @api.model def create(self,vals): #partners=self.count_partner_request(vals) if vals['mailing_domain']=="[]" : vals['mailing_domain']="[[\"id\",\"!=\",False]]" res=super(opendons_request, self).create(vals) #mise à jour du nombre de requête pour l'ensemble,le segment et l'opération liée ensemble=self.env['opendons.ensemble'].search([('id','=',int(res.ensemble_id))]) #partner_count=self.count_partners_ensemble(ensemble) #mise à jour du domaine pour l'ensemble ensemble_domain=self.update_ensemble_domain(ensemble) ensemble.write( {'request_count':len(ensemble.request_ids), 'mailing_domain':ensemble_domain }) segment=self.env['opendons.segment'].search([('id','=',int(ensemble.segment_id))]) segment_domain=ensemble.update_segment_domain(segment) segment.write({ 'mailing_domain':segment_domain }) operation=self.env['opendons.operation'].search([('id','=',int(segment.operation_id))]) __last_update=fields.Date.context_today(self) operation.write({'__last_update':__last_update}) return res def write(self,vals): mailing_domain_update=False for val in vals: if val=='mailing_domain': mailing_domain_update=True if mailing_domain_update==True : if vals['mailing_domain']=="[]" : vals['mailing_domain']="[[\"id\",\"!=\",False]]" res=super(opendons_request, self).write(vals) ensemble=self.env['opendons.ensemble'].search([('id','=',int(self.ensemble_id))]) segment=self.env['opendons.segment'].search([('id','=',int(ensemble.segment_id))]) if mailing_domain_update==True: ensemble_domain=self.update_ensemble_domain(ensemble) # partner_count=self.count_partners_ensemble(ensemble) ensemble.write({ 'request_count':len(ensemble.request_ids), 'mailing_domain':ensemble_domain }) segment_domain=ensemble.update_segment_domain(segment) segment.write({ 'mailing_domain':segment_domain }) operation=self.env['opendons.operation'].search([('id','=',int(segment.operation_id))]) __last_update=fields.Date.context_today(self) operation.write({'__last_update':__last_update}) return res def copy(self,default_values=None): vals={} vals['name']='copy of ' + self.name vals['ensemble_id']=int(self.ensemble_id) vals['mailing_domain']=self.mailing_domain dup_request=super(opendons_request, self).create(vals) return dup_request