diff --git a/addons/account/account_installer.xml b/addons/account/account_installer.xml
index 50e431935bc..0bce735f1b3 100644
--- a/addons/account/account_installer.xml
+++ b/addons/account/account_installer.xml
@@ -17,7 +17,7 @@
- You can enhance OpenERP's basic accounting support with a few additional OpenERP applications.
+ From this screen, you can configure your accounting system. Choose a Chart of Account matched to your accounting system or the Generic Chart of Account which allows you to configure which suit your needs.Configure
diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py
index 272933018ad..0bc9e476601 100644
--- a/addons/account/account_move_line.py
+++ b/addons/account/account_move_line.py
@@ -801,15 +801,19 @@ class account_move_line(osv.osv):
ids = journal_pool.search(cr, uid, [])
journals = journal_pool.browse(cr, uid, ids)
all_journal = [None]
+ common_fields = {}
+ total = len(journals)
for journal in journals:
all_journal.append(journal.id)
for field in journal.view_id.columns_id:
if not field.field in fields:
- fields[field.field] = [None, journal.id]
+ fields[field.field] = [journal.id]
fld.append((field.field, field.sequence))
flds.append(field.field)
+ common_fields[field.field] = 1
else:
fields.get(field.field).append(journal.id)
+ common_fields[field.field] = common_fields[field.field] + 1
fld.append(('period_id', 3))
fld.append(('journal_id', 10))
@@ -831,6 +835,10 @@ class account_move_line(osv.osv):
for field_it in fld:
field = field_it[0]
+
+ if common_fields.get(field) == total:
+ fields.get(field).append(None)
+
if field=='state':
state = 'colors="red:state==\'draft\'"'
diff --git a/addons/account_anglo_saxon/stock.py b/addons/account_anglo_saxon/stock.py
index ac0dbeadcec..6b0f367abf0 100644
--- a/addons/account_anglo_saxon/stock.py
+++ b/addons/account_anglo_saxon/stock.py
@@ -26,7 +26,7 @@ from osv import fields,osv
#----------------------------------------------------------
class stock_picking(osv.osv):
_inherit = "stock.picking"
- _description = "Packing List"
+ _description = "Picking List"
def action_invoice_create(self, cr, uid, ids, journal_id=False,
group=False, type='out_invoice', context=None):
diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py
index 7d9d7ea63ff..8b9e6f700e8 100644
--- a/addons/base_action_rule/base_action_rule.py
+++ b/addons/base_action_rule/base_action_rule.py
@@ -95,23 +95,23 @@ trigger date, like sending a reminder 15 minutes before a meeting."),
'trg_state_to': fields.selection(_state_get, 'Button Pressed', size=16),
'act_method': fields.char('Call Object Method', size=64),
- 'act_user_id': fields.many2one('res.users', 'Set responsible to'),
- 'act_state': fields.selection(_state_get, 'Set state to', size=16),
- 'act_email_cc': fields.char('Add watchers (Cc)', size=250, help="\
+ 'act_user_id': fields.many2one('res.users', 'Set Responsible to'),
+ 'act_state': fields.selection(_state_get, 'Set State to', size=16),
+ 'act_email_cc': fields.char('Add Watchers (Cc)', size=250, help="\
These people will receive a copy of the future communication between partner \
and users by email"),
'act_remind_partner': fields.boolean('Remind Partner', help="Check \
this if you want the rule to send a reminder by email to the partner."),
- 'act_remind_user': fields.boolean('Remind responsible', help="Check \
+ 'act_remind_user': fields.boolean('Remind Responsible', help="Check \
this if you want the rule to send a reminder by email to the user."),
'act_reply_to': fields.char('Reply-To', size=64),
- 'act_remind_attach': fields.boolean('Remind with attachment', help="Check this if you want that all documents attached to the object be attached to the reminder email sent."),
- 'act_mail_to_user': fields.boolean('Mail to responsible', help="Check\
+ 'act_remind_attach': fields.boolean('Remind with Attachment', help="Check this if you want that all documents attached to the object be attached to the reminder email sent."),
+ 'act_mail_to_user': fields.boolean('Mail to Responsible', help="Check\
this if you want the rule to send an email to the responsible person."),
- 'act_mail_to_watchers': fields.boolean('Mail to watchers (CC)',
+ 'act_mail_to_watchers': fields.boolean('Mail to Watchers (CC)',
help="Check this if you want \
the rule to mark CC(mail to any other person defined in actions)."),
- 'act_mail_to_email': fields.char('Mail to these emails', size=128, \
+ 'act_mail_to_email': fields.char('Mail to these Emails', size=128, \
help="Email-id of the persons whom mail is to be sent"),
'act_mail_body': fields.text('Mail body', help="Content of mail"),
'regex_name': fields.char('Regular Expression on Model Name', size=128),
diff --git a/addons/base_action_rule/base_action_rule_view.xml b/addons/base_action_rule/base_action_rule_view.xml
index 0882b6f0e1b..4f502a4406b 100644
--- a/addons/base_action_rule/base_action_rule_view.xml
+++ b/addons/base_action_rule/base_action_rule_view.xml
@@ -18,7 +18,8 @@
-
+
+
@@ -54,7 +55,6 @@
-
diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py
index 8a9764bff63..c16477280c7 100644
--- a/addons/base_calendar/base_calendar.py
+++ b/addons/base_calendar/base_calendar.py
@@ -377,6 +377,9 @@ property or property parameter."),
_defaults = {
'state': lambda *x: 'needs-action',
}
+
+ def copy(self, cr, uid, id, default=None, context=None):
+ raise osv.except_osv(_('Warning!'), _('Can not Duplicate'))
def get_ics_file(self, cr, uid, event_obj, context=None):
"""
@@ -875,7 +878,7 @@ class calendar_event(osv.osv):
value = {
'duration': 24
}
- return {'value': value}
+ duration = 0.0
start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
if end_date and not duration:
@@ -1005,17 +1008,17 @@ class calendar_event(osv.osv):
_columns = {
'id': fields.integer('ID'),
'sequence': fields.integer('Sequence'),
- 'name': fields.char('Description', size=64, required=False),
- 'date': fields.datetime('Date'),
- 'date_deadline': fields.datetime('Deadline'),
+ 'name': fields.char('Description', size=64, required=False, states={'done': [('readonly', True)]}),
+ 'date': fields.datetime('Date', states={'done': [('readonly', True)]}),
+ 'date_deadline': fields.datetime('Deadline', states={'done': [('readonly', True)]}),
'create_date': fields.datetime('Created', readonly=True),
- 'duration': fields.float('Duration'),
- 'description': fields.text('Your action'),
+ 'duration': fields.float('Duration', states={'done': [('readonly', True)]}),
+ 'description': fields.text('Description', states={'done': [('readonly', True)]}),
'class': fields.selection([('public', 'Public'), ('private', 'Private'), \
- ('confidential', 'Confidential')], 'Mark as'),
- 'location': fields.char('Location', size=264, help="Location of Event"),
+ ('confidential', 'Confidential')], 'Mark as', states={'done': [('readonly', True)]}),
+ 'location': fields.char('Location', size=264, help="Location of Event", states={'done': [('readonly', True)]}),
'show_as': fields.selection([('free', 'Free'), ('busy', 'Busy')], \
- 'Show as'),
+ 'Show as', states={'done': [('readonly', True)]}),
'base_calendar_url': fields.char('Caldav URL', size=264),
'exdate': fields.text('Exception Date/Times', help="This property \
defines the list of date/time exceptions for arecurring calendar component."),
@@ -1029,14 +1032,14 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=-1SU'),
'rrule_type': fields.selection([('none', ''), ('daily', 'Daily'), \
('weekly', 'Weekly'), ('monthly', 'Monthly'), \
- ('yearly', 'Yearly'), ('custom', 'Custom')], 'Recurrency'),
- 'alarm_id': fields.many2one('res.alarm', 'Alarm'),
+ ('yearly', 'Yearly'), ('custom', 'Custom')], 'Recurrency', states={'done': [('readonly', True)]}),
+ 'alarm_id': fields.many2one('res.alarm', 'Alarm', states={'done': [('readonly', True)]}),
'base_calendar_alarm_id': fields.many2one('calendar.alarm', 'Alarm'),
'recurrent_uid': fields.integer('Recurrent ID'),
'recurrent_id': fields.datetime('Recurrent ID date'),
'vtimezone': fields.related('user_id', 'context_tz', type='char', size=24, \
string='Timezone', store=True),
- 'user_id': fields.many2one('res.users', 'Responsible'),
+ 'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}),
'freq': fields.selection([('None', 'No Repeat'), \
('secondly', 'Secondly'), \
('minutely', 'Minutely'), \
@@ -1068,7 +1071,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
'end_date': fields.date('Repeat Until'),
'attendee_ids': fields.many2many('calendar.attendee', 'event_attendee_rel', \
'event_id', 'attendee_id', 'Attendees'),
- 'allday': fields.boolean('All Day'),
+ 'allday': fields.boolean('All Day', states={'done': [('readonly', True)]}),
'active': fields.boolean('Active', help="If the active field is set to \
true, it will allow you to hide the event alarm information without removing it.")
}
diff --git a/addons/base_calendar/base_calendar_view.xml b/addons/base_calendar/base_calendar_view.xml
index 21b16443e0a..aa63dc696de 100644
--- a/addons/base_calendar/base_calendar_view.xml
+++ b/addons/base_calendar/base_calendar_view.xml
@@ -121,7 +121,7 @@
+ domain="[]" context="{'group_by':'state'}" />
@@ -138,7 +138,7 @@
+ parent="base.menu_base_config" sequence="15" />
diff --git a/addons/base_calendar/wizard/base_calendar_invite_attendee.py b/addons/base_calendar/wizard/base_calendar_invite_attendee.py
index cb3e4d2e62a..ebbfe6c48d2 100644
--- a/addons/base_calendar/wizard/base_calendar_invite_attendee.py
+++ b/addons/base_calendar/wizard/base_calendar_invite_attendee.py
@@ -65,7 +65,6 @@ send an Email to Invited Person')
context = {}
model = False
- model_field = False
context_id = context and context.get('active_id', False) or False
if not context or not context.get('model'):
return {}
diff --git a/addons/base_calendar/wizard/base_calendar_set_exrule.py b/addons/base_calendar/wizard/base_calendar_set_exrule.py
index ec7fcc50e9e..febfb39e1ef 100644
--- a/addons/base_calendar/wizard/base_calendar_set_exrule.py
+++ b/addons/base_calendar/wizard/base_calendar_set_exrule.py
@@ -104,11 +104,14 @@ class base_calendar_set_exrule(osv.osv_memory):
weekstring = ''
monthstring = ''
yearstring = ''
-
+ ex_id = base_calendar.base_calendar_id2real_id(context['active_id'])
+ model = context.get('model', False)
+ model_obj = self.pool.get(model)
for datas in self.read(cr, uid, ids, context=context):
freq = datas.get('freq')
if freq == 'None':
- return ''
+ model_obj.write(cr, uid, ex_id,{'exrule': ''})
+ return{}
interval_srting = datas.get('interval') and (';INTERVAL=' + str(datas.get('interval'))) or ''
@@ -143,12 +146,8 @@ class base_calendar_set_exrule(osv.osv_memory):
exrule_string = 'FREQ=' + freq.upper() + weekstring + interval_srting \
+ enddate + monthstring + yearstring
- ex_id = base_calendar.base_calendar_id2real_id(context['active_id'])
- model = context.get('model', False)
- model_obj = self.pool.get(model)
- exrule_value = model_obj.write(cr, uid,ex_id,{
- 'exrule': exrule_string,
- })
+
+ model_obj.write(cr, uid, ex_id,{'exrule': exrule_string})
return {}
_defaults = {
diff --git a/addons/base_calendar/wizard/base_calendar_set_exrule_view.xml b/addons/base_calendar/wizard/base_calendar_set_exrule_view.xml
index cc88dc90c02..bc663e0835f 100644
--- a/addons/base_calendar/wizard/base_calendar_set_exrule_view.xml
+++ b/addons/base_calendar/wizard/base_calendar_set_exrule_view.xml
@@ -49,11 +49,13 @@
attrs="{'required' : [('freq','=','yearly')]}"/>
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/addons/base_setup/base_setup_installer.xml b/addons/base_setup/base_setup_installer.xml
index 46c4dbc6e0b..6608cea061b 100644
--- a/addons/base_setup/base_setup_installer.xml
+++ b/addons/base_setup/base_setup_installer.xml
@@ -13,9 +13,7 @@
Now that OpenERP is installed, We have selected applications commonly useful to users which you can install directly, as well as sets of applications for more specific types or groups of businesses (the vertical modules).
-
-If you don't think you need any of these right now, you can easily install them later on.
+ >The most common applications have been pre selected. You can directly install them. You can either choose generic modules, or select modules for specific business sectors and industries. If you do not think you need any of these right now, you can easily install them later.
diff --git a/addons/base_setup/base_setup_todo.xml b/addons/base_setup/base_setup_todo.xml
index 5ec9be53d76..0bf0fbb09be 100644
--- a/addons/base_setup/base_setup_todo.xml
+++ b/addons/base_setup/base_setup_todo.xml
@@ -26,9 +26,9 @@
-
+
diff --git a/addons/base_setup/gtk_contact_form.xml b/addons/base_setup/gtk_contact_form.xml
index 41711032bf0..fcbaa9867e7 100644
--- a/addons/base_setup/gtk_contact_form.xml
+++ b/addons/base_setup/gtk_contact_form.xml
@@ -11,7 +11,7 @@
Contact Information
- Information about you and the company you are related too. The Basic information related to the company has already been collected and some more information required by you to fill.
+ To receive more information, please fill in the form about you and your company. Documentation and/or information will be sent to you as soon as possible.Would you like more information or documentation ?
diff --git a/addons/crm/__init__.py b/addons/crm/__init__.py
index 362444ccc2c..e56ed80af30 100644
--- a/addons/crm/__init__.py
+++ b/addons/crm/__init__.py
@@ -20,7 +20,6 @@
##############################################################################
import crm
-import crm_mailgate
import crm_action_rule
import crm_segmentation
import crm_meeting
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index 7510bc48914..feb49ba7ae7 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -121,11 +121,16 @@ class crm_case(object):
s = self.get_stage_dict(cr, uid, ids, context=context)
section = self._name
+ stage_pool = self.pool.get('crm.case.stage')
for case in self.browse(cr, uid, ids, context):
if section in s:
st = case.stage_id.id or False
if st in s[section]:
- self.write(cr, uid, [case.id], {'stage_id': s[section][st]})
+ data = {'stage_id': s[section][st]}
+ stage = stage_pool.browse(cr, uid, s[section][st], context=context)
+ if stage.on_change:
+ data.update({'probability': stage.probability})
+ self.write(cr, uid, [case.id], data)
return True
def get_stage_dict(self, cr, uid, ids, context=None):
@@ -162,12 +167,17 @@ class crm_case(object):
s = self.get_stage_dict(cr, uid, ids, context=context)
section = self._name
+ stage_pool = self.pool.get('crm.case.stage')
for case in self.browse(cr, uid, ids, context):
if section in s:
st = case.stage_id.id or False
s[section] = dict([(v, k) for (k, v) in s[section].iteritems()])
if st in s[section]:
- self.write(cr, uid, [case.id], {'stage_id': s[section][st]})
+ data = {'stage_id': s[section][st]}
+ stage = stage_pool.browse(cr, uid, s[section][st], context=context)
+ if stage.on_change:
+ data.update({'probability': stage.probability})
+ self.write(cr, uid, [case.id], data)
return True
def onchange_partner_id(self, cr, uid, ids, part, email=False):
@@ -181,7 +191,8 @@ class crm_case(object):
"""
if not part:
return {'value': {'partner_address_id': False,
- 'email_from': False,
+ 'email_from': False,
+ 'phone': False
}}
addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact'])
data = {'partner_address_id': addr['contact']}
@@ -200,7 +211,7 @@ class crm_case(object):
if not add:
return {'value': {'email_from': False}}
address = self.pool.get('res.partner.address').browse(cr, uid, add)
- return {'value': {'email_from': address.email}}
+ return {'value': {'email_from': address.email, 'phone': address.phone}}
def _history(self, cr, uid, cases, keyword, history=False, subject=None, email=False, details=None, email_from=False, message_id=False, attach=[], context={}):
mailgate_pool = self.pool.get('mailgate.thread')
@@ -541,7 +552,7 @@ class crm_case_resource_type(osv.osv):
_rec_name = "name"
_columns = {
- 'name': fields.char('Case Resource Type', size=64, required=True, translate=True),
+ 'name': fields.char('Resource Type', size=64, required=True, translate=True),
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
'object_id': fields.many2one('ir.model', 'Object Name'),
}
diff --git a/addons/crm/crm_installer_view.xml b/addons/crm/crm_installer_view.xml
index 9d92f6f1b95..6d8f4b7120f 100644
--- a/addons/crm/crm_installer_view.xml
+++ b/addons/crm/crm_installer_view.xml
@@ -25,26 +25,25 @@
Configure
-
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py
index 2260a9f0965..2700581d6cc 100644
--- a/addons/crm/crm_lead.py
+++ b/addons/crm/crm_lead.py
@@ -27,6 +27,9 @@ import time
import mx.DateTime
from tools.translate import _
from crm import crm_case
+import collections
+import binascii
+import tools
class crm_lead(osv.osv, crm_case):
""" CRM Lead Case """
@@ -117,6 +120,8 @@ and users by email"),
domain="[('section_id','=',section_id),\
('object_id.model', '=', 'crm.lead')]"),
'partner_name': fields.char("Partner Name", size=64),
+ 'optin': fields.selection([('yes','Yes'),('no','No'),('unknown','/')],'Opt-In'),
+ 'optout': fields.selection([('yes','Yes'),('no','No'),('unknown','/')],'Opt-Out'),
'type':fields.selection([
('lead','Lead'),
('opportunity','Opportunity'),
@@ -139,8 +144,8 @@ and users by email"),
\nIf the case is in progress the state is set to \'Open\'.\
\nWhen the case is over, the state is set to \'Done\'.\
\nIf the case needs to be reviewed then the state is set to \'Pending\'.'),
- 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('history', '=', True),('res_model','=',_name)]),
- 'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False),('res_model','=',_name)]),
+ 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('history', '=', True),('model','=',_name)]),
+ 'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False),('model','=',_name)]),
}
_defaults = {
@@ -148,6 +153,8 @@ and users by email"),
'user_id': crm_case._get_default_user,
'email_from': crm_case._get_default_email,
'state': lambda *a: 'draft',
+ 'optin': lambda *a: 'unknown',
+ 'optout': lambda *a: 'unknown',
'section_id': crm_case._get_section,
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
@@ -231,6 +238,130 @@ and users by email"),
}
return value
+ def message_new(self, cr, uid, msg, context):
+ """
+ Automatically calls when new email message arrives
+
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks
+ """
+
+ mailgate_pool = self.pool.get('email.server.tools')
+
+ subject = msg.get('subject')
+ body = msg.get('body')
+ msg_from = msg.get('from')
+ priority = msg.get('priority')
+
+ vals = {
+ 'name': subject,
+ 'email_from': msg_from,
+ 'email_cc': msg.get('cc'),
+ 'description': body,
+ 'user_id': False,
+ }
+ if msg.get('priority', False):
+ vals['priority'] = priority
+
+ res = mailgate_pool.get_partner(cr, uid, msg.get('from'))
+ if res:
+ vals.update(res)
+ res = self.create(cr, uid, vals, context)
+
+
+ attachents = msg.get('attachments', [])
+ 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': self._name,
+ 'res_id': res,
+ }
+ self.pool.get('ir.attachment').create(cr, uid, data_attach)
+
+ return res
+
+ def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context={}):
+ """
+ @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 ids: List of update mail’s IDs
+ """
+
+ if isinstance(ids, (str, int, long)):
+ ids = [ids]
+
+ msg_from = msg['from']
+ vals.update({
+ 'description': msg['body']
+ })
+ if msg.get('priority', False):
+ vals['priority'] = msg.get('priority')
+
+ maps = {
+ 'cost':'planned_cost',
+ 'revenue': 'planned_revenue',
+ 'probability':'probability'
+ }
+ vls = { }
+ for line in msg['body'].split('\n'):
+ line = line.strip()
+ res = tools.misc.command_re.match(line)
+ if res and maps.get(res.group(1).lower(), False):
+ key = maps.get(res.group(1).lower())
+ vls[key] = res.group(2).lower()
+
+ vals.update(vls)
+ res = self.write(cr, uid, ids, vals)
+ return res
+
+ def emails_get(self, cr, uid, ids, context=None):
+
+ """
+ Get Emails
+ @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 ids: List of email’s IDs
+ @param context: A standard dictionary for contextual values
+ """
+ res = {}
+
+ if isinstance(ids, (str, int, long)):
+ select = [long(ids)]
+ else:
+ select = ids
+
+ for thread in self.browse(cr, uid, select, context=context):
+ values = collections.defaultdict(set)
+
+ for message in thread.message_ids:
+ user_email = (message.user_id and message.user_id.address_id and message.user_id.address_id.email) or False
+ values['user_email'].add(user_email)
+ values['email_from'].add(message.email_from)
+ values['email_cc'].add(message.email_cc or False)
+ values['priority'] = thread.priority
+
+ res[thread.id] = dict((key,list(values[key])) for key, value in values.iteritems())
+
+ return res
+
+ def msg_send(self, cr, uid, id, *args, **argv):
+
+ """ Send The Message
+ @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 ids: List of email’s IDs
+ @param *args: Return Tuple Value
+ @param **args: Return Dictionary of Keyword Value
+ """
+ return True
+
crm_lead()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml
index c33339047d8..0c5afb27739 100644
--- a/addons/crm/crm_lead_view.xml
+++ b/addons/crm/crm_lead_view.xml
@@ -146,6 +146,11 @@
+
+
+
+
+
@@ -256,12 +261,11 @@
calendar
-
+
-
+
diff --git a/addons/crm/crm_mailgate.py b/addons/crm/crm_mailgate.py
deleted file mode 100644
index d39293e4716..00000000000
--- a/addons/crm/crm_mailgate.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# -*- 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 time
-import re
-import os
-
-import binascii
-import mx.DateTime
-import base64
-
-from tools.translate import _
-
-import tools
-from osv import fields,osv,orm
-from osv.orm import except_orm
-import collections
-
-from tools import command_re
-
-class mailgate_thread(osv.osv):
- """ mailgate_thread """
- _name = "mailgate.thread"
- _inherit = "mailgate.thread"
-
- def message_new(self, cr, uid, msg, context):
- """
- Automatically calls when new email message arrives
-
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks
- """
-
- mailgate_pool = self.pool.get('email.server.tools')
-
- subject = msg.get('subject')
- body = msg.get('body')
- msg_from = msg.get('from')
- priority = msg.get('priority')
-
- vals = {
- 'name': subject,
- 'email_from': msg_from,
- 'email_cc': msg.get('cc'),
- 'description': body,
- 'user_id': False,
- }
- if msg.get('priority', False):
- vals['priority'] = priority
-
- res = mailgate_pool.get_partner(cr, uid, msg.get('from'))
- if res:
- vals.update(res)
- res = self.create(cr, uid, vals, context)
- cases = self.browse(cr, uid, [res])
- self._history(cr, uid, cases, _('Receive'), history=True, details=body, email_from=msg_from, message_id=msg.get('id'))
-
- attachents = msg.get('attachments', [])
- 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': self._name,
- 'res_id': res,
- }
- self.pool.get('ir.attachment').create(cr, uid, data_attach)
-
- return res
-
- def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context={}):
- """
- @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 ids: List of update mail’s IDs
- """
-
- if isinstance(ids, (str, int, long)):
- ids = [ids]
-
- msg_from = msg['from']
- vals.update({
- 'description': msg['body']
- })
- if msg.get('priority', False):
- vals['priority'] = msg.get('priority')
-
- maps = {
- 'cost':'planned_cost',
- 'revenue': 'planned_revenue',
- 'probability':'probability'
- }
- vls = { }
- for line in msg['body'].split('\n'):
- line = line.strip()
- res = command_re.match(line)
- if res and maps.get(res.group(1).lower(), False):
- key = maps.get(res.group(1).lower())
- vls[key] = res.group(2).lower()
-
- vals.update(vls)
- res = self.write(cr, uid, ids, vals)
- cases = self.browse(cr, uid, ids)
- message_id = context.get('references_id', False)
- self._history(cr, uid, cases, _('Receive'), history=True, details=msg['body'], email_from=msg_from, message_id=message_id)
- #getattr(self, act)(cr, uid, select)
- return res
-
- def emails_get(self, cr, uid, ids, context=None):
-
- """
- Get Emails
- @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 ids: List of email’s IDs
- @param context: A standard dictionary for contextual values
- """
- res = {}
-
- if isinstance(ids, (str, int, long)):
- select = [long(ids)]
- else:
- select = ids
-
- for thread in self.browse(cr, uid, select, context=context):
- values = collections.defaultdict(set)
-
- for message in thread.message_ids:
- user_email = (message.user_id and message.user_id.address_id and message.user_id.address_id.email) or False
- values['user_email'].add(user_email)
- values['email_from'].add(message.email_from)
- values['email_cc'].add(message.email_cc or False)
-
- res[str(thread.id)] = dict((key,list(values[key])) for key, value in values.iteritems())
-
- return res
-
- def msg_send(self, cr, uid, id, *args, **argv):
-
- """ Send The Message
- @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 ids: List of email’s IDs
- @param *args: Return Tuple Value
- @param **args: Return Dictionary of Keyword Value
- """
- return True
-
-mailgate_thread()
diff --git a/addons/crm/crm_meeting.py b/addons/crm/crm_meeting.py
index f90d1dc896f..4f68d2d2564 100644
--- a/addons/crm/crm_meeting.py
+++ b/addons/crm/crm_meeting.py
@@ -46,16 +46,17 @@ class crm_meeting(osv.osv, crm_case):
_inherit = ['mailgate.thread',"calendar.event"]
_columns = {
# From crm.case
- 'name': fields.char('Summary', size=124, required=True),
- 'partner_id': fields.many2one('res.partner', 'Partner'),
+ 'name': fields.char('Summary', size=124, required=True, states={'done': [('readonly', True)]}),
+ 'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)]}),
'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \
- domain="[('partner_id','=',partner_id)]"),
- 'section_id': fields.many2one('crm.case.section', 'Sales Team', \
+ domain="[('partner_id','=',partner_id)]", states={'done': [('readonly', True)]}),
+ 'section_id': fields.many2one('crm.case.section', 'Sales Team', states={'done': [('readonly', True)]}, \
select=True, help='Sales team to which Case belongs to.\
Define Responsible user and Email account for mail gateway.'),
- 'email_from': fields.char('Email', size=128, help="These people will receive email."),
+ 'email_from': fields.char('Email', size=128, states={'done': [('readonly', True)]}, help="These people will receive email."),
'id': fields.integer('ID'),
-
+ 'create_date': fields.datetime('Creation Date' , readonly=True),
+ 'write_date': fields.datetime('Write Date' , readonly=True),
# Meeting fields
'categ_id': fields.many2one('crm.case.categ', 'Meeting Type', \
domain="[('object_id.model', '=', 'crm.meeting')]", \
@@ -63,11 +64,11 @@ class crm_meeting(osv.osv, crm_case):
'phonecall_id': fields.many2one ('crm.phonecall', 'Phonecall'),
'opportunity_id': fields.many2one ('crm.lead', 'Opportunity', domain="[('type', '=', 'opportunity')]"),
'attendee_ids': fields.many2many('calendar.attendee', 'meeting_attendee_rel',\
- 'event_id', 'attendee_id', 'Attendees'),
+ 'event_id', 'attendee_id', 'Attendees', states={'done': [('readonly', True)]}),
'date_closed': fields.datetime('Closed', readonly=True),
- 'date_deadline': fields.datetime('Deadline'),
- 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('history', '=', True),('res_model','=',_name)]),
- 'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False),('res_model','=',_name)]),
+ 'date_deadline': fields.datetime('Deadline', states={'done': [('readonly', True)]}),
+ 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('history', '=', True),('model','=',_name)]),
+ 'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False),('model','=',_name)]),
'state': fields.selection([('open', 'Confirmed'),
('draft', 'Unconfirmed'),
('cancel', 'Cancelled'),
diff --git a/addons/crm/crm_meeting_view.xml b/addons/crm/crm_meeting_view.xml
index 34bdafe1c58..5d1bb3f29d7 100644
--- a/addons/crm/crm_meeting_view.xml
+++ b/addons/crm/crm_meeting_view.xml
@@ -148,6 +148,7 @@
@@ -214,10 +215,27 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -268,6 +286,10 @@
help="Current Meetings"/>
+
+
+
+
+
+
+
+
+
diff --git a/addons/crm/crm_opportunity.py b/addons/crm/crm_opportunity.py
index 440033a2400..0adbf2f2489 100644
--- a/addons/crm/crm_opportunity.py
+++ b/addons/crm/crm_opportunity.py
@@ -143,9 +143,10 @@ class crm_opportunity(osv.osv):
context = {
'default_opportunity_id': opp.id,
'default_partner_id': opp.partner_id and opp.partner_id.id or False,
+ 'default_user_id': uid,
'default_section_id': opp.section_id and opp.section_id.id or False,
'default_email_from': opp.email_from,
- 'default_state': 'open',
+ 'default_state': 'open',
'default_name': opp.name
}
value = {
diff --git a/addons/crm/crm_opportunity_menu.xml b/addons/crm/crm_opportunity_menu.xml
index cb24990416f..36163abfa00 100644
--- a/addons/crm/crm_opportunity_menu.xml
+++ b/addons/crm/crm_opportunity_menu.xml
@@ -7,9 +7,9 @@
name="Phone calls"
res_model="crm.phonecall"
src_model="crm.lead"
- view_mode="calendar,tree,form"
+ view_mode="tree,calendar,form"
context="{'default_duration': 1.0 ,'default_opportunity_id': active_id}"
- domain="[('user_id','=',uid),('opportunity_id', '=', active_id)]"
+ domain="[('opportunity_id', '=', active_id)]"
view_type="form"/>
diff --git a/addons/crm/crm_opportunity_view.xml b/addons/crm/crm_opportunity_view.xml
index 550d82f86ac..4060337bdd7 100644
--- a/addons/crm/crm_opportunity_view.xml
+++ b/addons/crm/crm_opportunity_view.xml
@@ -258,6 +258,8 @@
+
+
@@ -33,6 +34,7 @@
+
@@ -49,6 +51,7 @@
+
@@ -63,6 +66,7 @@
+
@@ -78,6 +82,7 @@
+
@@ -94,6 +99,7 @@
+
diff --git a/addons/crm/crm_phonecall_menu.xml b/addons/crm/crm_phonecall_menu.xml
index 74bba86be04..f1363456e2c 100644
--- a/addons/crm/crm_phonecall_menu.xml
+++ b/addons/crm/crm_phonecall_menu.xml
@@ -12,6 +12,16 @@
domain="[('partner_id', '=', active_id)]"
groups="base.group_extended"
/>
+
+
diff --git a/addons/crm/crm_phonecall_view.xml b/addons/crm/crm_phonecall_view.xml
index 59b8013b167..790debb007f 100644
--- a/addons/crm/crm_phonecall_view.xml
+++ b/addons/crm/crm_phonecall_view.xml
@@ -118,9 +118,6 @@
-
@@ -132,6 +129,9 @@
+
@@ -146,8 +146,8 @@
form
-
diff --git a/addons/crm_helpdesk/crm_helpdesk.py b/addons/crm_helpdesk/crm_helpdesk.py
index e3f0bd476d9..f6106db5903 100644
--- a/addons/crm_helpdesk/crm_helpdesk.py
+++ b/addons/crm_helpdesk/crm_helpdesk.py
@@ -21,6 +21,7 @@
from crm import crm
from osv import fields, osv
+import time
class crm_helpdesk(osv.osv, crm.crm_case):
""" Helpdesk Cases """
@@ -85,6 +86,7 @@ class crm_helpdesk(osv.osv, crm.crm_case):
'partner_address_id': crm.crm_case._get_default_partner_address,
'email_from': crm.crm_case. _get_default_email,
'state': lambda *a: 'draft',
+ 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'section_id': crm.crm_case. _get_section,
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
diff --git a/addons/crm_helpdesk/crm_helpdesk_view.xml b/addons/crm_helpdesk/crm_helpdesk_view.xml
index f91650a315a..c077c219162 100644
--- a/addons/crm_helpdesk/crm_helpdesk_view.xml
+++ b/addons/crm_helpdesk/crm_helpdesk_view.xml
@@ -29,7 +29,7 @@
@@ -179,24 +179,18 @@
-
-
+
+
-
-
@@ -204,6 +198,12 @@
groups="base.group_extended"
states="open,draft,pending" type="object"
icon="gtk-go-up" />
+
+
@@ -255,12 +255,30 @@
-
+
+
+
+
+
+
+
+
diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py
index 91dfed96f11..27091dda8bd 100644
--- a/addons/delivery/stock.py
+++ b/addons/delivery/stock.py
@@ -28,8 +28,6 @@ import decimal_precision as dp
# Overloaded stock_picking to manage carriers :
class stock_picking(osv.osv):
- _name = "stock.picking"
- _description = "Packing List"
_inherit = 'stock.picking'
def _cal_weight(self, cr, uid, ids, name, args, context=None):
diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py
index 9190bd17e2b..133a47563a8 100644
--- a/addons/email_template/email_template.py
+++ b/addons/email_template/email_template.py
@@ -108,8 +108,7 @@ class email_template(osv.osv):
'enforce_from_account':fields.many2one(
'email_template.account',
string="Enforce From Account",
- help="Emails will be sent only from this account(which are approved).",
- domain=[('state','=','approved')]),
+ help="Emails will be sent only from this account(which are approved)."),
'from_email' : fields.related('enforce_from_account', 'email_id',
type='char', string='From',),
'def_to':fields.char(
diff --git a/addons/email_template/email_template_account.py b/addons/email_template/email_template_account.py
index 52cff384ec4..9ea1cd5b8fa 100644
--- a/addons/email_template/email_template_account.py
+++ b/addons/email_template/email_template_account.py
@@ -97,9 +97,12 @@ class email_template_account(osv.osv):
context
)['name'],
'state':lambda * a:'draft',
+ 'smtpport':lambda *a:25,
+ 'smtpserver':lambda *a:'localhost',
+ 'company':lambda *a:'yes',
'user':lambda self, cursor, user, context:user,
- 'send_pref':lambda * a: 'html',
- 'smtptls':lambda * a:True,
+ 'send_pref':lambda *a: 'html',
+ 'smtptls':lambda *a:True,
}
_sql_constraints = [
diff --git a/addons/email_template/email_template_view.xml b/addons/email_template/email_template_view.xml
index 12a24ca0934..1d574ff8675 100644
--- a/addons/email_template/email_template_view.xml
+++ b/addons/email_template/email_template_view.xml
@@ -56,14 +56,16 @@
-
-
+
+
-
-
-
+
+
+
+
+
diff --git a/addons/fetchmail/__openerp__.py b/addons/fetchmail/__openerp__.py
index a1276c11643..31037cca63f 100644
--- a/addons/fetchmail/__openerp__.py
+++ b/addons/fetchmail/__openerp__.py
@@ -24,7 +24,7 @@
{
"name" : "Fetchmail Server",
"version" : "1.0",
- "depends" : ["base"],
+ "depends" : ["base", 'mail_gateway'],
"author" : "Tiny",
"description": """Fetchmail:
* Fetch email from Pop / IMAP server
diff --git a/addons/fetchmail/fetchmail.py b/addons/fetchmail/fetchmail.py
index aa403cc0090..14981c13837 100644
--- a/addons/fetchmail/fetchmail.py
+++ b/addons/fetchmail/fetchmail.py
@@ -19,140 +19,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']))
@@ -160,201 +77,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('mail.server.history')
- 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:
- logger.notifyChannel('imap', netsvc.LOG_WARNING, 'method def message_new is not define in model %s' % (model_pool._name))
- return False
-
- 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,
- }
- 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': message_id,
- 'res_id': res_id,
- 'server_id': server.id,
- 'note': msg.get('body', msg.get('from')),
- 'ref_id':msg.get('references', msg.get('id')),
- 'type':server.type
- }
- 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':
@@ -363,17 +120,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':
@@ -382,7 +143,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)
@@ -393,79 +154,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"
- _name = "mail.server.history"
- _description = "Mail Server History"
-
_columns = {
- 'name': fields.char('Message Id', size=256, readonly=True, help="Message Id in Email Server.", select=True),
- 'ref_id': fields.char('Referance Id', size=256, readonly=True, help="Message Id in Email Server.", select=True),
- 'res_id': fields.integer("Resource ID", readonly=True, select=True),
- 'server_id': fields.many2one('email.server',"Mail Server", readonly=True, select=True),
- 'model_id':fields.related('server_id', 'object_id', type='many2one', relation='ir.model', string='Model', readonly=True, select=True),
- 'note': fields.text('Notes', readonly=True),
- 'create_date': fields.datetime('Created Date', readonly=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/fetchmail_view.xml b/addons/fetchmail/fetchmail_view.xml
index 510b31b6131..330f9fec349 100644
--- a/addons/fetchmail/fetchmail_view.xml
+++ b/addons/fetchmail/fetchmail_view.xml
@@ -103,82 +103,54 @@
action="action_email_server_tree_imap"
/>
-
- mail.server.history.tree
- mail.server.history
- tree
-
-
-
-
-
-
-
-
-
-
-
- mail.server.history.form
- mail.server.history
- form
-
-
-
-
-
+
+
+
+
+ res_model="mailgate.message" src_model="email.server"/>
-
+ Received Email History
- mail.server.history
+ mailgate.messageformtree,form
-
+ {'type':'imap'}[('type','=','imap')]
+ id="menu_action_mailgate_message_tree"
+ action="action_mailgate_message_tree"/>
-
+ Received Email History
- mail.server.history
+ mailgate.messageformtree,form
-
+ {'type':'pop'}[('type','=','pop')]
+ id="menu_action_mailgate_message_tree_pop"
+ action="action_mailgate_message_tree_pop"/>
diff --git a/addons/fetchmail/security/ir.model.access.csv b/addons/fetchmail/security/ir.model.access.csv
index 907aa187913..7aa6c84d22b 100644
--- a/addons/fetchmail/security/ir.model.access.csv
+++ b/addons/fetchmail/security/ir.model.access.csv
@@ -1,4 +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_mail_server_history","mail.server.history","model_mail_server_history",,1,1,1,1
-"access_email_server_tools","email.server.tools","model_email_server_tools",,1,1,1,1
diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py
index 4764887fff5..9d6c2cfbbbb 100644
--- a/addons/hr_recruitment/hr_recruitment.py
+++ b/addons/hr_recruitment/hr_recruitment.py
@@ -21,6 +21,11 @@
from osv import fields, osv
from crm import crm
+import tools
+import collections
+import binascii
+import tools
+from tools.translate import _
AVAILABLE_STATES = [
('draft', 'New'),
@@ -60,8 +65,8 @@ class hr_applicant(osv.osv, crm.crm_case):
_inherit = ['mailgate.thread']
_columns = {
'name': fields.char('Name', size=128, required=True),
- 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('history', '=', True),('res_model','=',_name)]),
- 'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False),('res_model','=',_name)]),
+ 'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('history', '=', True),('model','=',_name)]),
+ 'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False),('model','=',_name)]),
'active': fields.boolean('Active', help="If the active field is set to false, it will allow you to hide the case without removing it."),
'description': fields.text('Description'),
'section_id': fields.many2one('crm.case.section', 'Sales Team', \
@@ -240,6 +245,131 @@ class hr_applicant(osv.osv, crm.crm_case):
context.update({'survey_id': record.survey.id, 'response_id' : [record.response], 'response_no':0, })
value = self.pool.get("survey").action_print_survey(cr, uid, ids, context)
return value
+
+ def message_new(self, cr, uid, msg, context):
+ """
+ Automatically calls when new email message arrives
+
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks
+ """
+
+ mailgate_pool = self.pool.get('email.server.tools')
+
+ subject = msg.get('subject')
+ body = msg.get('body')
+ msg_from = msg.get('from')
+ priority = msg.get('priority')
+
+ vals = {
+ 'name': subject,
+ 'email_from': msg_from,
+ 'email_cc': msg.get('cc'),
+ 'description': body,
+ 'user_id': False,
+ }
+ if msg.get('priority', False):
+ vals['priority'] = priority
+
+ res = mailgate_pool.get_partner(cr, uid, msg.get('from'))
+ if res:
+ vals.update(res)
+ res = self.create(cr, uid, vals, context)
+
+
+ attachents = msg.get('attachments', [])
+ 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': self._name,
+ 'res_id': res,
+ }
+ self.pool.get('ir.attachment').create(cr, uid, data_attach)
+
+ return res
+
+ def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context={}):
+ """
+ @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 ids: List of update mail’s IDs
+ """
+
+ if isinstance(ids, (str, int, long)):
+ ids = [ids]
+
+ msg_from = msg['from']
+ vals.update({
+ 'description': msg['body']
+ })
+ if msg.get('priority', False):
+ vals['priority'] = msg.get('priority')
+
+ maps = {
+ 'cost':'planned_cost',
+ 'revenue': 'planned_revenue',
+ 'probability':'probability'
+ }
+ vls = { }
+ for line in msg['body'].split('\n'):
+ line = line.strip()
+ res = tools.misc.command_re.match(line)
+ if res and maps.get(res.group(1).lower(), False):
+ key = maps.get(res.group(1).lower())
+ vls[key] = res.group(2).lower()
+
+ vals.update(vls)
+ res = self.write(cr, uid, ids, vals)
+ return res
+
+ def emails_get(self, cr, uid, ids, context=None):
+
+ """
+ Get Emails
+ @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 ids: List of email’s IDs
+ @param context: A standard dictionary for contextual values
+ """
+ res = {}
+
+ if isinstance(ids, (str, int, long)):
+ select = [long(ids)]
+ else:
+ select = ids
+
+ for thread in self.browse(cr, uid, select, context=context):
+ values = collections.defaultdict(set)
+
+ for message in thread.message_ids:
+ user_email = (message.user_id and message.user_id.address_id and message.user_id.address_id.email) or False
+ values['user_email'].add(user_email)
+ values['email_from'].add(message.email_from)
+ values['email_cc'].add(message.email_cc or False)
+ values['priority'] = thread.priority
+
+ res[thread.id] = dict((key,list(values[key])) for key, value in values.iteritems())
+
+ return res
+
+ def msg_send(self, cr, uid, id, *args, **argv):
+
+ """ Send The Message
+ @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 ids: List of email’s IDs
+ @param *args: Return Tuple Value
+ @param **args: Return Dictionary of Keyword Value
+ """
+ return True
+
hr_applicant()
class hr_job(osv.osv):
diff --git a/addons/mail_gateway/mail_gateway.py b/addons/mail_gateway/mail_gateway.py
index fd7a5753ecf..edb723a3ba0 100644
--- a/addons/mail_gateway/mail_gateway.py
+++ b/addons/mail_gateway/mail_gateway.py
@@ -22,7 +22,12 @@
from osv import osv, fields
import time
import tools
+import binascii
+import email
+from email.header import decode_header
import base64
+import re
+from tools.translate import _
class mailgate_thread(osv.osv):
'''
@@ -30,11 +35,27 @@ class mailgate_thread(osv.osv):
'''
_name = 'mailgate.thread'
_description = 'Mailgateway Thread'
+ _rec_name = 'thread'
+
_columns = {
'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('history', '=', True)]),
'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False)]),
}
- def _history(self, cr, uid, cases, keyword, history=False, subject=None, email=False, details=None, email_from=False, message_id=False, attach=None, context=None):
+
+ def message_new(self, cr, uid, msg, context):
+ raise Exception, _('Method is not implemented')
+
+ def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context={}):
+ raise Exception, _('Method is not implemented')
+
+ def emails_get(self, cr, uid, ids, context=None):
+ raise Exception, _('Method is not implemented')
+
+ def msg_send(self, cr, uid, id, *args, **argv):
+ raise Exception, _('Method is not implemented')
+
+ def _history(self, cr, uid, cases, keyword, history=False, subject=None, email=False, details=None, \
+ email_from=False, message_id=False, references=None, attach=None, context=None):
"""
@param self: The object pointer
@param cr: the current row, from the database cursor,
@@ -63,8 +84,8 @@ class mailgate_thread(osv.osv):
data = {
'name': keyword,
'user_id': uid,
- 'res_model' : case._name,
- 'res_id': case.id,
+ 'model' : case._name,
+ 'res_id': case.id,
'date': time.strftime('%Y-%m-%d %H:%M:%S'),
'message_id': message_id,
}
@@ -77,7 +98,7 @@ class mailgate_thread(osv.osv):
'name': subject or 'History',
'history': True,
'user_id': uid,
- 'res_model' : case._name,
+ 'model' : case._name,
'res_id': case.id,
'date': time.strftime('%Y-%m-%d %H:%M:%S'),
'description': details or (hasattr(case, 'description') and case.description or False),
@@ -88,6 +109,7 @@ class mailgate_thread(osv.osv):
(hasattr(case, 'user_id') and case.user_id and case.user_id.address_id and \
case.user_id.address_id.email) or tools.config.get('email_from', False),
'partner_id': hasattr(case, 'partner_id') and (case.partner_id and case.partner_id.id or False) or False,
+ 'references': references,
'message_id': message_id,
'attachment_ids': [(6, 0, attachments)]
}
@@ -104,8 +126,9 @@ class mailgate_message(osv.osv):
_order = 'id desc'
_columns = {
'name':fields.char('Message', size=64),
- 'res_model': fields.char('Object Name', size=128),
+ 'model': fields.char('Object Name', size=128),
'res_id': fields.integer('Resource ID'),
+ 'ref_id': fields.char('Reference Id', size=256, readonly=True, help="Message Id in Email Server.", select=True),
'date': fields.datetime('Date'),
'history': fields.boolean('Is History?'),
'user_id': fields.many2one('res.users', 'User Responsible', readonly=True),
@@ -114,7 +137,8 @@ class mailgate_message(osv.osv):
'email_to': fields.char('Email To', size=84),
'email_cc': fields.char('Email CC', size=84),
'email_bcc': fields.char('Email BCC', size=84),
- 'message_id': fields.char('Message Id', size=1024, readonly=True, help="Message Id on Email Server.", select=True),
+ 'message_id': fields.char('Message Id', size=1024, readonly=True, help="Message Id on Email.", select=True),
+ 'references': fields.text('References', readonly=True, help="Referencess emails."),
'description': fields.text('Description'),
'partner_id': fields.many2one('res.partner', 'Partner', required=False),
'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments'),
@@ -122,3 +146,320 @@ class mailgate_message(osv.osv):
mailgate_message()
+class mailgate_tool(osv.osv_memory):
+
+ _name = 'email.server.tools'
+ _description = "Email Server Tools"
+
+ 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, 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, res_ids, msg, attach):
+ """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 res_ids: Ids of the record of OpenObject model created
+ @param msg: Email details
+ @param attach: Email attachments
+ """
+ if isinstance(res_ids, (int, long)):
+ res_ids = [res_ids]
+
+ msg_pool = self.pool.get('mailgate.message')
+ for res_id in res_ids:
+ msg_data = {
+ 'name': msg.get('subject', 'No subject'),
+ 'date': msg.get('date') ,
+ 'description': msg.get('body', msg.get('from')),
+ 'history': True,
+ 'res_model': model,
+ 'email_cc': msg.get('cc'),
+ 'email_from': msg.get('from'),
+ 'email_to': msg.get('to'),
+ 'message_id': msg.get('message-id'),
+ 'references': msg.get('references'),
+ 'res_id': res_id,
+ 'user_id': uid,
+ 'attachment_ids': [(6, 0, attach)]
+ }
+ msg_id = msg_pool.create(cr, uid, msg_data)
+ return True
+
+ def email_send(self, cr, uid, model, res_id, msg, from_email=False, email_default=False):
+ """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
+ """
+ history_pool = self.pool.get('mailgate.message')
+ model_pool = self.pool.get(model)
+ from_email = from_email or tools.config.get('email_from', None)
+ message = email.message_from_string(str(msg))
+ subject = "[%s] %s" %(res_id, self._decode_header(message['Subject']))
+ #msg_mails = []
+ #mails = [self._decode_header(message['From']), self._decode_header(message['To'])]
+ #mails += self._decode_header(message.get('Cc', '')).split(',')
+
+ values = {}
+ if hasattr(model_pool, 'emails_get'):
+ values = model_pool.emails_get(cr, uid, [res_id])
+ emails = values.get(res_id, {})
+
+ priority = emails.get('priority', [3])[0]
+ em = emails['user_email'] + emails['email_from'] + emails['email_cc']
+ msg_mails = map(self.to_email, filter(None, em))
+
+ #mm = [self._decode_header(message['From']), self._decode_header(message['To'])]
+ #mm += self._decode_header(message.get('Cc', '')).split(',')
+
+ #msg_mails = map(self.to_email, filter(None, mm))
+
+ encoding = message.get_content_charset()
+ message['body'] = message.get_payload(decode=True)
+ if encoding:
+ message['body'] = tools.ustr(message['body'].decode(encoding))
+
+ body = _("""
+Hello %s,
+
+ Your Request ID: %s
+
+Thanks
+
+-------- Original Message --------
+%s
+""") %(self._decode_header(message['From']), res_id, message['body'])
+ res = None
+ try:
+ res = tools.email_send(from_email, msg_mails, subject, body, openobject_id=res_id)
+ except Exception, e:
+ if email_default:
+ temp_msg = '[%s] %s'%(res_id, self._decode_header(message['Subject']))
+ del message['Subject']
+ message['Subject'] = '[OpenERP-FetchError] %s' %(temp_msg)
+ tools.email_send(from_email, email_default, message.get('Subject'), message.get('body'), openobject_id=res_id)
+ return res
+
+ def process_email(self, cr, uid, model, message, attach=True, 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 context: A standard dictionary for contextual values"""
+
+ model_pool = self.pool.get(model)
+ if not context:
+ context = {}
+ res_id = False
+ # Create New Record into particular model
+ def create_record(msg):
+ 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)
+
+ 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))
+
+ 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
+
+ if 'Subject' in fields:
+ msg['subject'] = self._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'] = self._decode_header(msg_txt.get('From'))
+
+ if 'Delivered-To' in fields:
+ msg['to'] = self._decode_header(msg_txt.get('Delivered-To'))
+
+ if 'Cc' in fields:
+ msg['cc'] = self._decode_header(msg_txt.get('Cc'))
+
+ if 'Reply-To' in fields:
+ msg['reply'] = self._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-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'] = tools.ustr(msg['body'])
+
+ 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 = tools.ustr(res)
+
+ body += res
+
+ msg['body'] = body
+ msg['attachments'] = attachments
+ res_ids = []
+ new_res_id = False
+ if msg.get('references', False):
+ references = msg.get('references')
+ if '\r\n' in references:
+ references = msg.get('references').split('\r\n')
+ else:
+ references = msg.get('references').split(' ')
+ for ref in references:
+ ref = ref.strip()
+ res_id = tools.misc.reference_re.search(ref)
+ if res_id:
+ res_id = res_id.group(1)
+ else:
+ res_id = tools.misc.res_re.search(msg['subject'])
+ if res_id:
+ res_id = res_id.group(1)
+ if res_id:
+ res_id = int(res_id)
+ res_ids.append(res_id)
+ model_pool = self.pool.get(model)
+
+ vals = {}
+ if hasattr(model_pool, 'message_update'):
+ model_pool.message_update(cr, uid, [res_id], vals, msg, context=context)
+
+ if not len(res_ids):
+ new_res_id = create_record(msg)
+ res_ids = [new_res_id]
+ # Store messages
+ if hasattr(model_pool, '_history'):
+ model_pool._history(cr, uid, res_ids, _('Receive'), history=True,
+ subject = msg.get('subject'),
+ email = msg.get('to'),
+ details = msg.get('body'),
+ email_from = msg.get('from'),
+ message_id = msg.get('message-id'),
+ references = msg.get('references', False),
+ attach = msg.get('attachments', {}).items(),
+ context = {'model' : model})
+ else:
+ self.history(cr, uid, model, res_ids, msg, att_ids)
+ return new_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
+ """
+ address_pool = self.pool.get('res.partner.address')
+ res = {
+ 'partner_address_id': False,
+ 'partner_id': False
+ }
+ from_email = self.to_email(from_email)
+ address_ids = address_pool.search(cr, uid, [('email', '=', from_email)])
+ if address_ids:
+ address = address_pool.browse(cr, uid, address_ids[0])
+ res['partner_address_id'] = address_ids[0]
+ res['partner_id'] = address.partner_id.id
+
+ return res
+
+mailgate_tool()
+
+
diff --git a/addons/mail_gateway/mail_gateway_view.xml b/addons/mail_gateway/mail_gateway_view.xml
index 7b8ce52cbec..09a24f92772 100644
--- a/addons/mail_gateway/mail_gateway_view.xml
+++ b/addons/mail_gateway/mail_gateway_view.xml
@@ -8,21 +8,28 @@
@@ -53,7 +61,7 @@
search
-
+
@@ -68,28 +76,30 @@
form
-
- Mailgateway Threads
- mailgate.thread
+
+
+ Mailgateway Messages
+ mailgate.messagetree,formform
+ [('history', '=', True)]
+
+
+
+
+ Mailgateway Threads
+ mailgate.thread
+ tree,form
+ form
+
+
+
+
+
+ tree
+
+
+
+
+
+ form
+
+
+
+
+
+
+ Mailgateway Messages
+ mailgate.message
+ tree,form
+ form
+ [('history', '=', True)]
-
+
tree
@@ -166,7 +209,7 @@
-
-
\ No newline at end of file
+
diff --git a/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py b/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py
index b9a9eefee15..f294c819c78 100755
--- a/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py
+++ b/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py
@@ -21,104 +21,15 @@
#
###########################################################################################
-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
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
+import email
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
@@ -130,296 +41,28 @@ class email_parser(object):
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)
self.model = str(model)
except:
+ self.model_id = self.rpc('ir.model', 'search', [('model', '=', model)])[0]
self.model = str(model)
self.email = email
self.email_default = email_default
- self.canal_id = False
-
- 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, text):
- if text:
- text = decode_header(text.replace('\r', ''))
- return ''.join(map(lambda x:self._to_decode(x[0], [x[1]]), text or []))
-
- 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 '')
- 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))
-
- values = {
- 'message_ids' : [
- (0, 0, {
- 'res_model' : self.model,
- 'date' : time.strftime('%Y-%m-%d %H:%M:%S'),
- 'description' : message['body'],
- 'email_from' : msg_from,
- 'email_to' : msg_to,
- 'name' : 'Receive',
- 'history' : True,
- 'user_id' : self.rpc.user_id,
- }
- )
- ]
- }
- oid = 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': oid
- }
- self.rpc('ir.attachment', 'create', data_attach)
-
- return (oid, )
-
- 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_user(self, msg, id):
- body = self.msg_body_get(msg)
-
- # handle email body commands (ex: Set-State: Draft)
- actions = {}
- body_data=''
- for line in body['body'].split('\n'):
- res = command_re.match(line)
- if res:
- actions[res.group(1).lower()] = res.group(2).lower()
- else:
- body_data += line+'\n'
- body['body'] = body_data
-
- data = {
-# 'description': body['body'],
- }
- act = 'case_open'
- if 'state' in actions:
- if actions['state'] in ['draft', 'close', 'cancel', 'open', 'pending']:
- act = 'case_' + actions['state']
-
- for k1, k2 in [('cost', 'planned_cost'), ('revenue', 'planned_revenue'), ('probability', 'probability')]:
- try:
- data[k2] = float(actions[k1])
- except:
- pass
-
- if 'priority' in actions:
- if actions['priority'] in ('1', '2', '3', '4', '5'):
- data['priority'] = actions['priority']
-
- if 'partner' in actions:
- data['email_from'] = actions['partner'][:128]
-
- if 'user' in actions:
- uids = self.rpc('res.users', 'name_search', actions['user'])
- if uids:
- data['user_id'] = uids[0][0]
-
- self.rpc(self.model, act, [id])
- self.rpc(self.model, 'write', [id], data)
- attachments = body['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': id
- }
- self.rpc('ir.attachment', 'create', data_attach)
-
- self.create_message(id, msg['From'], msg['To'], 'Send', message['body'])
-
- return id
-
- def create_message(self, oid, email_from, email_to, name, body):
- """
- This functions creates a message in the thread
-
- > create_message(id, msg['From'], msg['To'], 'Send', message['body'])
- """
- values = {
- 'res_model' : self.model,
- 'res_id' : oid,
- 'date' : time.strftime('%Y-%m-%d %H:%M:%S'),
- 'description' : body,
- 'email_from' : self._decode_header(email_from),
- 'email_to' : self._decode_header(email_to),
- 'name' : name,
- 'history' : True,
- 'user_id' : self.rpc.user_id,
- }
- return self.rpc('mailgate.message', 'create', values)
-
- def msg_send(self, msg, emails, priority=None):
- if not emails:
- return False
-
- msg['To'] = emails[0]
- if len(emails) > 1:
- msg['Cc'] = ','.join(emails[1:])
- msg['Reply-To'] = self.email
-
- if priority:
- msg['X-Priority'] = priorities.get(priority, '3 (Normal)')
-
- s = smtplib.SMTP()
- s.connect()
- s.sendmail(self.email, emails, msg.as_string())
- s.close()
- return True
-
- def msg_partner(self, msg, id):
- message = self.msg_body_get(msg)
- body = message['body']
- act = 'case_open'
- self.rpc(self.model, act, [id])
- 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': id
- }
- self.rpc('ir.attachment', 'create', data_attach)
-
- self.create_message(id, msg['From'], msg['To'], 'Send', message['body'])
- return id
-
- def msg_test(self, msg, case_str):
- if not case_str:
- return (False, False)
- case_id = int(case_str)
- emails = self.rpc(self.model, 'emails_get', [case_id])
- return (case_id, emails)
+
- def parse(self, msg):
- case_str = reference_re.search(msg.get('References', ''))
- if case_str:
- case_str = case_str.group(1)
- else:
- case_str = case_re.search(msg.get('Subject', ''))
- if case_str:
- case_str = case_str.group(1)
-
- logging.info("email: %s -> %s -- %s", msg['From'], self.email, msg['Subject'])
- (case_id, emails) = self.msg_test(msg, case_str)
- if case_id:
- user_email = filter(None, emails['user_email'])[0]
-
- if user_email and self.email_get(user_email) == self.email_get(self._decode_header(msg['From'])):
- self.msg_user(msg, case_id)
- else:
- self.msg_partner(msg, case_id)
- else:
- case_id = self.msg_new(msg)
- subject = self._decode_header(msg['Subject'])
- msg['Subject'] = "[%s] %s" % (case_id, subject,)
- msg['Message-Id'] = "<%s-openerpcrm-%s@%s>" % (time.time(), case_id, socket.gethostname(),)
-
- logging.info(" case: %r", case_id)
-
- values = self.rpc(self.model, 'emails_get', [case_id])
-
- emails = values[str(thread_id)]
-
- priority = emails.get('piority', [3])[0]
- em = emails['user_email'] + emails['email_from'] + emails['email_cc']
- emails = map(self.email_get, filter(None, em))
-
- 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))
-
- emails = filter(lambda m: m and m not in msg_mails, emails)
+ def parse(self, message):
try:
- self.msg_send(msg, emails, priority)
- except:
- if self.email_default:
- a = self._decode_header(msg['Subject'])
- msg['Subject'] = '[OpenERP-CaseError] ' + a
- self.msg_send(msg, self.email_default.split(','))
- return case_id, emails
+ res_id = self.rpc('email.server.tools', 'process_email', self.model, message)
+ except Exception, e:
+ res_id = False
+
+# Reply mail
+ if res_id:
+ self.rpc('email.server.tools', 'email_send', self.model, res_id, message, self.email, self.email_default)
+
+ return res_id
if __name__ == '__main__':
parser = optparse.OptionParser(usage='usage: %prog [options]', version='%prog v1.0')
@@ -442,7 +85,7 @@ if __name__ == '__main__':
parser = email_parser(options.userid, options.password, options.model, options.email, options.default, dbname=options.dbname, host=options.host, port=options.port)
- msg_txt = email.message_from_file(sys.stdin)
+ msg_txt = sys.stdin.read()
parser.parse(msg_txt)
diff --git a/addons/marketing_campaign/__openerp__.py b/addons/marketing_campaign/__openerp__.py
index 8abbf9eb706..16e45a36c96 100644
--- a/addons/marketing_campaign/__openerp__.py
+++ b/addons/marketing_campaign/__openerp__.py
@@ -28,9 +28,15 @@
"document",
"email_template"
],
- "author" : "Tiny",
+ "author" : "OpenERP SA",
"category": 'Generic Modules/Marketing',
"description": """
+Allows you to setup leads automation through marketing campaigns. The campaigns
+are dynamic and multi-channels. The process:
+* Design marketing campaigns that incluces mail templates, reports to print,
+ miscelleanous actions, etc.
+* Define segments that are selections of target people
+* Launch your campaign to automate communications.
""",
'website': 'http://www.openerp.com',
'init_xml': [],
diff --git a/addons/marketing_campaign/marketing_campaign.py b/addons/marketing_campaign/marketing_campaign.py
index c36b669ffcc..386e5639dd7 100644
--- a/addons/marketing_campaign/marketing_campaign.py
+++ b/addons/marketing_campaign/marketing_campaign.py
@@ -79,7 +79,7 @@ you required for the campaign"),
('start', '=', True)])
if not act_ids :
raise osv.except_osv("Error", "There is no starting activitity in the campaign")
- act_ids = act_obj.search(cr, uid, [('id', 'in', actvity_idsf),
+ act_ids = act_obj.search(cr, uid, [('id', 'in', actvity_ids),
('type', '=', 'email')])
for activity in act_obj.browse(cr, uid, act_ids):
if not activity.email_template_id.enforce_from_account :
diff --git a/addons/marketing_campaign/marketing_campaign_view.xml b/addons/marketing_campaign/marketing_campaign_view.xml
index f954d5de006..62cb4a9cf50 100644
--- a/addons/marketing_campaign/marketing_campaign_view.xml
+++ b/addons/marketing_campaign/marketing_campaign_view.xml
@@ -451,7 +451,7 @@
{'group_by': [], 'search_default_todo': 1}
-
+
search
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
+
+
+
@@ -103,7 +103,7 @@
-
+
@@ -171,7 +171,7 @@
[('state','in',('manual','progress')),('date','<=', time.strftime('%Y-%m-%d')),('date','>',(datetime.date.today()-datetime.timedelta(days=90)).strftime('%Y-%m-%d'))]
-
+ graph
@@ -233,7 +233,7 @@
sale.reporttree
-
+
@@ -245,7 +245,7 @@
sale.reportgraph
-
+
@@ -253,11 +253,11 @@
- Sold product and Total Price
+ Sales by Productssale.reportformgraph,tree
- [('state','in',('manual','progress','done')),('date','<=', time.strftime('%Y-%m-%d')),('date','>',(datetime.date.today()-datetime.timedelta(days=90)).strftime('%Y-%m-%d'))]
+ [('state','in',('manual','progress','done')),('date','>',(datetime.date.today()-datetime.timedelta(days=90)).strftime('%Y-%m-%d'))]
@@ -273,29 +273,29 @@
-
- view.sales.by.month.tree
- sale.report
- tree
-
-
-
-
-
-
-
-
- Sales By Month
- sale.report
- form
- tree
- [('state','in',('manual','progress','done')),('year','=', time.strftime('%Y'))]
- {'group_by_no_leaf':1,'group_by':['month']}
-
-
-
+
+ view.sales.by.month.tree
+ sale.report
+ tree
+
+
+
+
+
+
+
+
+
+ Sales By Month
+ sale.report
+ form
+ tree,graph
+ [('state','in',('manual','progress','done')),('year','=', time.strftime('%Y'))]
+ {'group_by_no_leaf':1,'group_by':['month','product_id']}
+
+
-
+ sale.top.quotations.treesale.ordertree
@@ -317,7 +317,7 @@
formtree,form[('state','=','draft')]
-
+
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index 2442320d3ba..0e2f5499ba6 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -535,10 +535,10 @@ class stock_picking(osv.osv):
seq_obj_name = 'stock.picking.' + vals['type']
vals['name'] = self.pool.get('ir.sequence').get(cr, user, seq_obj_name)
type_list = {
- 'out':_('Packing List'),
+ 'out':_('Picking List'),
'in':_('Reception'),
- 'internal': _('Internal picking'),
- 'delivery': _('Delivery order')
+ 'internal': _('Internal Picking'),
+ 'delivery': _('Delivery Order')
}
new_id = super(stock_picking, self).create(cr, user, vals, context)
if not vals.get('auto_picking', False):
@@ -711,7 +711,7 @@ class stock_picking(osv.osv):
"""
for pick in self.browse(cr, uid, ids, context=context):
type_list = {
- 'out':'Packing List',
+ 'out':'Picking List',
'in':'Reception',
'internal': 'Internal picking',
'delivery': 'Delivery order'
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index 11d89996c60..076ec85a88e 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -1135,7 +1135,7 @@
- Outgoing Packings
+ Outgoing Ordersstock.pickingir.actions.act_windowform
diff --git a/addons/thunderbird/thunderbird_installer.xml b/addons/thunderbird/thunderbird_installer.xml
index 17b7db6f71d..7b50b70fba5 100644
--- a/addons/thunderbird/thunderbird_installer.xml
+++ b/addons/thunderbird/thunderbird_installer.xml
@@ -17,6 +17,9 @@
This module is required for the thuderbird plug-in to work properly.This allows you to select an object that you had like to add to your email and its attachments. You can select a partner, a task, a project, an analytical account, or any other object and attach selected mail as .eml file in attachment of selected record.
+
+ Configure
+ 15