bzr revid: hmo@tinyerp.com-20091028103803-usx82gkyh377nv5g
This commit is contained in:
Harry (Open ERP) 2009-10-28 16:08:03 +05:30
commit b3fbf22f71
13 changed files with 562 additions and 267 deletions

View File

@ -94,21 +94,20 @@ This test checks the Unit Test(PyUnit) Cases of the module. Note that 'unit_test
res = detail + html + '</table></body></html>' res = detail + html + '</table></body></html>'
return res return res
else: else:
detail_lst = [] detail_dict = {}
cnt = 0
detail += '''<th class="tdatastyle">Details</th></tr>''' detail += '''<th class="tdatastyle">Details</th></tr>'''
data = data_list[1].split("======================================================================") data = data_list[1].split("======================================================================")
test = data[0].split('\n') test = data[0].split('\n')
for err in (data_list[0].failures,data_list[0].errors): for err in (data_list[0].failures,data_list[0].errors):
for value in err: for value in err:
detail_lst.append(value[1]) detail_dict[value[0]._testMethodName] = value[1]
for case in map(lambda x:x.split('...'), test): for case in map(lambda x:x.split('...'), test):
if len(case[0]) < 2: if len(case[0]) < 2:
continue continue
test_name = case[0].split(' (')[0] test_name = case[0].split(' (')[0]
html += '<tr><th class="tdatastyle">%s</th><th class="tdatastyle">%s</th><td class="tdatastyle">%s</td></tr>'%(test_name,case[1],detail_lst[cnt]) if not detail_dict.has_key(test_name):
cnt += 1 detail_dict[test_name] = ''
html += '<tr><th class="tdatastyle">%s</th><th class="tdatastyle">%s</th><td class="tdatastyle">%s</td></tr>'%(test_name, case[1], detail_dict[test_name])
return detail + html +'</tr></table></body></html>' return detail + html +'</tr></table></body></html>'
return '' return ''

View File

