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'] #_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,track_visibility='always') partner_count = fields.Integer(string="count",readonly=True) exported_date=fields.Date(string='Exported Date',track_visibility='always') #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 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')], 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 ) _sql_constraints = [ ('name_uniq', 'unique (name)', "Name already exists !") ] 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') #[["id","not in",[1,3,7,8,10]]] #["&",["id","not in",[22]],["id","not in",[1,3,7,8,10]]] #List of Domain operators: ! (Not), | (Or), & (And) #List of Term operators: '=', '!=', '<=', '<', '>', '>=', '=?', '=like', '=ilike', 'like', 'not like', 'ilike', 'not ilike', 'in', 'not in', 'child_of' 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) return True @api.model def create(self,vals): 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 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 ) @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}) 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', readonly=True) 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) 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