[MPERGE] Sync with trunk.

bzr revid: tde@openerp.com-20121221160130-x07kk2mscu5zo3ta
This commit is contained in:
Thibault Delavallée 2012-12-21 17:01:30 +01:00
commit 6e8d8a0863
20 changed files with 504 additions and 530 deletions

View File

@ -599,12 +599,11 @@ class account_voucher(osv.osv):
This function returns True if the line is considered as noise and should not be displayed
"""
if line.reconcile_partial_id:
sign = 1 if ttype == 'receipt' else -1
if currency_id == line.currency_id.id:
if line.amount_residual_currency * sign <= 0:
if line.amount_residual_currency <= 0:
return True
else:
if line.amount_residual * sign <= 0:
if line.amount_residual <= 0:
return True
return False

View File

@ -20,5 +20,6 @@
##############################################################################
import base_action_rule
import test_models
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -19,27 +19,30 @@
#
##############################################################################
from datetime import datetime
from datetime import timedelta
import re
from datetime import datetime, timedelta
import time
import logging
from openerp.osv import fields, osv, orm
from openerp.tools.translate import _
from openerp.tools.safe_eval import safe_eval
from openerp.tools import ustr
from openerp import pooler
from openerp import tools
from openerp import SUPERUSER_ID
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
_logger = logging.getLogger(__name__)
def get_datetime(date_field):
DATE_RANGE_FUNCTION = {
'minutes': lambda interval: timedelta(minutes=interval),
'hour': lambda interval: timedelta(hours=interval),
'day': lambda interval: timedelta(days=interval),
'month': lambda interval: timedelta(months=interval),
False: lambda interval: timedelta(0),
}
def get_datetime(date_str):
'''Return a datetime from a date string or a datetime string'''
#complete date time if date_field contains only a date
date_split = date_field.split(' ')
if len(date_split) == 1:
date_field = date_split[0] + " 00:00:00"
return datetime.strptime(date_field[:19], '%Y-%m-%d %H:%M:%S')
# complete date time if date_str contains only a date
if ' ' not in date_str:
date_str = date_str + " 00:00:00"
return datetime.strptime(date_str, DEFAULT_SERVER_DATETIME_FORMAT)
class base_action_rule(osv.osv):
@ -48,320 +51,208 @@ class base_action_rule(osv.osv):
_name = 'base.action.rule'
_description = 'Action Rules'
def _state_get(self, cr, uid, context=None):
""" Get State """
return self.state_get(cr, uid, context=context)
def state_get(self, cr, uid, context=None):
""" Get State """
return [('', '')]
def priority_get(self, cr, uid, context=None):
""" Get Priority """
return [('', '')]
_columns = {
'name': fields.char('Rule Name', size=64, required=True),
'model_id': fields.many2one('ir.model', 'Related Document Model', required=True, domain=[('osv_memory','=', False)]),
'model_id': fields.many2one('ir.model', 'Related Document Model',
required=True, domain=[('osv_memory', '=', False)]),
'model': fields.related('model_id', 'model', type="char", size=256, string='Model'),
'create_date': fields.datetime('Create Date', readonly=1),
'active': fields.boolean('Active', help="If the active field is set to False,\
it will allow you to hide the rule without removing it."),
'sequence': fields.integer('Sequence', help="Gives the sequence order \
when displaying a list of rules."),
'trg_date_type': fields.selection([
('none', 'None'),
('create', 'Creation Date'),
('write', 'Last Modified Date'),
('action_last', 'Last Action Date'),
('date', 'Date'),
('deadline', 'Deadline'),
], 'Trigger Date', size=16),
'trg_date_range': fields.integer('Delay after trigger date', \
help="Delay After Trigger Date,\
specifies you can put a negative number. If you need a delay before the \
trigger date, like sending a reminder 15 minutes before a meeting."),
'trg_date_range_type': fields.selection([('minutes', 'Minutes'), ('hour', 'Hours'), \
'active': fields.boolean('Active',
help="When unchecked, the rule is hidden and will not be executed."),
'sequence': fields.integer('Sequence',
help="Gives the sequence order when displaying a list of rules."),
'trg_date_id': fields.many2one('ir.model.fields', string='Trigger Date',
domain="[('model_id', '=', model_id), ('ttype', 'in', ('date', 'datetime'))]"),
'trg_date_range': fields.integer('Delay after trigger date',
help="Delay after the trigger date." \
"You can put a negative number if you need a delay before the" \
"trigger date, like sending a reminder 15 minutes before a meeting."),
'trg_date_range_type': fields.selection([('minutes', 'Minutes'), ('hour', 'Hours'),
('day', 'Days'), ('month', 'Months')], 'Delay type'),
'trg_user_id': fields.many2one('res.users', 'Responsible'),
'trg_partner_id': fields.many2one('res.partner', 'Partner'),
'trg_partner_categ_id': fields.many2one('res.partner.category', 'Partner Category'),
'trg_state_from': fields.selection(_state_get, 'Status', size=16),
'trg_state_to': fields.selection(_state_get, 'Button Pressed', size=16),
'act_user_id': fields.many2one('res.users', 'Set Responsible to'),
'act_state': fields.selection(_state_get, 'Set State to', size=16),
'act_followers': fields.many2many("res.partner", string="Set Followers"),
'regex_name': fields.char('Regex on Resource Name', size=128, help="Regular expression for matching name of the resource\
\ne.g.: 'urgent.*' will search for records having name starting with the string 'urgent'\
\nNote: This is case sensitive search."),
'server_action_ids': fields.one2many('ir.actions.server', 'action_rule_id', 'Server Action', help="Define Server actions.\neg:Email Reminders, Call Object Service, etc.."), #TODO: set domain [('model_id','=',model_id)]
'filter_id':fields.many2one('ir.filters', 'Filter', required=False), #TODO: set domain [('model_id','=',model_id.model)]
'act_user_id': fields.many2one('res.users', 'Set Responsible'),
'act_followers': fields.many2many("res.partner", string="Add Followers"),
'server_action_ids': fields.many2many('ir.actions.server', string='Server Actions',
domain="[('model_id', '=', model_id)]",
help="Examples: email reminders, call object service, etc."),
'filter_pre_id': fields.many2one('ir.filters', string='Before Update Filter',
ondelete='restrict',
domain="[('model_id', '=', model_id.model)]",
help="If present, this condition must be satisfied before the update of the record."),
'filter_id': fields.many2one('ir.filters', string='After Update Filter',
ondelete='restrict',
domain="[('model_id', '=', model_id.model)]",
help="If present, this condition must be satisfied after the update of the record."),
'last_run': fields.datetime('Last Run', readonly=1),
}
_defaults = {
'active': True,
'trg_date_type': 'none',
'trg_date_range_type': 'day',
}
_order = 'sequence'
def _filter(self, cr, uid, action, action_filter, record_ids, context=None):
""" filter the list record_ids that satisfy the action filter """
if record_ids and action_filter:
assert action.model == action_filter.model_id, "Filter model different from action rule model"
model = self.pool.get(action_filter.model_id)
domain = [('id', 'in', record_ids)] + eval(action_filter.domain)
ctx = dict(context or {})
ctx.update(eval(action_filter.context))
record_ids = model.search(cr, uid, domain, context=ctx)
return record_ids
def _process(self, cr, uid, action, record_ids, context=None):
""" process the given action on the records """
# execute server actions
model = self.pool.get(action.model_id.model)
if action.server_action_ids:
server_action_ids = map(int, action.server_action_ids)
for record in model.browse(cr, uid, record_ids, context):
action_server_obj = self.pool.get('ir.actions.server')
ctx = dict(context, active_model=model._name, active_ids=[record.id], active_id=record.id)
action_server_obj.run(cr, uid, server_action_ids, context=ctx)
# modify records
values = {}
if 'date_action_last' in model._all_columns:
values['date_action_last'] = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
if action.act_user_id and 'user_id' in model._all_columns:
values['user_id'] = action.act_user_id.id
if values:
model.write(cr, uid, record_ids, values, context=context)
if action.act_followers and hasattr(model, 'message_subscribe'):
follower_ids = map(int, action.act_followers)
model.message_subscribe(cr, uid, record_ids, follower_ids, context=context)
def post_action(self, cr, uid, ids, model, context=None):
# Searching for action rules
cr.execute("SELECT model.model, rule.id FROM base_action_rule rule \
LEFT JOIN ir_model model on (model.id = rule.model_id) \
WHERE active and model = %s", (model,))
res = cr.fetchall()
# Check if any rule matching with current object
for obj_name, rule_id in res:
obj = self.pool.get(obj_name)
# If the rule doesn't involve a time condition, run it immediately
# Otherwise we let the scheduler run the action
if self.browse(cr, uid, rule_id, context=context).trg_date_type == 'none':
self._action(cr, uid, [rule_id], obj.browse(cr, uid, ids, context=context), context=context)
return True
def _create(self, old_create, model, context=None):
def _wrap_create(self, old_create, model):
""" Return a wrapper around `old_create` calling both `old_create` and
`_process`, in that order.
"""
Return a wrapper around `old_create` calling both `old_create` and
`post_action`, in that order.
"""
def wrapper(cr, uid, vals, context=context):
if context is None:
context = {}
def wrapper(cr, uid, vals, context=None):
# avoid loops or cascading actions
if context and context.get('action'):
return old_create(cr, uid, vals, context=context)
context = dict(context or {}, action=True)
new_id = old_create(cr, uid, vals, context=context)
if not context.get('action'):
self.post_action(cr, uid, [new_id], model, context=context)
# as it is a new record, we do not consider the actions that have a prefilter
action_dom = [('model', '=', model), ('trg_date_id', '=', False), ('filter_pre_id', '=', False)]
action_ids = self.search(cr, uid, action_dom, context=context)
# check postconditions, and execute actions on the records that satisfy them
for action in self.browse(cr, uid, action_ids, context=context):
if self._filter(cr, uid, action, action.filter_id, [new_id], context=context):
self._process(cr, uid, action, [new_id], context=context)
return new_id
return wrapper
def _write(self, old_write, model, context=None):
def _wrap_write(self, old_write, model):
""" Return a wrapper around `old_write` calling both `old_write` and
`_process`, in that order.
"""
Return a wrapper around `old_write` calling both `old_write` and
`post_action`, in that order.
"""
def wrapper(cr, uid, ids, vals, context=context):
if context is None:
context = {}
if isinstance(ids, (str, int, long)):
ids = [ids]
def wrapper(cr, uid, ids, vals, context=None):
# avoid loops or cascading actions
if context and context.get('action'):
return old_write(cr, uid, ids, vals, context=context)
context = dict(context or {}, action=True)
ids = [ids] if isinstance(ids, (int, long, str)) else ids
# retrieve the action rules to possibly execute
action_dom = [('model', '=', model), ('trg_date_id', '=', False)]
action_ids = self.search(cr, uid, action_dom, context=context)
actions = self.browse(cr, uid, action_ids, context=context)
# check preconditions
pre_ids = {}
for action in actions:
pre_ids[action] = self._filter(cr, uid, action, action.filter_pre_id, ids, context=context)
# execute write
old_write(cr, uid, ids, vals, context=context)
if not context.get('action'):
self.post_action(cr, uid, ids, model, context=context)
# check postconditions, and execute actions on the records that satisfy them
for action in actions:
post_ids = self._filter(cr, uid, action, action.filter_id, pre_ids[action], context=context)
if post_ids:
self._process(cr, uid, action, post_ids, context=context)
return True
return wrapper
def _register_hook(self, cr, uid, ids, context=None):
def _register_hook(self, cr, ids=None):
""" Wrap the methods `create` and `write` of the models specified by
the rules given by `ids` (or all existing rules if `ids` is `None`.)
"""
Wrap every `create` and `write` methods of the models specified by
the rules (given by `ids`).
"""
for action_rule in self.browse(cr, uid, ids, context=context):
if ids is None:
ids = self.search(cr, SUPERUSER_ID, [])
for action_rule in self.browse(cr, SUPERUSER_ID, ids):
model = action_rule.model_id.model
obj_pool = self.pool.get(model)
if not hasattr(obj_pool, 'base_action_ruled'):
obj_pool.create = self._create(obj_pool.create, model, context=context)
obj_pool.write = self._write(obj_pool.write, model, context=context)
obj_pool.base_action_ruled = True
model_obj = self.pool.get(model)
if not hasattr(model_obj, 'base_action_ruled'):
model_obj.create = self._wrap_create(model_obj.create, model)
model_obj.write = self._wrap_write(model_obj.write, model)
model_obj.base_action_ruled = True
return True
def create(self, cr, uid, vals, context=None):
res_id = super(base_action_rule, self).create(cr, uid, vals, context=context)
self._register_hook(cr, uid, [res_id], context=context)
self._register_hook(cr, [res_id])
return res_id
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
super(base_action_rule, self).write(cr, uid, ids, vals, context=context)
self._register_hook(cr, uid, ids, context=context)
self._register_hook(cr, ids)
return True
def _check(self, cr, uid, automatic=False, use_new_cursor=False, \
context=None):
"""
This Function is call by scheduler.
"""
rule_ids = self.search(cr, uid, [], context=context)
self._register_hook(cr, uid, rule_ids, context=context)
if context is None:
context = {}
for rule in self.browse(cr, uid, rule_ids, context=context):
model = rule.model_id.model
model_pool = self.pool.get(model)
last_run = False
if rule.last_run:
last_run = get_datetime(rule.last_run)
def _check(self, cr, uid, automatic=False, use_new_cursor=False, context=None):
""" This Function is called by scheduler. """
context = context or {}
# retrieve all the action rules that have a trg_date_id and no precondition
action_dom = [('trg_date_id', '!=', False), ('filter_pre_id', '=', False)]
action_ids = self.search(cr, uid, action_dom, context=context)
for action in self.browse(cr, uid, action_ids, context=context):
now = datetime.now()
ctx = dict(context)
if rule.filter_id and rule.model_id.model == rule.filter_id.model_id:
ctx.update(eval(rule.filter_id.context))
obj_ids = model_pool.search(cr, uid, eval(rule.filter_id.domain), context=ctx)
else:
obj_ids = model_pool.search(cr, uid, [], context=ctx)
for obj in model_pool.browse(cr, uid, obj_ids, context=ctx):
# Calculate when this action should next occur for this object
base = False
if rule.trg_date_type=='create' and hasattr(obj, 'create_date'):
base = obj.create_date
elif rule.trg_date_type=='write' and hasattr(obj, 'write_date'):
base = obj.write_date
elif (rule.trg_date_type=='action_last'
and hasattr(obj, 'create_date')):
if hasattr(obj, 'date_action_last') and obj.date_action_last:
base = obj.date_action_last
else:
base = obj.create_date
elif (rule.trg_date_type=='deadline'
and hasattr(obj, 'date_deadline')
and obj.date_deadline):
base = obj.date_deadline
elif (rule.trg_date_type=='date'
and hasattr(obj, 'date')
and obj.date):
base = obj.date
if base:
fnct = {
'minutes': lambda interval: timedelta(minutes=interval),
'day': lambda interval: timedelta(days=interval),
'hour': lambda interval: timedelta(hours=interval),
'month': lambda interval: timedelta(months=interval),
}
base = get_datetime(base)
delay = fnct[rule.trg_date_range_type](rule.trg_date_range)
action_date = base + delay
if (not last_run or (last_run <= action_date < now)):
try:
self._action(cr, uid, [rule.id], obj, context=ctx)
self.write(cr, uid, [rule.id], {'last_run': now}, context=context)
except Exception, e:
import traceback
print traceback.format_exc()
last_run = get_datetime(action.last_run) if action.last_run else False
def do_check(self, cr, uid, action, obj, context=None):
""" check Action """
if context is None:
context = {}
ok = True
if action.filter_id and action.model_id.model == action.filter_id.model_id:
# retrieve all the records that satisfy the action's condition
model = self.pool.get(action.model_id.model)
domain = []
ctx = dict(context)
ctx.update(eval(action.filter_id.context))
obj_ids = self.pool.get(action.model_id.model).search(cr, uid, eval(action.filter_id.domain), context=ctx)
ok = ok and obj.id in obj_ids
if getattr(obj, 'user_id', False):
ok = ok and (not action.trg_user_id.id or action.trg_user_id.id==obj.user_id.id)
if getattr(obj, 'partner_id', False):
ok = ok and (not action.trg_partner_id.id or action.trg_partner_id.id==obj.partner_id.id)
ok = ok and (
not action.trg_partner_categ_id.id or
(
obj.partner_id.id and
(action.trg_partner_categ_id.id in map(lambda x: x.id, obj.partner_id.category_id or []))
)
)
state_to = context.get('state_to', False)
state = getattr(obj, 'state', False)
if state:
ok = ok and (not action.trg_state_from or action.trg_state_from==state)
if state_to:
ok = ok and (not action.trg_state_to or action.trg_state_to==state_to)
elif action.trg_state_to:
ok = False
reg_name = action.regex_name
result_name = True
if reg_name:
ptrn = re.compile(ustr(reg_name))
_result = ptrn.search(ustr(obj.name))
if not _result:
result_name = False
regex_n = not reg_name or result_name
ok = ok and regex_n
return ok
if action.filter_id:
domain = eval(action.filter_id.domain)
ctx.update(eval(action.filter_id.context))
record_ids = model.search(cr, uid, domain, context=ctx)
def do_action(self, cr, uid, action, obj, context=None):
""" Do Action """
if context is None:
context = {}
ctx = dict(context)
model_obj = self.pool.get(action.model_id.model)
action_server_obj = self.pool.get('ir.actions.server')
if action.server_action_ids:
ctx.update({'active_model': action.model_id.model, 'active_id':obj.id, 'active_ids':[obj.id]})
action_server_obj.run(cr, uid, [x.id for x in action.server_action_ids], context=ctx)
# determine when action should occur for the records
date_field = action.trg_date_id.name
if date_field == 'date_action_last' and 'create_date' in model._all_columns:
get_record_dt = lambda record: record[date_field] or record.create_date
else:
get_record_dt = lambda record: record[date_field]
write = {}
if hasattr(obj, 'user_id') and action.act_user_id:
write['user_id'] = action.act_user_id.id
if hasattr(obj, 'date_action_last'):
write['date_action_last'] = time.strftime('%Y-%m-%d %H:%M:%S')
if hasattr(obj, 'state') and action.act_state:
write['state'] = action.act_state
delay = DATE_RANGE_FUNCTION[action.trg_date_range_type](action.trg_date_range)
model_obj.write(cr, uid, [obj.id], write, context)
if hasattr(obj, 'state') and hasattr(obj, 'message_post') and action.act_state:
model_obj.message_post(cr, uid, [obj], _(action.act_state), context=context)
if hasattr(obj, 'message_subscribe') and action.act_followers:
exits_followers = [x.id for x in obj.message_follower_ids]
new_followers = [x.id for x in action.act_followers if x.id not in exits_followers]
if new_followers:
model_obj.message_subscribe(cr, uid, [obj.id], new_followers, context=context)
return True
# process action on the records that should be executed
for record in model.browse(cr, uid, record_ids, context=context):
record_dt = get_record_dt(record)
if not record_dt:
continue
action_dt = get_datetime(record_dt) + delay
if last_run and (last_run <= action_dt < now) or (action_dt < now):
try:
self._process(cr, uid, action, [record.id], context=context)
except Exception:
import traceback
_logger.error(traceback.format_exc())
def _action(self, cr, uid, ids, objects, scrit=None, context=None):
""" Do Action """
if context is None:
context = {}
context.update({'action': True})
if not scrit:
scrit = []
if not isinstance(objects, list):
objects = [objects]
for action in self.browse(cr, uid, ids, context=context):
for obj in objects:
if self.do_check(cr, uid, action, obj, context=context):
self.do_action(cr, uid, action, obj, context=context)
context.update({'action': False})
return True
base_action_rule()
class actions_server(osv.osv):
_inherit = 'ir.actions.server'
_columns = {
'action_rule_id': fields.many2one("base.action.rule", string="Action Rule")
}
actions_server()
class ir_cron(osv.osv):
_inherit = 'ir.cron'
_init_done = False
def _poolJobs(self, db_name, check=False):
if not self._init_done:
self._init_done = True
try:
db = pooler.get_db(db_name)
except:
return False
cr = db.cursor()
try:
next = datetime.now().strftime('%Y-%m-%d %H:00:00')
# Putting nextcall always less than current time in order to call it every time
cr.execute('UPDATE ir_cron set nextcall = \'%s\' where numbercall<>0 and active and model=\'base.action.rule\' ' % (next))
finally:
cr.commit()
cr.close()
super(ir_cron, self)._poolJobs(db_name, check=check)
ir_cron()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
action.write({'last_run': now.strftime(DEFAULT_SERVER_DATETIME_FORMAT)})

