[MERGE]: merge with lp:~openerp-dev/openobject-addons/trunk-dev-addons2
bzr revid: atp@tinyerp.co.in-20100614123457-wmekvp27ez9vev3v
This commit is contained in:
@@ -115,5 +115,85 @@
|
||||
<field eval="'Gardner Group'" name="name"/>
|
||||
<field eval="'(282) 603-7489'" name="phone"/>
|
||||
</record>
|
||||
<record id="crm_case_webvisitor0" model="crm.lead">
|
||||
<field eval="1" name="active"/>
|
||||
<field name="type_id" ref="crm.type_lead5"/>
|
||||
<field eval=""1"" name="priority"/>
|
||||
<field name="type">lead</field>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field eval=""Questionnaire"" name="name"/>
|
||||
<field eval=""open"" name="state"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field eval=""Smith John"" name="partner_name2"/>
|
||||
<field eval=""(487) 013-1504"" name="partner_mobile"/>
|
||||
<field name="categ_id" ref="crm.categ_oppor6"/>
|
||||
<field eval=""Website Visitor"" name="partner_name"/>
|
||||
<field eval=""smith_john@gmail.com"" name="email_from"/>
|
||||
<field eval="time.strftime('%Y-%m-16 10:05:15')" name="date"/>
|
||||
</record>
|
||||
<record id="crm_case_business_card0" model="crm.lead">
|
||||
<field eval="1" name="active"/>
|
||||
<field name="type_id" ref="crm.type_lead3"/>
|
||||
<field eval=""2"" name="priority"/>
|
||||
<field name="type">lead</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field eval=""Marketing Products"" name="name"/>
|
||||
<field eval=""open"" name="state"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field eval=""Elvis Borne"" name="partner_name2"/>
|
||||
<field eval=""(147) 014-6574"" name="partner_mobile"/>
|
||||
<field name="categ_id" ref="crm.categ_oppor5"/>
|
||||
<field name="stage_id" ref="crm.stage_lead1"/>
|
||||
<field eval=""Business Card"" name="partner_name"/>
|
||||
<field eval=""(124) 587-8974"" name="partner_phone"/>
|
||||
<field eval="time.strftime('%Y-%m-16 11:05:15')" name="date"/>
|
||||
</record>
|
||||
<record id="crm_case_imported_contact0" model="crm.lead">
|
||||
<field eval="1" name="active"/>
|
||||
<field name="type_id" ref="crm.type_lead8"/>
|
||||
<field eval=""2"" name="priority"/>
|
||||
<field name="type">lead</field>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field eval=""Business Group"" name="name"/>
|
||||
<field eval=""cancel"" name="state"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field eval=""Drew Clark"" name="partner_name2"/>
|
||||
<field eval=""(125) 021-4781"" name="partner_mobile"/>
|
||||
<field name="categ_id" ref="crm.categ_oppor1"/>
|
||||
<field name="stage_id" ref="crm.stage_lead2"/>
|
||||
<field eval=""Imported Contact"" name="partner_name"/>
|
||||
</record>
|
||||
<record id="crm_case_employee0" model="crm.lead">
|
||||
<field eval="1" name="active"/>
|
||||
<field name="type_id" ref="crm.type_lead3"/>
|
||||
<field eval=""3"" name="priority"/>
|
||||
<field name="type">lead</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field eval=""OpenERP Campaining"" name="name"/>
|
||||
<field eval=""draft"" name="state"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field eval=""Carl Nelson"" name="partner_name2"/>
|
||||
<field eval=""(874) 022-6574"" name="partner_mobile"/>
|
||||
<field name="categ_id" ref="crm.categ_oppor3"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
<field eval=""Campaining"" name="partner_name"/>
|
||||
<field eval=""(232) 147-6329"" name="partner_phone"/>
|
||||
</record>
|
||||
<record id="crm_case_company_partnership0" model="crm.lead">
|
||||
<field eval="1" name="active"/>
|
||||
<field name="type_id" ref="crm.type_lead3"/>
|
||||
<field eval=""3"" name="priority"/>
|
||||
<field name="type">lead</field>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field eval=""Offer"" name="name"/>
|
||||
<field eval=""done"" name="state"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field eval=""Joe Lucas"" name="partner_name2"/>
|
||||
<field eval=""(234) 031-1254"" name="partner_mobile"/>
|
||||
<field name="categ_id" ref="crm.categ_oppor7"/>
|
||||
<field name="stage_id" ref="crm.stage_lead1"/>
|
||||
<field eval=""Partnership Offer"" name="partner_name"/>
|
||||
<field eval=""(254) 954-2147"" name="partner_phone"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -151,6 +151,19 @@
|
||||
<field name="day_open"/>
|
||||
<field name="day_close"/>
|
||||
</group>
|
||||
<field name="log_ids" nolabel="1" colspan="4">
|
||||
<tree string="Logs">
|
||||
<field name="name" colspan="4"/>
|
||||
<field name="date"/>
|
||||
<field name="user_id"/>
|
||||
</tree>
|
||||
<form string="Logs">
|
||||
<separator string="Action Information" colspan="4"/>
|
||||
<field name="name" colspan="4"/>
|
||||
<field name="date"/>
|
||||
<field name="user_id"/>
|
||||
</form>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Emails" groups="base.group_extended">
|
||||
<group colspan="4">
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
<field name="type_id" ref="crm.type_oppor1"/>
|
||||
<field name="partner_id" ref="base.res_partner_8"/>
|
||||
<field eval=""3"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field eval=""pending"" name="state"/>
|
||||
<field eval="55000.0" name="planned_revenue"/>
|
||||
<field eval="70" name="probability"/>
|
||||
@@ -89,5 +89,90 @@
|
||||
<field name="stage_id" ref="crm.stage_oppor6"/>
|
||||
<field eval=""Dirt Mining Ltd 271742 - 1000 units"" name="name"/>
|
||||
</record>
|
||||
<record id="crm_case_dreamweaversunits0" model="crm.lead">
|
||||
<field eval="75" name="probability"/>
|
||||
<field name="partner_address_id" ref="base.res_partner_address_9"/>
|
||||
<field name="type">opportunity</field>
|
||||
<field eval="1" name="active"/>
|
||||
<field name="type_id" ref="crm.type_oppor1"/>
|
||||
<field name="partner_id" ref="base.res_partner_2"/>
|
||||
<field eval=""3"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field eval=""open"" name="state"/>
|
||||
<field eval="35000.0" name="planned_cost"/>
|
||||
<field eval="42000.0" name="planned_revenue"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="categ_id" ref="crm.categ_oppor4"/>
|
||||
<field name="stage_id" ref="crm.stage_oppor1"/>
|
||||
<field eval=""Dream Weavers - 2000 units"" name="name"/>
|
||||
</record>
|
||||
<record id="crm_case_supervalueunits0" model="crm.lead">
|
||||
<field eval="70" name="probability"/>
|
||||
<field name="partner_address_id" ref="base.res_partner_address_11"/>
|
||||
<field name="type">opportunity</field>
|
||||
<field eval="1" name="active"/>
|
||||
<field name="type_id" ref="crm.type_oppor1"/>
|
||||
<field name="partner_id" ref="base.res_partner_sednacom"/>
|
||||
<field eval=""2"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field eval=""open"" name="state"/>
|
||||
<field eval="38000.0" name="planned_cost"/>
|
||||
<field eval="49000.0" name="planned_revenue"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="categ_id" ref="crm.categ_oppor5"/>
|
||||
<field name="stage_id" ref="crm.stage_oppor3"/>
|
||||
<field eval=""Super Value 84581- 1500 units"" name="name"/>
|
||||
</record>
|
||||
<record id="crm_case_business_solutionunits0" model="crm.lead">
|
||||
<field eval="85" name="probability"/>
|
||||
<field name="partner_address_id" ref="base.res_partner_address_zen"/>
|
||||
<field name="type">opportunity</field>
|
||||
<field eval="1" name="active"/>
|
||||
<field name="type_id" ref="crm.type_oppor1"/>
|
||||
<field name="partner_id" ref="base.res_partner_3"/>
|
||||
<field eval=""1"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field eval=""done"" name="state"/>
|
||||
<field eval="35000.0" name="planned_cost"/>
|
||||
<field eval="40000.0" name="planned_revenue"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="categ_id" ref="crm.categ_oppor6"/>
|
||||
<field name="stage_id" ref="crm.stage_oppor6"/>
|
||||
<field eval=""Business Solutions 21478- 2000 units"" name="name"/>
|
||||
</record>
|
||||
<record id="crm_case_sunrisemarketingunits0" model="crm.lead">
|
||||
<field eval="45" name="probability"/>
|
||||
<field name="partner_address_id" ref="base.res_partner_address_7"/>
|
||||
<field name="type">opportunity</field>
|
||||
<field eval="1" name="active"/>
|
||||
<field name="type_id" ref="crm.type_oppor1"/>
|
||||
<field name="partner_id" ref="base.res_partner_4"/>
|
||||
<field eval=""3"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field eval=""draft"" name="state"/>
|
||||
<field eval="45000.0" name="planned_cost"/>
|
||||
<field eval="50000.0" name="planned_revenue"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="categ_id" ref="crm.categ_oppor6"/>
|
||||
<field name="stage_id" ref="crm.stage_oppor6"/>
|
||||
<field eval=""Sunrise Marketing - 3000 units"" name="name"/>
|
||||
</record>
|
||||
<record id="crm_case_alphaunits0" model="crm.lead">
|
||||
<field eval="50" name="probability"/>
|
||||
<field name="partner_address_id" ref="base.res_partner_address_6"/>
|
||||
<field name="type">opportunity</field>
|
||||
<field eval="1" name="active"/>
|
||||
<field name="type_id" ref="crm.type_oppor1"/>
|
||||
<field name="partner_id" ref="base.res_partner_6"/>
|
||||
<field eval=""2"" name="priority"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field eval=""cancel"" name="state"/>
|
||||
<field eval="30000.0" name="planned_cost"/>
|
||||
<field eval="40000.0" name="planned_revenue"/>
|
||||
<field name="section_id" ref="crm.section_sales_department"/>
|
||||
<field name="categ_id" ref="crm.categ_oppor7"/>
|
||||
<field name="stage_id" ref="crm.stage_oppor2"/>
|
||||
<field eval=""Alpha Appliances - 2000 units"" name="name"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 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 document_configuration
|
||||
@@ -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': [],
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -19,6 +19,6 @@
|
||||
#
|
||||
##############################################################################
|
||||
import ftp_browse
|
||||
import document_configuration
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -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,
|
||||
|
||||
+65
-352
@@ -2,7 +2,7 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). 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
|
||||
## <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 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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -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:
|
||||
|
||||
@@ -181,6 +181,7 @@
|
||||
<field name="res_model">mailgate.message</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[('history', '=', True)]</field>
|
||||
<field name="view_id" ref="view_mailgate_message_tree"/>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -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
|
||||
## <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 = 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('<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, ' ')
|
||||
|
||||
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)
|
||||
|
||||
|
||||
+12
-1
@@ -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 = []
|
||||
|
||||
@@ -171,6 +171,7 @@
|
||||
<field name="routing_id" ref="mrp_routing_2"/>
|
||||
<field name="type">normal</field>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_cpu_gen"/>
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
|
||||
<record id="mrp_bom_1" model="mrp.bom">
|
||||
@@ -180,6 +181,7 @@
|
||||
<field name="product_qty">1.0</field>
|
||||
<field name="routing_id" ref="mrp_routing_0"/>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_pc2" />
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
<record id="mrp_bom_2" model="mrp.bom">
|
||||
<field name="name">Assembly Medium PC</field>
|
||||
@@ -187,6 +189,7 @@
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="product_qty">1.0</field>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_pc3"/>
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
<record id="mrp_bom_3" model="mrp.bom">
|
||||
<field name="name">Assembly Customizable PC</field>
|
||||
@@ -194,6 +197,7 @@
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="product_qty">1.0</field>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_pc4"/>
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
<record id="mrp_bom_4" model="mrp.bom">
|
||||
<field name="name">HDD on demand</field>
|
||||
@@ -202,6 +206,7 @@
|
||||
<field name="product_qty">1.0</field>
|
||||
<field name="type">phantom</field>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_20"/>
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
<record id="mrp_bom_5" model="mrp.bom">
|
||||
<field name="name">RAM on demand</field>
|
||||
@@ -210,6 +215,7 @@
|
||||
<field name="product_qty">1.0</field>
|
||||
<field name="type">phantom</field>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_21"/>
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
<record id="mrp_bom_6" model="mrp.bom">
|
||||
<field name="name">HDD on demand</field>
|
||||
@@ -218,6 +224,7 @@
|
||||
<field name="product_qty">1.0</field>
|
||||
<field name="type">phantom</field>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_20"/>
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
<record id="mrp_bom_7" model="mrp.bom">
|
||||
<field name="name">HDD on demand</field>
|
||||
@@ -226,6 +233,7 @@
|
||||
<field name="product_qty">1.0</field>
|
||||
<field name="type">phantom</field>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_20"/>
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
<record id="mrp_bom_8" model="mrp.bom">
|
||||
<field name="name">HDD Seagate</field>
|
||||
@@ -234,6 +242,7 @@
|
||||
<field name="product_qty">1.0</field>
|
||||
<field name="type">normal</field>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_hdd1"/>
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
<record id="mrp_bom_9" model="mrp.bom">
|
||||
<field name="name">Assembly Basic PC</field>
|
||||
@@ -243,6 +252,7 @@
|
||||
<field name="routing_id" ref="mrp_routing_0"/>
|
||||
<field name="type">normal</field>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_pc1"/>
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
<record id="mrp_bom_10" model="mrp.bom">
|
||||
<field name="name">Complete PC with peripherals</field>
|
||||
@@ -250,6 +260,7 @@
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="product_qty">1.0</field>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_23"/>
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
<record id="mrp_bom_11" model="mrp.bom">
|
||||
<field name="name">RAM on demand</field>
|
||||
@@ -258,6 +269,7 @@
|
||||
<field name="product_qty">1.0</field>
|
||||
<field name="type">phantom</field>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_23"/>
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
|
||||
<record id="mrp_bom_13" model="mrp.bom">
|
||||
@@ -266,6 +278,7 @@
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="product_qty">1.0</field>
|
||||
<field model="product.product" name="product_id" ref="product.product_product_cpu3"/>
|
||||
<field name="multi_level_bom">True</field>
|
||||
</record>
|
||||
|
||||
<record id="mrp_bom_kit" model="mrp.bom">
|
||||
|
||||
@@ -329,7 +329,7 @@
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">mrp.bom</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[('bom_id','=',False)]</field>
|
||||
<field name="domain">[('bom_id','=',False),('multi_level_bom','=',True)]</field>
|
||||
<field name="search_view_id" ref="view_mrp_bom_filter"/>
|
||||
</record>
|
||||
<record id="mrp_bom_form_action2" model="ir.actions.act_window">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<field name="move_id" groups="base.group_extended"/>
|
||||
<field name="date_close"/>
|
||||
<field name="close_move" groups="base.group_extended"/>
|
||||
<group colspan="4" groups="base.group_extended">
|
||||
<group colspan="4" groups="base.group_extended" expand="1">
|
||||
<separator colspan="4" string="Properties" />
|
||||
<field colspan="4" name="property_ids" nolabel="1"/>
|
||||
</group>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user