[MERGE] Merge with trunk addons
bzr revid: mra@mra-laptop-20100625030207-r995xyuf3zjt7eho
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
</separator>
|
||||
<xpath expr="//label[@string='description']"
|
||||
position="attributes">
|
||||
<attribute name="string">You can enhance OpenERP's basic accounting support with a few additional OpenERP applications.</attribute>
|
||||
<attribute name="string">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.</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//button[@string='Install Modules']" position="attributes">
|
||||
<attribute name="string">Configure</attribute>
|
||||
|
||||
@@ -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\'"'
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
<field name="active"/>
|
||||
<field name="sequence"/>
|
||||
<field name="max_level" />
|
||||
<field name="domain" colspan="4"/>
|
||||
<field name="domain"/>
|
||||
<field name="filter_id"/>
|
||||
<notebook colspan="4">
|
||||
<page string="Conditions">
|
||||
<group col="2" colspan="2" name="model">
|
||||
@@ -54,7 +55,6 @@
|
||||
<field name="act_state"/>
|
||||
<separator colspan="4" string="Server Action to be Triggered"/>
|
||||
<field name="server_action_id"/>
|
||||
<field name="filter_id"/>
|
||||
</page>
|
||||
<page string="Email Actions">
|
||||
<!-- <group col="4" colspan="2">-->
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
<filter string="Contact" icon="terp-gtk-jump-to-ltr" domain="[]"
|
||||
context="{'group_by':'partner_address_id'}" />
|
||||
<filter string="State" icon="terp-stock_effects-object-colorize" help="Invitation Type"
|
||||
domain="[]" context="{'group_by':' state'}" />
|
||||
domain="[]" context="{'group_by':'state'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
@@ -138,7 +138,7 @@
|
||||
|
||||
<!-- Calenadar's menu -->
|
||||
<menuitem id="base.menu_calendar_configuration" name="Calendar"
|
||||
parent="base.menu_config_address_book" sequence="15" />
|
||||
parent="base.menu_base_config" sequence="15" />
|
||||
|
||||
<!-- Invitation menu -->
|
||||
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -49,11 +49,13 @@
|
||||
attrs="{'required' : [('freq','=','yearly')]}"/>
|
||||
</group>
|
||||
</group>
|
||||
<group colspan="4" col="6">
|
||||
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
|
||||
<button icon="gtk-ok" string="ok" name="compute_exrule_string" type="object"/>
|
||||
</group>
|
||||
</form>
|
||||
<group colspan="4" col="6">
|
||||
<separator string="" colspan="8"/>
|
||||
<label string="" colspan="2"/>
|
||||
<button icon="gtk-cancel" special="cancel" string="_Cancel"/>
|
||||
<button icon="gtk-ok" string="_Ok" name="compute_exrule_string" type="object"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -13,9 +13,7 @@
|
||||
<xpath expr="//label[@string='description']"
|
||||
position="attributes">
|
||||
<attribute name="string"
|
||||
>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.</attribute>
|
||||
>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.</attribute>
|
||||
</xpath>
|
||||
<xpath expr='//separator[@string="vsep"]' position='attributes'>
|
||||
<attribute name='string'></attribute>
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="name" required="True"/><field name="account_no"/>
|
||||
<field name="currency" widget="selection"/><field name="street"/>
|
||||
<field name="zip"/>
|
||||
<field name="street2"/>
|
||||
<field name="country_id"/>
|
||||
<field name="zip"/>
|
||||
<field name="state_id"/>
|
||||
<field name="city"/>
|
||||
<field name="email"/>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<attribute name="string">Contact Information</attribute>
|
||||
</form>
|
||||
<xpath expr="//label[@string='description']" position="attributes">
|
||||
<attribute name="string">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.</attribute>
|
||||
<attribute name="string">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.</attribute>
|
||||
</xpath>
|
||||
<xpath expr='//separator[@string="title"]' position='attributes'>
|
||||
<attribute name='string'>Would you like more information or documentation ?</attribute>
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
##############################################################################
|
||||
|
||||
import crm
|
||||
import crm_mailgate
|
||||
import crm_action_rule
|
||||
import crm_segmentation
|
||||
import crm_meeting
|
||||
|
||||
+16
-5
@@ -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'),
|
||||
}
|
||||
|
||||
@@ -25,26 +25,25 @@
|
||||
<xpath expr="//button[@string='Install Modules']" position="attributes">
|
||||
<attribute name="string">Configure</attribute>
|
||||
</xpath>
|
||||
<group colspan="8">
|
||||
<group colspan="8">
|
||||
<group colspan="4" col="4" groups="base.group_extended" name="other">
|
||||
<field name="crm_claim" />
|
||||
<field name="crm_helpdesk" />
|
||||
<field name="crm_fundraising" />
|
||||
<field name="wiki_sale_faq"/>
|
||||
<field name="sale_crm" invisible="1"/>
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Synchronization" colspan="4" />
|
||||
<field name="crm_caldav" />
|
||||
<field name="fetchmail" />
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Plugins" colspan="4" />
|
||||
<separator string="Plugins" colspan="4" />
|
||||
<field name="thunderbird" />
|
||||
<label string="*Outlook*" />
|
||||
<!-- TODO <field name="outlook" />-->
|
||||
</group>
|
||||
<group colspan="4" col="4" groups="base.group_extended" name="other">
|
||||
<separator string="Additional features" colspan="4" />
|
||||
<field name="crm_claim" />
|
||||
<field name="crm_helpdesk" />
|
||||
<field name="crm_fundraising" />
|
||||
<field name="wiki_sale_faq"/>
|
||||
<field name="sale_crm" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
</data>
|
||||
</field>
|
||||
|
||||
+133
-2
@@ -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:
|
||||
|
||||
@@ -146,6 +146,11 @@
|
||||
<field name="date_open"/>
|
||||
<field name="date_closed"/>
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Mailings" colspan="2" col="2"/>
|
||||
<field name="optin"/>
|
||||
<field name="optout"/>
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Statistics" colspan="2" col="2"/>
|
||||
<field name="day_open"/>
|
||||
@@ -256,12 +261,11 @@
|
||||
<field name="type">calendar</field>
|
||||
<field name="priority" eval="2"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar string="Meeting For Leads Generation"
|
||||
date_start="create_date" color="user_id"
|
||||
date_delay="duration">
|
||||
<calendar string="Leads Generation"
|
||||
date_start="create_date" color="user_id">
|
||||
<field name="name" />
|
||||
<field name="partner_name" />
|
||||
<field name="partner_contact" />
|
||||
<field name="partner_address_id" />
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import 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()
|
||||
+11
-10
@@ -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'),
|
||||
|
||||
@@ -148,6 +148,7 @@
|
||||
<button string="Invite People"
|
||||
name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d"
|
||||
icon="terp-partner" type="action"
|
||||
attrs="{'readonly': [('state', '=', 'done')]}"
|
||||
context="{'model' : 'crm.meeting', 'attendee_field':'attendee_ids'}" colspan="2"/>
|
||||
<field name="attendee_ids" colspan="4"
|
||||
nolabel="1" widget="one2many" mode="tree,form">
|
||||
@@ -214,10 +215,27 @@
|
||||
<tree string="Meetings"
|
||||
colors="red:state=='open';black:state in ('draft', 'cancel','done','pending')">
|
||||
<field name="name" string="Subject" />
|
||||
<field name="partner_id" string="Partner" />
|
||||
<field name="date" string="Meeting Date" />
|
||||
<field name="date" string="Start Date" />
|
||||
<field name="duration" />
|
||||
<field name="partner_id" string="Partner" />
|
||||
<field name="location" />
|
||||
<field name="categ_id" string="Meeting Type"/>
|
||||
<field name="class" string="Privacy"/>
|
||||
<field name="state"/>
|
||||
<field name="user_id" invisible="1"/>
|
||||
<field name="show_as" invisible="1" string="Show time as"/>
|
||||
<button name="case_reset" string="Reset to Unconfirmed"
|
||||
states="open,done,cancel" type="object"
|
||||
icon="gtk-convert" />
|
||||
<button name="case_open" string="Confirm"
|
||||
states="draft" type="object"
|
||||
icon="gtk-go-forward" />
|
||||
<button name="case_close" string="Done"
|
||||
states="open" type="object"
|
||||
icon="gtk-jump-to" />
|
||||
<button name="case_cancel" string="Cancel"
|
||||
states="draft,open" type="object"
|
||||
icon="gtk-cancel" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -268,6 +286,10 @@
|
||||
help="Current Meetings"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name" select="1" string="Subject"/>
|
||||
<field name="categ_id" select="1"
|
||||
string="Meeting Type" widget="selection"
|
||||
domain="[('object_id.model', '=', 'crm.meeting')]" />
|
||||
<separator orientation="vertical"/>
|
||||
<field name="partner_id" select="1" />
|
||||
<field name="section_id" select="1" widget="selection">
|
||||
<filter icon="terp-personal+"
|
||||
@@ -282,16 +304,26 @@
|
||||
domain="[]" context="{'group_by':'date'}" />
|
||||
<filter string="Privacy" icon="terp-locked"
|
||||
domain="[]" context="{'group_by':'class'}" />
|
||||
<filter string="Show time as" icon="terp-project"
|
||||
domain="[]" context="{'group_by':'show_as'}" />
|
||||
<separator orientation="vertical" />
|
||||
<filter string="Responsible" icon="terp-personal" domain="[]"
|
||||
context="{'group_by':'user_id'}" />
|
||||
<filter string="Partner" icon="terp-personal" domain="[]"
|
||||
context="{'group_by':'partner_id'}" />
|
||||
<separator orientation="vertical" />
|
||||
<filter string="Meeting Type" help="Meeting Type"
|
||||
icon="terp-stock_symbol-selection" domain="[]"
|
||||
context="{'group_by':'categ_id'}" />
|
||||
<filter string="State" icon="terp-stock_effects-object-colorize" domain="[]"
|
||||
context="{'group_by':'state'}" />
|
||||
<separator orientation="vertical" />
|
||||
</group>
|
||||
<newline/>
|
||||
<group expand="0" string="Extended Options...">
|
||||
<field name="class" select="1" string="Privacy"/>
|
||||
<field name="show_as" string="Show time as" select="1"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
<act_window
|
||||
@@ -19,7 +19,7 @@
|
||||
src_model="crm.lead"
|
||||
view_mode="tree,form,calendar,"
|
||||
context="{'default_duration': 4.0, 'default_opportunity_id': active_id}"
|
||||
domain="[('user_id','=',uid), ('opportunity_id', '=', active_id)]"
|
||||
domain="[('opportunity_id', '=', active_id)]"
|
||||
view_type="form"/>
|
||||
|
||||
<record model="ir.actions.act_window" id="crm_case_category_act_oppor11">
|
||||
|
||||
@@ -258,6 +258,8 @@
|
||||
<field name="section_id"
|
||||
invisible="context.get('invisible_section', True)" />
|
||||
<field name="user_id"/>
|
||||
<field name="priority" invisible="1"/>
|
||||
<field name="categ_id" invisible="1"/>
|
||||
<field name="state"/>
|
||||
<button name="case_open" string="Open"
|
||||
states="draft,pending" type="object"
|
||||
@@ -299,14 +301,14 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Opportunities">
|
||||
<filter icon="terp-check"
|
||||
string="Current"
|
||||
string="Current" help="Current Opportunities"
|
||||
name="current"
|
||||
domain="[('state','in',('draft','open'))]"/>
|
||||
<filter icon="terp-camera_test"
|
||||
string="Open"
|
||||
string="Open" help="Open Opportunities"
|
||||
domain="[('state','=','open')]"/>
|
||||
<filter icon="terp-gtk-media-pause"
|
||||
string="Pending"
|
||||
string="Pending" help="Pending Opportunities"
|
||||
domain="[('state','=','pending')]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-go-today" string="Today"
|
||||
@@ -352,8 +354,10 @@
|
||||
<separator orientation="vertical" />
|
||||
<filter string="Creation" icon="terp-go-month"
|
||||
domain="[]" context="{'group_by':'create_date'}" />
|
||||
<filter string="Exp.Closing" icon="terp-go-month"
|
||||
domain="[]" context="{'group_by':'date_deadline'}" />
|
||||
<filter string="Exp.Closing"
|
||||
icon="terp-go-month"
|
||||
help="Expected Closing" domain="[]"
|
||||
context="{'group_by':'date_deadline'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
||||
@@ -82,8 +82,8 @@ class crm_phonecall(osv.osv, crm_case):
|
||||
'date_closed': fields.datetime('Closed', readonly=True),
|
||||
'date': fields.datetime('Date'),
|
||||
'opportunity_id': fields.many2one ('crm.lead', 'Opportunity'),
|
||||
'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 = {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<field name="categ_id" ref="crm.categ_phone1"/>
|
||||
<field name="stage_id" ref="crm.stage_phone1"/>
|
||||
<field eval=""(769) 703-274"" name="partner_phone"/>
|
||||
<field eval="2.3" name="duration"/>
|
||||
</record>
|
||||
<record id="crm_case_phone02" model="crm.phonecall">
|
||||
<field name="partner_address_id" ref="base.res_partner_address_6"/>
|
||||
@@ -33,6 +34,7 @@
|
||||
<field name="categ_id" ref="crm.categ_phone1"/>
|
||||
<field name="stage_id" ref="crm.stage_phone2"/>
|
||||
<field eval=""(956) 293-2595"" name="partner_phone"/>
|
||||
<field eval="1.5" name="duration"/>
|
||||
</record>
|
||||
<record id="crm_case_phone03" model="crm.phonecall">
|
||||
<field name="partner_address_id" ref="base.res_partner_address_2"/>
|
||||
@@ -49,6 +51,7 @@
|
||||
<field name="stage_id" ref="crm.stage_phone3"/>
|
||||
<field eval=""(079) 681-2139"" name="partner_phone"/>
|
||||
<field eval=""contact@tecsas.fr"" name="email_from"/>
|
||||
<field eval="5.0" name="duration"/>
|
||||
</record>
|
||||
<record id="crm_case_phone04" model="crm.phonecall">
|
||||
<field eval="time.strftime('%Y-%m-21 14:10:23')" name="date"/>
|
||||
@@ -63,6 +66,7 @@
|
||||
<field name="categ_id" ref="crm.categ_phone2"/>
|
||||
<field name="stage_id" ref="crm.stage_phone1"/>
|
||||
<field eval=""(514) 698-4118"" name="partner_phone"/>
|
||||
<field eval="3.45" name="duration"/>
|
||||
</record>
|
||||
<record id="crm_case_phone05" model="crm.phonecall">
|
||||
<field name="partner_address_id" ref="base.res_partner_address_10"/>
|
||||
@@ -78,6 +82,7 @@
|
||||
<field name="categ_id" ref="crm.categ_phone2"/>
|
||||
<field name="stage_id" ref="crm.stage_phone2"/>
|
||||
<field eval=""(855) 924-4364"" name="partner_phone"/>
|
||||
<field eval="2.08" name="duration"/>
|
||||
</record>
|
||||
<record id="crm_case_phone06" model="crm.phonecall">
|
||||
<field name="partner_address_id" ref="base.res_partner_address_1"/>
|
||||
@@ -94,6 +99,7 @@
|
||||
<field name="stage_id" ref="crm.stage_phone3"/>
|
||||
<field eval=""(373) 907-1009"" name="partner_phone"/>
|
||||
<field eval=""info@opensides.be"" name="email_from"/>
|
||||
<field eval="8.56" name="duration"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -12,6 +12,16 @@
|
||||
domain="[('partner_id', '=', active_id)]"
|
||||
groups="base.group_extended"
|
||||
/>
|
||||
|
||||
<act_window
|
||||
id="act_crm_phonecall_crm_meeting_new"
|
||||
name="Meetings"
|
||||
res_model="crm.meeting"
|
||||
src_model="crm.phonecall"
|
||||
view_mode="tree,form,calendar,"
|
||||
context="{'default_duration': 4.0, 'default_phonecall_id': active_id}"
|
||||
domain="[('phonecall_id', '=', active_id)]"
|
||||
view_type="form"/>
|
||||
|
||||
<!-- PHONE CALLS (menu) -->
|
||||
|
||||
|
||||
@@ -118,9 +118,6 @@
|
||||
<separator colspan="4" />
|
||||
<group col="8" colspan="4">
|
||||
<field name="state" select="1" />
|
||||
<button name="case_cancel" string="Cancel"
|
||||
states="draft,open,pending" type="object"
|
||||
icon="gtk-cancel" />
|
||||
<button name="case_open" string="Open"
|
||||
states="draft,pending" type="object"
|
||||
icon="gtk-go-forward" />
|
||||
@@ -132,6 +129,9 @@
|
||||
<button name="case_reset" string="Reset to Draft"
|
||||
states="done,cancel" type="object"
|
||||
icon="gtk-convert" />
|
||||
<button name="case_cancel" string="Cancel"
|
||||
states="draft,open,pending" type="object"
|
||||
icon="gtk-cancel" />
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
@@ -146,8 +146,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Phone Call">
|
||||
<group colspan="4" col="7">
|
||||
<field name="name" string="Summary"/>
|
||||
<field name="date" string="Planned Date"/>
|
||||
<field name="name" string="Summary" required="1"/>
|
||||
<field name="date" string="Planned Date" required="1"/>
|
||||
<field name="user_id"/>
|
||||
<button string="Schedule a Meeting"
|
||||
name="action_make_meeting"
|
||||
@@ -181,8 +181,6 @@
|
||||
<field name="partner_address_id" string="Contact"
|
||||
on_change="onchange_partner_address_id(partner_address_id, email_from)" />
|
||||
<newline/>
|
||||
<field name="partner_phone" />
|
||||
<newline/>
|
||||
<field name="partner_mobile" />
|
||||
</group>
|
||||
<group col="2" colspan="2">
|
||||
@@ -212,6 +210,7 @@
|
||||
<field name="partner_phone"/>
|
||||
<field name="user_id"/>
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="create_date" invisible="1"/>
|
||||
<button string="Opportunity"
|
||||
name="%(phonecall2opportunity_act)d"
|
||||
states="draft,open,pending"
|
||||
|
||||
@@ -147,7 +147,7 @@ class crm_lead_report(osv.osv):
|
||||
0 as avg_answers,
|
||||
0.0 as perc_done,
|
||||
0.0 as perc_cancel,
|
||||
(select count(id) from mailgate_message where res_id=c.id and res_model='crm.lead') as email,
|
||||
(select count(id) from mailgate_message where res_id=c.id and model='crm.lead') as email,
|
||||
date_trunc('day',c.create_date) as create_date,
|
||||
avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as delay_close,
|
||||
avg(extract('epoch' from (c.date_deadline - c.date_closed)))/(3600*24) as delay_expected,
|
||||
|
||||
@@ -30,9 +30,8 @@
|
||||
"access_crm_lead2opportunity_partner","crm.lead2opportunity.partner","model_crm_lead2opportunity_partner","crm.group_crm_user",1,1,1,1
|
||||
"access_crm_installer","crm.installer.rule","model_crm_installer","base.group_system",1,1,1,1
|
||||
"access_crm_lead_forward_to_partner","crm.lead.forward.to.partner","model_crm_lead_forward_to_partner","crm.group_crm_user",1,1,1,1
|
||||
"access_mailgate_thread","mailgate.thread","model_mailgate_thread","crm.group_crm_user",1,1,1,1
|
||||
"access_res_partner","res.partner.crm.user","base.model_res_partner","crm.group_crm_user",1,0,0,0
|
||||
"access_res_partner_address","res.partner.address.crm.user","base.model_res_partner_address","crm.group_crm_user",1,0,0,0
|
||||
"access_res_partner_category","res.partner.category.crm.user","base.model_res_partner_category","crm.group_crm_user",1,0,0,0
|
||||
"mail_gateway_mailgate_message","mail_gateway.mailgate.message","mail_gateway.model_mailgate_message","crm.group_crm_user",1,1,1,1
|
||||
"mail_gateway_mailgate_thread","mail_gateway.mailgate.thread","model_mailgate_thread","crm.group_crm_user",1,1,1,1
|
||||
"mail_gateway_mailgate_thread","mail_gateway.mailgate.thread","mail_gateway.model_mailgate_thread","crm.group_crm_user",1,1,1,1
|
||||
|
||||
|
@@ -183,14 +183,18 @@ class crm_lead_forward_to_partner(osv.osv_memory):
|
||||
emails = [this.email_to]
|
||||
body = case_pool.format_body(this.message)
|
||||
email_from = this.email_from or False
|
||||
case_pool._history(cr, uid, [case], _('Forward'), history=True, email=this.email_to, details=body, email_from=email_from)
|
||||
|
||||
flag = False
|
||||
flag = tools.email_send(
|
||||
email_from,
|
||||
emails,
|
||||
this.subject,
|
||||
body,
|
||||
)
|
||||
if flag:
|
||||
case_pool._history(cr, uid, [case], _('Forward'), history=True, email=this.email_to, subject=this.subject, details=body, email_from=email_from)
|
||||
if not flag:
|
||||
raise osv.except_osv(_('Error!'), _('Unable to send mail. Please check SMTP is configured properly.'))
|
||||
if this.add_cc:
|
||||
case_pool.write(cr, uid, case.id, {'email_cc' : case.email_cc and case.email_cc + ', ' + this.email_to or this.email_to})
|
||||
return {}
|
||||
@@ -199,7 +203,7 @@ class crm_lead_forward_to_partner(osv.osv_memory):
|
||||
message = []
|
||||
lead_proxy = self.pool.get('crm.lead')
|
||||
lead = lead_proxy.browse(cr, uid, lead_id, context=context)
|
||||
if lead.type == 'lead':
|
||||
if not lead.type or lead.type == 'lead':
|
||||
field_names = [
|
||||
'partner_name', 'title', 'function', 'street', 'street2',
|
||||
'zip', 'city', 'country_id', 'state_id', 'email_from',
|
||||
|
||||
@@ -9,13 +9,15 @@
|
||||
<field name="model">crm.opportunity2phonecall</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Schedule Other Call">
|
||||
<form string="Schedule a PhoneCall">
|
||||
<separator string="Schedule a PhoneCall" colspan="4"/>
|
||||
<group colspan="2" >
|
||||
<field name="name"/>
|
||||
<field name="date" string="Planned Date"/>
|
||||
<newline/>
|
||||
<field name="user_id" />
|
||||
<field name="section_id"/>
|
||||
<separator string="" colspan="4"/>
|
||||
<label string ="" colspan="2"/>
|
||||
<button name="action_cancel" string="_Cancel"
|
||||
icon="gtk-cancel" special="cancel" />
|
||||
|
||||
@@ -85,11 +85,19 @@ Call Could not convert into Opportunity"))
|
||||
id3 = data_obj.browse(cr, uid, id3, context=context).res_id
|
||||
|
||||
for this in self.browse(cr, uid, ids, context=context):
|
||||
address = None
|
||||
if this.partner_id:
|
||||
address_id = self.pool.get('res.partner').address_get(cr, uid, [this.partner_id.id])
|
||||
if address_id:
|
||||
address = self.pool.get('res.partner.address').browse(cr, uid, address_id['default'], context=context)
|
||||
new_opportunity_id = opp_obj.create(cr, uid, {
|
||||
'name': this.name,
|
||||
'planned_revenue': this.planned_revenue,
|
||||
'probability': this.probability,
|
||||
'partner_id': this.partner_id and this.partner_id.id or False,
|
||||
'partner_address_id': address and address.id,
|
||||
'phone': address and address.phone,
|
||||
'mobile': address and address.mobile,
|
||||
'section_id': case.section_id and case.section_id.id or False,
|
||||
'description': case.description or False,
|
||||
'phonecall_id': case.id,
|
||||
|
||||
@@ -94,6 +94,7 @@ class crm_phonecall2phonecall(osv.osv_memory):
|
||||
'views': [(id2, 'form'), (id3, 'tree'), (False, 'calendar'), (False, 'graph')],
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_id': phonecall_id,
|
||||
'domain': [('id', 'in', [int(phonecall_id)])],
|
||||
'search_view_id': res['res_id']
|
||||
}
|
||||
return res
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
from osv import osv, fields
|
||||
from tools.translate import _
|
||||
import base64
|
||||
import time
|
||||
import tools
|
||||
from crm import crm
|
||||
|
||||
@@ -96,42 +97,46 @@ class crm_send_new_email(osv.osv_memory):
|
||||
else:
|
||||
hist = hist_obj.browse(cr, uid, res_id)
|
||||
message_id = hist.message_id
|
||||
model = hist.res_model
|
||||
model = hist.model
|
||||
model_pool = self.pool.get(model)
|
||||
case = model_pool.browse(cr, uid, hist.res_id)
|
||||
res_id = hist.res_id
|
||||
emails = [obj.email_to] + (obj.email_cc or '').split(',')
|
||||
res_ids = model_pool.search(cr, uid, [('thread_id','=', hist.thread_id.id)])
|
||||
res_id = res_ids and res_ids[0] or False
|
||||
case = model_pool.browse(cr, uid, res_id)
|
||||
emails = [obj.email_to]
|
||||
email_cc = (obj.email_cc or '').split(',')
|
||||
emails = filter(None, emails)
|
||||
body = obj.text
|
||||
|
||||
body = case_pool.format_body(body)
|
||||
email_from = getattr(obj, 'email_from', False)
|
||||
|
||||
case_pool._history(cr, uid, [case], _('Send'), history=True, \
|
||||
email=obj.email_to, details=body, \
|
||||
subject=obj.subject, email_from=email_from, \
|
||||
message_id=message_id, attach=attach)
|
||||
|
||||
x_headers = dict()
|
||||
#x_headers = {
|
||||
# 'Reply-To':"%s" % case.section_id.reply_to,
|
||||
#}
|
||||
x_headers = {
|
||||
'model': model,
|
||||
'resource-id': res_id
|
||||
}
|
||||
if message_id:
|
||||
x_headers['References'] = "%s" % (message_id)
|
||||
|
||||
print case.section_id
|
||||
flag = False
|
||||
|
||||
flag = tools.email_send(
|
||||
email_from,
|
||||
emails,
|
||||
obj.subject,
|
||||
body,
|
||||
email_cc=email_cc,
|
||||
attach=attach,
|
||||
reply_to=case.section_id and case.section_id.reply_to or email_from,
|
||||
openobject_id=str(case.id),
|
||||
x_headers=x_headers
|
||||
)
|
||||
|
||||
if not flag:
|
||||
raise osv.except_osv(_('Error!'), _('Unable to send mail. Please check SMTP is configured properly.'))
|
||||
if flag:
|
||||
case_pool._history(cr, uid, [case], _('Send'), history=True, \
|
||||
email=obj.email_to, details=body, \
|
||||
subject=obj.subject, email_from=email_from, \
|
||||
message_id=message_id, attach=attach)
|
||||
if obj.state == 'unchanged':
|
||||
pass
|
||||
elif obj.state == 'done':
|
||||
@@ -194,14 +199,15 @@ class crm_send_new_email(osv.osv_memory):
|
||||
include_original = context and context.get('include_original', False) or False
|
||||
res = {}
|
||||
for hist in hist_obj.browse(cr, uid, res_ids, context=context):
|
||||
model = hist.res_model
|
||||
model = hist.model
|
||||
|
||||
# In the case where the crm.case does not exist in the database
|
||||
if not model:
|
||||
return {}
|
||||
|
||||
model_pool = self.pool.get(model)
|
||||
res_id = hist.res_id or False
|
||||
res_ids = model_pool.search(cr, uid, [('thread_id','=', hist.thread_id.id)])
|
||||
res_id = res_ids and res_ids[0] or False
|
||||
case = model_pool.browse(cr, uid, res_id)
|
||||
if 'email_to' in fields:
|
||||
res.update({'email_to': case.email_from or hist.email_from or False})
|
||||
@@ -222,10 +228,10 @@ class crm_send_new_email(osv.osv_memory):
|
||||
|
||||
original = [header, sender, to, sentdate, desc, signature]
|
||||
|
||||
res['text']= '\n'.join(original)
|
||||
res['text']= '\n\n\n' + '\n'.join(original)
|
||||
|
||||
if 'subject' in fields:
|
||||
res.update({'subject': '[%s] %s' %(str(case.id), case.name or '')})
|
||||
res.update({'subject': 'Re: %s' %(hist.name or '')})
|
||||
if 'state' in fields:
|
||||
res['state']='pending'
|
||||
return res
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
from osv import fields, osv
|
||||
from crm import crm
|
||||
|
||||
import time
|
||||
class crm_claim(osv.osv, crm.crm_case):
|
||||
"""
|
||||
Crm claim
|
||||
@@ -88,8 +88,8 @@ class crm_claim(osv.osv, crm.crm_case):
|
||||
\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 = {
|
||||
@@ -100,9 +100,46 @@ class crm_claim(osv.osv, crm.crm_case):
|
||||
'email_from':crm.crm_case. _get_default_email,
|
||||
'state': lambda *a: 'draft',
|
||||
'section_id':crm.crm_case. _get_section,
|
||||
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c),
|
||||
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
|
||||
}
|
||||
|
||||
def onchange_partner_id(self, cr, uid, ids, part, email=False):
|
||||
"""This function returns value of partner address based on partner
|
||||
@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 case IDs
|
||||
@param part: Partner's id
|
||||
@email: Partner's email ID
|
||||
"""
|
||||
if not part:
|
||||
return {'value': {'partner_address_id': False,
|
||||
'email_from': False,
|
||||
'partner_phone': False,
|
||||
'partner_mobile': False
|
||||
}}
|
||||
addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact'])
|
||||
data = {'partner_address_id': addr['contact']}
|
||||
data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value'])
|
||||
print data
|
||||
return {'value': data}
|
||||
|
||||
def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
|
||||
"""This function returns value of partner email based on Partner Address
|
||||
@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 case IDs
|
||||
@param add: Id of Partner's address
|
||||
@email: Partner's email ID
|
||||
"""
|
||||
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, 'partner_phone': address.phone, 'partner_mobile': address.mobile}}
|
||||
|
||||
|
||||
crm_claim()
|
||||
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Claims" colors="blue:state=='pending';black:state=='open';grey:state in ('close', 'cancel')">
|
||||
<field name="id"/>
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="user_id" />
|
||||
@@ -67,6 +66,8 @@
|
||||
<field name="date_deadline" string="Deadline"/>
|
||||
<field name="date_closed" string="Closure Date"/>
|
||||
<field name="categ_id" string="Type" select="1"/>
|
||||
<field name="stage_id" invisible="1"/>
|
||||
<field name="som" invisible="1"/>
|
||||
<field name="state"/>
|
||||
<button name="case_open" string="Open"
|
||||
states="draft,pending" type="object"
|
||||
@@ -123,8 +124,9 @@
|
||||
<field name="user_id" string="Responsible"/>
|
||||
<field name="section_id" widget="selection"/>
|
||||
<label string="Stage: " align="1.0"/>
|
||||
<group colspan="1" col="2">
|
||||
<group colspan="1" col="3">
|
||||
<field name="stage_id" select="1" nolabel="1"/>
|
||||
<button name="stage_previous" string="" type="object" icon="gtk-go-back" />
|
||||
<button icon="gtk-go-forward" string="" name="stage_next" type="object"/>
|
||||
</group>
|
||||
<field name="categ_id" select="1" widget="selection"
|
||||
@@ -143,18 +145,18 @@
|
||||
<separator colspan="4" string=""/>
|
||||
<group col="8" colspan="4">
|
||||
<field name="state" select="1"/>
|
||||
<button name="case_close" string="Done"
|
||||
states="open,draft,pending" type="object"
|
||||
icon="gtk-jump-to" />
|
||||
<button name="case_open" string="Open"
|
||||
states="draft,pending" type="object"
|
||||
icon="gtk-go-forward" />
|
||||
<button name="case_cancel" string="Cancel"
|
||||
states="draft,open,pending" type="object"
|
||||
icon="gtk-cancel" />
|
||||
<button name="case_pending" string="Pending"
|
||||
states="draft,open" type="object"
|
||||
icon="gtk-media-pause" />
|
||||
<button name="case_close" string="Done"
|
||||
states="open,draft,pending" type="object"
|
||||
icon="gtk-jump-to" />
|
||||
<button name="case_cancel" string="Cancel"
|
||||
states="draft,open,pending" type="object"
|
||||
icon="gtk-cancel" />
|
||||
<button name="case_reset"
|
||||
string="Reset to Draft" states="done,cancel"
|
||||
type="object" icon="gtk-convert" />
|
||||
@@ -291,12 +293,37 @@
|
||||
widget="selection"/>
|
||||
<newline/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Partner" icon="terp-partner"
|
||||
domain="[]" help="Partner"
|
||||
context="{'group_by':'partner_id'}" />
|
||||
<filter string="Responsible" icon="terp-personal"
|
||||
domain="[]" help="Responsible User"
|
||||
context="{'group_by':'user_id'}" />
|
||||
<filter string="Sales Team" icon="terp-personal+"
|
||||
domain="[]" help="Sales Team"
|
||||
context="{'group_by':'section_id'}" />
|
||||
<separator orientation="vertical"/>
|
||||
<filter string="Stage" icon="terp-stage"
|
||||
domain="[]" context="{'group_by':'stage_id'}" />
|
||||
<filter string="Type" icon="terp-stock_symbol-selection"
|
||||
domain="[]" context="{'group_by':'categ_id'}" />
|
||||
<filter string="State"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
domain="[]" context="{'group_by':'state'}" />
|
||||
<separator orientation="vertical"/>
|
||||
<filter string="Claim Date" icon="terp-go-month"
|
||||
domain="[]" help="Claim Date"
|
||||
context="{'group_by':'date'}" />
|
||||
<filter string="Deadline" icon="terp-go-month"
|
||||
domain="[]"
|
||||
context="{'group_by':'date_deadline'}" />
|
||||
<filter string="Closure" icon="terp-go-month"
|
||||
domain="[]"
|
||||
domain="[]" help="Date Closed"
|
||||
context="{'group_by':'date_closed'}" />
|
||||
<separator orientation="vertical"/>
|
||||
<filter string="State of Mind"
|
||||
icon="terp-face-plain"
|
||||
domain="[]" context="{'group_by':'som'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
||||
@@ -85,8 +85,8 @@ class crm_fundraising(osv.osv, crm.crm_case):
|
||||
\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 = {
|
||||
|
||||
@@ -72,21 +72,21 @@
|
||||
<field name="user_id" />
|
||||
<field name="state" />
|
||||
<field name="partner_id" invisible="1"/>
|
||||
<button name="case_close" string="Done"
|
||||
states="open,draft,pending" type="object"
|
||||
icon="gtk-close" />
|
||||
<button name="case_open" string="Open"
|
||||
states="draft,pending" type="object"
|
||||
icon="gtk-go-forward" />
|
||||
<button name="case_cancel" string="Cancel"
|
||||
states="draft,open,pending" type="object"
|
||||
icon="gtk-cancel" />
|
||||
<button name="case_pending" string="Pending"
|
||||
states="draft,open" type="object" icon="gtk-media-pause" />
|
||||
<button name="case_escalate" string="Escalate"
|
||||
states="open,draft,pending" type="object"
|
||||
groups="base.group_extended"
|
||||
icon="gtk-go-up" />
|
||||
<button name="case_close" string="Done"
|
||||
states="open,draft,pending" type="object"
|
||||
icon="gtk-close" />
|
||||
<button name="case_cancel" string="Cancel"
|
||||
states="draft,open,pending" type="object"
|
||||
icon="gtk-cancel" />
|
||||
<button name="case_reset" string="Reset to Draft"
|
||||
states="done,cancel" type="object" icon="gtk-convert" />
|
||||
</tree>
|
||||
@@ -133,15 +133,9 @@
|
||||
<separator colspan="4"/>
|
||||
<group col="8" colspan="4">
|
||||
<field name="state"/>
|
||||
<button name="case_close" string="Done"
|
||||
states="open,draft,pending" type="object"
|
||||
icon="gtk-close" />
|
||||
<button name="case_open" string="Open"
|
||||
states="draft,pending" type="object"
|
||||
icon="gtk-go-forward" />
|
||||
<button name="case_cancel" string="Cancel"
|
||||
states="draft,open,pending" type="object"
|
||||
icon="gtk-cancel" />
|
||||
<button name="case_pending" string="Pending"
|
||||
states="draft,open" type="object"
|
||||
icon="gtk-media-pause" />
|
||||
@@ -149,6 +143,12 @@
|
||||
states="open,draft,pending" type="object"
|
||||
groups="base.group_extended"
|
||||
icon="gtk-go-up" />
|
||||
<button name="case_close" string="Done"
|
||||
states="open,draft,pending" type="object"
|
||||
icon="gtk-close" />
|
||||
<button name="case_cancel" string="Cancel"
|
||||
states="draft,open,pending" type="object"
|
||||
icon="gtk-cancel" />
|
||||
<button name="case_reset"
|
||||
string="Reset to Draft" states="done,cancel"
|
||||
type="object" icon="gtk-convert" />
|
||||
@@ -304,9 +304,15 @@
|
||||
domain="[]"
|
||||
context="{'group_by':'user_id'}" />
|
||||
<separator orientation="vertical" />
|
||||
<filter string="Type" icon="terp-stock_symbol-selection"
|
||||
domain="[]"
|
||||
<filter string="Category" help="Fund Category"
|
||||
icon="terp-stock_symbol-selection" domain="[]"
|
||||
context="{'group_by':'categ_id'}" />
|
||||
<filter string="Payment Mode" help="Payment Mode"
|
||||
icon="terp-dolar" domain="[]"
|
||||
context="{'group_by':'type_id'}" />
|
||||
<separator orientation="vertical" />
|
||||
<filter string="State" icon="terp-stock_effects-object-colorize" domain="[]"
|
||||
context="{'group_by':'state'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Helpdesk Support">
|
||||
<group colspan="4" col="7">
|
||||
<field name="name" select="1"/>
|
||||
<field name="name" select="1" string="Query"/>
|
||||
<field name="section_id" widget="selection"/>
|
||||
<field name="user_id" select="1"/>
|
||||
<button name="remind_user"
|
||||
@@ -71,26 +71,26 @@
|
||||
<separator colspan="4"/>
|
||||
<group col="8" colspan="4">
|
||||
<field name="state" select="1"/>
|
||||
<button name="case_close"
|
||||
states="open,draft,pending" string="Close"
|
||||
type="object" icon="gtk-close" />
|
||||
<button name="case_open"
|
||||
states="draft,pending" string="Open"
|
||||
type="object" icon="gtk-go-forward" />
|
||||
<button name="case_cancel"
|
||||
states="draft,open,pending" string="Cancel"
|
||||
type="object" icon="gtk-cancel" />
|
||||
states="draft,pending" string="Open"
|
||||
type="object" icon="gtk-go-forward" />
|
||||
<button name="case_pending"
|
||||
states="draft,open" string="Pending"
|
||||
type="object" icon="gtk-media-pause" />
|
||||
states="draft,open" string="Pending"
|
||||
type="object" icon="gtk-media-pause" />
|
||||
<button name="case_escalate"
|
||||
states="open,draft,pending"
|
||||
string="Escalate" type="object"
|
||||
icon="gtk-go-up" />
|
||||
states="open,draft,pending"
|
||||
string="Escalate" type="object"
|
||||
icon="gtk-go-up" />
|
||||
<button name="case_close"
|
||||
states="open,draft,pending" string="Close"
|
||||
type="object" icon="gtk-jump-to" />
|
||||
<button name="case_cancel"
|
||||
states="draft,open,pending" string="Cancel"
|
||||
type="object" icon="gtk-cancel" />
|
||||
<button name="case_reset"
|
||||
states="done,cancel"
|
||||
string="Reset to Draft" type="object"
|
||||
icon="gtk-convert" />
|
||||
states="done,cancel"
|
||||
string="Reset to Draft" type="object"
|
||||
icon="gtk-convert" />
|
||||
</group>
|
||||
</page>
|
||||
<page string="History" groups="base.group_extended">
|
||||
@@ -179,24 +179,18 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Helpdesk Support Tree"
|
||||
colors="black:state=='open';blue:state=='pending';grey:state in ('cancel','close')">
|
||||
<field name="id"/>
|
||||
<field name="name" string="Query Description" />
|
||||
<field name="name" string="Query" />
|
||||
<field name="partner_id" string="Partner"/>
|
||||
<field name="date" string="Date"/>
|
||||
<field name="date_deadline"/>
|
||||
<field name="user_id"/>
|
||||
<field name="section_id"/>
|
||||
<field name="priority"/>
|
||||
<field name="som" invisible="1"/>
|
||||
<field name="state"/>
|
||||
<button name="case_close" string="Done"
|
||||
states="open,draft,pending" type="object"
|
||||
icon="gtk-close" />
|
||||
<button name="case_open" string="Open"
|
||||
states="draft,pending" type="object"
|
||||
icon="gtk-go-forward" />
|
||||
<button name="case_cancel" string="Cancel"
|
||||
states="draft,open,pending" type="object"
|
||||
icon="gtk-cancel" />
|
||||
<button name="case_pending" string="Pending"
|
||||
states="draft,open" type="object"
|
||||
icon="gtk-media-pause" />
|
||||
@@ -204,6 +198,12 @@
|
||||
groups="base.group_extended"
|
||||
states="open,draft,pending" type="object"
|
||||
icon="gtk-go-up" />
|
||||
<button name="case_close" string="Done"
|
||||
states="open,draft,pending" type="object"
|
||||
icon="gtk-close" />
|
||||
<button name="case_cancel" string="Cancel"
|
||||
states="draft,open,pending" type="object"
|
||||
icon="gtk-cancel" />
|
||||
<button name="case_reset" string="Reset to Draft"
|
||||
states="done,cancel" type="object" icon="gtk-convert" />
|
||||
</tree>
|
||||
@@ -255,12 +255,30 @@
|
||||
</field>
|
||||
<newline/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Customer" icon="terp-personal"
|
||||
domain="[]" context="{'group_by':'partner_id'}" />
|
||||
<filter string="Partner" icon="terp-partner"
|
||||
domain="[]" help="Partner"
|
||||
context="{'group_by':'partner_id'}" />
|
||||
<filter string="Responsible" icon="terp-personal"
|
||||
domain="[]" help="Responsible User"
|
||||
context="{'group_by':'user_id'}" />
|
||||
<filter string="Sales Team" icon="terp-personal+"
|
||||
domain="[]" help="Sales Team"
|
||||
context="{'group_by':'section_id'}" />
|
||||
<separator orientation="vertical"/>
|
||||
<filter string="State" icon="terp-stock_effects-object-colorize" domain="[]"
|
||||
context="{'group_by':'state'}" />
|
||||
<filter string="Priority" icon="terp-rating-rated"
|
||||
domain="[]" context="{'group_by':'priority'}" />
|
||||
<filter string="State of Mind"
|
||||
icon="terp-face-plain"
|
||||
domain="[]" context="{'group_by':'som'}" />
|
||||
<separator orientation="vertical"/>
|
||||
<filter string="Date" icon="terp-go-month"
|
||||
domain="[]" help="Request Date"
|
||||
context="{'group_by':'date'}" />
|
||||
<filter string="Deadline" icon="terp-go-month"
|
||||
domain="[]"
|
||||
context="{'group_by':'date_deadline'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -56,14 +56,16 @@
|
||||
<field name="model_int_name" invisible="1" />
|
||||
<notebook colspan="4">
|
||||
<page string="Mail Details">
|
||||
<field name="enforce_from_account" required= "1"/>
|
||||
<group col="4" colspan="2">
|
||||
<group col="2" colspan="2">
|
||||
<separator string="Addresses" colspan="2"/>
|
||||
<field name="from_email"/>
|
||||
<field name="def_to" colspan="4" required="1" />
|
||||
<field name="def_cc" colspan="4" />
|
||||
<field name="def_bcc" colspan="4" />
|
||||
<field name="def_to" required="1"/>
|
||||
<field name="def_cc"/>
|
||||
<field name="def_bcc"/>
|
||||
</group>
|
||||
<group col="2" colspan="2">
|
||||
<separator string="Email Data" colspan="2"/>
|
||||
<field name="enforce_from_account" required= "1"/>
|
||||
<field name="def_subject" colspan="4" required="1" />
|
||||
<field name="use_sign" colspan="4" />
|
||||
<field name="lang" colspan="4" />
|
||||
|
||||
@@ -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
|
||||
|
||||
+65
-345
@@ -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
|
||||
## <peter@fry-it.com>
|
||||
## download here: http://www.peterbe.com/plog/html2plaintext
|
||||
|
||||
""" from an HTML text, convert the HTML to plain text.
|
||||
If @body_id is provided then this is the tag where the
|
||||
body (not necessarily <body>) starts.
|
||||
"""
|
||||
try:
|
||||
from BeautifulSoup import BeautifulSoup, SoupStrainer, Comment
|
||||
except:
|
||||
return html
|
||||
|
||||
urls = []
|
||||
if body_id is not None:
|
||||
strainer = SoupStrainer(id=body_id)
|
||||
else:
|
||||
strainer = SoupStrainer('body')
|
||||
|
||||
soup = BeautifulSoup(html, parseOnlyThese=strainer, fromEncoding=encoding)
|
||||
for link in soup.findAll('a'):
|
||||
title = link.renderContents()
|
||||
for url in [x[1] for x in link.attrs if x[0]=='href']:
|
||||
urls.append(dict(url=url, tag=str(link), title=title))
|
||||
|
||||
html = soup.__str__()
|
||||
|
||||
url_index = []
|
||||
i = 0
|
||||
for d in urls:
|
||||
if d['title'] == d['url'] or 'http://'+d['title'] == d['url']:
|
||||
html = html.replace(d['tag'], d['url'])
|
||||
else:
|
||||
i += 1
|
||||
html = html.replace(d['tag'], '%s [%s]' % (d['title'], i))
|
||||
url_index.append(d['url'])
|
||||
|
||||
html = html.replace('<strong>','*').replace('</strong>','*')
|
||||
html = html.replace('<b>','*').replace('</b>','*')
|
||||
html = html.replace('<h3>','*').replace('</h3>','*')
|
||||
html = html.replace('<h2>','**').replace('</h2>','**')
|
||||
html = html.replace('<h1>','**').replace('</h1>','**')
|
||||
html = html.replace('<em>','/').replace('</em>','/')
|
||||
|
||||
# the only line breaks we respect is those of ending tags and
|
||||
# breaks
|
||||
|
||||
html = html.replace('\n',' ')
|
||||
html = html.replace('<br>', '\n')
|
||||
html = html.replace('<tr>', '\n')
|
||||
html = html.replace('</p>', '\n\n')
|
||||
html = re.sub('<br\s*/>', '\n', html)
|
||||
html = html.replace(' ' * 2, ' ')
|
||||
|
||||
# for all other tags we failed to clean up, just remove then and
|
||||
# complain about them on the stderr
|
||||
def desperate_fixer(g):
|
||||
#print >>sys.stderr, "failed to clean up %s" % str(g.group())
|
||||
return ' '
|
||||
|
||||
html = re.sub('<.*?>', desperate_fixer, html)
|
||||
|
||||
# lstrip all lines
|
||||
html = '\n'.join([x.lstrip() for x in html.splitlines()])
|
||||
|
||||
for i, url in enumerate(url_index):
|
||||
if i == 0:
|
||||
html += '\n\n'
|
||||
html += '[%s] %s\n' % (i+1, url)
|
||||
return html
|
||||
|
||||
class email_server(osv.osv):
|
||||
|
||||
|
||||
_name = 'email.server'
|
||||
_description = "POP/IMAP Server"
|
||||
|
||||
|
||||
_columns = {
|
||||
'name':fields.char('Name', size=256, required=True, readonly=False),
|
||||
'active':fields.boolean('Active', required=False),
|
||||
'name':fields.char('Name', size=256, required=True, readonly=False),
|
||||
'active':fields.boolean('Active', required=False),
|
||||
'state':fields.selection([
|
||||
('draft','Not Confirmed'),
|
||||
('wating','Waiting for Verification'),
|
||||
('done','Confirmed'),
|
||||
],'State', select=True, readonly=True),
|
||||
'server' : fields.char('Server', size=256, required=True, readonly=True, states={'draft':[('readonly',False)]}),
|
||||
'port' : fields.integer('Port', required=True, readonly=True, states={'draft':[('readonly',False)]}),
|
||||
('draft', 'Not Confirmed'),
|
||||
('wating', 'Waiting for Verification'),
|
||||
('done', 'Confirmed'),
|
||||
], 'State', select=True, readonly=True),
|
||||
'server' : fields.char('Server', size=256, required=True, readonly=True, states={'draft':[('readonly', False)]}),
|
||||
'port' : fields.integer('Port', required=True, readonly=True, states={'draft':[('readonly', False)]}),
|
||||
'type':fields.selection([
|
||||
('pop','POP Server'),
|
||||
('imap','IMAP Server'),
|
||||
],'State', select=True, readonly=False),
|
||||
'is_ssl':fields.boolean('SSL ?', required=False),
|
||||
'attach':fields.boolean('Add Attachments ?', required=False),
|
||||
'date': fields.date('Date', readonly=True, states={'draft':[('readonly',False)]}),
|
||||
'user' : fields.char('User Name', size=256, required=True, readonly=True, states={'draft':[('readonly',False)]}),
|
||||
'password' : fields.char('Password', size=1024, invisible=True, required=True, readonly=True, states={'draft':[('readonly',False)]}),
|
||||
'note': fields.text('Description'),
|
||||
'action_id':fields.many2one('ir.actions.server', 'Reply Email', required=False, domain="[('state','=','email')]"),
|
||||
'object_id': fields.many2one('ir.model',"Model", required=True),
|
||||
'priority': fields.integer('Server Priority', readonly=True, states={'draft':[('readonly',False)]}, help="Priority between 0 to 10, select define the order of Processing"),
|
||||
'user_id':fields.many2one('res.users', 'User', required=False),
|
||||
('pop', 'POP Server'),
|
||||
('imap', 'IMAP Server'),
|
||||
], 'State', select=True, readonly=False),
|
||||
'is_ssl':fields.boolean('SSL ?', required=False),
|
||||
'attach':fields.boolean('Add Attachments ?', required=False),
|
||||
'date': fields.date('Date', readonly=True, states={'draft':[('readonly', False)]}),
|
||||
'user' : fields.char('User Name', size=256, required=True, readonly=True, states={'draft':[('readonly', False)]}),
|
||||
'password' : fields.char('Password', size=1024, invisible=True, required=True, readonly=True, states={'draft':[('readonly', False)]}),
|
||||
'note': fields.text('Description'),
|
||||
'action_id':fields.many2one('ir.actions.server', 'Reply Email', required=False, domain="[('state','=','email')]"),
|
||||
'object_id': fields.many2one('ir.model', "Model", required=True),
|
||||
'priority': fields.integer('Server Priority', readonly=True, states={'draft':[('readonly', False)]}, help="Priority between 0 to 10, select define the order of Processing"),
|
||||
'user_id':fields.many2one('res.users', 'User', required=False),
|
||||
}
|
||||
_defaults = {
|
||||
'state': lambda *a: "draft",
|
||||
'active': lambda *a: True,
|
||||
'priority': lambda *a: 5,
|
||||
'date': lambda *a: time.strftime('%Y-%m-%d'),
|
||||
'user_id': lambda self, cr, uid, ctx: uid,
|
||||
'state': lambda *a: "draft",
|
||||
'active': lambda *a: True,
|
||||
'priority': lambda *a: 5,
|
||||
'date': lambda *a: time.strftime('%Y-%m-%d'),
|
||||
'user_id': lambda self, cr, uid, ctx: uid,
|
||||
}
|
||||
|
||||
|
||||
def check_duplicate(self, cr, uid, ids):
|
||||
vals = self.read(cr, uid, ids, ['user', 'password'])[0]
|
||||
cr.execute("select count(id) from email_server where user='%s' and password='%s'" % (vals['user'], vals['password']))
|
||||
@@ -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:
|
||||
|
||||
@@ -103,82 +103,54 @@
|
||||
action="action_email_server_tree_imap"
|
||||
/>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_server_history_tree">
|
||||
<field name="name">mail.server.history.tree</field>
|
||||
<field name="model">mail.server.history</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Received Mail History">
|
||||
<field name="server_id" select="1"/>
|
||||
<field name="create_date" select="1"/>
|
||||
<field name="model_id"/>
|
||||
<field name="name" select="1"/>
|
||||
<field name="ref_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="view_email_server_history_form">
|
||||
<field name="name">mail.server.history.form</field>
|
||||
<field name="model">mail.server.history</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Email History">
|
||||
<group col="6" colspan="4">
|
||||
<record model="ir.ui.view" id="mailgate_message_tree_view">
|
||||
<field name="name">mailgate.message.tree</field>
|
||||
<field name="model">mailgate.message</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="inherit_id" ref="mail_gateway.view_mailgate_message_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="user_id" position="after">
|
||||
<field name="server_id" select="1"/>
|
||||
<field name="create_date" select="1"/>
|
||||
</group>
|
||||
<group col="2" colspan="2">
|
||||
<separator string="Resource Information" colspan="2"/>
|
||||
<field name="model_id" select="1"/>
|
||||
<field name="res_id"/>
|
||||
</group>
|
||||
<group col="2" colspan="2">
|
||||
<separator string="Meta Information" colspan="2"/>
|
||||
<field name="name" select="1"/>
|
||||
<field name="ref_id"/>
|
||||
</group>
|
||||
<newline/>
|
||||
<separator string="Description" colspan="4"/>
|
||||
<field name="note" colspan="4" nolabel="1"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<act_window
|
||||
context="{'server_id': active_id}"
|
||||
domain="[('server_id', '=', active_id)]"
|
||||
id="act_server_history" name="Email History"
|
||||
res_model="mail.server.history" src_model="email.server"/>
|
||||
res_model="mailgate.message" src_model="email.server"/>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_email_server_history_tree">
|
||||
<record model="ir.actions.act_window" id="action_mailgate_message_tree">
|
||||
<field name="name">Received Email History</field>
|
||||
<field name="res_model">mail.server.history</field>
|
||||
<field name="res_model">mailgate.message</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="view_mail_server_history_tree"/>
|
||||
<field name="view_id" ref="mailgate_message_tree_view"/>
|
||||
<field name="context">{'type':'imap'}</field>
|
||||
<field name="domain">[('type','=','imap')]</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
parent="menu_action_email_server_tree_imap"
|
||||
id="menu_action_email_server_history_tree"
|
||||
action="action_email_server_history_tree"/>
|
||||
id="menu_action_mailgate_message_tree"
|
||||
action="action_mailgate_message_tree"/>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_email_server_history_tree_pop">
|
||||
<record model="ir.actions.act_window" id="action_mailgate_message_tree_pop">
|
||||
<field name="name">Received Email History</field>
|
||||
<field name="res_model">mail.server.history</field>
|
||||
<field name="res_model">mailgate.message</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="view_mail_server_history_tree"/>
|
||||
<field name="view_id" ref="mailgate_message_tree_view"/>
|
||||
<field name="context">{'type':'pop'}</field>
|
||||
<field name="domain">[('type','=','pop')]</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
parent="menu_action_email_server_tree"
|
||||
id="menu_action_email_server_history_tree_pop"
|
||||
action="action_email_server_history_tree_pop"/>
|
||||
id="menu_action_mailgate_message_tree_pop"
|
||||
action="action_mailgate_message_tree_pop"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -8,21 +8,28 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="mailgate message">
|
||||
<group colspan="4" col="6">
|
||||
<field name="name" required="1" select="1"/>
|
||||
<field name="date" required="1" select="1"/>
|
||||
<field name="user_id" string="Responsible" select="1"/>
|
||||
</group>
|
||||
<field name="name" string="Subject" required="1" select="1"/>
|
||||
<field name="date" required="1" select="1"/>
|
||||
<field name="user_id" string="Responsible" select="1"/>
|
||||
<field name="partner_id" readonly="1" />
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="Details">
|
||||
<group col="4" colspan="4">
|
||||
<separator string="Email Details" colspan="4"/>
|
||||
<field name="email_from" />
|
||||
<field name="email_to" />
|
||||
<field name="email_cc" />
|
||||
<field name="email_bcc" />
|
||||
<field name="message_id" />
|
||||
<page string="Details">
|
||||
<group col="2" colspan="2">
|
||||
<separator string="Email Details" colspan="4"/>
|
||||
<field name="email_from" />
|
||||
<field name="email_to" />
|
||||
<field name="email_cc" />
|
||||
<field name="email_bcc" groups="base.group_extended"/>
|
||||
</group>
|
||||
<group col="2" colspan="2">
|
||||
<separator string="Message Details" colspan="4"/>
|
||||
<field name="model" readonly="1"/>
|
||||
<field name="res_id" readonly="1"/>
|
||||
<field name="message_id" />
|
||||
<field name="ref_id" />
|
||||
</group>
|
||||
<separator string="Description" colspan="4"/>
|
||||
<separator string="Description" colspan="4"/>
|
||||
<field name="description" nolabel="1" colspan="4" />
|
||||
</page>
|
||||
<page string="Attachments">
|
||||
@@ -41,8 +48,9 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Mailgateway Message">
|
||||
<field name="date" />
|
||||
<field name="email_from" />
|
||||
<field name="user_id" />
|
||||
<field name="name" string="Subject"/>
|
||||
<field name="email_from" string="From"/>
|
||||
<field name="user_id" string="Responsible"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -53,7 +61,7 @@
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Mailgateway Message Search">
|
||||
<field name="name" />
|
||||
<field name="name" string="Subject"/>
|
||||
<field name="date" />
|
||||
<field name="user_id" />
|
||||
<field name="message_id" />
|
||||
@@ -68,28 +76,30 @@
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Mailgateway Thread">
|
||||
<field name="thread" select="1"/>
|
||||
<group col="6" colspan="4">
|
||||
<field name="thread" select="1" colspan="3"/>
|
||||
</group>
|
||||
<separator string="Logs" colspan="4"/>
|
||||
<field name="log_ids" nolabel="1" colspan="4" domain="[('history', '=', True)]">
|
||||
<tree string="Mailgateway Logs">
|
||||
<field name="name" select="1" />
|
||||
<field name="date" />
|
||||
</tree>
|
||||
<form string="Mailgate Logs">
|
||||
<field name="name" />
|
||||
<field name="date" />
|
||||
<field name="user_id" />
|
||||
<field name="message_id" />
|
||||
<notebook colspan="4">
|
||||
<page string="Email Details">
|
||||
<group col="4" colspan="4">
|
||||
<separator string="Email Details" colspan="4"/>
|
||||
<field name="email_from" />
|
||||
<field name="email_to" />
|
||||
<field name="email_cc" />
|
||||
<field name="email_bcc" />
|
||||
</group>
|
||||
<separator string="Description" colspan="4"/>
|
||||
<tree string="Mailgateway Logs">
|
||||
<field name="name" select="1" />
|
||||
<field name="date" />
|
||||
</tree>
|
||||
<form string="Mailgate Logs">
|
||||
<field name="name" />
|
||||
<field name="date" />
|
||||
<field name="user_id" />
|
||||
<field name="message_id" />
|
||||
<notebook colspan="4">
|
||||
<page string="Email Details">
|
||||
<group col="4" colspan="4">
|
||||
<separator string="Email Details" colspan="4"/>
|
||||
<field name="email_from" />
|
||||
<field name="email_to" />
|
||||
<field name="email_cc" />
|
||||
<field name="email_bcc" />
|
||||
</group>
|
||||
<separator string="Description" colspan="4"/>
|
||||
<field name="description" nolabel="1" colspan="4" />
|
||||
</page>
|
||||
<page string="Attachments">
|
||||
@@ -144,14 +154,47 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_view_mailgate_thread">
|
||||
<field name="name">Mailgateway Threads</field>
|
||||
<field name="res_model">mailgate.thread</field>
|
||||
<!-- Mailgateway message action-->
|
||||
<record model="ir.actions.act_window" id="action_view_mailgate_message">
|
||||
<field name="name">Mailgateway Messages</field>
|
||||
<field name="res_model">mailgate.message</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[('history', '=', True)]</field>
|
||||
<field name="view_id" ref="view_mailgate_message_tree"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_view_mailgate_thread">
|
||||
<field name="name">Mailgateway Threads</field>
|
||||
<field name="res_model">mailgate.thread</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="view_mailgate_thread_tree"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view1">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_mailgate_thread_tree"/>
|
||||
<field name="act_window_id" ref="action_view_mailgate_thread"/>
|
||||
</record>
|
||||
<record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view2">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_mailgate_thread_form"/>
|
||||
<field name="act_window_id" ref="action_view_mailgate_thread"/>
|
||||
</record>
|
||||
|
||||
<!-- Mailgateway message action-->
|
||||
<record model="ir.actions.act_window" id="action_view_mailgate_message">
|
||||
<field name="name">Mailgateway Messages</field>
|
||||
<field name="res_model">mailgate.message</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[('history', '=', True)]</field>
|
||||
<field name="view_id" ref="view_mailgate_thread_tree"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window.view" id="action_view_mailgate_thread_view1">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view_mode">tree</field>
|
||||
@@ -166,7 +209,7 @@
|
||||
</record>
|
||||
|
||||
<menuitem id="base.menu_base_config_mail" name="Mail" parent="base.menu_base_config" sequence="10"/>
|
||||
<menuitem id="menu_mailgate_thread" name="Mailgateway Threads" action="action_view_mailgate_thread"
|
||||
<menuitem id="menu_mailgate_message" name="Mailgateway Messages" action="action_view_mailgate_message"
|
||||
parent="base.menu_base_config_mail" sequence="5"/>
|
||||
|
||||
<act_window domain="[('partner_id', '=', active_id), ('history', '=', True)]"
|
||||
@@ -177,4 +220,4 @@
|
||||
/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
</openerp>
|
||||
|
||||
@@ -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
|
||||
## <peter@fry-it.com>
|
||||
## download here: http://www.peterbe.com/plog/html2plaintext
|
||||
|
||||
|
||||
""" from an HTML text, convert the HTML to plain text.
|
||||
If @body_id is provided then this is the tag where the
|
||||
body (not necessarily <body>) starts.
|
||||
"""
|
||||
try:
|
||||
from BeautifulSoup import BeautifulSoup, SoupStrainer, Comment
|
||||
except:
|
||||
return html
|
||||
|
||||
urls = []
|
||||
if body_id is not None:
|
||||
strainer = SoupStrainer(id=body_id)
|
||||
else:
|
||||
strainer = SoupStrainer('body')
|
||||
|
||||
soup = BeautifulSoup(html, parseOnlyThese=strainer, fromEncoding=encoding)
|
||||
for link in soup.findAll('a'):
|
||||
title = unicode(link)
|
||||
for url in [x[1] for x in link.attrs if x[0]=='href']:
|
||||
urls.append(dict(url=url, tag=unicode(link), title=title))
|
||||
|
||||
html = unicode(soup)
|
||||
|
||||
url_index = []
|
||||
i = 0
|
||||
for d in urls:
|
||||
if d['title'] == d['url'] or 'http://'+d['title'] == d['url']:
|
||||
html = html.replace(d['tag'], d['url'])
|
||||
else:
|
||||
i += 1
|
||||
html = html.replace(d['tag'], '%s [%s]' % (d['title'], i))
|
||||
url_index.append(d['url'])
|
||||
|
||||
html = html.replace('<strong>', '*').replace('</strong>', '*')
|
||||
html = html.replace('<b>', '*').replace('</b>', '*')
|
||||
html = html.replace('<h3>', '*').replace('</h3>', '*')
|
||||
html = html.replace('<h2>', '**').replace('</h2>', '**')
|
||||
html = html.replace('<h1>', '**').replace('</h1>', '**')
|
||||
html = html.replace('<em>', '/').replace('</em>', '/')
|
||||
|
||||
# the only line breaks we respect is those of ending tags and
|
||||
# breaks
|
||||
|
||||
html = html.replace('\n', ' ')
|
||||
html = html.replace('<br>', '\n')
|
||||
html = html.replace('<tr>', '\n')
|
||||
html = html.replace('</p>', '\n\n')
|
||||
html = re.sub('<br\s*/>', '\n', html)
|
||||
html = html.replace(' ' * 2, ' ')
|
||||
|
||||
html = re.sub('<.*?>', ' ', html)
|
||||
|
||||
# lstrip all lines
|
||||
html = '\n'.join([x.lstrip() for x in html.splitlines()])
|
||||
|
||||
for i, url in enumerate(url_index):
|
||||
if i == 0:
|
||||
html += '\n\n'
|
||||
html += '[%s] %s\n' % (i+1, url)
|
||||
return html
|
||||
import sys
|
||||
import xmlrpclib
|
||||
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)
|
||||
|
||||
|
||||
@@ -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': [],
|
||||
|
||||
@@ -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 :
|
||||
|
||||
@@ -451,7 +451,7 @@
|
||||
<field name="context">{'group_by': [], 'search_default_todo': 1}</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_action_marketing_campaign_workitem" parent="menu_marketing_campaign" action="action_marketing_campaign_workitem" sequence="30" groups="base.group_extended"/>
|
||||
<menuitem id="menu_action_marketing_campaign_workitem" parent="menu_marketing_campaign" action="action_marketing_campaign_workitem" sequence="30"/>
|
||||
|
||||
<act_window name="All Segments"
|
||||
res_model="marketing.campaign.segment"
|
||||
|
||||
@@ -31,6 +31,9 @@ from crm import crm
|
||||
from osv import fields,osv,orm
|
||||
from osv.orm import except_orm
|
||||
from tools.translate import _
|
||||
import collections
|
||||
import binascii
|
||||
import tools
|
||||
|
||||
class project_issue(osv.osv, crm.crm_case):
|
||||
_name = "project.issue"
|
||||
@@ -178,8 +181,8 @@ class project_issue(osv.osv, crm.crm_case):
|
||||
method=True, multi='working_days_open', type="float", store=True),
|
||||
'working_hours_close': fields.function(_compute_day, string='Working Hours to Close the Issue', \
|
||||
method=True, multi='working_days_close', type="float", store=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)]),
|
||||
}
|
||||
|
||||
def _get_project(self, cr, uid, context):
|
||||
@@ -299,5 +302,129 @@ class project_issue(osv.osv, crm.crm_case):
|
||||
self.write(cr, uid, [case.id], data)
|
||||
return True
|
||||
|
||||
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
|
||||
|
||||
project_issue()
|
||||
|
||||
|
||||
@@ -50,41 +50,41 @@
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Sales Analysis">
|
||||
<group>
|
||||
<filter icon="terp-go-year" string=" 365 Days "
|
||||
domain="[('date','<=', time.strftime('%%Y-%%m-%%d')),('date','>',(datetime.date.today()-datetime.timedelta(days=365)).strftime('%%Y-%%m-%%d'))]"
|
||||
help="Tasks performed in last 365 days"/>
|
||||
<filter icon="terp-go-month" string=" 30 Days "
|
||||
name="month"
|
||||
domain="[('date','<=', time.strftime('%%Y-%%m-%%d')), ('date','>',(datetime.date.today()-datetime.timedelta(days=30)).strftime('%%Y-%%m-%%d'))]"
|
||||
help="Tasks performed in last 30 days"/>
|
||||
<filter icon="terp-go-week"
|
||||
string=" 7 Days "
|
||||
separator="1"
|
||||
domain="[('date','<=', time.strftime('%%Y-%%m-%%d')), ('date','>',(datetime.date.today()-datetime.timedelta(days=7)).strftime('%%Y-%%m-%%d'))]"
|
||||
help="Tasks during last 7 days"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-document-new"
|
||||
string="Quotations"
|
||||
domain="[('state','=','draft')]"/>
|
||||
<filter icon="terp-dolar"
|
||||
string="Sales"
|
||||
domain="[('state','not in',('draft','done','cancel'))]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-sale"
|
||||
string="Picked"
|
||||
domain="[('shipped','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="partner_id"/>
|
||||
<group>
|
||||
<filter icon="terp-go-year" string=" 365 Days "
|
||||
domain="[('date','<=', time.strftime('%%Y-%%m-%%d')),('date','>',(datetime.date.today()-datetime.timedelta(days=365)).strftime('%%Y-%%m-%%d'))]"
|
||||
help="Tasks performed in last 365 days"/>
|
||||
<filter icon="terp-go-month" string=" 30 Days "
|
||||
name="month"
|
||||
domain="[('date','<=', time.strftime('%%Y-%%m-%%d')), ('date','>',(datetime.date.today()-datetime.timedelta(days=30)).strftime('%%Y-%%m-%%d'))]"
|
||||
help="Tasks performed in last 30 days"/>
|
||||
<filter icon="terp-go-week"
|
||||
string=" 7 Days "
|
||||
separator="1"
|
||||
domain="[('date','<=', time.strftime('%%Y-%%m-%%d')), ('date','>',(datetime.date.today()-datetime.timedelta(days=7)).strftime('%%Y-%%m-%%d'))]"
|
||||
help="Tasks during last 7 days"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-document-new"
|
||||
string="Quotations"
|
||||
domain="[('state','=','draft')]"/>
|
||||
<filter icon="terp-dolar"
|
||||
string="Sales"
|
||||
domain="[('state','not in',('draft','done','cancel'))]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-sale"
|
||||
string="Picked"
|
||||
domain="[('shipped','=',True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="product_id"/>
|
||||
<field name="user_id" widget="selection">
|
||||
<filter icon="terp-personal"
|
||||
<field name="user_id" widget="selection">
|
||||
<filter icon="terp-personal"
|
||||
string="My Sales"
|
||||
help="My Sales"
|
||||
domain="[('user_id','=',uid)]"/>
|
||||
</field>
|
||||
</group>
|
||||
<newline/>
|
||||
</field>
|
||||
</group>
|
||||
<newline/>
|
||||
<group expand="1" string="Group By..." colspan="10" col="12">
|
||||
<filter string="Salesman" icon="terp-personal" name="User" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Partner" icon="terp-personal" context="{'group_by':'partner_id'}"/>
|
||||
@@ -103,7 +103,7 @@
|
||||
<filter string="Month" icon="terp-go-month" context="{'group_by':'month'}"/>
|
||||
<filter string="Year" icon="terp-go-year" context="{'group_by':'year'}"/>
|
||||
</group>
|
||||
<newline/>
|
||||
<newline/>
|
||||
<group expand="0" string="Extended options..." groups="base.group_extended">
|
||||
<field name="date"/>
|
||||
<field name="date_confirm"/>
|
||||
@@ -171,7 +171,7 @@
|
||||
<field name="domain">[('state','in',('manual','progress')),('date','<=', time.strftime('%Y-%m-%d')),('date','>',(datetime.date.today()-datetime.timedelta(days=90)).strftime('%Y-%m-%d'))]</field>
|
||||
<field name="view_id" ref="view_sales_by_partner_graph"/>
|
||||
</record>
|
||||
<record id="open_view_sales_by_partner_graph" model="ir.actions.act_window.view">
|
||||
<record id="open_view_sales_by_partner_graph" model="ir.actions.act_window.view">
|
||||
<field eval="1" name="sequence"/>
|
||||
<field name="view_mode">graph</field>
|
||||
<field name="view_id" ref="view_sales_by_partner_graph"/>
|
||||
@@ -233,7 +233,7 @@
|
||||
<field name="model">sale.report</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Sold products and Total Price">
|
||||
<tree string="Sales by Products">
|
||||
<field name="product_id"/>
|
||||
<field name="price_total"/>
|
||||
</tree>
|
||||
@@ -245,7 +245,7 @@
|
||||
<field name="model">sale.report</field>
|
||||
<field name="type">graph</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Sold products and Total Price" type="bar">
|
||||
<graph string="Sales by Products" type="bar">
|
||||
<field name="product_id" />
|
||||
<field name="price_total" operator="+"/>
|
||||
</graph>
|
||||
@@ -253,11 +253,11 @@
|
||||
</record>
|
||||
|
||||
<record id="action_sales_product_total_price" model="ir.actions.act_window">
|
||||
<field name="name">Sold product and Total Price</field>
|
||||
<field name="name">Sales by Products</field>
|
||||
<field name="res_model">sale.report</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">graph,tree</field>
|
||||
<field name="domain">[('state','in',('manual','progress','done')),('date','<=', time.strftime('%Y-%m-%d')),('date','>',(datetime.date.today()-datetime.timedelta(days=90)).strftime('%Y-%m-%d'))]</field>
|
||||
<field name="domain">[('state','in',('manual','progress','done')),('date','>',(datetime.date.today()-datetime.timedelta(days=90)).strftime('%Y-%m-%d'))]</field>
|
||||
<field name="view_id" ref="view_sales_product_total_price_graph"/>
|
||||
</record>
|
||||
<record id="open_view_sales_product_total_price_graph" model="ir.actions.act_window.view">
|
||||
@@ -273,29 +273,29 @@
|
||||
<field name="act_window_id" ref="action_sales_product_total_price"/>
|
||||
</record>
|
||||
|
||||
<record id="view_sales_by_month_tree" model="ir.ui.view">
|
||||
<field name="name">view.sales.by.month.tree</field>
|
||||
<field name="model">sale.report</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Total Sales By Month">
|
||||
<field name="month" />
|
||||
<field name="price_total" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_view_sales_by_month" model="ir.actions.act_window">
|
||||
<field name="name">Sales By Month</field>
|
||||
<field name="res_model">sale.report</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[('state','in',('manual','progress','done')),('year','=', time.strftime('%Y'))]</field>
|
||||
<field name="context">{'group_by_no_leaf':1,'group_by':['month']}</field>
|
||||
<field name="view_id" ref="view_sales_by_month_tree"/>
|
||||
<field name="search_view_id" ref="view_order_product_search"/>
|
||||
</record>
|
||||
<record id="view_sales_by_month_tree" model="ir.ui.view">
|
||||
<field name="name">view.sales.by.month.tree</field>
|
||||
<field name="model">sale.report</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Sales By Month">
|
||||
<field name="month" />
|
||||
<field name="product_id" />
|
||||
<field name="price_total" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_view_sales_by_month" model="ir.actions.act_window">
|
||||
<field name="name">Sales By Month</field>
|
||||
<field name="res_model">sale.report</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,graph</field>
|
||||
<field name="domain">[('state','in',('manual','progress','done')),('year','=', time.strftime('%Y'))]</field>
|
||||
<field name="context">{'group_by_no_leaf':1,'group_by':['month','product_id']}</field>
|
||||
<field name="view_id" ref="view_sales_by_month_tree"/>
|
||||
</record>
|
||||
|
||||
<record id="view_top_quotations_tree" model="ir.ui.view">
|
||||
<record id="view_top_quotations_tree" model="ir.ui.view">
|
||||
<field name="name">sale.top.quotations.tree</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="type">tree</field>
|
||||
@@ -317,7 +317,7 @@
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('state','=','draft')]</field>
|
||||
<field name="view_id" ref="view_top_quotations_tree"/>
|
||||
<field name="view_id" ref="view_top_quotations_tree"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -1135,7 +1135,7 @@
|
||||
</record>
|
||||
|
||||
<record id="action_picking_tree" model="ir.actions.act_window">
|
||||
<field name="name">Outgoing Packings</field>
|
||||
<field name="name">Outgoing Orders</field>
|
||||
<field name="res_model">stock.picking</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_type">form</field>
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
<xpath expr="//label[@string='description']" position="attributes">
|
||||
<attribute name="string">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.</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//button[@string='Install Modules']" position="attributes">
|
||||
<attribute name="string">Configure</attribute>
|
||||
</xpath>
|
||||
<xpath expr='//separator[@string="vsep"]' position='attributes'>
|
||||
<attribute name='string'></attribute>
|
||||
<attribute name='rowspan'>15</attribute>
|
||||
|
||||
Reference in New Issue
Block a user