@ -25,5 +25,8 @@ import crm_segmentation
import report import report
import wizard import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -32,6 +32,11 @@ import tools
from osv import fields,osv,orm from osv import fields,osv,orm
from osv.orm import except_orm from osv.orm import except_orm
from scripts.openerp_mailgate import openerp_mailgate
import email
import netsvc
from poplib import POP3, POP3_SSL
from imaplib import IMAP4, IMAP4_SSL
MAX_LEVEL = 15 MAX_LEVEL = 15
AVAILABLE_STATES = [ AVAILABLE_STATES = [
@ -203,101 +208,128 @@ class crm_case_section(osv.osv):
return res return res
crm_case_section() crm_case_section()
class crm_email_gateway_server(osv.osv):
_name = "crm.email.gateway.server"
_description = "Email Gateway Server"
_columns = {
'name': fields.char('Server Address',size=64,required=True ,help="IMAP/POP Address Of Email gateway Server"),
'login': fields.char('User',size=64,required=True,help="User Login Id of Email gateway"),
'password': fields.char('Password',size=64,required=True,help="User Password Of Email gateway"),
'server_type': fields.selection([("pop","POP"),("imap","Imap")],"Type of Server", required=True, help="Type of Email gateway Server"),
'port': fields.integer("Port" , help="Port Of Email gateway Server. If port is omitted, the standard POP3 port (110) is used for POP EMail Server and the standard IMAP4 port (143) is used for IMAP Sever."),
'ssl': fields.boolean('SSL',help ="Use Secure Authentication"),
'active': fields.boolean('Active'),
}
_defaults = {
'server_type':lambda * a:'pop',
'active':lambda * a:True,
}
crm_email_gateway_server()
class crm_email_gateway(osv.osv): class crm_email_gateway(osv.osv):
_name = "crm.email.gateway" _name = "crm.email.gateway"
_description = "Email Gateway" _description = "Email Gateway"
_rec_name="login"
_columns = {
'pop': fields.char('POP Server Name',size=64,required=True ,help="POP Server Name Of Email gateway"),
'login': fields.char('User',size=64,required=True,help="User Login Id of Email gateway"),
'password': fields.char('Password',size=64,required=True,help="User Password Of Email gateway"),
'email': fields.char('Email Id',size=64,help="Default eMail in case of any trouble."),
'mailgateway':fields.selection([("fetchmail","Using Fetchmail"),("postfix","Using Postfix")],"EMail gateway", readonly=True),
'section_id':fields.many2one('crm.case.section',"Section"),
'path':fields.char("Path ",size=255,required= True,help ="Path of script file of Email gateway"),
'port':fields.integer("Port" , help="Port Number "),
'ssl':fields.boolean('Use Secure Authentication',help ="Use Secure Authentication"),
}
_defaults = {
'path': lambda *a: tools.config['addons_path']+"/crm/scripts/openerp-mailgate/openerp-mailgate.py",
'port':lambda * a:110,
'mailgateway':lambda * a:'fetchmail',
}
def _get_fetchmail_path(self, cr):
return os.path.join(tools.config['root_path'], '.fetchmail', cr.dbname)
def fetch_mail(self, cr, uid, section_ids=[], context={}):
path = self._get_fetchmail_path(cr)
sect_obj = self.pool.get("crm.case.section")
if not len(section_ids):
section_ids = sect_obj.search(cr, uid, [])
for section in sect_obj.browse(cr, uid, section_ids):
fetch_file = path + "/" +section.name
if os.path.isfile(fetch_file):
try :
os.system("fetchmail -f %s" %(fetch_file))
except Exception, e:
import netsvc
netsvc.Logger().notifyChannel('fetchmail', netsvc.LOG_ERROR, "%s" % e)
return True
def make_fetchmail(self, cr, uid, section_ids=[], context={}):
sect_obj = self.pool.get("crm.case.section")
user_obj = self.pool.get("res.users")
for section in sect_obj.browse(cr, uid, section_ids, context):
user = user_obj.browse(cr,uid,uid)
path = self._get_fetchmail_path(cr)
if not os.path.isdir(path):
try:
os.makedirs(path)
except:
raise except_orm(_('Permission Denied !'), _('You do not permissions to write on the server side.'))
fmail_path = path + "/" +section.name
fmail = open(fmail_path , 'w')
os.chmod(fmail_path,0710)
for mailgateway in section.gateway_ids:
mdatext = '%s -u %d -p %s -s %d -d %s '%(mailgateway.path,uid,user.password,mailgateway.section_id.id,cr.dbname)
if mailgateway.email:
mdatext += ' -m %s'%(mailgateway.email)
if section.reply_to:
mdatext += ' -e %s'%(section.reply_to)
text = "\npoll %s user '%s' password '%s'"%(mailgateway.pop,mailgateway.login,mailgateway.password)
if mailgateway.port:
text += ' port %d' % (mailgateway.port)
if mailgateway.ssl:
text += ' ssl'
text += " mda '%s'"%(mdatext)
fmail.write(text)
fmail.close()
def create(self, cr, uid, vals, context=None):
res = super(crm_email_gateway, self).create(cr, uid, vals, context=context)
self.make_fetchmail(cr, uid, [vals['section_id']])
return res
def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True):
res = super(crm_email_gateway, self).write(cr, uid, ids, vals, context)
section_ids = []
for gateway in self.browse(cr, uid, ids, context):
if gateway.section_id.id not in section_ids:
section_ids.append(gateway.section_id.id)
self.make_fetchmail(cr, uid, section_ids)
return res
def unlink(self, cr, uid, ids, context={}, check=True):
section_ids = []
for gateway in self.browse(cr, uid, ids, context):
if gateway.section_id.id not in section_ids:
section_ids.append(gateway.section_id.id)
res = super(crm_email_gateway, self).unlink(cr, uid,ids, context=context)
self.make_fetchmail(cr, uid, section_ids) _columns = {
return res 'name': fields.char('Name',size=64,help="Name of Mail Gateway."),
'server_id': fields.many2one('crm.email.gateway.server',"Gateway Server", required=True),
'to_email_id': fields.char('TO', size=64, help="Email address used in the From field of outgoing messages"),
'cc_email_id': fields.char('CC',size=64,help="Default eMail in case of any trouble."),
'section_id': fields.many2one('crm.case.section',"Section",required=True),
'mail_history': fields.one2many("crm.email.history","gateway_id","History", readonly=True)
}
def _fetch_mails(self, cr, uid, ids=False, context={}):
'''
Function called by the scheduler to fetch mails
'''
cr.execute('select * from crm_email_gateway gateway \
inner join crm_email_gateway_server server \
on server.id = gateway.server_id where server.active = True')
ids2 = map(lambda x: x[0], cr.fetchall() or [])
return self.fetch_mails(cr, uid, ids=ids2, context=context)
def fetch_mails(self, cr, uid, ids=[], section_ids=[], context={}):
if len(section_ids):
casesection_obj = self.pool.get('crm.case.section')
for section in casesection_obj.read(cr, uid, section_ids, ['gateway_ids']):
ids += section['gateway_ids']
log_messages = []
user_obj = self.pool.get('res.users')
mail_history_obj = self.pool.get('crm.email.history')
for mailgateway in self.browse(cr, uid, ids):
try :
mailgate_server = mailgateway.server_id
if not mailgate_server.active:
continue
mailgate_name = mailgateway.name or "%s (%s)" % (mailgate_server.login, mailgate_server.name)
log_messages.append("Mail Server : %s" % mailgate_name)
log_messages.append("="*40)
email_messages = []
read_messages = mailgateway.mail_history and map(lambda x:x.name, mailgateway.mail_history) or []
new_messages = []
if mailgate_server.server_type == 'pop':
if mailgate_server.ssl:
pop_server = POP3_SSL(mailgate_server.name or 'localhost', mailgate_server.port or 110)
else:
pop_server = POP3(mailgate_server.name or 'localhost', mailgate_server.port or 110)
pop_server.user(mailgate_server.login)
pop_server.pass_(mailgate_server.password)
pop_server.list()
(numMsgs, totalSize) = pop_server.stat()
for i in range(1, numMsgs + 1):
(header, msges, octets) = pop_server.retr(i)
email_messages.append((i,'\n'.join(msges)))
new_messages.append(i)
pop_server.quit()
elif mailgate_server.server_type == 'imap':
if mailgate_server.ssl:
imap_server = IMAP4_SSL(mailgate_server.name or 'localhost', mailgate_server.port or 143)
else:
imap_server = IMAP4(mailgate_server.name or 'localhost', mailgate_server.port or 143)
imap_server.login(mailgate_server.login, mailgate_server.password)
imap_server.select()
typ, data = imap_server.search(None, '(UNSEEN)')
for num in data[0].split():
typ, data = imap_server.fetch(num, '(RFC822)')
email_messages.append((num, data[0][1]))
new_messages.append(num)
imap_server.close()
imap_server.logout()
except Exception, e:
log_messages.append("Error in Fetching Mail: %s " %(str(e)))
netsvc.Logger().notifyChannel('Emailgate:Fetching mail:[%d]%s' % (mailgate_server.id, mailgate_server.name), netsvc.LOG_ERROR, str(e))
if len(email_messages):
users = user_obj.read(cr, uid, uid, ['password'])
parser = openerp_mailgate.email_parser(uid, users['password'], mailgateway.section_id.id,
mailgateway.to_email_id or '', mailgateway.cc_email_id or '', dbname=cr.dbname,
host=tools.config['interface'] or 'localhost', port=tools.config['port'] or '8069')
for msg, message in email_messages:
msg_id = case_id = note = False
try :
msg_txt = email.message_from_string(message)
msg_id = msg_txt['Message-ID']
case_id = parser.parse(msg_txt)[0]
log_messages.append('Case Successfull created : %d' % case_id)
except Exception, e:
note = "Error in Parsing Mail: %s " %(str(e))
log_messages.append(note)
netsvc.Logger().notifyChannel('Emailgate:Parsing mail:[%d]%s' % (mailgate_server.id, mailgate_server.name), netsvc.LOG_ERROR, str(e))
mail_history_obj.create(cr, uid, {'name':msg_id, 'case_id': case_id, 'gateway_id':mailgateway.id, 'note':note})
log_messages.append("-"*25)
log_messages.append("Total Read Mail: %d\n\n" %(len(new_messages)))
return log_messages
crm_email_gateway() crm_email_gateway()
class crm_case_categ(osv.osv): class crm_case_categ(osv.osv):
_name = "crm.case.categ" _name = "crm.case.categ"
_description = "Category of case" _description = "Category of case"
@ -980,6 +1012,17 @@ class crm_case_history(osv.osv):
} }
crm_case_history() crm_case_history()
class crm_email_history(osv.osv):
_name = "crm.email.history"
_description = "Email History"
_columns = {
'name': fields.char('Message Id', size=64, help="Message Id in Email Server."),
'case_id': fields.many2one('crm.case',"Case"),
'gateway_id': fields.many2one('crm.email.gateway',"Email Gateway", required=True),
'note': fields.text('Notes'),
}
_order = 'id desc'
crm_email_history()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -74,7 +74,7 @@
<field name="numbercall">-1</field> <field name="numbercall">-1</field>
<field eval="False" name="doall"/> <field eval="False" name="doall"/>
<field eval="'crm.email.gateway'" name="model"/> <field eval="'crm.email.gateway'" name="model"/>
<field eval="'fetch_mail'" name="function"/> <field eval="'_fetch_mails'" name="function"/>
<field eval="'()'" name="args"/> <field eval="'()'" name="args"/>
</record> </record>