View File

@ -4,57 +4,55 @@
<menuitem id="base.menu_base_action_rule_admin" name="Automated Actions"
parent="base.menu_custom" />
<!--
Action Rule Form View
-->
<record id="view_base_action_rule_form" model="ir.ui.view">
<field name="name">base.action.rule.form</field>
<!-- Action Rule Form View -->
<record id="view_base_action_rule_form" model="ir.ui.view">
<field name="name">base.action.rule.form</field>
<field name="model">base.action.rule</field>
<field name="arch" type="xml">
<form string="Action Rule" version="7.0">
<sheet>
<group col="4">
<field name="name"/>
<field name="model_id"/>
<field name="filter_id" domain="[('model_id','=',model), ('user_id', '=', False)]" context="{'default_model_id': model}"/>
<field name="sequence"/>
<field name="active"/>
<field name="model" invisible="1"/>
<label for="name" class="oe_edit_only"/>
<h1><field name="name"/></h1>
<group>
<group>
<field name="model_id"/>
<field name="model" invisible="1"/>
</group>
<group>
<field name="active"/>
<field name="sequence"/>
</group>
</group>
<notebook>
<page string="Conditions">
<group>
<group name="model" string="Conditions on Model Fields">
<field name="regex_name"/>
<field name="trg_user_id"/>
<group name="filter" string="Filter Condition">
<field name="filter_pre_id" domain="[('model_id','=',model), ('user_id', '=', False)]" context="{'default_model_id': model}"/>
<field name="filter_id" domain="[('model_id','=',model), ('user_id', '=', False)]" context="{'default_model_id': model}"/>
</group>
<group name="partner" string="Conditions on Model Partner">
<field name="trg_partner_id"/>
<field name="trg_partner_categ_id"/>
</group>
<group name="state" string="Conditions on Status">
<field name="trg_state_from"/>
<field name="trg_state_to"/>
</group>
<group name="timing" string="Conditions on Timing">
<field name="trg_date_type"/>
<field name="trg_date_range" string="Delay After Trigger Date" attrs="{'invisible': [('trg_date_type', '=', 'none')]}"/>
<field name="trg_date_range_type" attrs="{'invisible': [('trg_date_type', '=', 'none')]}"/>
<group name="timing" string="Timer">
<field name="trg_date_id"/>
<label for="trg_date_range" string="Delay After Trigger Date" attrs="{'invisible': [('trg_date_id','=',False)]}"/>
<div attrs="{'invisible': [('trg_date_id','=',False)]}">
<field name="trg_date_range" class="oe_inline"/>
<field name="trg_date_range_type" class="oe_inline" attrs="{'required': [('trg_date_id','!=',False)]}"/>
</div>
</group>
</group>
<group string="Note">
<label string="The rule uses the AND operator. The model must match all non-empty fields so that the rule executes the action described in the 'Actions' tab." />
</group>
<p class="oe_grey">
<b>Select a filter or a timer as condition.</b><br/> An action rule is checked when you create or modify the "Related Document Model". The precondition filter is checked right before the modification while the postcondition filter is checked after the modification. A precondition filter will therefore not work during a creation.<br/>
<b>To create a new filter:</b><br/>
- Go to your "Related Document Model" page and set the filter parameters in the "Search" view (Example of filter based on Leads/Opportunities: Creation Date "is equal to" 01/01/2012)<br/>
- In this same "Search" view, select the menu "Save Current Filter", enter the name (Ex: Create the 01/01/2012) and add the option "Share with all users"<br/>
The filter must therefore be available in this page.
</p>
</page>
<page string="Actions">
<group name="action_followers">
<field name="act_followers" widget="many2many_tags"/>
</group>
<group name="action_field" col="4" string="Fields to Change">
<field name="act_user_id"/>
<field name="act_state"/>
<field name="act_followers" widget="many2many_tags"/>
</group>
<group name="action_server" string="Server Actions to be Triggered (eg. Email Reminder, Call Object Method, etc...)" >
<group name="action_server" string="Server actions to run">
<field name="server_action_ids" domain="[('model_id','=', model_id)]" nolabel="1" context="{'default_model_id': model_id}">
<tree string="Server Actions">
<field name="sequence"/>
@ -63,30 +61,27 @@
</tree>
</field>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
</record>
<!-- Action Rule Tree View -->
<record id="view_base_action_rule_tree" model="ir.ui.view">
<field name="name">base.action.rule.tree</field>
<!-- Action Rule Tree View -->
<record id="view_base_action_rule_tree" model="ir.ui.view">
<field name="name">base.action.rule.tree</field>
<field name="model">base.action.rule</field>
<field name="arch" type="xml">
<tree string="Action Rule">
<field name="sequence"/>
<field name="name" colspan="4"/>
<field name="name"/>
<field name="filter_id"/>
</tree>
</field>
</record>
<!-- Action Rule Action -->
</record>
<!-- Action Rule Action -->
<record id="base_action_rule_act" model="ir.actions.act_window">
<field name="name">Automated Actions</field>
<field name="res_model">base.action.rule</field>
@ -109,6 +104,5 @@
<menuitem id="menu_base_action_rule_form"
parent="base.menu_base_action_rule_admin" action="base_action_rule_act" sequence="1"/>
</data>
</openerp>

