You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

791 lines
27 KiB

from odoo import models, fields, api
from odoo.exceptions import UserError, ValidationError
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)
#begin_date=fields.Date(string='Date begin',required=True, translate=True,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 _compute_segment_count(self):
# # The current user may not have access rights for donations
# for operation in self:
# try:
# operation.segment_count = len(operation.segment_ids)
# except Exception:
# operation.segment_count = 0
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)
writer.writerow(['id','key','name', 'email','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
email=p.email
operation=int(self.id)
segment=int(seg.id)
writer.writerow([id,key,name,email,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'
os.unlink(csv_filename)
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]]]
# partner_excl=self.env['res.partner'].search(eval(segexcl.mailing_domain))
# raise UserError(str(partner_excl))
# str_excl="["
# i=1
# for p in partner_excl:
# if i==1: str_excl=str_excl+str(p.id)
# if i>1 : str_excl=str_excl+','+str(p.id)
# str_excl=str_excl+']'
# excl_domain='["id","not in"',+str_excl +']'
#List of Domain operators: ! (Not), | (Or), & (And)
#List of Term operators: '=', '!=', '<=', '<', '>', '>=', '=?', '=like', '=ilike', 'like', 'not like', 'ilike', 'not ilike', 'in', 'not in', 'child_of'
#excl_domain='[("operation_excl_ids","!=","'+self.id+"')]"
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
return True
def select_partner_operation_old(self):
#le principe est de selectionner les contacts associés aux segments avec les règles suivantes :
#RG1: la priorité des segments est l'ordre de présentation dans la liste des segments
#RG2: au final, un contact sélectionné est rattaché à l'opération avec le segment d'appartenance
#RG1: si un contact est présent dans un segment d'exclusion, alors on le retire de la sélection operateur "!""
#suppression des contacts déjà présents pour l'opération
self.env["opendons.operation_partner"].search([('operation_id','=',self.id)]).unlink()
segments=self.env['opendons.segment'].search(['&',('operation_id','=',self.id),('exclusion','=','False')],order='sequence asc')
i=1
for sg in segments:
if i==1:
partners=self.env['res.partner'].search(eval(sg.mailing_domain))
for p in partners:
vals={}
vals['operation_id']=self.id
vals['segment_id']=sg.id
vals['partner_id']=p.id
res2=self.env['opendons.operation_partner'].create(vals)
if i>1:
partners=self.env['res.partner'].search(eval(sg.mailing_domain))
for p in partners:
pp=self.env["opendons.operation_partner"].search_count(
['&',('operation_id','=',int(self.id)),
('partner_id','=',int(p.id))])
if pp==0:
vals={}
vals['operation_id']=self.id
vals['segment_id']=sg.id
vals['partner_id']=p.id
res2=self.env['opendons.operation_partner'].create(vals)
i=i+1
return self.env["opendons.operation_partner"].search([('operation_id','=',self.id)])
@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_operation_partner(models.Model):
_name = 'opendons.operation_partner'
_description = 'opération marketing : lignes des contacts sélectionnés et exportés'
operation_id = fields.Many2one(
'opendons.operation',
String='Operation',
index=True,
readonly=True)
segment_id = fields.Many2one(
'opendons.segment',
String='Segemnt',
index=True,
readonly=True)
partner_id = fields.Many2one(
'res.partner',
String='Partner',
index=True,
readonly=True)
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', readonly=True)
@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