View File

@ -40,29 +40,27 @@
<field name="allow_unlink" select="2"/> <field name="allow_unlink" select="2"/>
<field name="reply_to" select="2"/> <field name="reply_to" select="2"/>
<field name="gateway_ids" widget="one2many_list" nolabel="1" colspan="4"> <field name="gateway_ids" widget="one2many_list" nolabel="1" colspan="4">
<tree string="Email Gateway"> <tree string="Email Gateway" editable="bottom">
<field name="pop" /> <field name="name" />
<field name="login"/> <field name="server_id" />
<field name="email" />
<field name="mailgateway"/>
</tree> </tree>
<form string="Email Gateway"> <form string="Email Gateway">
<field name="mailgateway"/> <field name="name"/>
<notebook colspan="4"> <field name="server_id" />
<page string="Server Setting"> <field name="to_email_id"/>
<field name="pop" /> <field name="cc_email_id" />
<field name="port" /> <field name="mail_history" widget="one2many_list" nolabel="1" colspan="4">
<field name="login" /> <tree string="Email History">
<field name="password" password="True"/> <field name="name"/>
</page> <field name="case_id"/>
<page string="Security Setting"> <field name="note"/>
<field name="ssl" /> </tree>
</page> <form string="Email History">
<page string="Email Gateway"> <field name="name"/>
<field name="email" /> <field name="case_id"/>
<field name="path" /> <field name="note"/>
</page> </form>
</notebook> </field>
</form> </form>
</field> </field>
</page> </page>
@ -288,8 +286,9 @@
<field name="model">crm.case</field> <field name="model">crm.case</field>
<field name="type">tree</field> <field name="type">tree</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree colors="red:date_deadline&lt;current_date and state=='open'" string="Cases"> <tree colors="red:date_deadline&lt;current_date and state=='open'" string="Cases">
<field name="id"/> <field name="id"/>
<field name="section_id"/>
<field name="date"/> <field name="date"/>
<field name="date_deadline"/> <field name="date_deadline"/>
<field name="name"/> <field name="name"/>
@ -626,56 +625,52 @@
<act_window domain="[('user_id', '=', active_id),('state','&lt;&gt;','done'),('state','&lt;&gt;','cancel'),('state','&lt;&gt;','pending')]" id="act_res_users_2_crm_case_opened" name="Open cases" res_model="crm.case" src_model="res.users" view_mode="tree,form,calendar" view_type="form"/> <act_window domain="[('user_id', '=', active_id),('state','&lt;&gt;','done'),('state','&lt;&gt;','cancel'),('state','&lt;&gt;','pending')]" id="act_res_users_2_crm_case_opened" name="Open cases" res_model="crm.case" src_model="res.users" view_mode="tree,form,calendar" view_type="form"/>
<record id="crm_email_gateway_form" model="ir.ui.view">
<field name="name">crm.email.gateway.form</field> <record id="crm_email_gateway_server_form" model="ir.ui.view">
<field name="model">crm.email.gateway</field> <field name="name">crm.email.gateway.server.form</field>
<field name="model">crm.email.gateway.server</field>
<field name="type">form</field> <field name="type">form</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Email Gateway"> <form string="Email Gateway Server">
<field name="section_id" widget="selection"/> <field name="server_type"/>
<field name="mailgateway"/>
<notebook colspan="4"> <notebook colspan="4">
<page string="Server Setting"> <page string="Server Info">
<field name="pop" /> <field name="name"/>
<field name="port" /> <field name="port" />
<field name="login" /> <field name="login" />
<field name="password" password="True"/> <field name="password" password="True"/>
</page> <field name="ssl" />
<page string="Security Setting"> <field name="active" />
<field name="ssl" /> </page>
</page>
<page string="Email Gateway">
<field name="email" />
<field name="path" />
</page>
</notebook> </notebook>
</form> </form>
</field> </field>
</record> </record>
<record id="crm_email_gateway_tree" model="ir.ui.view"> <record id="crm_email_gateway_server_tree" model="ir.ui.view">
<field name="name">crm.email.gateway.tree</field> <field name="name">crm.email.gateway.server.tree</field>
<field name="model">crm.email.gateway</field> <field name="model">crm.email.gateway.server</field>
<field name="type">tree</field> <field name="type">tree</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Gateway"> <tree string="Email Gateway Server">
<field name="section_id"/> <field name="name"/>
<field name="mailgateway"/> <field name="port" />
<field name="pop" /> <field name="server_type"/>
<field name="login"/> <field name="ssl" />
<field name="email" />
<field name="path" />
</tree> </tree>
</field> </field>
</record> </record>
<record id="crm_email_gateway_act" model="ir.actions.act_window">
<field name="name">Email Gateway</field>
<field name="res_model">crm.email.gateway</field> <record id="crm_email_gateway_server_act" model="ir.actions.act_window">
<field name="name">Email Gateway Server</field>
<field name="res_model">crm.email.gateway.server</field>
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="view_id" ref="crm_email_gateway_tree"/> <field name="view_id" ref="crm_email_gateway_server_tree"/>
</record> </record>
<menuitem id="crm_email_gateway_menu" name="Email Gateway" parent="next_id_51" action="crm_email_gateway_act" /> <menuitem id="crm_email_gateway_server_menu" name="Email Gateway Server" parent="next_id_51" action="crm_email_gateway_server_act" />
</data> </data>
</openerp> </openerp>

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import openerp_mailgate

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import openerp_mailgate