View File

@ -0,0 +1,32 @@
from osv import osv, fields
AVAILABLE_STATES = [
('draft', 'New'),
('cancel', 'Cancelled'),
('open', 'In Progress'),
('pending', 'Pending'),
('done', 'Closed')
]
class lead_test(osv.Model):
_name = "base.action.rule.lead.test"
_columns = {
'name': fields.char('Subject', size=64, required=True, select=1),
'user_id': fields.many2one('res.users', 'Responsible'),
'state': fields.selection(AVAILABLE_STATES, string="Status", readonly=True),
'active': fields.boolean('Active', required=False),
'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null'),
'date_action_last': fields.datetime('Last Action', readonly=1),
}
_defaults = {
'state' : 'draft',
'active' : True,
}
def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification', subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
pass
def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None):
pass

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
#
# 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/>.
#
##############################################################################
from . import base_action_rule_test
checks = [
base_action_rule_test,
]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,146 @@
from openerp import SUPERUSER_ID
from openerp.tests import common
from .. import test_models
class base_action_rule_test(common.TransactionCase):
def setUp(self):
"""*****setUp*****"""
super(base_action_rule_test, self).setUp()
cr, uid = self.cr, self.uid
self.demo = self.registry('ir.model.data').get_object(cr, uid, 'base', 'user_demo').id
self.admin = SUPERUSER_ID
self.model = self.registry('base.action.rule.lead.test')
self.base_action_rule = self.registry('base.action.rule')
def create_filter_done(self, cr, uid, context=None):
filter_pool = self.registry('ir.filters')
return filter_pool.create(cr, uid, {
'name': "Lead is in done state",
'is_default': False,
'model_id': 'base.action.rule.lead.test',
'domain' : "[('state','=','done')]",
}, context=context)
def create_filter_draft(self, cr, uid, context=None):
filter_pool = self.registry('ir.filters')
return filter_pool.create(cr, uid, {
'name': "Lead is in draft state",
'is_default': False,
'model_id': "base.action.rule.lead.test",
'domain' : "[('state','=','draft')]",
}, context=context)
def create_lead_test_1(self, cr, uid, context=None):
"""
Create a new lead_test
"""
return self.model.create(cr, uid, {
'name': "Lead Test 1",
'user_id': self.admin,
}, context=context)
def create_rule(self, cr, uid, filter_id=False, filter_pre_id=False, context=None):
"""
The "Rule 1" says that when a lead goes to the 'draft' state, the responsible for that lead changes to user "demo"
"""
return self.base_action_rule.create(cr,uid,{
'name' : "Rule 1",
'model_id': self.registry('ir.model').search(cr, uid, [('model','=','base.action.rule.lead.test')], context=context)[0],
'active' : 1,
'filter_pre_id' : filter_pre_id,
'filter_id' : filter_id,
'act_user_id': self.demo,
}, context=context)
def delete_rules(self, cr, uid, context=None):
""" delete all the rules on model 'base.action.rule.lead.test' """
action_ids = self.base_action_rule.search(cr, uid, [('model', '=', self.model._name)], context=context)
return self.base_action_rule.unlink(cr, uid, action_ids, context=context)
def test_00_check_to_state_draft_pre(self):
"""
Check that a new record (with state = draft) doesn't change its responsible when there is a precondition filter which check that the state is draft.
"""
cr, uid = self.cr, self.uid
filter_draft = self.create_filter_draft(cr, uid)
self.create_rule(cr, uid, filter_pre_id=filter_draft)
new_lead_id = self.create_lead_test_1(cr, uid)
new_lead = self.model.browse(cr, uid, new_lead_id)
self.assertEquals(new_lead.state, 'draft')
self.assertEquals(new_lead.user_id.id, self.admin)
self.delete_rules(cr, uid)
def test_01_check_to_state_draft_post(self):
"""
Check that a new record (with state = draft) changes its responsible when there is a postcondition filter which check that the state is draft.
"""
cr, uid = self.cr, self.uid
filter_draft = self.create_filter_draft(cr, uid)
self.create_rule(cr, uid, filter_id=filter_draft)
new_lead_id = self.create_lead_test_1(cr, uid)
new_lead = self.model.browse(cr, uid, new_lead_id)
self.assertEquals(new_lead.state, 'draft')
self.assertEquals(new_lead.user_id.id, self.demo)
self.delete_rules(cr, uid)
def test_02_check_from_draft_to_done_with_steps(self):
"""
A new record will be created and will goes from draft to done state via the other states (open, pending and cancel)
We will create a rule that says in precondition that the record must be in the "draft" state while a postcondition filter says
that the record will be done. If the state goes from 'draft' to 'done' the responsible will change. If those two conditions aren't
verified, the responsible will stay the same
The responsible in that test will never change
"""
cr, uid = self.cr, self.uid
filter_draft = self.create_filter_draft(cr, uid)
filter_done = self.create_filter_done(cr, uid)
self.create_rule(cr, uid, filter_pre_id=filter_draft, filter_id=filter_done)
new_lead_id = self.create_lead_test_1(cr, uid)
new_lead = self.model.browse(cr, uid, new_lead_id)
self.assertEquals(new_lead.state, 'draft')
self.assertEquals(new_lead.user_id.id, self.admin)
""" change the state of new_lead to open and check that responsible doen't change"""
new_lead.write({'state': 'open'})
new_lead = self.model.browse(cr, uid, new_lead_id)
self.assertEquals(new_lead.state, 'open')
self.assertEquals(new_lead.user_id.id, self.admin)
""" change the state of new_lead to pending and check that responsible doen't change"""
new_lead.write({'state': 'pending'})
new_lead = self.model.browse(cr, uid, new_lead_id)
self.assertEquals(new_lead.state, 'pending')
self.assertEquals(new_lead.user_id.id, self.admin)
""" change the state of new_lead to cancel and check that responsible doen't change"""
new_lead.write({'state': 'cancel'})
new_lead = self.model.browse(cr, uid, new_lead_id)
self.assertEquals(new_lead.state, 'cancel')
self.assertEquals(new_lead.user_id.id, self.admin)
""" change the state of new_lead to done and check that responsible doen't change """
new_lead.write({'state': 'done'})
new_lead = self.model.browse(cr, uid, new_lead_id)
self.assertEquals(new_lead.state, 'done')
self.assertEquals(new_lead.user_id.id, self.admin)
self.delete_rules(cr, uid)
def test_02_check_from_draft_to_done_without_steps(self):
"""
A new record will be created and will goes from draft to done in one operation
We will create a rule that says in precondition that the record must be in the "draft" state while a postcondition filter says
that the record will be done. If the state goes from 'draft' to 'done' the responsible will change. If those two conditions aren't
verified, the responsible will stay the same
The responsible in that test will change to user "demo"
"""
cr, uid = self.cr, self.uid
filter_draft = self.create_filter_draft(cr, uid)
filter_done = self.create_filter_done(cr, uid)
self.create_rule(cr, uid, filter_pre_id=filter_draft, filter_id=filter_done)
new_lead_id = self.create_lead_test_1(cr, uid)
new_lead = self.model.browse(cr, uid, new_lead_id)
self.assertEquals(new_lead.state, 'draft')
self.assertEquals(new_lead.user_id.id, self.admin)
""" change the state of new_lead to done and check that responsible change to Demo_user"""
new_lead.write({'state': 'done'})
new_lead = self.model.browse(cr, uid, new_lead_id)
self.assertEquals(new_lead.state, 'done')
self.assertEquals(new_lead.user_id.id, self.demo)
self.delete_rules(cr, uid)

