diff --git a/addons/crm/crm_lead_demo.xml b/addons/crm/crm_lead_demo.xml
index 5336f52a000..862f93126db 100644
--- a/addons/crm/crm_lead_demo.xml
+++ b/addons/crm/crm_lead_demo.xml
@@ -115,5 +115,85 @@
+
+
+
+
+ lead
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ lead
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ lead
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ lead
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ lead
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml
index c33339047d8..ed4466c9746 100644
--- a/addons/crm/crm_lead_view.xml
+++ b/addons/crm/crm_lead_view.xml
@@ -151,6 +151,19 @@
+
+
+
+
+
+
+
+
diff --git a/addons/crm/crm_mailgate.py b/addons/crm/crm_mailgate.py
index f2380182aad..04a837e9bf4 100644
--- a/addons/crm/crm_mailgate.py
+++ b/addons/crm/crm_mailgate.py
@@ -42,7 +42,7 @@ class mailgate_thread(osv.osv):
_name = "mailgate.thread"
_inherit = "mailgate.thread"
- def message_new(self, cr, uid, msg, context):
+ def message_new(self, cr, uid, msg, context=None):
"""
Automatically calls when new email message arrives
diff --git a/addons/crm/crm_opportunity_demo.xml b/addons/crm/crm_opportunity_demo.xml
index aa965a07594..d03cf78ebbc 100644
--- a/addons/crm/crm_opportunity_demo.xml
+++ b/addons/crm/crm_opportunity_demo.xml
@@ -47,7 +47,7 @@
-
+
@@ -89,5 +89,90 @@
+
+
+
+ opportunity
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ opportunity
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ opportunity
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ opportunity
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ opportunity
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/document/__init__.py b/addons/document/__init__.py
index 694989bdd79..12306b0499b 100644
--- a/addons/document/__init__.py
+++ b/addons/document/__init__.py
@@ -27,5 +27,6 @@ import directory_content
import directory_report
import document
import report
+import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/document/__openerp__.py b/addons/document/__openerp__.py
index ac227827d11..2064ba32375 100644
--- a/addons/document/__openerp__.py
+++ b/addons/document/__openerp__.py
@@ -41,6 +41,7 @@
'update_xml': [
'document_view.xml',
'document_data.xml',
+ 'wizard/document_configuration_view.xml',
'security/document_security.xml',
'security/ir.model.access.csv',
'report/document_report_view.xml'
diff --git a/addons/document/security/ir.model.access.csv b/addons/document/security/ir.model.access.csv
index 245bcbffeab..a4412195745 100644
--- a/addons/document/security/ir.model.access.csv
+++ b/addons/document/security/ir.model.access.csv
@@ -19,3 +19,5 @@
"access_report_document_file_group_system","report.document.file group system","model_report_document_file","base.group_system",1,0,0,0
"access_report_document_wall_group_document_manager","report.document.wall document manager","model_report_document_wall","document.group_document_manager",1,0,0,0
"access_report_document_wall_group_system","report.document.wall group system","model_report_document_wall","base.group_system",1,0,0,0
+"access_document_configuation","document.configuration document manager","model_document_configuration","document.group_document_manager",1,1,1,1
+"access_document_configuation_system","document.configuration group system","model_document_configuration","base.group_system",1,1,1,1
diff --git a/addons/document/wizard/__init__.py b/addons/document/wizard/__init__.py
new file mode 100644
index 00000000000..b143b583426
--- /dev/null
+++ b/addons/document/wizard/__init__.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# 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 .
+#
+##############################################################################
+import document_configuration
diff --git a/addons/document_ftp/wizard/document_configuration.py b/addons/document/wizard/document_configuration.py
similarity index 100%
rename from addons/document_ftp/wizard/document_configuration.py
rename to addons/document/wizard/document_configuration.py
diff --git a/addons/document_ftp/wizard/document_configuration_view.xml b/addons/document/wizard/document_configuration_view.xml
similarity index 100%
rename from addons/document_ftp/wizard/document_configuration_view.xml
rename to addons/document/wizard/document_configuration_view.xml
diff --git a/addons/document_ftp/__openerp__.py b/addons/document_ftp/__openerp__.py
index 92ecac1d4ed..854bda27b7f 100644
--- a/addons/document_ftp/__openerp__.py
+++ b/addons/document_ftp/__openerp__.py
@@ -33,8 +33,7 @@
'depends': ['base', 'document'],
'init_xml': [],
'update_xml': [
- 'wizard/ftp_browse_view.xml',
- 'wizard/document_configuration_view.xml',
+ 'wizard/ftp_browse_view.xml',
'security/ir.model.access.csv'
],
'demo_xml': [],
diff --git a/addons/document_ftp/security/ir.model.access.csv b/addons/document_ftp/security/ir.model.access.csv
index c9c082f4397..3aba63999ed 100644
--- a/addons/document_ftp/security/ir.model.access.csv
+++ b/addons/document_ftp/security/ir.model.access.csv
@@ -1,3 +1,3 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
-"access_document_configuation_wizard","document.configuration.wizard document manager","model_document_configuration_wizard","document.group_document_manager",1,1,1,1
-"access_document_configuation_wizard_sytem","document.configuration.wizard group system","model_document_configuration_wizard","base.group_system",1,1,1,1
+"access_document_ftp_browse","document.ftp.browse document manager","model_document_ftp_browse","document.group_document_manager",1,1,1,1
+"access_document_ftp_browse","document.ftp.browse group system","model_document_ftp_browse","base.group_system",1,1,1,1
diff --git a/addons/document_ftp/wizard/__init__.py b/addons/document_ftp/wizard/__init__.py
index 22971249113..e6762d7fda8 100644
--- a/addons/document_ftp/wizard/__init__.py
+++ b/addons/document_ftp/wizard/__init__.py
@@ -19,6 +19,6 @@
#
##############################################################################
import ftp_browse
-import document_configuration
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/document_ftp/wizard/ftp_browse.py b/addons/document_ftp/wizard/ftp_browse.py
index 06c9766997b..67dae5cb95a 100644
--- a/addons/document_ftp/wizard/ftp_browse.py
+++ b/addons/document_ftp/wizard/ftp_browse.py
@@ -36,14 +36,13 @@ class document_ftp_browse(osv.osv_memory):
if 'url' in fields:
user_pool = self.pool.get('res.users')
current_user = user_pool.browse(cr, uid, uid, context=context)
- res['url'] = '%s@%s:%d'%(current_user.login, ftpserver.HOST, ftpserver.PORT)
+ res['url'] = 'ftp://%s@%s:%d'%(current_user.login, ftpserver.HOST, ftpserver.PORT)
return res
def browse_ftp(self, cr, uid, ids, context):
data_id = ids and ids[0] or False
- data = self.browse(cr, uid, data_id, context)
- ftp_url = 'ftp://'
- final_url = ftp_url + data.url
+ data = self.browse(cr, uid, data_id, context)
+ final_url = data.url
return {
'type': 'ir.actions.act_url',
'url':final_url,
diff --git a/addons/fetchmail/fetchmail.py b/addons/fetchmail/fetchmail.py
index a96f1bf9af9..450362b80a5 100644
--- a/addons/fetchmail/fetchmail.py
+++ b/addons/fetchmail/fetchmail.py
@@ -2,7 +2,7 @@
#-*- coding:utf-8 -*-
##############################################################################
#
-# OpenERP, Open Source Management Solution
+# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (). All Rights Reserved
# mga@tinyerp.com
#
@@ -21,140 +21,57 @@
#
##############################################################################
-import os
-import re
import time
-import email
-import binascii
-import mimetypes
-
from imaplib import IMAP4
-from imaplib import IMAP4_SSL
-
+from imaplib import IMAP4_SSL
from poplib import POP3
from poplib import POP3_SSL
-from email.header import Header
-from email.header import decode_header
-
import netsvc
-from osv import osv
-from osv import fields
-from tools.translate import _
+from osv import osv, fields
logger = netsvc.Logger()
-def html2plaintext(html, body_id=None, encoding='utf-8'):
- ## (c) Fry-IT, www.fry-it.com, 2007
- ##
- ## 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 ) 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('','*').replace('','*')
- html = html.replace('','*').replace('','*')
- html = html.replace('','*').replace('
','*')
- html = html.replace('','**').replace('
','**')
- html = html.replace('','**').replace('
','**')
- html = html.replace('','/').replace('','/')
-
- # the only line breaks we respect is those of ending tags and
- # breaks
-
- html = html.replace('\n',' ')
- html = html.replace('
', '\n')
- html = html.replace('', '\n')
- html = html.replace('', '\n\n')
- html = re.sub('
', '\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 email_server(osv.osv):
-
+
_name = 'email.server'
_description = "POP/IMAP Server"
-
+
_columns = {
- 'name':fields.char('Name', size=256, required=True, readonly=False),
- 'active':fields.boolean('Active', required=False),
+ 'name':fields.char('Name', size=256, required=True, readonly=False),
+ 'active':fields.boolean('Active', required=False),
'state':fields.selection([
- ('draft','Not Confirmed'),
- ('wating','Waiting for Verification'),
- ('done','Confirmed'),
- ],'State', select=True, readonly=True),
- 'server' : fields.char('Server', size=256, required=True, readonly=True, states={'draft':[('readonly',False)]}),
- 'port' : fields.integer('Port', required=True, readonly=True, states={'draft':[('readonly',False)]}),
+ ('draft', 'Not Confirmed'),
+ ('wating', 'Waiting for Verification'),
+ ('done', 'Confirmed'),
+ ], 'State', select=True, readonly=True),
+ 'server' : fields.char('Server', size=256, required=True, readonly=True, states={'draft':[('readonly', False)]}),
+ 'port' : fields.integer('Port', required=True, readonly=True, states={'draft':[('readonly', False)]}),
'type':fields.selection([
- ('pop','POP Server'),
- ('imap','IMAP Server'),
- ],'State', select=True, readonly=False),
- 'is_ssl':fields.boolean('SSL ?', required=False),
- 'attach':fields.boolean('Add Attachments ?', required=False),
- 'date': fields.date('Date', readonly=True, states={'draft':[('readonly',False)]}),
- 'user' : fields.char('User Name', size=256, required=True, readonly=True, states={'draft':[('readonly',False)]}),
- 'password' : fields.char('Password', size=1024, invisible=True, required=True, readonly=True, states={'draft':[('readonly',False)]}),
- 'note': fields.text('Description'),
- 'action_id':fields.many2one('ir.actions.server', 'Reply Email', required=False, domain="[('state','=','email')]"),
- 'object_id': fields.many2one('ir.model',"Model", required=True),
- 'priority': fields.integer('Server Priority', readonly=True, states={'draft':[('readonly',False)]}, help="Priority between 0 to 10, select define the order of Processing"),
- 'user_id':fields.many2one('res.users', 'User', required=False),
+ ('pop', 'POP Server'),
+ ('imap', 'IMAP Server'),
+ ], 'State', select=True, readonly=False),
+ 'is_ssl':fields.boolean('SSL ?', required=False),
+ 'attach':fields.boolean('Add Attachments ?', required=False),
+ 'date': fields.date('Date', readonly=True, states={'draft':[('readonly', False)]}),
+ 'user' : fields.char('User Name', size=256, required=True, readonly=True, states={'draft':[('readonly', False)]}),
+ 'password' : fields.char('Password', size=1024, invisible=True, required=True, readonly=True, states={'draft':[('readonly', False)]}),
+ 'note': fields.text('Description'),
+ 'action_id':fields.many2one('ir.actions.server', 'Reply Email', required=False, domain="[('state','=','email')]"),
+ 'object_id': fields.many2one('ir.model', "Model", required=True),
+ 'priority': fields.integer('Server Priority', readonly=True, states={'draft':[('readonly', False)]}, help="Priority between 0 to 10, select define the order of Processing"),
+ 'user_id':fields.many2one('res.users', 'User', required=False),
}
_defaults = {
- 'state': lambda *a: "draft",
- 'active': lambda *a: True,
- 'priority': lambda *a: 5,
- 'date': lambda *a: time.strftime('%Y-%m-%d'),
- 'user_id': lambda self, cr, uid, ctx: uid,
+ 'state': lambda *a: "draft",
+ 'active': lambda *a: True,
+ 'priority': lambda *a: 5,
+ 'date': lambda *a: time.strftime('%Y-%m-%d'),
+ 'user_id': lambda self, cr, uid, ctx: uid,
}
-
+
def check_duplicate(self, cr, uid, ids):
vals = self.read(cr, uid, ids, ['user', 'password'])[0]
cr.execute("select count(id) from email_server where user='%s' and password='%s'" % (vals['user'], vals['password']))
@@ -162,215 +79,41 @@ class email_server(osv.osv):
if res:
if res[0] > 1:
return False
- return True
+ return True
_constraints = [
(check_duplicate, 'Warning! Can\'t have duplicate server configuration!', ['user', 'password'])
]
-
+
def onchange_server_type(self, cr, uid, ids, server_type=False, ssl=False):
port = 0
if server_type == 'pop':
port = ssl and 995 or 110
elif server_type == 'imap':
port = ssl and 993 or 143
-
+
return {'value':{'port':port}}
-
- def _process_email(self, cr, uid, server, message, context={}):
- context.update({
- 'server_id':server.id
- })
- history_pool = self.pool.get('mailgate.message')
- msg_txt = email.message_from_string(message)
- message_id = msg_txt.get('Message-ID', False)
-
- msg = {}
- if not message_id:
- return False
-
- fields = msg_txt.keys()
-
- msg['id'] = message_id
- msg['message-id'] = message_id
-
- def _decode_header(txt):
- txt = txt.replace('\r', '')
- return ' '.join(map(lambda (x, y): unicode(x, y or 'ascii'), decode_header(txt)))
-
- if 'Subject' in fields:
- msg['subject'] = _decode_header(msg_txt.get('Subject'))
-
- if 'Content-Type' in fields:
- msg['content-type'] = msg_txt.get('Content-Type')
-
- if 'From' in fields:
- msg['from'] = _decode_header(msg_txt.get('From'))
-
- if 'Delivered-To' in fields:
- msg['to'] = _decode_header(msg_txt.get('Delivered-To'))
-
- if 'Cc' in fields:
- msg['cc'] = _decode_header(msg_txt.get('Cc'))
-
- if 'Reply-To' in fields:
- msg['reply'] = _decode_header(msg_txt.get('Reply-To'))
-
- if 'Date' in fields:
- msg['date'] = msg_txt.get('Date')
-
- if 'Content-Transfer-Encoding' in fields:
- msg['encoding'] = msg_txt.get('Content-Transfer-Encoding')
-
- if 'References' in fields:
- msg['references'] = msg_txt.get('References')
-
- if 'X-openerp-caseid' in fields:
- msg['caseid'] = msg_txt.get('X-openerp-caseid')
-
- if 'X-Priority' in fields:
- msg['priority'] = msg_txt.get('X-priority', '3 (Normal)').split(' ')[0]
-
- if not msg_txt.is_multipart() or 'text/plain' in msg.get('content-type', None):
- encoding = msg_txt.get_content_charset()
- msg['body'] = msg_txt.get_payload(decode=True)
- if encoding:
- msg['body'] = msg['body'].decode(encoding).encode('utf-8')
-
- attachents = {}
- if msg_txt.is_multipart() or 'multipart/alternative' in msg.get('content-type', None):
- body = ""
- counter = 1
- for part in msg_txt.walk():
- if part.get_content_maintype() == 'multipart':
- continue
-
- encoding = part.get_content_charset()
-
- if part.get_content_maintype()=='text':
- content = part.get_payload(decode=True)
- filename = part.get_filename()
- if filename :
- attachents[filename] = content
- else:
- if encoding:
- content = unicode(content, encoding)
- if part.get_content_subtype() == 'html':
- body = html2plaintext(content)
- elif part.get_content_subtype() == 'plain':
- body = content
- elif part.get_content_maintype()=='application' or part.get_content_maintype()=='image' or part.get_content_maintype()=='text':
- filename = part.get_filename();
- if filename :
- attachents[filename] = part.get_payload(decode=True)
- else:
- res = part.get_payload(decode=True)
- if encoding:
- res = res.decode(encoding).encode('utf-8')
-
- body += res
-
- msg['body'] = body
- msg['attachments'] = attachents
-
-
- res_id = False
- if msg.get('references', False):
- id = False
- ref = msg.get('references')
- if '\r\n' in ref:
- ref = msg.get('references').split('\r\n')
- else:
- ref = msg.get('references').split(' ')
- if ref:
- hids = history_pool.search(cr, uid, [('name','=',ref[0].strip())])
- if hids:
- id = hids[0]
- history = history_pool.browse(cr, uid, id)
- model_pool = self.pool.get(server.object_id.model)
- context.update({
- 'references_id':ref[0]
- })
- vals = {
-
- }
- if hasattr(model_pool, 'message_update'):
- model_pool.message_update(cr, uid, [history.res_id], vals, msg, context=context)
- else:
- logger.notifyChannel('imap', netsvc.LOG_WARNING, 'method def message_update is not define in model %s' % (model_pool._name))
- return False
- res_id = id
- else:
- model_pool = self.pool.get(server.object_id.model)
- if hasattr(model_pool, 'message_new'):
- res_id = model_pool.message_new(cr, uid, msg, context)
- else:
- data = {
- 'name': msg.get('subject'),
- 'email_from': msg.get('from'),
- 'email_cc': msg.get('cc'),
- 'user_id': False,
- 'description': msg.get('body'),
- 'state' : 'draft',
- }
- res_id = model_pool.create(cr, uid, data, context=context)
- logger.notifyChannel('imap', netsvc.LOG_WARNING, 'method def message_new is not define in model %s. Using default method' % (model_pool._name))
-
- att_ids = []
- if server.attach:
- for attactment in attachents or []:
- data_attach = {
- 'name': attactment,
- 'datas':binascii.b2a_base64(str(attachents.get(attactment))),
- 'datas_fname': attactment,
- 'description': 'Mail attachment',
- 'res_model': server.object_id.model,
- 'res_id': res_id,
- }
- att_ids.append(self.pool.get('ir.attachment').create(cr, uid, data_attach))
-
- if server.action_id:
- action_pool = self.pool.get('ir.actions.server')
- action_pool.run(cr, uid, [server.action_id.id], {'active_id':res_id, 'active_ids':[res_id]})
- res = {
- 'name': msg.get('subject', 'No subject'),
- 'message_id': message_id,
- 'date': msg.get('date'),
- 'res_id': res_id,
- 'email_from': msg.get('from'),
- 'email_to': msg.get('to'),
- 'email_cc': msg.get('cc'),
- 'model': server.object_id.model,
- 'server_id': server.id,
- 'description': msg.get('body', msg.get('from')),
- 'ref_id':msg.get('references', msg.get('id')),
- 'type':server.type,
- 'attachment_ids': [(6, 0, att_ids)]
- }
- his_id = history_pool.create(cr, uid, res)
-
- return res_id
def set_draft(self, cr, uid, ids, context={}):
self.write(cr, uid, ids , {'state':'draft'})
return True
-
+
def button_fetch_mail(self, cr, uid, ids, context={}):
self.fetch_mail(cr, uid, ids)
# sendmail_thread = threading.Thread(target=self.fetch_mail, args=(cr, uid, ids))
# sendmail_thread.start()
return True
-
+
def _fetch_mails(self, cr, uid, ids=False, context={}):
if not ids:
ids = self.search(cr, uid, [])
return self.fetch_mail(cr, uid, ids, context)
-
- def fetch_mail(self, cr, uid, ids, context={}):
+ def fetch_mail(self, cr, uid, ids, context={}):
+ email_tool = self.pool.get('email.server.tools')
for server in self.browse(cr, uid, ids, context):
logger.notifyChannel('imap', netsvc.LOG_INFO, 'fetchmail start checking for new emails on %s' % (server.name))
-
+
count = 0
try:
if server.type == 'imap':
@@ -379,17 +122,21 @@ class email_server(osv.osv):
imap_server = IMAP4_SSL(server.server, int(server.port))
else:
imap_server = IMAP4(server.server, int(server.port))
-
+
imap_server.login(server.user, server.password)
imap_server.select()
result, data = imap_server.search(None, '(UNSEEN)')
for num in data[0].split():
result, data = imap_server.fetch(num, '(RFC822)')
- if self._process_email(cr, uid, server, data[0][1], context):
+ res_id = email_tool.process_email(cr, uid, server.object_id.model, data[0][1], attach=server.attach, server_id=server.id, server_type=server.type, context=context)
+ if res_id and server.action_id:
+ action_pool = self.pool.get('ir.actions.server')
+ action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]})
+
imap_server.store(num, '+FLAGS', '\\Seen')
- count += 1
+ count += 1
logger.notifyChannel('imap', netsvc.LOG_INFO, 'fetchmail fetch/process %s email(s) from %s' % (count, server.name))
-
+
imap_server.close()
imap_server.logout()
elif server.type == 'pop':
@@ -398,7 +145,7 @@ class email_server(osv.osv):
pop_server = POP3_SSL(server.server, int(server.port))
else:
pop_server = POP3(server.server, int(server.port))
-
+
#TODO: use this to remove only unread messages
#pop_server.user("recent:"+server.user)
pop_server.user(server.user)
@@ -409,72 +156,38 @@ class email_server(osv.osv):
for num in range(1, numMsgs + 1):
(header, msges, octets) = pop_server.retr(num)
msg = '\n'.join(msges)
- self._process_email(cr, uid, server, msg, context)
+ res_id = email_tool.process_email(cr, uid, server.object_id.model, data[0][1], attach=server.attach, server_id=server.id, server_type=server.type, context=context)
+ if res_id and server.action_id:
+ action_pool = self.pool.get('ir.actions.server')
+ action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id]})
+
pop_server.dele(num)
pop_server.quit()
-
+
logger.notifyChannel('imap', netsvc.LOG_INFO, 'fetchmail fetch %s email(s) from %s' % (numMsgs, server.name))
-
+
self.write(cr, uid, [server.id], {'state':'done'})
except Exception, e:
logger.notifyChannel(server.type, netsvc.LOG_WARNING, '%s' % (e))
-
+
return True
email_server()
-class mail_server_history(osv.osv):
+class mailgate_message(osv.osv):
_inherit = "mailgate.message"
-
+
_columns = {
- 'server_id': fields.many2one('email.server',"Mail Server", readonly=True, select=True),
+ 'server_id': fields.many2one('email.server', "Mail Server", readonly=True, select=True),
'type':fields.selection([
- ('pop','POP Server'),
- ('imap','IMAP Server'),
- ],'State', select=True, readonly=True),
+ ('pop', 'POP Server'),
+ ('imap', 'IMAP Server'),
+ ], 'State', select=True, readonly=True),
}
_order = 'id desc'
-
-mail_server_history()
-class fetchmail_tool(osv.osv):
+mailgate_message()
- _name = 'email.server.tools'
- _description = "Email Tools"
- _auto = False
-
- def to_email(self, text):
- _email = re.compile(r'.*<.*@.*\..*>', re.UNICODE)
- def record(path):
- eml = path.group()
- index = eml.index('<')
- eml = eml[index:-1].replace('<','').replace('>','')
- return eml
-
- bits = _email.sub(record, text)
- return bits
-
- def get_partner(self, cr, uid, from_email, context=None):
- """
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks
- @param from_email: email address based on that function will search for the correct
- """
-
- res = {
- 'partner_address_id': False,
- 'partner_id': False
- }
- from_email = self.to_email(from_email)
- address_ids = self.pool.get('res.partner.address').search(cr, uid, [('email', '=', from_email)])
- if address_ids:
- address = self.pool.get('res.partner.address').browse(cr, uid, address_ids[0])
- res['partner_address_id'] = address_ids[0]
- res['partner_id'] = address.partner_id.id
-
- return res
-
-fetchmail_tool()
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/fetchmail/security/ir.model.access.csv b/addons/fetchmail/security/ir.model.access.csv
index d31fd067485..7aa6c84d22b 100644
--- a/addons/fetchmail/security/ir.model.access.csv
+++ b/addons/fetchmail/security/ir.model.access.csv
@@ -1,3 +1,2 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_email_server","email.server","model_email_server",,1,1,1,1
-"access_email_server_tools","email.server.tools","model_email_server_tools",,1,1,1,1
diff --git a/addons/mail_gateway/mail_gateway.py b/addons/mail_gateway/mail_gateway.py
index 1b79337f1ca..809c9310768 100644
--- a/addons/mail_gateway/mail_gateway.py
+++ b/addons/mail_gateway/mail_gateway.py
@@ -21,7 +21,17 @@
from osv import osv, fields
import time
-import base64
+import base64
+import re
+import tools
+import binascii
+import socket
+import email
+from email.header import decode_header
+import netsvc
+
+logger = netsvc.Logger()
+
class mailgate_thread(osv.osv):
'''
@@ -32,9 +42,9 @@ class mailgate_thread(osv.osv):
_rec_name = 'thread'
_columns = {
- 'thread': fields.char('Thread', size=124, required=False),
- 'message_ids': one2many_domain('mailgate.message', 'thread_id', 'Messages', domain=[('history', '=', True)], required=False),
- 'log_ids': one2many_domain('mailgate.message', 'thread_id', 'Logs', domain=[('history', '=', False)], required=False),
+ 'thread': fields.char('Thread', size=32, required=False),
+ 'message_ids': fields.one2many('mailgate.message', 'thread_id', 'Messages', domain=[('history', '=', True)], required=False),
+ 'log_ids': fields.one2many('mailgate.message', 'thread_id', 'Logs', domain=[('history', '=', False)], required=False),
'model': fields.char('Model Name', size=64, required=False),
'res_id': fields.integer('Resource ID'),
}
@@ -139,4 +149,308 @@ class mailgate_message(osv.osv):
mailgate_message()
+class mailgate_tool(osv.osv):
+
+ _name = 'email.server.tools'
+ _description = "Email Tools"
+ _auto = False
+
+ def _to_decode(self, s, charsets):
+ for charset in charsets:
+ if charset:
+ try:
+ return s.decode(charset)
+ except UnicodeError:
+ pass
+ return s.decode('latin1')
+
+ def _decode_header(self, text):
+ if text:
+ text = decode_header(text.replace('\r', ''))
+ return ''.join(map(lambda x:self._to_decode(x[0], [x[1]]), text or []))
+
+ def to_email(self, cr, uid, text):
+ _email = re.compile(r'.*<.*@.*\..*>', re.UNICODE)
+ def record(path):
+ eml = path.group()
+ index = eml.index('<')
+ eml = eml[index:-1].replace('<', '').replace('>', '')
+ return eml
+
+ bits = _email.sub(record, text)
+ return bits
+
+ def history(self, cr, uid, model, new_id, msg, attach, server_id=None, server_type=None):
+ """This function creates history for mails fetched
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param model: OpenObject Model
+ @param new_id: Id of the record of OpenObject model created from the Email details
+ @param msg: Email details
+ @param attach: Email attachments
+ @param server_id: Id of the POP/IMAP Server if the mail is fetched using OpenERP interface
+ @param server_type: Type of Fetchmail server"""
+ try:
+ thread_id = self.pool.get(model).read(cr, uid, new_id, ['thread_id'])['thread_id'][0]
+ except Exception, e:
+ thread_id = None
+ msg_data = {
+ 'name': msg.get('subject', 'No subject'),
+ 'date': msg.get('date') ,
+ 'description': msg.get('body', msg.get('from')),
+ 'history': True,
+ 'model': model,
+ 'email_cc': msg.get('cc'),
+ 'email_from': msg.get('from'),
+ 'email_to': msg.get('to'),
+ 'message_id': msg.get('message-id'),
+ 'ref_id': msg.get('references', msg.get('id')),
+ 'res_id': new_id,
+ 'server_id': server_id,
+ 'thread_id': thread_id,
+ 'type': server_type,
+ 'user_id': uid,
+ 'attachment_ids': [(6, 0, attach)]
+ }
+ msg_id = self.pool.get('mailgate.message').create(cr, uid, msg_data)
+ return True
+
+ def email_send(self, cr, uid, model, res_id, msg, email_default ):
+ """This function Sends return email on submission of Fetched email in OpenERP database
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param model: OpenObject Model
+ @param res_id: Id of the record of OpenObject model created from the Email details
+ @param msg: Email details
+ @param email_default: Default Email address in case of any Problem
+ """
+ message = email.message_from_string(str(msg))
+ subject = '['+str(res_id)+'] '+ self._decode_header(message['Subject'])
+ message['Message-Id'] = '<' + str(time.time()) + '-openerp-' + \
+ model + '-' + str(res_id) + '@'+socket.gethostname() + '>'
+ msg_mails = []
+ mails = [self._decode_header(message['From']), self._decode_header(message['To'])]+self._decode_header(message.get('Cc', '')).split(',')
+ for mail in mails:
+ if mail:
+ msg_mails.append(self.to_email(cr, uid, mail))
+ encoding = message.get_content_charset()
+ message['body'] = message.get_payload(decode=True)
+ if encoding:
+ message['body'] = message['body'].decode(encoding).encode('utf-8')
+
+ try:
+ tools.email_send(email_default or tools.config.get('email_from', None), msg_mails, subject, message.get('body'))
+ except Exception, e:
+ if email_default:
+ temp_msg = '['+str(res_id)+'] ' + self._decode_header(message['Subject'])
+ del message['Subject']
+ message['Subject'] = '[OpenERP-FetchError] ' + temp_msg
+ tools.email_send(email_default, email_default, message.get('Subject'), message.get('body'))
+ return True
+
+ def process_email(self, cr, uid, model, message, attach=True, server_id=None, server_type=None, context=None):
+ """This function Processes email and create record for given OpenERP model
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param model: OpenObject Model
+ @param message: Email details
+ @param attach: Email attachments
+ @param server_id: Id of the POP/IMAP Server if the mail is fetched using OpenERP interface
+ @param server_type: Type of Fetchmail server
+ @param context: A standard dictionary for contextual values"""
+ if not context:
+ context = {}
+ context.update({
+ 'server_id': server_id
+ })
+ res_id = False
+ def create_record(msg):
+ model_pool = self.pool.get(model)
+ if hasattr(model_pool, 'message_new'):
+ res_id = model_pool.message_new(cr, uid, msg, context)
+ else:
+ data = {
+ 'name': msg.get('subject'),
+ 'email_from': msg.get('from'),
+ 'email_cc': msg.get('cc'),
+ 'user_id': False,
+ 'description': msg.get('body'),
+ 'state' : 'draft',
+ }
+ data.update(self.get_partner(cr, uid, msg.get('from'), context=context))
+ res_id = model_pool.create(cr, uid, data, context=context)
+ logger.notifyChannel('imap', netsvc.LOG_WARNING, 'method def message_new is not define in model %s. Using default method' % (model_pool._name))
+ att_ids = []
+ if attach:
+ for attachment in msg.get('attachments', []):
+ data_attach = {
+ 'name': attachment,
+ 'datas': binascii.b2a_base64(str(attachments.get(attachment))),
+ 'datas_fname': attachment,
+ 'description': 'Mail attachment',
+ 'res_model': model,
+ 'res_id': res_id,
+ }
+ att_ids.append(self.pool.get('ir.attachment').create(cr, uid, data_attach))
+
+ if hasattr(model_pool, '_history'):
+ res = model_pool.browse(cr, uid, [res_id])
+ model_pool._history(cr, uid, res, 'Receive', True,
+ subject=msg.get('subject'),
+ email=msg.get('to'), details=msg.get('body'),
+ email_from=msg.get('from'),
+ message_id=msg.get('message-id'),
+ attach=msg.get('attachments', {}).items(),
+ context={'model' : model})
+ else:
+ self.history(cr, uid, model, res_id, msg, att_ids, server_id=server_id, server_type=server_type)
+
+ return res_id
+
+ history_pool = self.pool.get('mailgate.message')
+ msg_txt = email.message_from_string(message)
+ message_id = msg_txt.get('Message-ID', False)
+
+ msg = {}
+ if not message_id:
+ return False
+
+ fields = msg_txt.keys()
+ msg['id'] = message_id
+ msg['message-id'] = message_id
+
+ def _decode_header(txt):
+ txt = txt.replace('\r', '')
+ return ' '.join(map(lambda (x, y): unicode(x, y or 'ascii'), decode_header(txt)))
+
+ if 'Subject' in fields:
+ msg['subject'] = _decode_header(msg_txt.get('Subject'))
+
+ if 'Content-Type' in fields:
+ msg['content-type'] = msg_txt.get('Content-Type')
+
+ if 'From' in fields:
+ msg['from'] = _decode_header(msg_txt.get('From'))
+
+ if 'Delivered-To' in fields:
+ msg['to'] = _decode_header(msg_txt.get('Delivered-To'))
+
+ if 'Cc' in fields:
+ msg['cc'] = _decode_header(msg_txt.get('Cc'))
+
+ if 'Reply-To' in fields:
+ msg['reply'] = _decode_header(msg_txt.get('Reply-To'))
+
+ if 'Date' in fields:
+ msg['date'] = msg_txt.get('Date')
+
+ if 'Content-Transfer-Encoding' in fields:
+ msg['encoding'] = msg_txt.get('Content-Transfer-Encoding')
+
+ if 'References' in fields:
+ msg['references'] = msg_txt.get('References')
+
+ if 'X-openerp-caseid' in fields:
+ msg['caseid'] = msg_txt.get('X-openerp-caseid')
+
+ if 'X-Priority' in fields:
+ msg['priority'] = msg_txt.get('X-priority', '3 (Normal)').split(' ')[0]
+
+ if not msg_txt.is_multipart() or 'text/plain' in msg.get('content-type', None):
+ encoding = msg_txt.get_content_charset()
+ msg['body'] = msg_txt.get_payload(decode=True)
+ if encoding:
+ msg['body'] = msg['body'].decode(encoding).encode('utf-8')
+
+ attachments = {}
+ if msg_txt.is_multipart() or 'multipart/alternative' in msg.get('content-type', None):
+ body = ""
+ counter = 1
+ for part in msg_txt.walk():
+ if part.get_content_maintype() == 'multipart':
+ continue
+
+ encoding = part.get_content_charset()
+
+ if part.get_content_maintype()=='text':
+ content = part.get_payload(decode=True)
+ filename = part.get_filename()
+ if filename :
+ attachments[filename] = content
+ else:
+ if encoding:
+ content = unicode(content, encoding)
+ if part.get_content_subtype() == 'html':
+ body = tools.html2plaintext(content)
+ elif part.get_content_subtype() == 'plain':
+ body = content
+ elif part.get_content_maintype()=='application' or part.get_content_maintype()=='image' or part.get_content_maintype()=='text':
+ filename = part.get_filename();
+ if filename :
+ attachments[filename] = part.get_payload(decode=True)
+ else:
+ res = part.get_payload(decode=True)
+ if encoding:
+ res = res.decode(encoding).encode('utf-8')
+
+ body += res
+
+ msg['body'] = body
+ msg['attachments'] = attachments
+
+ if msg.get('references', False):
+ id = False
+ ref = msg.get('references')
+ if '\r\n' in ref:
+ ref = msg.get('references').split('\r\n')
+ else:
+ ref = msg.get('references').split(' ')
+ if ref:
+ hids = history_pool.search(cr, uid, [('name', '=', ref[0].strip())])
+ if hids:
+ id = hids[0]
+ history = history_pool.browse(cr, uid, id)
+ model_pool = self.pool.get(model)
+ context.update({
+ 'references_id':ref[0]
+ })
+ vals = {}
+ if hasattr(model_pool, 'message_update'):
+ model_pool.message_update(cr, uid, [history.res_id], vals, msg, context=context)
+ else:
+ logger.notifyChannel('imap', netsvc.LOG_WARNING, 'method def message_update is not define in model %s' % (model_pool._name))
+ return False
+ else:
+ res_id = create_record(msg)
+
+ else:
+ res_id = create_record(msg)
+
+ return res_id
+
+ def get_partner(self, cr, uid, from_email, context=None):
+ """This function returns partner Id based on email passed
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks
+ @param from_email: email address based on that function will search for the correct
+ """
+ res = {
+ 'partner_address_id': False,
+ 'partner_id': False
+ }
+ from_email = self.to_email(cr, uid, from_email)
+ address_ids = self.pool.get('res.partner.address').search(cr, uid, [('email', '=', from_email)])
+ if address_ids:
+ address = self.pool.get('res.partner.address').browse(cr, uid, address_ids[0])
+ res['partner_address_id'] = address_ids[0]
+ res['partner_id'] = address.partner_id.id
+
+ return res
+
+mailgate_tool()
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/mail_gateway/mail_gateway_view.xml b/addons/mail_gateway/mail_gateway_view.xml
index fbec97e6a17..bd286875e7a 100644
--- a/addons/mail_gateway/mail_gateway_view.xml
+++ b/addons/mail_gateway/mail_gateway_view.xml
@@ -181,6 +181,7 @@
mailgate.message
tree,form
form
+ [('history', '=', True)]
diff --git a/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py b/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py
index eb483bff0f0..e0ea723c75f 100755
--- a/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py
+++ b/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py
@@ -21,104 +21,16 @@
#
###########################################################################################
-import re
-import smtplib
-import email
-import mimetypes
-from email.Header import decode_header
-from email.MIMEText import MIMEText
-import xmlrpclib
-import os
-import binascii
-import time
-import socket
+from email.header import decode_header
+import email as EMAIL
import logging
-import sys
import optparse
-
-email_re = re.compile(r"([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6})")
-case_re = re.compile(r"\[([0-9]+)\]", re.UNICODE)
-command_re = re.compile("^Set-([a-z]+) *: *(.+)$", re.I + re.UNICODE)
-reference_re = re.compile("<.*-openobject-(\\d+)@(.*)>", re.UNICODE)
-
-priorities = {
- '1': '1 (Highest)',
- '2': '2 (High)',
- '3': '3 (Normal)',
- '4': '4 (Low)',
- '5': '5 (Lowest)',
-}
-
-def html2plaintext(html, body_id=None, encoding='utf-8'):
- ## (c) Fry-IT, www.fry-it.com, 2007
- ##
- ## 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 ) 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 = unicode(link)
- for url in [x[1] for x in link.attrs if x[0]=='href']:
- urls.append(dict(url=url, tag=unicode(link), title=title))
-
- html = unicode(soup)
-
- 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('', '*').replace('', '*')
- html = html.replace('', '*').replace('', '*')
- html = html.replace('', '*').replace('
', '*')
- html = html.replace('', '**').replace('
', '**')
- html = html.replace('', '**').replace('
', '**')
- html = html.replace('', '/').replace('', '/')
-
- # the only line breaks we respect is those of ending tags and
- # breaks
-
- html = html.replace('\n', ' ')
- html = html.replace('
', '\n')
- html = html.replace('', '\n')
- html = html.replace('', '\n\n')
- html = re.sub('
', '\n', html)
- html = html.replace(' ' * 2, ' ')
-
- html = re.sub('<.*?>', ' ', 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
+import sys
+import xmlrpclib
class rpc_proxy(object):
- 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))
+ 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), allow_none=True)
self.user_id = uid
self.passwd = passwd
self.dbname = dbname
@@ -127,7 +39,7 @@ class rpc_proxy(object):
return self.rpc.execute(self.dbname, self.user_id, self.passwd, *request, **kwargs)
class email_parser(object):
- def __init__(self, uid, password, model, email, email_default, dbname, host, port, smtp_server=None, smtp_port=None, smtp_ssl=None, smtp_user=None, smtp_password=None):
+ def __init__(self, uid, password, model, email, email_default, dbname, host, port):
self.rpc = rpc_proxy(uid, password, host=host, port=port, dbname=dbname)
try:
self.model_id = int(model)
@@ -138,204 +50,25 @@ class email_parser(object):
self.email = email
self.email_default = email_default
self.canal_id = False
- self.smtp_server = smtp_server
- self.smtp_port = smtp_port
- self.smtp_ssl = smtp_ssl
- self.smtp_user = smtp_user
- self.smtp_password = smtp_password
- def email_get(self, email_from):
- res = email_re.search(email_from)
- return res and res.group(1)
- def partner_get(self, email):
- mail = self.email_get(email)
- adr_ids = self.rpc('res.partner.address', 'search', [('email', '=', mail)])
- if not adr_ids:
- return {}
- adr = self.rpc('res.partner.address', 'read', adr_ids, ['partner_id'])
- return {
- 'partner_address_id': adr[0]['id'],
- 'partner_id': adr[0].get('partner_id', False) and adr[0]['partner_id'][0] or False
- }
-
- def _to_decode(self, s, charsets):
- for charset in charsets:
- if charset:
- try:
- return s.decode(charset)
- except UnicodeError:
- pass
- return s.decode('latin1')
-
- def _decode_header(self, s):
- from email.Header import decode_header
- s = decode_header(s.replace('\r', ''))
- return ''.join(map(lambda x:self._to_decode(x[0], [x[1]]), s or []))
-
- def history(self, model, new_id, msg_id, ref_id, subject, msg_to, msg_from, body, attach):
+ def parse(self, message):
try:
- thread_id = self.rpc(model, 'read', new_id, ['thread_id'])['thread_id'][0]
+ res_id = self.rpc('email.server.tools', 'process_email', self.model, message)
except Exception, e:
- thread_id = None
- msg_data = {
- 'name': subject,
- 'history': True,
- 'model': model,
- 'res_id': new_id,
- 'thread_id': thread_id,
- 'message_id': msg_id,
- 'ref_id': ref_id or '',
- 'user_id': self.rpc.user_id,
- 'date': time.strftime('%Y-%m-%d %H:%M:%S'),
- 'email_from': msg_from,
- 'email_to': msg_to,
- 'description': body,
- 'attachment_ids': [(6, 0, attach)]
- }
- msg_id = self.rpc('mailgate.message', 'create', msg_data)
- return True
-
- def msg_new(self, msg):
- message = self.msg_body_get(msg)
- msg_subject = self._decode_header(msg['Subject'])
- msg_from = self._decode_header(msg['From'])
- msg_to = self._decode_header(msg['To'])
- msg_cc = self._decode_header(msg['Cc'] or '')
+ res_id = False
+
+# Reply mail
+ if res_id:
+ self.rpc('email.server.tools', 'email_send', self.model, res_id, message, self.email_default)
- data = {
- 'name': msg_subject,
- 'email_from': msg_from,
- 'email_cc': msg_cc,
- 'user_id': False,
- 'description': message['body'],
- 'state' : 'draft',
- }
- data.update(self.partner_get(msg_from))
-
- try:
- att_ids = []
- new_id = self.rpc(self.model, 'create', data)
- attachments = message['attachment']
- for attach in attachments or []:
- data_attach = {
- 'name': str(attach),
- 'datas': binascii.b2a_base64(str(attachments[attach])),
- 'datas_fname': str(attach),
- 'description': 'Mail attachment',
- 'res_model': self.model,
- 'res_id': new_id
- }
- att_ids.append(self.rpc('ir.attachment', 'create', data_attach))
- try:
- self.rpc(self.model, 'history', [new_id], 'Receive', True, msg_to, message['body'], msg_from, False, {'model' : self.model})
- except Exception, e:
- self.history(self.model, new_id, msg['Message-Id'], msg['References'], msg_subject, msg_to, msg_from, message['body'], att_ids)
- except Exception, e:
- if getattr(e, 'faultCode', '') and 'AccessError' in e.faultCode:
- e = '\n\nThe Specified user does not have an access to the Model.'
- print e
-
-
- return new_id
-
-# #change the return type format to dictionary
-# {
-# 'body':'body part',
-# 'attachment':{
-# 'file_name':'file data',
-# 'file_name':'file data',
-# 'file_name':'file data',
-# }
-# }
-
- def msg_body_get(self, msg):
- message = {
- 'body' : '',
- 'attachment' : {},
- }
- attachment = message['attachment'];
- counter = 1;
-
- for part in msg.walk():
- if part.get_content_maintype() == 'multipart':
- continue
-
- if part.get_content_maintype()=='text':
- buf = part.get_payload(decode=True)
- if buf:
- txt = self._to_decode(buf, part.get_charsets())
- txt = re.sub("<\/?(\w)>", '', txt)
- if txt and part.get_content_subtype() == 'plain':
- message['body'] += txt
- elif txt and part.get_content_subtype() == 'html':
- message['body'] += html2plaintext(txt)
-
- filename = part.get_filename();
- if filename :
- attachment[filename] = part.get_payload(decode=True);
-
- elif part.get_content_maintype() in ('application', 'image', 'text'):
- filename = part.get_filename();
- if not filename :
- filename = 'attach_file'+str(counter);
- counter += 1;
-
- attachment[filename] = part.get_payload(decode=True);
- message['attachment'] = attachment
- #end for
- return message
-
- def msg_send(self, msg, emails, priority=None):
- if not emails:
- return False
-
- msg['To'] = emails[0]
- msg['Subject'] = 'OpenERP Record-id:' + msg['Subject']
- if len(emails)>1:
- if 'Cc' in msg:
- del msg['Cc']
- msg['Cc'] = ','.join(emails[1:])
- msg['Reply-To'] = self.email
- if self.smtp_user and self.smtp_password:
- s = smtplib.SMTP(self.smtp_server, self.smtp_port)
- s.ehlo()
- s.starttls()
- s.ehlo()
- s.login(self.smtp_user, self.smtp_password)
- s.sendmail(self.email, emails, msg.as_string())
- s.close()
- return True
- return False
-
-
- def parse(self, msg):
- #TODO: Something with Message hierarchy
- res_id = self.msg_new(msg)
- subject = self._decode_header(msg['subject'])
- if msg.get('Subject', ''):
- del msg['Subject']
- msg['Subject'] = '['+str(res_id)+'] '+subject
-# msg['Message-Id'] = '<'+str(time.time())+'-openerpcrm-'+str(res_id)+'@'+socket.gethostname()+'>'
-
- mm = [self._decode_header(msg['From']), self._decode_header(msg['To'])]+self._decode_header(msg.get('Cc', '')).split(',')
- msg_mails = map(self.email_get, filter(None, mm))
- try:
- self.msg_send(msg, msg_mails)
- except Exception, e:
- if self.email_default:
- a = self._decode_header(msg['Subject'])
- del msg['Subject']
- msg['Subject'] = '[OpenERP-FetchError] ' + a
- self.msg_send(msg, self.email_default.split(','))
- return res_id, msg_mails
+ return res_id
if __name__ == '__main__':
parser = optparse.OptionParser(usage='usage: %prog [options]', version='%prog v1.0')
group = optparse.OptionGroup(parser, "Note",
- "This program parse a mail from standard input and communicate "
- "with the Open ERP server for case management in the CRM module.")
+ "This program parse a mail from standard input and communicate with the Open ERP server")
parser.add_option_group(group)
parser.add_option("-u", "--user", dest="userid", help="ID of the user in Open ERP", default=1, type='int')
parser.add_option("-p", "--password", dest="password", help="Password of the user in Open ERP", default='admin')
@@ -345,16 +78,13 @@ if __name__ == '__main__':
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("--port", dest="port", help="Port of the Open ERP Server", default="8069")
- parser.add_option('--smtp', dest='smtp_server', default='', help='specify the SMTP server for sending email')
- parser.add_option('--smtp-port', dest='smtp_port', default='25', help='specify the SMTP port', type="int")
- parser.add_option('--smtp-ssl', dest='smtp_ssl', default='', help='specify the SMTP server support SSL or not')
- parser.add_option('--smtp-user', dest='smtp_user', default='', help='specify the SMTP username for sending email')
- parser.add_option('--smtp-password', dest='smtp_password', default='', help='specify the SMTP password for sending email')
(options, args) = parser.parse_args()
- parser = email_parser(options.userid, options.password, options.model, options.email, options.default, dbname=options.dbname, host=options.host, port=options.port, smtp_server=options.smtp_server, smtp_port=options.smtp_port, smtp_ssl=options.smtp_ssl, smtp_user=options.smtp_user, smtp_password=options.smtp_password)
- msg_txt = email.message_from_file(sys.stdin)
+ logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s %(message)s")
+
+ parser = email_parser(options.userid, options.password, options.model, options.email, options.default, dbname=options.dbname, host=options.host, port=options.port)
+ msg_txt = sys.stdin.read()
parser.parse(msg_txt)
diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py
index 8c2b6789cea..72eb3a7f51c 100644
--- a/addons/mrp/mrp.py
+++ b/addons/mrp/mrp.py
@@ -511,7 +511,18 @@ class mrp_production(osv.osv):
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.production', context=c),
}
_order = 'date_planned asc, priority desc';
-
+
+ def _check_qty(self, cr, uid, ids):
+ orders = self.browse(cr, uid, ids)
+ for order in orders:
+ if order.product_qty <= 0:
+ return False
+ return True
+
+ _constraints = [
+ (_check_qty, 'Order quantity cannot be negative or zero !', ['product_qty']),
+ ]
+
def unlink(self, cr, uid, ids, context=None):
productions = self.read(cr, uid, ids, ['state'])
unlink_ids = []
diff --git a/addons/mrp/mrp_demo.xml b/addons/mrp/mrp_demo.xml
index 581cc04c05e..21415ad56f9 100644
--- a/addons/mrp/mrp_demo.xml
+++ b/addons/mrp/mrp_demo.xml
@@ -171,6 +171,7 @@
normal
+ True
@@ -180,6 +181,7 @@
1.0
+ True
Assembly Medium PC
@@ -187,6 +189,7 @@
1.0
+ True
Assembly Customizable PC
@@ -194,6 +197,7 @@
1.0
+ True
HDD on demand
@@ -202,6 +206,7 @@
1.0
phantom
+ True
RAM on demand
@@ -210,6 +215,7 @@
1.0
phantom
+ True
HDD on demand
@@ -218,6 +224,7 @@
1.0
phantom
+ True
HDD on demand
@@ -226,6 +233,7 @@
1.0
phantom
+ True
HDD Seagate
@@ -234,6 +242,7 @@
1.0
normal
+ True
Assembly Basic PC
@@ -243,6 +252,7 @@
normal
+ True
Complete PC with peripherals
@@ -250,6 +260,7 @@
1.0
+ True
RAM on demand
@@ -258,6 +269,7 @@
1.0
phantom
+ True
@@ -266,6 +278,7 @@
1.0
+ True
diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml
index 936a207caa3..af8a159deb1 100644
--- a/addons/mrp/mrp_view.xml
+++ b/addons/mrp/mrp_view.xml
@@ -329,7 +329,7 @@
ir.actions.act_window
mrp.bom
form
- [('bom_id','=',False)]
+ [('bom_id','=',False),('multi_level_bom','=',True)]
diff --git a/addons/mrp/product.py b/addons/mrp/product.py
index ea717bd546c..6a73063e591 100644
--- a/addons/mrp/product.py
+++ b/addons/mrp/product.py
@@ -26,6 +26,30 @@ from tools.translate import _
class product_product(osv.osv):
_inherit = "product.product"
+ def get_product_accounts(self, cr, uid, product_id, context={}):
+ """ To get the stock input account, stock output account and stock journal related to product.
+ @param product_id: product id
+ @return: dictionary which contains information regarding stock input account, stock output account and stock journal
+ """
+ product_obj = self.pool.get('product.product').browse(cr, uid, product_id, False)
+
+ stock_input_acc = product_obj.property_stock_account_input and product_obj.property_stock_account_input.id or False
+ if not stock_input_acc:
+ stock_input_acc = product_obj.categ_id.property_stock_account_input_categ and product_obj.categ_id.property_stock_account_input_categ.id or False
+
+ stock_output_acc = product_obj.property_stock_account_output and product_obj.property_stock_account_output.id or False
+ if not stock_output_acc:
+ stock_output_acc = product_obj.categ_id.property_stock_account_output_categ and product_obj.categ_id.property_stock_account_output_categ.id or False
+
+ journal_id = product_obj.categ_id.property_stock_journal and product_obj.categ_id.property_stock_journal.id or False
+
+ res = {}
+ res.update({'stock_account_input': stock_input_acc})
+ res.update({'stock_account_output': stock_output_acc})
+ res.update({'stock_journal': journal_id})
+
+ return res
+
def do_change_standard_price(self, cr, uid, ids, datas, context={}):
""" Changes the Standard Price of Product and parent products and creates an account move accordingly.
@param datas: dict. contain default datas like new_price, stock_output_account, stock_input_account, stock_journal
diff --git a/addons/procurement/procurement_view.xml b/addons/procurement/procurement_view.xml
index e517f289809..8d45b59baf8 100644
--- a/addons/procurement/procurement_view.xml
+++ b/addons/procurement/procurement_view.xml
@@ -66,7 +66,7 @@
-
+
diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv
index 4358e8ec9ab..6e84c8d0877 100644
--- a/addons/stock/security/ir.model.access.csv
+++ b/addons/stock/security/ir.model.access.csv
@@ -29,7 +29,6 @@
"access_stock_inventory_line_split","stock.inventory.line.split","model_stock_inventory_line_split","stock.group_stock_user",1,1,1,1
"access_stock_invoice_onshipping","stock.invoice.onshipping","model_stock_invoice_onshipping","stock.group_stock_user",1,1,1,1
"access_stock_location_product","stock.location.product","model_stock_location_product","stock.group_stock_user",1,0,0,0
-"access_report_stock_picking","report.stock.picking","model_report_stock_picking","stock.group_stock_user",1,0,0,0
"access_stock_traceability_upstream","stock.traceability.upstream","model_stock_traceability_upstream","stock.group_stock_user",1,0,0,0
"access_stock_traceability_downstream","stock.traceability.downstream","model_stock_traceability_downstream","stock.group_stock_user",1,0,0,0
"access_stock_traceability_lot_upstream","stock.traceability.lot.upstream","model_stock_traceability_lot_upstream","stock.group_stock_user",1,0,0,0