View File

@ -49,8 +49,83 @@ priorities = {
'5': '5 (Lowest)', '5': '5 (Lowest)',
} }
def html2plaintext(html, body_id=None, encoding='utf-8'):
## (c) Fry-IT, www.fry-it.com, 2007
## <peter@fry-it.com>
## download here: http://www.peterbe.com/plog/html2plaintext
""" from an HTML text, convert the HTML to plain text.
If @body_id is provided then this is the tag where the
body (not necessarily <body>) starts.
"""
try:
from BeautifulSoup import BeautifulSoup, SoupStrainer, Comment
except:
return html
urls = []
if body_id is not None:
strainer = SoupStrainer(id=body_id)
else:
strainer = SoupStrainer('body')
soup = BeautifulSoup(html, parseOnlyThese=strainer, fromEncoding=encoding)
for link in soup.findAll('a'):
title = link.renderContents()
for url in [x[1] for x in link.attrs if x[0]=='href']:
urls.append(dict(url=url, tag=str(link), title=title))
html = soup.__str__()
url_index = []
i = 0
for d in urls:
if d['title'] == d['url'] or 'http://'+d['title'] == d['url']:
html = html.replace(d['tag'], d['url'])
else:
i += 1
html = html.replace(d['tag'], '%s [%s]' % (d['title'], i))
url_index.append(d['url'])
html = html.replace('<strong>','*').replace('</strong>','*')
html = html.replace('<b>','*').replace('</b>','*')
html = html.replace('<h3>','*').replace('</h3>','*')
html = html.replace('<h2>','**').replace('</h2>','**')
html = html.replace('<h1>','**').replace('</h1>','**')
html = html.replace('<em>','/').replace('</em>','/')
# the only line breaks we respect is those of ending tags and
# breaks
html = html.replace('\n',' ')
html = html.replace('<br>', '\n')
html = html.replace('<tr>', '\n')
html = html.replace('</p>', '\n\n')
html = re.sub('<br\s*/>', '\n', html)
html = html.replace(' ' * 2, ' ')
# for all other tags we failed to clean up, just remove then and
# complain about them on the stderr
def desperate_fixer(g):
#print >>sys.stderr, "failed to clean up %s" % str(g.group())
return ' '
html = re.sub('<.*?>', desperate_fixer, html)
# lstrip all lines
html = '\n'.join([x.lstrip() for x in html.splitlines()])
for i, url in enumerate(url_index):
if i == 0:
html += '\n\n'
html += '[%s] %s\n' % (i+1, url)
return html
class rpc_proxy(object): class rpc_proxy(object):
def __init__(self, uid, passwd, host='localhost', port=8069, path='object', dbname='terp'): def __init__(self, uid, passwd, host='localhost', port=8069, path='object', dbname='terp'):
self.rpc = xmlrpclib.ServerProxy('http://%s:%s/xmlrpc/%s' % (host, port, path)) self.rpc = xmlrpclib.ServerProxy('http://%s:%s/xmlrpc/%s' % (host, port, path))
self.user_id = uid self.user_id = uid
self.passwd = passwd self.passwd = passwd
@ -60,8 +135,8 @@ class rpc_proxy(object):
return self.rpc.execute(self.dbname, self.user_id, self.passwd, *request) return self.rpc.execute(self.dbname, self.user_id, self.passwd, *request)
class email_parser(object): class email_parser(object):
def __init__(self, uid, password, section, email, email_default, dbname, host): def __init__(self, uid, password, section, email, email_default, dbname, host, port):
self.rpc = rpc_proxy(uid, password, host=host, dbname=dbname) self.rpc = rpc_proxy(uid, password, host=host, port=port, dbname=dbname)
try: try:
self.section_id = int(section) self.section_id = int(section)
except: except:
@ -82,7 +157,7 @@ class email_parser(object):
adr = self.rpc('res.partner.address', 'read', adr_ids, ['partner_id']) adr = self.rpc('res.partner.address', 'read', adr_ids, ['partner_id'])
return { return {
'partner_address_id': adr[0]['id'], 'partner_address_id': adr[0]['id'],
'partner_id': adr[0]['partner_id'][0] 'partner_id': adr[0].get('partner_id',False) and adr[0]['partner_id'][0] or False
} }
def _decode_header(self, s): def _decode_header(self, s):
@ -142,7 +217,7 @@ class email_parser(object):
# # # #
def msg_body_get(self, msg): def msg_body_get(self, msg):
message = {}; message = {};
message['body'] = u''; message['body'] = '';
message['attachment'] = {}; message['attachment'] = {};
attachment = message['attachment']; attachment = message['attachment'];
counter = 1; counter = 1;
@ -153,13 +228,17 @@ class email_parser(object):
if part.get_content_maintype() == 'multipart': if part.get_content_maintype() == 'multipart':
continue continue
if part.get_content_maintype()=='text' and part.get_content_subtype() in ('plain','html'): if part.get_content_maintype()=='text':
buf = part.get_payload(decode=True) buf = part.get_payload(decode=True)
if buf: if buf:
txt = buf.decode(part.get_charsets()[0] or 'ascii', 'replace') txt = buf.decode(part.get_charsets()[0] or 'ascii', 'replace')
txt = re.sub("<(\w)>", replace, txt) txt = re.sub("<(\w)>", replace, txt)
txt = re.sub("<\/(\w)>", replace, txt) txt = re.sub("<\/(\w)>", replace, txt)
message['body'] += txt if txt and part.get_content_subtype() == 'plain':
message['body'] += txt
elif txt and part.get_content_subtype() == 'html':
message['body'] += html2plaintext(txt)
elif part.get_content_maintype()=='application' or part.get_content_maintype()=='image' or part.get_content_maintype()=='text': elif part.get_content_maintype()=='application' or part.get_content_maintype()=='image' or part.get_content_maintype()=='text':
filename = part.get_filename(); filename = part.get_filename();
if filename : if filename :
@ -171,7 +250,7 @@ class email_parser(object):
#end if #end if
#end if #end if
message['attachment'] = attachment message['attachment'] = attachment
#end for #end for
return message return message
#end def #end def
@ -297,7 +376,7 @@ class email_parser(object):
del msg['Subject'] del msg['Subject']
msg['Subject'] = '[OpenERP-CaseError] ' + a msg['Subject'] = '[OpenERP-CaseError] ' + a
self.msg_send(msg, self.email_default.split(',')) self.msg_send(msg, self.email_default.split(','))
return emails return case_id, emails
if __name__ == '__main__': if __name__ == '__main__':
import sys, optparse import sys, optparse
@ -317,10 +396,11 @@ if __name__ == '__main__':
parser.add_option("-m", "--default", dest="default", help="Default eMail in case of any trouble.", default=None) parser.add_option("-m", "--default", dest="default", help="Default eMail in case of any trouble.", default=None)
parser.add_option("-d", "--dbname", dest="dbname", help="Database name (default: terp)", default='terp') parser.add_option("-d", "--dbname", dest="dbname", help="Database name (default: terp)", default='terp')
parser.add_option("--host", dest="host", help="Hostname of the Open ERP Server", default="localhost") parser.add_option("--host", dest="host", help="Hostname of the Open ERP Server", default="localhost")
parser.add_option("--port", dest="port", help="Port of the Open ERP Server", default="8069")
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
parser = email_parser(options.userid, options.password, options.section, options.email, options.default, dbname=options.dbname, host=options.host) parser = email_parser(options.userid, options.password, options.section, options.email, options.default, dbname=options.dbname, host=options.host, port=options.port)
msg_txt = email.message_from_file(sys.stdin) msg_txt = email.message_from_file(sys.stdin)