View File

@ -262,28 +262,3 @@ class base_stage(object):
if values_to_update:
self.write(cr, uid, ids, values_to_update, context=context)
return True
def write(self, cr, uid, ids, vals, context=None):
res = super(base_stage,self).write(cr, uid, ids, vals, context)
if vals.get('stage_id'):
for case in self.browse(cr, uid, ids, context=context):
self._action(cr, uid, case, case.stage_id.state, context=context)
return res
def _action(self, cr, uid, cases, state_to, scrit=None, context=None):
if context is None:
context = {}
context['state_to'] = state_to
rule_obj = self.pool.get('base.action.rule')
if not rule_obj:
return True
model_obj = self.pool.get('ir.model')
model_ids = model_obj.search(cr, uid, [('model','=',self._name)], context=context)
rule_ids = rule_obj.search(cr, uid, [('model_id','=',model_ids[0])], context=context)
return rule_obj._action(cr, uid, rule_ids, cases, scrit=scrit, context=context)
def _check(self, cr, uid, ids=False, context=None):
""" Function called by the scheduler to process cases for date actions.
Must be overriden by inheriting classes.
"""
return True

View File

@ -106,7 +106,7 @@ class base_state(object):
else:
raise osv.except_osv(_('Error !'), _('You can not escalate, you are already at the top level regarding your sales-team category.'))
self.write(cr, uid, [case.id], data, context=context)
self._action(cr, uid, cases, 'escalate', context=context)
case.case_escalate_send_note(parent_id, context=context)
return True
def case_open(self, cr, uid, ids, context=None):
@ -152,16 +152,50 @@ class base_state(object):
if update_values is None:
update_values = {}
update_values['state'] = state_name
res = self.write(cr, uid, ids, update_values, context=context)
self._action(cr, uid, cases, state_name, context=context)
return res
return self.write(cr, uid, ids, update_values, context=context)
# ******************************
# Notifications
# ******************************
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return ''
def _action(self, cr, uid, cases, state_to, scrit=None, context=None):
if context is None:
context = {}
context['state_to'] = state_to
rule_obj = self.pool.get('base.action.rule')
model_obj = self.pool.get('ir.model')
model_ids = model_obj.search(cr, uid, [('model','=',self._name)])
rule_ids = rule_obj.search(cr, uid, [('model_id','=',model_ids[0])])
return rule_obj._action(cr, uid, rule_ids, cases, scrit=scrit, context=context)
def case_open_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>opened</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_escalate_send_note(self, cr, uid, ids, new_section=None, context=None):
for id in ids:
if new_section:
msg = '%s has been <b>escalated</b> to <b>%s</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context), new_section.name)
else:
msg = '%s has been <b>escalated</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_close_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>closed</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_cancel_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>canceled</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_pending_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s is now <b>pending</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_reset_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>renewed</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_post(cr, uid, [id], body=msg, context=context)
return True