View File

@ -26,28 +26,59 @@ import tools
import os import os
_email_form = '''<?xml version="1.0"?> _email_form = '''<?xml version="1.0"?>
<form string="Email Gateway"> <form string="Email Gateway">
<label string="Fetch Email from Email Gate Way" /> <separator string="Fetching Emails from" />
<field name="server" colspan="4" nolabel="1" />
</form>''' </form>'''
_email_done_form = '''<?xml version="1.0"?>
<form string="Email Gateway">
<separator string="Log Detail" />
<newline/>
<field name="message" colspan="4" nolabel="1"/>
</form>'''
_email_fields = { _email_fields = {
'server': {'string':"Server", 'type':'text', 'readonly':True},
} }
_email_done_fields = {
'message': {'string':"Log Detail", 'type':'text', 'readonly':True},
}
def _default(self , cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
gateway_pool=pool.get('crm.case.section')
sections = gateway_pool.browse(cr, uid, data['ids'], context=context)
server = []
for section in sections:
for gateway in section.gateway_ids:
if gateway.server_id.active:
server.append(gateway.name or '%s (%s)'%(gateway.server_id.login, gateway.server_id.name) )
data['form']['server'] = '\n'.join(server)
return data['form']
def fetch_mail(self , cr, uid, data, context): def fetch_mail(self , cr, uid, data, context):
pool = pooler.get_pool(cr.dbname) pool = pooler.get_pool(cr.dbname)
gateway_pool=pool.get('crm.email.gateway') gateway_pool=pool.get('crm.email.gateway')
gateway_pool.fetch_mail(cr, uid, data['ids'], context=context) messages = gateway_pool.fetch_mails(cr, uid, ids=[], section_ids=data['ids'], context=context)
return {} data['form']['message'] = '\n'.join(messages)
return data['form']
class wiz_fetch_mail(wizard.interface): class wiz_fetch_mail(wizard.interface):
states = { states = {
'init': { 'init': {
'actions': [], 'actions': [_default],
'result': {'type': 'form', 'arch':_email_form, 'fields':_email_fields, 'state':[('run','Run','gtk-execute'),('end','Cancel','gtk-cancel')]} 'result': {'type': 'form', 'arch':_email_form, 'fields':_email_fields, 'state':[('end','Cancel','gtk-cancel'), ('fetch','Fetch','gtk-execute')]}
}, },
'run': { 'fetch': {
'actions': [fetch_mail], 'actions': [fetch_mail],
'result': {'type': 'state', 'state': 'end'} 'result': {'type': 'form', 'arch': _email_done_form,
}, 'fields': _email_done_fields,
'state': (
('end', 'Close'),
)
},
},
} }
wiz_fetch_mail('crm.case.section.fetchmail') wiz_fetch_mail('crm.case.section.fetchmail')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -109,6 +109,7 @@ class res_users(osv.osv):
def _child_compute(self, cr, uid, ids, name, args, context={}): def _child_compute(self, cr, uid, ids, name, args, context={}):
obj_dept = self.pool.get('hr.department') obj_dept = self.pool.get('hr.department')
obj_user = self.pool.get('res.users')
result = {} result = {}
for manager_id in ids: for manager_id in ids:
child_ids = [] child_ids = []
@ -116,8 +117,9 @@ class res_users(osv.osv):
ids_dept = obj_dept.search(cr, uid, [('id', 'child_of', mgnt_dept_ids)]) ids_dept = obj_dept.search(cr, uid, [('id', 'child_of', mgnt_dept_ids)])
if ids_dept: if ids_dept:
data_dept = obj_dept.read(cr, uid, ids_dept, ['member_ids']) data_dept = obj_dept.read(cr, uid, ids_dept, ['member_ids'])
childs = map(lambda x: x['member_ids'], data_dept) childs = map(lambda x: x['member_ids'], data_dept)
childs = tools.flatten(childs) childs = tools.flatten(childs)
childs = obj_user.search(cr, uid, [('id','in',childs),('active','=',True)])
if manager_id in childs: if manager_id in childs:
childs.remove(manager_id) childs.remove(manager_id)
@ -148,4 +150,4 @@ class res_users(osv.osv):
res_users() res_users()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -55,5 +55,40 @@ This is the same wizard that runs from Financial Management/Configuration/Financ
<field name="action_id" ref="account.action_wizard_multi_chart"/> <field name="action_id" ref="account.action_wizard_multi_chart"/>
</record> </record>
<!-- osv_memory for vat listing of clients -->
<record id="view_vat_listing" model="ir.ui.view">
<field name="name">step.vat.listing</field>
<field name="model">vat.listing.clients</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="VAT listing">
<field name="name"/>
<field name="vat"/>
<newline/>
<field name="country"/>
<newline/>
<field name="amount"/>
<field name="turnover"/>
</form>
</field>
</record>
<record id="view_vat_listing" model="ir.ui.view">
<field name="name">step.vat.listing</field>
<field name="model">vat.listing.clients</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="VAT listing">
<field name="name"/>
<field name="vat"/>
<newline/>
<field name="country"/>
<newline/>
<field name="amount"/>
<field name="turnover"/>
</tree>
</field>
</record>
</data> </data>
</openerp> </openerp>

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
# #
@ -15,16 +15,30 @@
# GNU Affero General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import wizard
import time import time
import datetime import datetime
import pooler
import base64 import base64
import wizard
import pooler
from tools.translate import _ from tools.translate import _
import tools import tools
from osv import fields, osv
class vat_listing_clients(osv.osv_memory):
_name = 'vat.listing.clients'
_columns = {
'name': fields.char('Cleint Name', size=64),
'vat': fields.char('VAT', size=64),
'country': fields.char('Country', size=64),
'amount': fields.float('Amount'),
'turnover': fields.float('Turnover'),
}
vat_listing_clients()
form = """<?xml version="1.0"?> form = """<?xml version="1.0"?>
<form string="Select Fiscal Year"> <form string="Select Fiscal Year">
@ -45,14 +59,29 @@ fields = {
'limit_amount':{'string':'Limit Amount','type':'integer','required': True, }, 'limit_amount':{'string':'Limit Amount','type':'integer','required': True, },
'test_xml': {'string':'Test XML file', 'type':'boolean', }, 'test_xml': {'string':'Test XML file', 'type':'boolean', },
} }
client_form = """<?xml version="1.0"?>
<form string="Select Fiscal Year">
<label string="You can remove clients/partners which you do not want in exported xml file" colspan="4"/>
<separator string="Clients" colspan="4"/>
<field name="partners" colspan="4" width="600" height="250" widget="one2many" nolabel="1"/>
</form>"""
client_fields = {
'partners': {'string': 'Cleints', 'type': 'many2many', 'relation': 'vat.listing.clients', 'required': False, 'help': 'You can remove clients/partners which you do not want to show in xml file'},
}
msg_form = """<?xml version="1.0"?> msg_form = """<?xml version="1.0"?>
<form string="Notification"> <form string="Notification">
<separator string="XML File has been Created." colspan="4"/> <separator string="XML File has been Created." colspan="4"/>
<field name="msg" colspan="4" nolabel="1"/> <field name="msg" colspan="4" nolabel="1"/>
<field name="name"/>
<newline/>
<field name="file_save" /> <field name="file_save" />
</form>""" </form>"""
msg_fields = { msg_fields = {
'name': {'string': 'File name', 'type':'char', 'size':'32'},
'msg': {'string':'File created', 'type':'text', 'size':'100','readonly':True}, 'msg': {'string':'File created', 'type':'text', 'size':'100','readonly':True},
'file_save':{'string': 'Save File', 'file_save':{'string': 'Save File',
'type': 'binary', 'type': 'binary',
@ -61,32 +90,89 @@ msg_fields = {
class wizard_vat(wizard.interface): class wizard_vat(wizard.interface):
def _get_partner(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
period_ids = pool.get('account.period').search(cr, uid, [('fiscalyear_id', '=', data['form']['fyear'])])
period = "("+','.join(map(lambda x: str(x), period_ids)) +")"
p_id_list = pool.get('res.partner').search(cr,uid,[('vat_subjected','!=',False)])
if not p_id_list:
raise wizard.except_wizard(_('Data Insufficient!'),_('No partner has a VAT Number asociated with him.'))
partners = []
records = []
for obj_partner in pool.get('res.partner').browse(cr, uid, p_id_list):
record = {} # this holds record per partner
#This listing is only for customers located in belgium, that's the
#reason why we skip all the partners that haven't their
#(or one of their) default address(es) located in Belgium.
go_ahead = False
for ads in obj_partner.address:
if ads.type == 'default' and (ads.country_id and ads.country_id.code == 'BE'):
go_ahead = True
break
if not go_ahead:
continue
query = 'select b.code,sum(credit)-sum(debit) from account_move_line l left join account_account a on (l.account_id=a.id) left join account_account_type b on (a.user_type=b.id) where b.code in ('"'produit'"','"'tax'"') and l.partner_id='+str(obj_partner.id)+' and l.period_id in '+period+' group by b.code'
cr.execute(query)
line_info = cr.fetchall()
if not line_info:
continue
record['vat'] = obj_partner.vat
#it seems that this listing is only for belgian customers
record['country'] = 'BE'
#...deprecated...
#~addr = pool.get('res.partner').address_get(cr, uid, [obj_partner.id], ['invoice'])
#~ if addr.get('invoice',False):
#~ads=pool.get('res.partner.address').browse(cr,uid,[addr['invoice']])[0]
#~ if ads.country_id:
#~ record.append(ads.country_id.code)
#~ else:
#~ error_message.append('Data Insufficient! : '+ 'The Partner "'+obj_partner.name + '"'' has no country associated with its Invoice address!')
#~ if len(record)<2:
#~ record.append('')
#~ error_message.append('Data Insufficient! : '+ 'The Partner "'+obj_partner.name + '"'' has no Invoice address!')
record['amount'] = 0
record['turnover'] = 0
record['name'] = obj_partner.name
for item in line_info:
if item[0]=='produit':
record['turnover'] += item[1]
else:
record['amount'] += item[1]
id_client = pool.get('vat.listing.clients').create(cr, uid, record)
partners.append(id_client)
records.append(record)
return {'partners':partners}
def _create_xml(self, cr, uid, data, context): def _create_xml(self, cr, uid, data, context):
datas=[] datas=[]
seq_controlref = pooler.get_pool(cr.dbname).get('ir.sequence').get(cr, uid,'controlref') pool = pooler.get_pool(cr.dbname)
seq_declarantnum = pooler.get_pool(cr.dbname).get('ir.sequence').get(cr, uid,'declarantnum') seq_controlref = pool.get('ir.sequence').get(cr, uid,'controlref')
obj_cmpny = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid).company_id seq_declarantnum = pool.get('ir.sequence').get(cr, uid,'declarantnum')
obj_cmpny = pool.get('res.users').browse(cr, uid, uid).company_id
company_vat = obj_cmpny.partner_id.vat company_vat = obj_cmpny.partner_id.vat
if not company_vat: if not company_vat:
raise wizard.except_wizard(_('Data Insufficient'),_('No VAT Number Associated with Main Company!')) raise wizard.except_wizard(_('Data Insufficient'),_('No VAT Number Associated with Main Company!'))
cref = company_vat + seq_controlref cref = company_vat + seq_controlref
dnum = cref + seq_declarantnum dnum = cref + seq_declarantnum
p_id_list = pooler.get_pool(cr.dbname).get('res.partner').search(cr,uid,[('vat_subjected','!=',False)]) obj_year=pool.get('account.fiscalyear').browse(cr,uid,data['form']['fyear'])
if not p_id_list:
raise wizard.except_wizard(_('Data Insufficient!'),_('No partner has a VAT Number asociated with him.'))
obj_year=pooler.get_pool(cr.dbname).get('account.fiscalyear').browse(cr,uid,data['form']['fyear'])
period_ids = pooler.get_pool(cr.dbname).get('account.period').search(cr, uid, [('fiscalyear_id', '=', data['form']['fyear'])])
period = "("+','.join(map(lambda x: str(x), period_ids)) +")"
street = zip_city = country = '' street = zip_city = country = ''
addr = pooler.get_pool(cr.dbname).get('res.partner').address_get(cr, uid, [obj_cmpny.partner_id.id], ['invoice']) addr = pool.get('res.partner').address_get(cr, uid, [obj_cmpny.partner_id.id], ['invoice'])
if addr.get('invoice',False): if addr.get('invoice',False):
ads=pooler.get_pool(cr.dbname).get('res.partner.address').browse(cr,uid,[addr['invoice']])[0] ads=pool.get('res.partner.address').browse(cr,uid,[addr['invoice']])[0]
zip_city = pooler.get_pool(cr.dbname).get('res.partner.address').get_city(cr,uid,ads.id) zip_city = pool.get('res.partner.address').get_city(cr,uid,ads.id)
if not zip_city: if not zip_city:
zip_city = '' zip_city = ''
if ads.street: if ads.street:
@ -97,7 +183,7 @@ class wizard_vat(wizard.interface):
country = ads.country_id.code country = ads.country_id.code
sender_date = time.strftime('%Y-%m-%d') sender_date = time.strftime('%Y-%m-%d')
data_file = '<?xml version="1.0"?>\n<VatList xmlns="http://www.minfin.fgov.be/VatList" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.minfin.fgov.be/VatList VatList.xml" RecipientId="VAT-ADMIN" SenderId="'+ str(company_vat) + '"' data_file = '<?xml version="1.0"?>\n<VatList xmlns="http://www.minfin.fgov.be/VatList" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.minfin.fgov.be/VatList VatList.xml" RecipientId="VAT-ADMIN" SenderId="'+ str(company_vat) + '"'
data_file +=' ControlRef="'+ cref + '" MandataireId="'+ tools.ustr(data['form']['mand_id']) + '" SenderDate="'+ str(sender_date)+ '"' data_file +=' ControlRef="'+ cref + '" MandataireId="'+ tools.ustr(data['form']['mand_id']) + '" SenderDate="'+ str(sender_date)+ '"'
if data['form']['test_xml']: if data['form']['test_xml']:
@ -109,55 +195,12 @@ class wizard_vat(wizard.interface):
data_period = '\n<Period>'+ tools.ustr(obj_year.date_stop[:4]) +'</Period>' data_period = '\n<Period>'+ tools.ustr(obj_year.date_stop[:4]) +'</Period>'
error_message = [] error_message = []
for p_id in p_id_list: for partner in data['form']['partners']:
record = {} # this holds record per partner if isinstance(partner, list) and partner:
obj_partner = pooler.get_pool(cr.dbname).get('res.partner').browse(cr,uid,p_id) datas.append(partner[2])
else:
#This listing is only for customers located in belgium, that's the client_data = pool.get('vat.listing.clients').read(cr, uid, partner, context=context)
#reason why we skip all the partners that haven't their datas.append(client_data)
#(or one of their) default address(es) located in Belgium.
go_ahead = False
for ads in obj_partner.address:
if ads.type == 'default' and (ads.country_id and ads.country_id.code == 'BE'):
go_ahead = True
break
if not go_ahead:
continue
query = 'select b.code,sum(credit)-sum(debit) from account_move_line l left join account_account a on (l.account_id=a.id) left join account_account_type b on (a.user_type=b.id) where b.code in ('"'produit'"','"'tax'"') and l.partner_id='+str(p_id)+' and l.period_id in '+period+' group by b.code'
cr.execute(query)
line_info = cr.fetchall()
if not line_info:
continue
record['vat'] = obj_partner.vat
#it seems that this listing is only for belgian customers
record['country'] = 'BE'
#...deprecated...
#~addr = pooler.get_pool(cr.dbname).get('res.partner').address_get(cr, uid, [obj_partner.id], ['invoice'])
#~ if addr.get('invoice',False):
#~ads=pooler.get_pool(cr.dbname).get('res.partner.address').browse(cr,uid,[addr['invoice']])[0]
#~ if ads.country_id:
#~ record.append(ads.country_id.code)
#~ else:
#~ error_message.append('Data Insufficient! : '+ 'The Partner "'+obj_partner.name + '"'' has no country associated with its Invoice address!')
#~ if len(record)<2:
#~ record.append('')
#~ error_message.append('Data Insufficient! : '+ 'The Partner "'+obj_partner.name + '"'' has no Invoice address!')
record['amount'] = 0
record['turnover'] = 0
for item in line_info:
if item[0]=='produit':
record['turnover'] += item[1]
else:
record['amount'] += item[1]
datas.append(record)
seq=0 seq=0
data_clientinfo='' data_clientinfo=''
@ -167,6 +210,8 @@ class wizard_vat(wizard.interface):
data['form']['msg']='Exception : \n' +'-'*50+'\n'+ '\n'.join(error_message) data['form']['msg']='Exception : \n' +'-'*50+'\n'+ '\n'.join(error_message)
return data['form'] return data['form']
for line in datas: for line in datas:
if not line:
continue
if line['turnover'] < data['form']['limit_amount']: if line['turnover'] < data['form']['limit_amount']:
continue continue
seq +=1 seq +=1
@ -179,12 +224,17 @@ class wizard_vat(wizard.interface):
data['form']['msg'] = 'Save the File with '".xml"' extension.' data['form']['msg'] = 'Save the File with '".xml"' extension.'
data['form']['file_save'] = base64.encodestring(data_file.encode('utf8')) data['form']['file_save'] = base64.encodestring(data_file.encode('utf8'))
data['form']['name'] = 'vat_list.xml'
return data['form'] return data['form']
states = { states = {
'init': { 'init': {
'actions': [], 'actions': [],
'result': {'type':'form', 'arch':form, 'fields':fields, 'state':[('end','Cancel'),('go','Create XML')]}, 'result': {'type':'form', 'arch':form, 'fields':fields, 'state':[('end','Cancel'),('go_step','View Clients')]},
},
'go_step': {
'actions': [_get_partner],
'result': {'type':'form', 'arch':client_form, 'fields':client_fields, 'state':[('end','Cancel'),('go','Create XML')]},
}, },
'go': { 'go': {
'actions': [_create_xml], 'actions': [_create_xml],
@ -194,4 +244,5 @@ class wizard_vat(wizard.interface):
} }
wizard_vat('list.vat.detail') wizard_vat('list.vat.detail')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -284,7 +284,7 @@ Business Days - (Time Allocation of Tasks + Time Allocation without Tasks + Holi
cr.execute(""" CREATE OR REPLACE VIEW report_account_analytic_planning_user AS ( cr.execute(""" CREATE OR REPLACE VIEW report_account_analytic_planning_user AS (
SELECT SELECT
planning.id AS planning_id, planning.id AS planning_id,
max(planning.id)+COALESCE(users.id,1) AS id, planning.id::varchar(32) || '-' || COALESCE(users.id,0)::varchar(32) AS id,
planning.business_days, planning.business_days,
users.id AS user_id, users.id AS user_id,
(SELECT sum(line1.amount_in_base_uom) (SELECT sum(line1.amount_in_base_uom)
@ -322,14 +322,13 @@ Business Days - (Time Allocation of Tasks + Time Allocation without Tasks + Holi
FROM report_account_analytic_planning planning FROM report_account_analytic_planning planning
LEFT JOIN report_account_analytic_planning_line line ON (line.planning_id = planning.id), res_users users LEFT JOIN report_account_analytic_planning_line line ON (line.planning_id = planning.id), res_users users
GROUP BY planning.id, planning.business_days, users.id, planning.date_from, planning.date_to GROUP BY planning.id, planning.business_days, users.id, planning.date_from, planning.date_to
UNION UNION
SELECT SELECT
planning.id AS planning_id, planning.id AS planning_id,
max(planning.id) AS id, planning.id::varchar(32) || '-' || '0' AS id,
planning.business_days, planning.business_days,
line.user_id, line.user_id,
(SELECT SUM(line1.amount_in_base_uom) (SELECT SUM(line1.amount_in_base_uom)
@ -411,25 +410,36 @@ class report_account_analytic_planning_account(osv.osv):
} }
def init(self, cr): def init(self, cr):
cr.execute(""" create or replace view report_account_analytic_planning_account as ( cr.execute(""" CREATE OR REPLACE VIEW report_account_analytic_planning_account AS (
select SELECT
min(l.id) as id, MIN(l.id) AS id,
l.account_id as account_id, l.account_id AS account_id,
sum(l.amount) as quantity, SUM(l.amount) AS quantity,
l.planning_id as planning_id, l.planning_id AS planning_id,
(Select sum(line1.amount_in_base_uom) from report_account_analytic_planning_line line1 ( SELECT SUM(line1.amount_in_base_uom)
where (select count(1) from project_task task where task.planning_line_id = line1.id) > 0 FROM report_account_analytic_planning_line line1
and l.account_id = line1.account_id WHERE
and l.planning_id = line1.planning_id ( SELECT COUNT(1)
) as plan_tasks, FROM project_task task
(Select sum(line1.amount_in_base_uom) from report_account_analytic_planning_line line1 WHERE task.planning_line_id = line1.id
where (select count(1) from project_task task where task.planning_line_id = line1.id) = 0 and l.account_id = line1.account_id ) > 0
and planning.id=l.planning_id AND l.account_id = line1.account_id
) as plan_open AND l.planning_id = line1.planning_id
from report_account_analytic_planning_line l ) AS plan_tasks,
inner join report_account_analytic_planning planning on planning.id=l.planning_id ( SELECT SUM(line1.amount_in_base_uom)
group by l.account_id, l.planning_id, planning.date_from, planning.date_to, planning.id FROM report_account_analytic_planning_line line1
) WHERE
( SELECT COUNT(1)
FROM project_task task
WHERE task.planning_line_id = line1.id
) = 0
AND l.account_id = line1.account_id
AND planning.id = line1.planning_id
) AS plan_open
FROM report_account_analytic_planning_line l
INNER JOIN report_account_analytic_planning planning ON planning.id=l.planning_id
GROUP BY l.account_id, l.planning_id, planning.date_from, planning.date_to, planning.id
)
""") """)