View File

@ -20,7 +20,6 @@
##############################################################################
import crm
import crm_action_rule
import crm_segmentation
import crm_lead
import crm_meeting

View File

@ -77,7 +77,6 @@ Dashboard for CRM will include:
'crm_view.xml',
'crm_action_rule_view.xml',
'crm_lead_view.xml',
'crm_lead_menu.xml',
@ -108,7 +107,6 @@ Dashboard for CRM will include:
'test/process/lead2opportunity_assign_salesmen.yml',
'test/process/merge_opportunity.yml',
'test/process/cancel_lead.yml',
'test/process/action_rule.yml',
'test/process/segmentation.yml',
'test/process/phonecalls.yml',
'test/ui/crm_demo.yml',

View File

@ -1,96 +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 re
from openerp import tools
from openerp.tools.translate import _
from openerp.tools import ustr
from openerp.osv import fields
from openerp.osv import osv
import crm
class base_action_rule(osv.osv):
""" Base Action Rule """
_inherit = 'base.action.rule'
_description = 'Action Rules'
_columns = {
'trg_section_id': fields.many2one('crm.case.section', 'Sales Team'),
'trg_max_history': fields.integer('Maximum Communication History'),
'trg_categ_id': fields.many2one('crm.case.categ', 'Category'),
'regex_history' : fields.char('Regular Expression on Case History', size=128),
'act_section_id': fields.many2one('crm.case.section', 'Set Team to'),
'act_categ_id': fields.many2one('crm.case.categ', 'Set Category to'),
}
def do_check(self, cr, uid, action, obj, context=None):
ok = super(base_action_rule, self).do_check(cr, uid, action, obj, context=context)
if hasattr(obj, 'section_id'):
ok = ok and (not action.trg_section_id or action.trg_section_id.id == obj.section_id.id)
if hasattr(obj, 'categ_ids'):
ok = ok and (not action.trg_categ_id or action.trg_categ_id.id in [x.id for x in obj.categ_ids])
#Cheking for history
regex = action.regex_history
if regex:
res = False
ptrn = re.compile(ustr(regex))
for history in obj.message_ids:
_result = ptrn.search(ustr(history.subject))
if _result:
res = True
break
ok = ok and res
if action.trg_max_history:
res_count = False
history_ids = filter(lambda x: x.email_from, obj.message_ids)
if len(history_ids) <= action.trg_max_history:
res_count = True
ok = ok and res_count
return ok
def do_action(self, cr, uid, action, obj, context=None):
res = super(base_action_rule, self).do_action(cr, uid, action, obj, context=context)
model_obj = self.pool.get(action.model_id.model)
write = {}
if hasattr(action, 'act_section_id') and action.act_section_id:
write['section_id'] = action.act_section_id.id
if hasattr(action, 'act_categ_id') and action.act_categ_id:
write['categ_ids'] = [(4, action.act_categ_id.id)]
model_obj.write(cr, uid, [obj.id], write, context)
return res
def state_get(self, cr, uid, context=None):
"""Gets available states for crm"""
res = super(base_action_rule, self).state_get(cr, uid, context=context)
return res + crm.AVAILABLE_STATES
def priority_get(self, cr, uid, context=None):
res = super(base_action_rule, self).priority_get(cr, uid, context=context)
return res + crm.AVAILABLE_PRIORITIES
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -27,7 +27,7 @@ Description: [[object.description]]
<field name="model_id" ref="model_crm_lead"/>
<field name="sequence">1</field>
<field name="filter_id" ref="filter_draft_lead"/>
<field name="trg_date_type">create</field>
<field name="trg_date_id" ref="field_crm_lead_create_date"/>
<field name="trg_date_range">5</field>
<field name="trg_date_range_type">day</field>
<field name="server_action_ids" eval="[(6,0,[ref('action_email_reminder_lead')])]"/>
@ -40,18 +40,27 @@ Description: [[object.description]]
<field name="domain">[('country_id','=','United States')]</field>
<field name="user_id" eval="False"/>
</record>
<record id="action_set_team_sales_department" model="ir.actions.server">
<field name="name">Set team to Sales Department</field>
<field name="model_id" ref="model_crm_lead"/>
<field name="condition">True</field>
<field name="type">ir.actions.server</field>
<field name="state">code</field>
<field name="code">sales_team = self.pool.get('ir.model.data').get_object(cr, uid, 'crm', 'section_sales_department')
object.write({'section_id': sales_team.id})
</field>
</record>
<record id="rule_set_followers_lead" model="base.action.rule">
<field name="name">Set Auto Followers on leads which are urgent and come from USA.</field>
<field name="model_id" ref="model_crm_lead"/>
<field name="sequence">2</field>
<field name="regex_name">urgent.*</field>
<field name="filter_id" ref="filter_usa_lead"/>
<field name="act_followers" eval="[(6,0,[ref('base.res_partner_4'), ref('base.res_partner_5'), ref('base.res_partner_6')])]"/>
<field name="act_user_id" ref="base.user_root"/>
<field name="act_section_id" ref="section_sales_department"/>
<field name="trg_date_type">create</field>
<field name="trg_date_id" ref="field_crm_lead_create_date"/>
<field name="trg_date_range">0</field>
<field name="trg_date_range_type">minutes</field>
<field name="server_action_ids" eval="[(6,0,[ref('action_set_team_sales_department')])]"/>
</record>
</data>
</openerp>

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Case rules -->
<record id="view_base_action_rule_line_form1" model="ir.ui.view">
<field name="name">base.action.rule.form.inherit</field>
<field name="model">base.action.rule</field>
<field name="inherit_id" ref="base_action_rule.view_base_action_rule_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='partner']" position="after">
<group name="case" string="Conditions on Case Fields">
<field name="trg_section_id" widget="selection"/>
<field name="trg_categ_id"/>
</group>
<group name="communication" string="Conditions on Communication History">
<field name="regex_history"/>
<field name="trg_max_history"/>
</group>
</xpath>
<xpath expr="//field[@name='act_user_id']" position="after">
<field name="act_section_id"/>
<field name="act_categ_id"/>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -30,7 +30,7 @@ access_res_partner,res.partner.crm.user,base.model_res_partner,base.group_sale_s
access_res_partner_category,res.partner.category.crm.user,base.model_res_partner_category,base.group_sale_salesman,1,1,1,0
mail_mailgate_thread,mail.thread,mail.model_mail_thread,base.group_sale_salesman,1,1,1,1
access_crm_case_categ_manager,crm.case.categ manager,model_crm_case_categ,base.group_sale_manager,1,1,1,1
access_base_action_rule_manager,base.action.rule manager,model_base_action_rule,base.group_sale_manager,1,1,1,1
access_base_action_rule_manager,base.action.rule manager,base_action_rule.model_base_action_rule,base.group_sale_manager,1,1,1,1
access_crm_lead_report_user,crm.lead.report user,model_crm_lead_report,base.group_sale_salesman,1,1,1,1
access_res_partner_bank_type_crm_user,res.partner.bank.type.crm.user,base.model_res_partner_bank_type,base.group_sale_salesman,1,0,0,0
access_crm_lead_partner_manager,crm.lead.partner.manager,model_crm_lead,base.group_partner_manager,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
30 access_res_partner_category res.partner.category.crm.user base.model_res_partner_category base.group_sale_salesman 1 1 1 0
31 mail_mailgate_thread mail.thread mail.model_mail_thread base.group_sale_salesman 1 1 1 1
32 access_crm_case_categ_manager crm.case.categ manager model_crm_case_categ base.group_sale_manager 1 1 1 1
33 access_base_action_rule_manager base.action.rule manager model_base_action_rule base_action_rule.model_base_action_rule base.group_sale_manager 1 1 1 1
34 access_crm_lead_report_user crm.lead.report user model_crm_lead_report base.group_sale_salesman 1 1 1 1
35 access_res_partner_bank_type_crm_user res.partner.bank.type.crm.user base.model_res_partner_bank_type base.group_sale_salesman 1 0 0 0
36 access_crm_lead_partner_manager crm.lead.partner.manager model_crm_lead base.group_partner_manager 1 0 0 0

View File

@ -1,20 +0,0 @@
-
I create a record rule.
-
!python {model: base.action.rule}: |
model_ids = self.pool.get("ir.model").search(cr, uid, [('model', '=', 'crm.lead')])
from datetime import datetime
new_id = self.create(cr, uid, {'name': 'New Rule', 'model_id': model_ids[0], 'trg_user_id': ref('base.user_root'), 'trg_partner_id': ref('base.res_partner_1'), 'act_user_id': ref('base.user_demo') })
self._check(cr, uid)
-
I create a new lead to check the record rule.
-
!record {model: crm.lead, id: crm_lead_test_rules_id}:
name: 'Test lead rules'
partner_id: base.res_partner_1
-
I check if the record rule is applied and the responsible is changed.
-
!python {model: crm.lead}: |
lead_user = self.browse(cr, uid, ref('crm_lead_test_rules_id'))
assert lead_user.user_id.id == ref('base.user_demo'), "Responsible of lead is not changed."

View File

@ -2,13 +2,15 @@
.oe_kanban_column .note_text_line_through {
text-decoration: line-through;
}
.openerp .oe_form .oe_form_field.oe_memo {
margin: 0;
padding: 0px;
width: 100%;
min-height: 200px;
}
.openerp .oe_form .oe_pad.oe_memo {
width: auto;
}
.openerp .oe_form .oe_form_field.oe_memo .cleditorMain {
border: none;
padding: 0px;

View File

@ -17,6 +17,8 @@
.openerp
.oe_form
.oe_pad.oe_memo
width: auto
.oe_form_field.oe_memo
margin: 0
padding: 0px

View File

@ -7,7 +7,7 @@
<field name="inherit_id" ref="note.view_note_note_form"/>
<field name="arch" type="xml">
<field name="memo" position="replace">
<field name="note_pad_url" widget="pad"/>
<field name="note_pad_url" widget="pad" class="oe_memo"/>
</field>
</field>
</record>

View File

@ -4,6 +4,13 @@ openerp.pad = function(instance) {
template: 'FieldPad',
configured: false,
content: "",
start: function() {
this._super();
var self = this;
this.on('change:effective_readonly',this,function(){
self.renderElement();
});
},
render_value: function() {
var self = this;
var _super = _.bind(this._super, this);
@ -27,6 +34,9 @@ openerp.pad = function(instance) {
renderElement: function(){
var self = this;
var value = this.get('value');
if (this.pad_loading_request) {
this.pad_loading_request.abort();
}
if(!_.str.startsWith(value,'http')){
this.configured = false;
this.content = "";
@ -36,7 +46,8 @@ openerp.pad = function(instance) {
this.content = '<iframe width="100%" height="100%" frameborder="0" src="'+value+'?showChat=false&userName='+this.session.username+'"></iframe>';
}else{
this.content = '<div class="oe_pad_loading">... Loading pad ...</div>';
$.get(value+'/export/html').success(function(data){
this.pad_loading_request = $.get(value+'/export/html')
.done(function(data){
groups = /\<\s*body\s*\>(.*?)\<\s*\/body\s*\>/.exec(data);
data = (groups || []).length >= 2 ? groups[1] : '';
self.$('.oe_pad_content').html('<div class="oe_pad_readonly"><div>');
@ -51,9 +62,6 @@ openerp.pad = function(instance) {
this.$('.oe_pad_switch').click(function(){
self.$el.toggleClass('oe_pad_fullscreen');
});
this.on('change:effective_readonly',this,function(){
self.renderElement();
});
},
});