From cfdaf8c50ad2da8fbec92ae01c3810a782d12dd7 Mon Sep 17 00:00:00 2001 From: "Yogesh (Serpent Consulting Services)" Date: Fri, 16 Mar 2012 17:49:15 +0530 Subject: [PATCH 01/87] [FIX] action :- send multiple email on server action. lp bug: https://launchpad.net/bugs/956898 fixed bzr revid: yogesh.sakhreliya@serpentcs.com-20120316121915-t5kjscd0bnwm72xi --- openerp/addons/base/ir/ir_actions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py index 236c6af7119..77eeb8d58f3 100644 --- a/openerp/addons/base/ir/ir_actions.py +++ b/openerp/addons/base/ir/ir_actions.py @@ -670,6 +670,9 @@ class actions_server(osv.osv): _logger.info('No partner email address specified, not sending any email.') continue + if not isinstance(address, tuple) and not isinstance(address, list): + address = [address] + if not email_from: _logger.debug('--email-from command line option is not specified, using a fallback value instead.') if user.user_email: @@ -681,7 +684,7 @@ class actions_server(osv.osv): body = self.merge_message(cr, uid, action.message, action, context) ir_mail_server = self.pool.get('ir.mail_server') - msg = ir_mail_server.build_email(email_from, [address], subject, body) + msg = ir_mail_server.build_email(email_from, address, subject, body) res_email = ir_mail_server.send_email(cr, uid, msg) if res_email: _logger.info('Email successfully sent to: %s', address) From 16e8bdc6b292082f962cfb6a193a21ef6cbda090 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Thu, 10 May 2012 15:50:20 +0200 Subject: [PATCH 02/87] [IMP] base_action_rule - Allow using any object dates as a triggering date bzr revid: yannick.vaucher@camptocamp.com-20120510135020-x7m08ojkxjzy4fs9 --- addons/base_action_rule/base_action_rule.py | 47 ++++++++----------- .../base_action_rule_view.xml | 4 +- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 50ebd96a3c2..c558939470e 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -37,7 +37,7 @@ def get_datetime(date_field): 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') @@ -79,13 +79,10 @@ class base_action_rule(osv.osv): 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'), - ('action_last', 'Last Action Date'), - ('date', 'Date'), - ('deadline', 'Deadline'), - ], 'Trigger Date', size=16), + 'trg_date_id': fields.many2one('ir.model.fields', + 'Trigger Date', + domain="[('model_id', '=', model_id)," + "('ttype','in',('date','datetime'))]"), '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 \ @@ -132,7 +129,6 @@ the rule to mark CC(mail to any other person defined in actions)."), _defaults = { 'active': lambda *a: True, - 'trg_date_type': lambda *a: 'none', 'trg_date_range_type': lambda *a: 'day', 'act_mail_to_user': lambda *a: 0, 'act_remind_partner': lambda *a: 0, @@ -168,7 +164,7 @@ the rule to mark CC(mail to any other person defined in actions)."), 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': + if not self.browse(cr, uid, rule_id, context=context).trg_date_id: self._action(cr, uid, [rule_id], obj.browse(cr, uid, ids, context=context), context=context) return True @@ -185,7 +181,7 @@ the rule to mark CC(mail to any other person defined in actions)."), self.post_action(cr, uid, [new_id], model, context=context) return new_id return wrapper - + def _write(self, old_write, model, context=None): """ Return a wrapper around `old_write` calling both `old_write` and @@ -230,7 +226,7 @@ the rule to mark CC(mail to any other person defined in actions)."), def _check(self, cr, uid, automatic=False, use_new_cursor=False, \ context=None): """ - This Function is call by scheduler. + This Function is called by scheduler. """ rule_pool = self.pool.get('base.action.rule') rule_ids = rule_pool.search(cr, uid, [], context=context) @@ -248,22 +244,17 @@ the rule to mark CC(mail to any other person defined in actions)."), obj = model_pool.browse(cr, uid, obj_id, context=context) # 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=='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 rule.trg_date_id: + date_type = rule.trg_date_id.name + if (date_type=='date_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 (hasattr(obj, date_type) + and obj.read([date_type])[0][date_type]): + base = obj.read([date_type])[0][date_type] if base: fnct = { 'minutes': lambda interval: timedelta(minutes=interval), diff --git a/addons/base_action_rule/base_action_rule_view.xml b/addons/base_action_rule/base_action_rule_view.xml index 4ba7e6f9018..b041d322193 100644 --- a/addons/base_action_rule/base_action_rule_view.xml +++ b/addons/base_action_rule/base_action_rule_view.xml @@ -39,8 +39,8 @@ - - + + From d41a234599e6d214761d6344e70a37051bff9d87 Mon Sep 17 00:00:00 2001 From: Ronald Portier Date: Wed, 24 Oct 2012 09:28:07 +0200 Subject: [PATCH 03/87] [FIX] Fix vacuum cleaning both for time and count based cleaning, adding a safeguard against cleaning recently used rows. bzr revid: ronald@therp.nl-20121024072807-vsvhub1m0y3j5ude --- openerp/osv/orm.py | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 4c3330c97b7..6916f94730a 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -684,9 +684,6 @@ class BaseModel(object): # Transience _transient = False # True in a TransientModel - _transient_max_count = None - _transient_max_hours = None - _transient_check_time = 20 # structure: # { 'parent_model': 'm2o_field', ... } @@ -5067,20 +5064,26 @@ class BaseModel(object): def _transient_clean_rows_older_than(self, cr, seconds): assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name - cr.execute("SELECT id FROM " + self._table + " WHERE" - " COALESCE(write_date, create_date, (now() at time zone 'UTC'))::timestamp <" - " ((now() at time zone 'UTC') - interval %s)", ("%s seconds" % seconds,)) - ids = [x[0] for x in cr.fetchall()] - self.unlink(cr, SUPERUSER_ID, ids) - - def _transient_clean_old_rows(self, cr, count): - assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name + '''Never delete rows used in last 5 minutes''' + seconds = max(seconds, 600) + now_str = "(now() at time zone 'UTC')" cr.execute( - "SELECT id, COALESCE(write_date, create_date, (now() at time zone 'UTC'))::timestamp" - " AS t FROM " + self._table + - " ORDER BY t LIMIT %s", (count,)) + "SELECT id FROM " + self._table + " WHERE" + " COALESCE(write_date, create_date, " + now_str + ")::timestamp" + "< (" + now_str + " - interval %s)", ("%s seconds" % seconds,)) ids = [x[0] for x in cr.fetchall()] - self.unlink(cr, SUPERUSER_ID, ids) + if ids: + self.unlink(cr, SUPERUSER_ID, ids) + + def _transient_clean_old_rows(self, cr, max_count): + # Check how many rows we have in the table + cr.execute( + "SELECT count(*) as row_count FROM %s", (self._table, )) + res = cr.fetchall() + row_count = res[0][0] + if row_count <= max_count: + return # max not reached, nothing to do + self._transient_clean_rows_older_than(cr, 600) def _transient_vacuum(self, cr, uid, force=False): """Clean the transient records. @@ -5092,10 +5095,12 @@ class BaseModel(object): a new record is created). """ assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name + _transient_check_time = 20 # arbitrary limit on vacuum executions self._transient_check_count += 1 - if (not force) and (self._transient_check_count % self._transient_check_time): - self._transient_check_count = 0 - return True + if ((not force) + and (self._transient_check_count >= _transient_check_time)): + return True # no vacuum cleaning this time + self._transient_check_count = 0 # Age-based expiration if self._transient_max_hours: From 2747e5bef21ef9361900d61218e594b85ed22b20 Mon Sep 17 00:00:00 2001 From: Ronald Portier Date: Wed, 24 Oct 2012 16:01:19 +0200 Subject: [PATCH 04/87] [FIX] Correct both age based as count based vacuum cleaning. lp bug: https://launchpad.net/bugs/1009014 fixed bzr revid: ronald@therp.nl-20121024140119-hv72ffxmf1gvf2sj --- openerp/osv/orm.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 6916f94730a..ca2a4fb6268 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -5065,7 +5065,7 @@ class BaseModel(object): def _transient_clean_rows_older_than(self, cr, seconds): assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name '''Never delete rows used in last 5 minutes''' - seconds = max(seconds, 600) + seconds = max(seconds, 300) now_str = "(now() at time zone 'UTC')" cr.execute( "SELECT id FROM " + self._table + " WHERE" @@ -5077,13 +5077,13 @@ class BaseModel(object): def _transient_clean_old_rows(self, cr, max_count): # Check how many rows we have in the table - cr.execute( - "SELECT count(*) as row_count FROM %s", (self._table, )) + sql_statement = "SELECT count(*) as row_count FROM %s" % (self._table, ) + cr.execute(sql_statement) res = cr.fetchall() row_count = res[0][0] if row_count <= max_count: return # max not reached, nothing to do - self._transient_clean_rows_older_than(cr, 600) + self._transient_clean_rows_older_than(cr, 300) def _transient_vacuum(self, cr, uid, force=False): """Clean the transient records. @@ -5093,12 +5093,20 @@ class BaseModel(object): Actual cleaning will happen only once every "_transient_check_time" calls. This means this method can be called frequently called (e.g. whenever a new record is created). + Example with both max_hours and max_count active: + Suppose max_hours = 0.2 (e.g. 12 minutes), max_count = 20, there are 55 rows in the + table, 10 created/changed in the last 5 minutes, an additional 12 created/changed between + 5 and 10 minutes ago, the rest created/changed more then 12 minutes ago. + - age based vacuum will leave the 22 rows created/changed in the last 12 minutes + - count based vacuum will wipe out another 12 rows. Not just 2, otherwise each addition + would immediately cause the maximum to be reached again. + - the 10 rows that have been created/changed the last 5 minutes will NOT be deleted """ assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name - _transient_check_time = 20 # arbitrary limit on vacuum executions + _transient_check_time = 20 # arbitrary limit on vacuum executions self._transient_check_count += 1 if ((not force) - and (self._transient_check_count >= _transient_check_time)): + and (self._transient_check_count < _transient_check_time)): return True # no vacuum cleaning this time self._transient_check_count = 0 From db5a4e70751cc3f1bdd7f72cacdcf3c7292614c5 Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Fri, 23 Nov 2012 11:31:13 +0100 Subject: [PATCH 05/87] [FIX] 1st test for automated action bzr revid: api@openerp.com-20121123103113-x9rspq1oh1887153 --- addons/base_action_rule/base_action_rule.py | 50 +++++++++++++-------- addons/base_status/base_stage.py | 8 +--- addons/base_status/base_state.py | 3 +- addons/crm/crm_action_rule.py | 4 +- addons/crm/crm_lead.py | 13 ++++-- addons/crm_claim/crm_claim.py | 6 ++- 6 files changed, 51 insertions(+), 33 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 856d2626f69..349721db8c4 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -109,7 +109,7 @@ trigger date, like sending a reminder 15 minutes before a meeting."), _order = 'sequence' - def post_action(self, cr, uid, ids, model, context=None): + def post_action(self, cr, uid, ids, model, old_records_states=None, 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) \ @@ -117,11 +117,11 @@ trigger date, like sending a reminder 15 minutes before a meeting."), res = cr.fetchall() # Check if any rule matching with current object for obj_name, rule_id in res: - obj = self.pool.get(obj_name) + model_pool = 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) + self._action(cr, uid, [rule_id], model_pool.browse(cr, uid, ids, context=context), old_records_states=old_records_states, context=context) return True def _create(self, old_create, model, context=None): @@ -144,13 +144,23 @@ trigger date, like sending a reminder 15 minutes before a meeting."), `post_action`, in that order. """ def wrapper(cr, uid, ids, vals, context=context): + old_records_states = {} if context is None: context = {} if isinstance(ids, (str, int, long)): ids = [ids] + model_pool = self.pool.get(model) + # get the old records (before the write) + if model and ids: + for record in model_pool.browse(cr,uid,ids,context=context): + old_records_states[record.id] = record.state + #old_records = model_pool.browse(cr,uid,ids,context=context) + #print old_records[0] + #print old_records[0].state old_write(cr, uid, ids, vals, context=context) + #print old_records[0].state if not context.get('action'): - self.post_action(cr, uid, ids, model, context=context) + self.post_action(cr, uid, ids, model, old_records_states=old_records_states, context=context) return True return wrapper @@ -242,7 +252,7 @@ trigger date, like sending a reminder 15 minutes before a meeting."), - def do_check(self, cr, uid, action, obj, context=None): + def do_check(self, cr, uid, action, obj, old_records_states=None, context=None): """ check Action """ if context is None: context = {} @@ -263,14 +273,21 @@ trigger date, like sending a reminder 15 minutes before a meeting."), (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 + #state_to = the state after a write or a create + state_to = getattr(obj, 'state', False) + #state_from = nothing or the state of old_records + state_from = "" + if old_records_states != None: + if old_records_states[obj.id]: + state_from = old_records_states[obj.id] + #if we have an action that check the status + if action.trg_state_from: + print action.trg_state_from, " and ", state_to + ok = ok and action.trg_state_from==state_to + if action.trg_state_to: + print action.trg_state_to, " for ", state_from, " and ", state_to + ok = state_from!=state_to + ok = ok and action.trg_state_to==state_to reg_name = action.regex_name result_name = True if reg_name: @@ -312,19 +329,16 @@ trigger date, like sending a reminder 15 minutes before a meeting."), model_obj.message_subscribe(cr, uid, [obj.id], new_followers, context=context) return True - def _action(self, cr, uid, ids, objects, scrit=None, context=None): + def _action(self, cr, uid, ids, objects, scrit=None, old_records_states=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): + if self.do_check(cr, uid, action, obj, old_records_states=old_records_states, context=context): self.do_action(cr, uid, action, obj, context=context) context.update({'action': False}) diff --git a/addons/base_status/base_stage.py b/addons/base_status/base_stage.py index 86edff46920..ba4f62a181a 100644 --- a/addons/base_status/base_stage.py +++ b/addons/base_status/base_stage.py @@ -19,6 +19,7 @@ # ############################################################################## +import pdb from osv import fields, osv from tools.translate import _ @@ -280,15 +281,10 @@ class base_stage(object): # 2. update values if values_to_update: self.write(cr, uid, ids, values_to_update, context=context) - # 3. call _action for base action rule - if new_state_name: - self._action(cr, uid, cases, new_state_name, context=context) - elif not (new_stage_id is None): - new_state_name = self.read(cr, uid, ids, ['state'], context=context)[0]['state'] - self._action(cr, uid, cases, new_state_name, context=context) return True def _action(self, cr, uid, cases, state_to, scrit=None, context=None): + print "--- Base_Stage (_action) ---" if context is None: context = {} context['state_to'] = state_to diff --git a/addons/base_status/base_state.py b/addons/base_status/base_state.py index cebf5537485..aad9129406e 100644 --- a/addons/base_status/base_state.py +++ b/addons/base_status/base_state.py @@ -157,13 +157,14 @@ class base_state(object): :params: update_values: values that will be added with the state update when writing values to the record. """ + print "-------------------------BASE STATE--------------------------------" cases = self.browse(cr, uid, ids, context=context) cases[0].state # fill browse record cache, for _action having old and new values if update_values is None: update_values = {} update_values['state'] = state_name self.write(cr, uid, ids, update_values, context=context) - self._action(cr, uid, cases, state_name, context=context) + #self._action(cr, uid, cases, state_name, context=context) def _action(self, cr, uid, cases, state_to, scrit=None, context=None): if context is None: diff --git a/addons/crm/crm_action_rule.py b/addons/crm/crm_action_rule.py index 4a8ee98fbae..d7caafd7d1d 100644 --- a/addons/crm/crm_action_rule.py +++ b/addons/crm/crm_action_rule.py @@ -43,8 +43,8 @@ class base_action_rule(osv.osv): '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) + def do_check(self, cr, uid, action, obj, old_records_states=None, context=None): + ok = super(base_action_rule, self).do_check(cr, uid, action, obj, old_records_states=old_records_states, 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) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 0a824aef83e..b9d92639e63 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -29,6 +29,7 @@ from tools.translate import _ from tools import html2plaintext from base.res.res_partner import format_address +import pdb CRM_LEAD_PENDING_STATES = ( crm.AVAILABLE_STATES[2][0], # Cancelled @@ -380,8 +381,10 @@ class crm_lead(base_stage, format_address, osv.osv): """ Mark the case as lost: state=cancel and probability=0 """ for lead in self.browse(cr, uid, ids): stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 0.0)], context=context) - if stage_id: - self.case_set(cr, uid, [lead.id], values_to_update={'probability': 0.0}, new_stage_id=stage_id, context=context) + if not stage_id: + return False + else: + self.write(cr, uid, ids, {'probability' : 0.0, 'stage_id' : stage_id}, context=context) self.case_mark_lost_send_note(cr, uid, ids, context=context) return True @@ -389,8 +392,10 @@ class crm_lead(base_stage, format_address, osv.osv): """ Mark the case as lost: state=done and probability=100 """ for lead in self.browse(cr, uid, ids): stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 100.0)], context=context) - if stage_id: - self.case_set(cr, uid, [lead.id], values_to_update={'probability': 100.0}, new_stage_id=stage_id, context=context) + if not stage_id: + return False + else: + self.write(cr, uid, ids, {'probability' : 100.0, 'stage_id' : stage_id}, context=context) self.case_mark_won_send_note(cr, uid, ids, context=context) return True diff --git a/addons/crm_claim/crm_claim.py b/addons/crm_claim/crm_claim.py index 70dcc18baf7..5c141c81080 100644 --- a/addons/crm_claim/crm_claim.py +++ b/addons/crm_claim/crm_claim.py @@ -167,8 +167,10 @@ class crm_claim(base_stage, osv.osv): """ Mark the case as refused: state=done and case_refused=True """ for lead in self.browse(cr, uid, ids): stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, ['&', ('state', '=', 'done'), ('case_refused', '=', True)], context=context) - if stage_id: - self.case_set(cr, uid, [lead.id], values_to_update={}, new_stage_id=stage_id, context=context) + if not stage_id: + return False + else: + self.write(cr, uid, ids, {'stage_id' : stage_id}, context=context) return self.case_refuse_send_note(cr, uid, ids, context=context) def onchange_partner_id(self, cr, uid, ids, part, email=False): From 084371b51b6fb7b72c5fb3eaa11c7d4696a4f6a6 Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Mon, 26 Nov 2012 12:22:05 +0100 Subject: [PATCH 06/87] [FIX] Automated action rules bzr revid: api@openerp.com-20121126112205-bf80xlf1ja5o2f7i --- openerp/modules/loading.py | 6 ++++++ openerp/osv/orm.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py index bcda1690c91..f7f70c51ad5 100644 --- a/openerp/modules/loading.py +++ b/openerp/modules/loading.py @@ -413,6 +413,12 @@ def load_modules(db, force_demo=False, status=None, update_module=False): _logger.error('At least one test failed when loading the modules.') else: _logger.info('Modules loaded.') + + # STEP 7: for every model call _register_hook + for models in pool.models: + pool_mod = pool.get(models) + pool_mod._register_hook(cr) + finally: cr.close() diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 6a1f917caa1..693757345f5 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -5155,6 +5155,10 @@ class BaseModel(object): # for backward compatibility resolve_o2m_commands_to_record_dicts = resolve_2many_commands + def _register_hook(self, cr): + """ This method is called right after the registry is filled """ + pass + # keep this import here, at top it will cause dependency cycle errors import expression From 72616d0ba2b355004f28590d3815ca18137c66c9 Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Mon, 26 Nov 2012 12:22:48 +0100 Subject: [PATCH 07/87] [FIX] Automated Action Rules bzr revid: api@openerp.com-20121126112248-oiq40hn9lgro0lq4 --- addons/base_action_rule/base_action_rule.py | 29 +++++++++++-------- .../base_action_rule_view.xml | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 0e9db7fc60d..71b376f6946 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -23,6 +23,7 @@ from datetime import datetime from datetime import timedelta import re import time +from openerp import SUPERUSER_ID from osv import fields, osv, orm from tools.translate import _ @@ -54,7 +55,7 @@ class base_action_rule(osv.osv): def state_get(self, cr, uid, context=None): """ Get State """ - return [('', '')] + return [('', ''), ('na','N/A (No previous state)')] def priority_get(self, cr, uid, context=None): """ Get Priority """ @@ -86,8 +87,8 @@ trigger date, like sending a reminder 15 minutes before a meeting."), '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), + 'trg_state_from': fields.selection(_state_get, 'and previously was', size=16), + 'trg_state_to': fields.selection(_state_get, 'Status changes to', size=16), 'act_user_id': fields.many2one('res.users', 'Set Responsible to'), 'act_state': fields.selection(_state_get, 'Set State to', size=16), @@ -162,29 +163,30 @@ trigger date, like sending a reminder 15 minutes before a meeting."), return True return wrapper - def _register_hook(self, cr, uid, ids, context=None): + def _register_hook(self, cr): """ 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): + ids = self.search(cr,SUPERUSER_ID,[]) + for action_rule in self.browse(cr, SUPERUSER_ID, ids, context=None): 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.create = self._create(obj_pool.create, model, context=None) + obj_pool.write = self._write(obj_pool.write, model, context=None) obj_pool.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) return res_id def write(self, cr, uid, ids, vals, context=None): super(base_action_rule, self).write(cr, uid, ids, vals, context=context) - self._register_hook(cr, uid, ids, context=context) + self._register_hook(cr) return True def _check(self, cr, uid, automatic=False, use_new_cursor=False, \ @@ -278,11 +280,14 @@ trigger date, like sending a reminder 15 minutes before a meeting."), if old_records != None: for record in old_records: state_from = record.state + else: + state_from = "na" #if we have an action that check the status - if action.trg_state_from: - ok = ok and action.trg_state_from==state_to if action.trg_state_to: - ok = state_from!=state_to + if action.trg_state_from: + ok = ok and action.trg_state_from==state_from + else: + ok = state_from!=state_to ok = ok and action.trg_state_to==state_to reg_name = action.regex_name result_name = True diff --git a/addons/base_action_rule/base_action_rule_view.xml b/addons/base_action_rule/base_action_rule_view.xml index bb711be9928..4815baa9ebb 100644 --- a/addons/base_action_rule/base_action_rule_view.xml +++ b/addons/base_action_rule/base_action_rule_view.xml @@ -33,8 +33,8 @@ - + From 041e43967c7a705a4a487d02577b476ff8ad8b57 Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Mon, 26 Nov 2012 12:44:05 +0100 Subject: [PATCH 08/87] [IMP] add comment bzr revid: api@openerp.com-20121126114405-s3b3lo6qncho2n37 --- addons/base_action_rule/base_action_rule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 71b376f6946..8892bec34f2 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -281,7 +281,7 @@ trigger date, like sending a reminder 15 minutes before a meeting."), for record in old_records: state_from = record.state else: - state_from = "na" + state_from = "na" #it means that there was no state before (creation for example) #if we have an action that check the status if action.trg_state_to: if action.trg_state_from: From 8474567cdf5a0df9be8963abf6a8598eeabee17c Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Mon, 26 Nov 2012 13:51:42 +0100 Subject: [PATCH 09/87] [FIX] Automated Action rule (register_hook) bzr revid: api@openerp.com-20121126125142-9gpsupy7617vddya --- addons/base_action_rule/base_action_rule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 8892bec34f2..693d46b9021 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -195,7 +195,7 @@ trigger date, like sending a reminder 15 minutes before a meeting."), This Function is call by scheduler. """ rule_ids = self.search(cr, uid, [], context=context) - self._register_hook(cr, uid, rule_ids, context=context) + self._register_hook(cr) if context is None: context = {} for rule in self.browse(cr, uid, rule_ids, context=context): From 41e9eadc6197b9082ac6e32c45330956ee469096 Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Mon, 26 Nov 2012 14:10:17 +0100 Subject: [PATCH 10/87] [FIX] _register_hook refactored bzr revid: api@openerp.com-20121126131017-55e2x4e75nmabt27 --- addons/base_action_rule/base_action_rule.py | 23 +++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 693d46b9021..11d62404502 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -169,7 +169,22 @@ trigger date, like sending a reminder 15 minutes before a meeting."), the rules (given by `ids`). """ ids = self.search(cr,SUPERUSER_ID,[]) - for action_rule in self.browse(cr, SUPERUSER_ID, ids, context=None): + return self._register_hook_(cr,SUPERUSER_ID,ids,context=None) + #for action_rule in self.browse(cr, SUPERUSER_ID, ids, context=None): + # 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=None) + # obj_pool.write = self._write(obj_pool.write, model, context=None) + # obj_pool.base_action_ruled = True + #return True + + def _register_hook_(self, cr, uid, ids, context=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): model = action_rule.model_id.model obj_pool = self.pool.get(model) if not hasattr(obj_pool, 'base_action_ruled'): @@ -181,12 +196,12 @@ trigger date, like sending a reminder 15 minutes before a meeting."), 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) + self._register_hook_(cr, uid, res_id,context=context) return res_id def write(self, cr, uid, ids, vals, context=None): super(base_action_rule, self).write(cr, uid, ids, vals, context=context) - self._register_hook(cr) + self._register_hook_(cr, uid, ids, context=context) return True def _check(self, cr, uid, automatic=False, use_new_cursor=False, \ @@ -195,7 +210,7 @@ trigger date, like sending a reminder 15 minutes before a meeting."), This Function is call by scheduler. """ rule_ids = self.search(cr, uid, [], context=context) - self._register_hook(cr) + 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): From 9137243cdc48d5f3e89b16b7ec56f95cb1ef894a Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Mon, 26 Nov 2012 14:32:57 +0100 Subject: [PATCH 11/87] [FIX] _register_hook refactored (test) bzr revid: api@openerp.com-20121126133257-f9j7vfgdfkqjewyn --- addons/base_action_rule/base_action_rule.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 11d62404502..14da227a367 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -170,28 +170,24 @@ trigger date, like sending a reminder 15 minutes before a meeting."), """ ids = self.search(cr,SUPERUSER_ID,[]) return self._register_hook_(cr,SUPERUSER_ID,ids,context=None) - #for action_rule in self.browse(cr, SUPERUSER_ID, ids, context=None): - # 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=None) - # obj_pool.write = self._write(obj_pool.write, model, context=None) - # obj_pool.base_action_ruled = True - #return True def _register_hook_(self, cr, uid, ids, context=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): + reg_ids = [] + if not isinstance(ids, list): + reg_ids.append(ids) + else: + reg_ids.extend(ids) + for action_rule in self.browse(cr, uid, reg_ids, context=context): 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=None) obj_pool.write = self._write(obj_pool.write, model, context=None) obj_pool.base_action_ruled = True - return True def create(self, cr, uid, vals, context=None): From 100f3199e7120948dcc24aed555e8323ba0871aa Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Mon, 26 Nov 2012 15:03:30 +0100 Subject: [PATCH 12/87] [IMP] Test for automated action rules bzr revid: api@openerp.com-20121126140330-maut337p5q8fsglo --- .../test/base_action_rule_test.py | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 addons/base_action_rule/test/base_action_rule_test.py diff --git a/addons/base_action_rule/test/base_action_rule_test.py b/addons/base_action_rule/test/base_action_rule_test.py new file mode 100644 index 00000000000..41eb0e31746 --- /dev/null +++ b/addons/base_action_rule/test/base_action_rule_test.py @@ -0,0 +1,111 @@ +import tools +from openerp.tests import common + +class myLead(orm.Model): + + _name = name('base_action_rule.myLead') + + _columns = { + 'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', + select=True, help="Linked partner (optional). Usually created when converting the lead."), + 'id': fields.integer('ID', readonly=True), + 'name': fields.char('Subject', size=64, required=True, select=1), + 'active': fields.boolean('Active', required=False), + 'date_action_last': fields.datetime('Last Action', readonly=1), + 'date_action_next': fields.datetime('Next Action', readonly=1), + 'email_from': fields.char('Email', size=128, help="Email address of the contact", select=1), + 'section_id': fields.many2one('crm.case.section', 'Sales Team', \ + select=True, help='When sending mails, the default email address is taken from the sales team.'), + 'create_date': fields.datetime('Creation Date' , readonly=True), + 'email_cc': fields.text('Global CC', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), + 'description': fields.text('Notes'), + 'write_date': fields.datetime('Update Date' , readonly=True), + 'categ_ids': fields.many2many('crm.case.categ', 'crm_lead_category_rel', 'lead_id', 'category_id', 'Categories', \ + domain="['|',('section_id','=',section_id),('section_id','=',False), ('object_id.model', '=', 'crm.lead')]"), + 'type_id': fields.many2one('crm.case.resource.type', 'Campaign', \ + domain="['|',('section_id','=',section_id),('section_id','=',False)]", help="From which campaign (seminar, marketing campaign, mass mailing, ...) did this contact come from?"), + 'channel_id': fields.many2one('crm.case.channel', 'Channel', help="Communication channel (mail, direct, phone, ...)"), + 'contact_name': fields.char('Contact Name', size=64), + 'partner_name': fields.char("Customer Name", size=64,help='The name of the future partner company that will be created while converting the lead into opportunity', select=1), + 'opt_out': fields.boolean('Opt-Out', oldname='optout', help="If opt-out is checked, this contact has refused to receive emails or unsubscribed to a campaign."), + 'type':fields.selection([ ('lead','Lead'), ('opportunity','Opportunity'), ],'Type', help="Type is used to separate Leads and Opportunities"), + 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True), + 'date_closed': fields.datetime('Closed', readonly=True), + 'stage_id': fields.many2one('crm.case.stage', 'Stage', + domain="['&', ('fold', '=', False), '&', '|', ('section_ids', '=', section_id), ('case_default', '=', True), '|', ('type', '=', type), ('type', '=', 'both')]"), + 'user_id': fields.many2one('res.users', 'Salesperson'), + 'referred': fields.char('Referred By', size=64), + 'date_open': fields.datetime('Opened', readonly=True), + 'day_open': fields.function(_compute_day, string='Days to Open', \ + multi='day_open', type="float", store=True), + 'day_close': fields.function(_compute_day, string='Days to Close', \ + multi='day_close', type="float", store=True), + 'state': fields.related('stage_id', 'state', type="selection", store=True, + selection=crm.AVAILABLE_STATES, string="Status", readonly=True, + help='The Status is set to \'Draft\', when a case is created. If the case is in progress the Status is set to \'Open\'. When the case is over, the Status is set to \'Done\'. If the case needs to be reviewed then the Status is set to \'Pending\'.'), + + # Only used for type opportunity + 'probability': fields.float('Success Rate (%)',group_operator="avg"), + 'planned_revenue': fields.float('Expected Revenue'), + 'ref': fields.reference('Reference', selection=crm._links_get, size=128), + 'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128), + 'phone': fields.char("Phone", size=64), + 'date_deadline': fields.date('Expected Closing', help="Estimate of the date on which the opportunity will be won."), + 'date_action': fields.date('Next Action Date', select=True), + 'title_action': fields.char('Next Action', size=64), + 'color': fields.integer('Color Index'), + 'partner_address_name': fields.related('partner_id', 'name', type='char', string='Partner Contact Name', readonly=True), + 'partner_address_email': fields.related('partner_id', 'email', type='char', string='Partner Contact Email', readonly=True), + 'company_currency': fields.related('company_id', 'currency_id', type='many2one', string='Currency', readonly=True, relation="res.currency"), + 'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True), + 'user_login': fields.related('user_id', 'login', type='char', string='User Login', readonly=True), + + # Fields for address, due to separation from crm and res.partner + 'street': fields.char('Street', size=128), + 'street2': fields.char('Street2', size=128), + 'zip': fields.char('Zip', change_default=True, size=24), + 'city': fields.char('City', size=128), + 'state_id': fields.many2one("res.country.state", 'State'), + 'country_id': fields.many2one('res.country', 'Country'), + 'phone': fields.char('Phone', size=64), + 'fax': fields.char('Fax', size=64), + 'mobile': fields.char('Mobile', size=64), + 'function': fields.char('Function', size=128), + 'title': fields.many2one('res.partner.title', 'Title'), + 'company_id': fields.many2one('res.company', 'Company', select=1), + 'payment_mode': fields.many2one('crm.payment.mode', 'Payment Mode', \ + domain="[('section_id','=',section_id)]"), + 'planned_cost': fields.float('Planned Costs'), + } + + _defaults = { + 'active': 1, + 'type': 'lead', + 'user_id': lambda s, cr, uid, c: s._get_default_user(cr, uid, c), + 'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c), + 'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c), + 'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c), + '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], + 'color': 0, + } + + +class base_action_rule_test(common.TransactionCase): + + def setUp(self): + """*****setUp*****""" + super(base_action_rule_test, self).setUp() + cr, uid = self.cr, self.uid + + """********TeamCreation******""" + self.team_pool = self.pool.get('crm.case.section') + self.team_1_id = self.team_pool.create(cr, uid, { + + }, context=None) + + self.myLead_pool = self.pool.get('base_action_rule.myLead') + self.new_myLead_id = self.myLead_pool.create(cr, uid, { + 'name' : "my_first_lead", + 'section_id' : + }, context=None) From a8f351029755cfa4564c8c90931d28f3a3143420 Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Tue, 27 Nov 2012 10:07:55 +0100 Subject: [PATCH 13/87] [TEST] automated action rule tests bzr revid: api@openerp.com-20121127090755-03sfycnz8nw9ldnv --- .../test/base_action_rule_test.py | 111 ------------------ addons/crm/tests/__init__.py | 27 +++++ addons/crm/tests/base_action_rule_test.py | 69 +++++++++++ 3 files changed, 96 insertions(+), 111 deletions(-) delete mode 100644 addons/base_action_rule/test/base_action_rule_test.py create mode 100644 addons/crm/tests/__init__.py create mode 100644 addons/crm/tests/base_action_rule_test.py diff --git a/addons/base_action_rule/test/base_action_rule_test.py b/addons/base_action_rule/test/base_action_rule_test.py deleted file mode 100644 index 41eb0e31746..00000000000 --- a/addons/base_action_rule/test/base_action_rule_test.py +++ /dev/null @@ -1,111 +0,0 @@ -import tools -from openerp.tests import common - -class myLead(orm.Model): - - _name = name('base_action_rule.myLead') - - _columns = { - 'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', - select=True, help="Linked partner (optional). Usually created when converting the lead."), - 'id': fields.integer('ID', readonly=True), - 'name': fields.char('Subject', size=64, required=True, select=1), - 'active': fields.boolean('Active', required=False), - 'date_action_last': fields.datetime('Last Action', readonly=1), - 'date_action_next': fields.datetime('Next Action', readonly=1), - 'email_from': fields.char('Email', size=128, help="Email address of the contact", select=1), - 'section_id': fields.many2one('crm.case.section', 'Sales Team', \ - select=True, help='When sending mails, the default email address is taken from the sales team.'), - 'create_date': fields.datetime('Creation Date' , readonly=True), - 'email_cc': fields.text('Global CC', size=252 , help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"), - 'description': fields.text('Notes'), - 'write_date': fields.datetime('Update Date' , readonly=True), - 'categ_ids': fields.many2many('crm.case.categ', 'crm_lead_category_rel', 'lead_id', 'category_id', 'Categories', \ - domain="['|',('section_id','=',section_id),('section_id','=',False), ('object_id.model', '=', 'crm.lead')]"), - 'type_id': fields.many2one('crm.case.resource.type', 'Campaign', \ - domain="['|',('section_id','=',section_id),('section_id','=',False)]", help="From which campaign (seminar, marketing campaign, mass mailing, ...) did this contact come from?"), - 'channel_id': fields.many2one('crm.case.channel', 'Channel', help="Communication channel (mail, direct, phone, ...)"), - 'contact_name': fields.char('Contact Name', size=64), - 'partner_name': fields.char("Customer Name", size=64,help='The name of the future partner company that will be created while converting the lead into opportunity', select=1), - 'opt_out': fields.boolean('Opt-Out', oldname='optout', help="If opt-out is checked, this contact has refused to receive emails or unsubscribed to a campaign."), - 'type':fields.selection([ ('lead','Lead'), ('opportunity','Opportunity'), ],'Type', help="Type is used to separate Leads and Opportunities"), - 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True), - 'date_closed': fields.datetime('Closed', readonly=True), - 'stage_id': fields.many2one('crm.case.stage', 'Stage', - domain="['&', ('fold', '=', False), '&', '|', ('section_ids', '=', section_id), ('case_default', '=', True), '|', ('type', '=', type), ('type', '=', 'both')]"), - 'user_id': fields.many2one('res.users', 'Salesperson'), - 'referred': fields.char('Referred By', size=64), - 'date_open': fields.datetime('Opened', readonly=True), - 'day_open': fields.function(_compute_day, string='Days to Open', \ - multi='day_open', type="float", store=True), - 'day_close': fields.function(_compute_day, string='Days to Close', \ - multi='day_close', type="float", store=True), - 'state': fields.related('stage_id', 'state', type="selection", store=True, - selection=crm.AVAILABLE_STATES, string="Status", readonly=True, - help='The Status is set to \'Draft\', when a case is created. If the case is in progress the Status is set to \'Open\'. When the case is over, the Status is set to \'Done\'. If the case needs to be reviewed then the Status is set to \'Pending\'.'), - - # Only used for type opportunity - 'probability': fields.float('Success Rate (%)',group_operator="avg"), - 'planned_revenue': fields.float('Expected Revenue'), - 'ref': fields.reference('Reference', selection=crm._links_get, size=128), - 'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128), - 'phone': fields.char("Phone", size=64), - 'date_deadline': fields.date('Expected Closing', help="Estimate of the date on which the opportunity will be won."), - 'date_action': fields.date('Next Action Date', select=True), - 'title_action': fields.char('Next Action', size=64), - 'color': fields.integer('Color Index'), - 'partner_address_name': fields.related('partner_id', 'name', type='char', string='Partner Contact Name', readonly=True), - 'partner_address_email': fields.related('partner_id', 'email', type='char', string='Partner Contact Email', readonly=True), - 'company_currency': fields.related('company_id', 'currency_id', type='many2one', string='Currency', readonly=True, relation="res.currency"), - 'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True), - 'user_login': fields.related('user_id', 'login', type='char', string='User Login', readonly=True), - - # Fields for address, due to separation from crm and res.partner - 'street': fields.char('Street', size=128), - 'street2': fields.char('Street2', size=128), - 'zip': fields.char('Zip', change_default=True, size=24), - 'city': fields.char('City', size=128), - 'state_id': fields.many2one("res.country.state", 'State'), - 'country_id': fields.many2one('res.country', 'Country'), - 'phone': fields.char('Phone', size=64), - 'fax': fields.char('Fax', size=64), - 'mobile': fields.char('Mobile', size=64), - 'function': fields.char('Function', size=128), - 'title': fields.many2one('res.partner.title', 'Title'), - 'company_id': fields.many2one('res.company', 'Company', select=1), - 'payment_mode': fields.many2one('crm.payment.mode', 'Payment Mode', \ - domain="[('section_id','=',section_id)]"), - 'planned_cost': fields.float('Planned Costs'), - } - - _defaults = { - 'active': 1, - 'type': 'lead', - 'user_id': lambda s, cr, uid, c: s._get_default_user(cr, uid, c), - 'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c), - 'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c), - 'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c), - '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], - 'color': 0, - } - - -class base_action_rule_test(common.TransactionCase): - - def setUp(self): - """*****setUp*****""" - super(base_action_rule_test, self).setUp() - cr, uid = self.cr, self.uid - - """********TeamCreation******""" - self.team_pool = self.pool.get('crm.case.section') - self.team_1_id = self.team_pool.create(cr, uid, { - - }, context=None) - - self.myLead_pool = self.pool.get('base_action_rule.myLead') - self.new_myLead_id = self.myLead_pool.create(cr, uid, { - 'name' : "my_first_lead", - 'section_id' : - }, context=None) diff --git a/addons/crm/tests/__init__.py b/addons/crm/tests/__init__.py new file mode 100644 index 00000000000..a2de50226da --- /dev/null +++ b/addons/crm/tests/__init__.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. +# +# 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 . +# +############################################################################## +from . import base_action_rule_test + +checks = [ + base_action_rule_test, +] + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm/tests/base_action_rule_test.py b/addons/crm/tests/base_action_rule_test.py new file mode 100644 index 00000000000..caca0c635da --- /dev/null +++ b/addons/crm/tests/base_action_rule_test.py @@ -0,0 +1,69 @@ +import tools +from openerp.tests import common + +class base_action_rule_test(common.TransactionCase): + + def setUp(self): + """*****setUp*****""" + super(base_action_rule_test, self).setUp() + cr, uid = self.cr, self.uid + + """*****TeamCreation******""" + self.team_pool = self.registry('crm.case.section') + self.team_1_id = self.team_pool.create(cr, uid, { + 'name' : "Team 1", + 'active' : 1, + 'user_id' : uid, + 'complete_name' : "Test / Team 1", + }, context=None) + self.team_2_id = self.team_pool.create(cr, uid, { + 'name' : "Team 2", + 'active' : 1, + 'user_id' : uid, + 'complete_name' : "Test / Team 2", + }, context=None) + + def create_rule_1(self, cr, uid, context=None): + """ + The "Rule 1" says that when a lead goes to the 'open' state, the team responsible for that lead changes to "Team 1" + """ + self.action_pool = self.registry('base.action.rule') + self.rule_1_id = self.action_pool.create(cr,uid,{ + 'name' : "Rule 1", + 'model_id' : self.registry('ir.model').search(cr, uid, [('model','=','crm.lead')], context=context)[0], + 'active' : 1, + 'trg_state_to' : 'open', + 'trg_date_type' : 'none', + 'act_section_id' : self.team_1_id, + }, context=context) + + def create_rule_2(self, cr, uid, context=None): + """ + The "Rule 2" says that when a lead goes from 'open' state to 'done' state, the team responsible for that lead changes to "Team 2" + """ + self.action_pool = self.registry('base.action.rule') + self.rule_2_id = self.action_pool.create(cr,uid,{ + 'name' : "Rule 2", + 'model_id' : self.registry('ir.model').search(cr, uid, [('model','=','crm.lead')], context=context)[0], + 'active' : 1, + 'trg_state_to' : 'done', + 'trg_state_from' : 'open', + 'trg_date_type' : 'none', + 'act_section_id' : self.team_2_id, + }, context=context) + + def test_00_check_to_state_draft(self): + """ + This test check that the team change when the state is open but doesn't change when the state is different + """ + cr, uid = self.cr, self.uid + context = {} + #First we need to create the Rule 1 + self.create_rule_1(cr, uid, context) + #Once it is done, we can create a new lead (with a state = 'draft') + #first we get our team 1 + self.team_1 = self.team_pool.browse(cr,uid,self.team_1_id,context=context) + #We get a lead + self.lead_1 = self.registry('ir.model.data').get_object_reference(cr, uid, 'crm', 'crm_case_1') + #We change the team of crm_case_1 + From 3df8c5fe9a048eac11d3c50e69bf3263f5b3b980 Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Wed, 28 Nov 2012 10:29:28 +0100 Subject: [PATCH 14/87] [IMP] Base Action Rule (only filter conditions) bzr revid: api@openerp.com-20121128092928-7kbs1d9g8ung4oul --- addons/base_action_rule/__init__.py | 1 + addons/base_action_rule/base_action_rule.py | 73 ++++----- .../base_action_rule_view.xml | 32 ++-- addons/base_action_rule/test_models.py | 32 ++++ .../tests/__init__.py | 0 .../tests/base_action_rule_test.py | 142 ++++++++++++++++++ addons/crm/crm_action_rule.py | 4 +- addons/crm/crm_action_rule_view.xml | 10 -- addons/crm/tests/base_action_rule_test.py | 69 --------- 9 files changed, 228 insertions(+), 135 deletions(-) create mode 100644 addons/base_action_rule/test_models.py rename addons/{crm => base_action_rule}/tests/__init__.py (100%) create mode 100644 addons/base_action_rule/tests/base_action_rule_test.py delete mode 100644 addons/crm/tests/base_action_rule_test.py diff --git a/addons/base_action_rule/__init__.py b/addons/base_action_rule/__init__.py index 207ed3cb30f..c8c827aaba2 100644 --- a/addons/base_action_rule/__init__.py +++ b/addons/base_action_rule/__init__.py @@ -20,5 +20,6 @@ ############################################################################## import base_action_rule +import test_models # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 14da227a367..2b6877a7c9c 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -97,7 +97,8 @@ trigger date, like sending a reminder 15 minutes before a meeting."), \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)] + 'filter_id':fields.many2one('ir.filters', 'Precondition Filter', required=False), #TODO: set domain [('model_id','=',model_id.model)] + 'filter_post_id': fields.many2one('ir.filters', 'Postcondition Filter', required=False), 'last_run': fields.datetime('Last Run', readonly=1), } @@ -110,7 +111,7 @@ trigger date, like sending a reminder 15 minutes before a meeting."), _order = 'sequence' - def post_action(self, cr, uid, ids, model, old_records=None, context=None): + def post_action(self, cr, uid, ids, model, precondition_ok=None, 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) \ @@ -122,7 +123,7 @@ trigger date, like sending a reminder 15 minutes before a meeting."), # 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], model_pool.browse(cr, uid, ids, context=context), old_records=old_records, context=context) + self._action(cr, uid, [rule_id], model_pool.browse(cr, uid, ids, context=context), precondition_ok=precondition_ok, context=context) return True def _create(self, old_create, model, context=None): @@ -134,8 +135,17 @@ trigger date, like sending a reminder 15 minutes before a meeting."), if context is None: context = {} new_id = old_create(cr, uid, vals, context=context) + #As it is a new record, we can assume that the precondition is true for every filter. + #(There is nothing before the create so no condition) + precondition_ok = {} + precondition_ok[new_id] = {} + for action in self.browse(cr, uid, self.search(cr, uid, [], context=context), context=context): + if action.filter_id: + precondition_ok[new_id][action.id] = False + else: + precondition_ok[new_id][action.id] = True if not context.get('action'): - self.post_action(cr, uid, [new_id], model, context=context) + self.post_action(cr, uid, [new_id], model, precondition_ok=precondition_ok, context=context) return new_id return wrapper @@ -151,15 +161,22 @@ trigger date, like sending a reminder 15 minutes before a meeting."), if isinstance(ids, (str, int, long)): ids = [ids] model_pool = self.pool.get(model) - # get the old records (before the write) - if model and ids: - old_records = model_pool.browse(cr,uid,ids,context=context) - # look at records' states to fill the record cache - for record in old_records: - record.state + # We check for the pre-filter. We must apply it before the write + precondition_ok = {} + for id in ids: + precondition_ok[id] = {} + for action in self.browse(cr, uid, self.search(cr, uid, [], context=context), context=context): + precondition_ok[id][action.id] = True + if action.filter_id and action.model_id.model == action.filter_id.model_id: + ctx = dict(context) + ctx.update(eval(action.filter_id.context)) + obj_ids = [] + if self.pool.get(action.model_id.model)!=None: + obj_ids = self.pool.get(action.model_id.model).search(cr, uid, eval(action.filter_id.domain), context=ctx) + precondition_ok[id][action.id] = id in obj_ids old_write(cr, uid, ids, vals, context=context) if not context.get('action'): - self.post_action(cr, uid, ids, model, old_records=old_records, context=context) + self.post_action(cr, uid, ids, model, precondition_ok=precondition_ok, context=context) return True return wrapper @@ -263,15 +280,15 @@ trigger date, like sending a reminder 15 minutes before a meeting."), - def do_check(self, cr, uid, action, obj, old_records=None, context=None): + def do_check(self, cr, uid, action, obj, precondition_ok=True, 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: + ok = precondition_ok + if action.filter_post_id and action.model_id.model == action.filter_post_id.model_id: 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) + ctx.update(eval(action.filter_post_id.context)) + obj_ids = self.pool.get(action.model_id.model).search(cr, uid, eval(action.filter_post_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) @@ -284,22 +301,6 @@ trigger date, like sending a reminder 15 minutes before a meeting."), (action.trg_partner_categ_id.id in map(lambda x: x.id, obj.partner_id.category_id or [])) ) ) - #state_to = the state after a write or a create - state_to = getattr(obj, 'state', False) - #state_from = nothing or the state of old_records - state_from = "" - if old_records != None: - for record in old_records: - state_from = record.state - else: - state_from = "na" #it means that there was no state before (creation for example) - #if we have an action that check the status - if action.trg_state_to: - if action.trg_state_from: - ok = ok and action.trg_state_from==state_from - else: - ok = state_from!=state_to - ok = ok and action.trg_state_to==state_to reg_name = action.regex_name result_name = True if reg_name: @@ -341,7 +342,7 @@ trigger date, like sending a reminder 15 minutes before a meeting."), model_obj.message_subscribe(cr, uid, [obj.id], new_followers, context=context) return True - def _action(self, cr, uid, ids, objects, scrit=None, old_records=None, context=None): + def _action(self, cr, uid, ids, objects, scrit=None, precondition_ok=None, context=None): """ Do Action """ if context is None: context = {} @@ -350,9 +351,11 @@ trigger date, like sending a reminder 15 minutes before a meeting."), objects = [objects] for action in self.browse(cr, uid, ids, context=context): for obj in objects: - if self.do_check(cr, uid, action, obj, old_records=old_records, context=context): + ok = True + if precondition_ok!=None: + ok = precondition_ok[obj.id][action.id] + if self.do_check(cr, uid, action, obj, precondition_ok=ok, context=context): self.do_action(cr, uid, action, obj, context=context) - context.update({'action': False}) return True diff --git a/addons/base_action_rule/base_action_rule_view.xml b/addons/base_action_rule/base_action_rule_view.xml index 4815baa9ebb..3bd90457828 100644 --- a/addons/base_action_rule/base_action_rule_view.xml +++ b/addons/base_action_rule/base_action_rule_view.xml @@ -15,36 +15,31 @@ - - - + + +

+ Select a filter or a timer as condition.
+ To create a new filter,
+ - Go to your Model's page and set the filter parameters in the "Search" view
+ - In this same "Search" view, select the menu "Save Current Filter", enter the name and add the option "Share with all users"
+ The filter must therefore be available in this page. +

- - - + + + - - - - - - - - - + - -
@@ -63,7 +58,6 @@
- diff --git a/addons/base_action_rule/test_models.py b/addons/base_action_rule/test_models.py new file mode 100644 index 00000000000..9469b42c705 --- /dev/null +++ b/addons/base_action_rule/test_models.py @@ -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 diff --git a/addons/crm/tests/__init__.py b/addons/base_action_rule/tests/__init__.py similarity index 100% rename from addons/crm/tests/__init__.py rename to addons/base_action_rule/tests/__init__.py diff --git a/addons/base_action_rule/tests/base_action_rule_test.py b/addons/base_action_rule/tests/base_action_rule_test.py new file mode 100644 index 00000000000..6f1c885df69 --- /dev/null +++ b/addons/base_action_rule/tests/base_action_rule_test.py @@ -0,0 +1,142 @@ +import tools +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_user = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'user_demo') + self.admin_user = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'user_admin') + + 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 + """ + lead_pool = self.registry('base.action.rule.lead.test') + return lead_pool.create(cr, uid, { + 'name': "Lead Test 1", + 'user_id': self.admin_user[1], + }, context=context) + + def create_rule(self, cr, uid, filter_id=None, filter_post_id=None, context=None): + """ + The "Rule 1" says that when a lead goes to the 'draft' state, the responsible for that lead changes to "demo_user" + """ + self.action_pool = self.registry('base.action.rule') + return self.action_pool.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, + 'trg_date_type' : 'none', + 'filter_post_id' : filter_post_id, + 'filter_id' : filter_id, + 'act_user_id': self.demo_user[1], + }, 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 + context = {} + filter_draft = self.create_filter_draft(cr, uid, context=context) + rule_1_id = self.create_rule(cr, uid, filter_id=filter_draft, context=context) + new_lead_id = self.create_lead_test_1(cr,uid,context=context) + new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) + self.assertTrue(new_lead.state=='draft') + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + + 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 + context = {} + filter_draft = self.create_filter_draft(cr, uid, context=context) + rule_1_id = self.create_rule(cr, uid, filter_post_id=filter_draft, context=context) + new_lead_id = self.create_lead_test_1(cr,uid,context=context) + new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) + self.assertTrue(new_lead.state=='draft') + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.demo_user[1], context=context)) + + 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 + context = {} + filter_draft = self.create_filter_draft(cr, uid, context=context) + filter_done = self.create_filter_done(cr, uid, context=context) + self.create_rule(cr, uid, filter_id=filter_draft, filter_post_id=filter_done, context=context) + new_lead_id = self.create_lead_test_1(cr,uid,context=context) + new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) + self.assertTrue(new_lead.state=='draft') + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + """ change the state of new_lead to open and check that responsible doen't change""" + new_lead.write({'state': 'open'}, context=context) + new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) + self.assertTrue(new_lead.state=='open') + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + """ change the state of new_lead to pending and check that responsible doen't change""" + new_lead.write({'state': 'pending'}, context=context) + new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) + self.assertTrue(new_lead.state=='pending') + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + """ change the state of new_lead to cancel and check that responsible doen't change""" + new_lead.write({'state': 'cancel'}, context=context) + new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) + self.assertTrue(new_lead.state=='cancel') + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + """ change the state of new_lead to done and check that responsible doen't change """ + new_lead.write({'state': 'done'}, context=context) + new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) + self.assertTrue(new_lead.state=='done') + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + + 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 "demo_user" + """ + cr, uid = self.cr, self.uid + context = {} + filter_draft = self.create_filter_draft(cr, uid, context=context) + filter_done = self.create_filter_done(cr, uid, context=context) + self.create_rule(cr, uid, filter_id=filter_draft, filter_post_id=filter_done, context=context) + new_lead_id = self.create_lead_test_1(cr,uid,context=context) + new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) + self.assertTrue(new_lead.state=='draft') + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + """ change the state of new_lead to done and check that responsible change to Demo_user""" + new_lead.write({'state': 'done'}, context=context) + new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) + self.assertTrue(new_lead.state=='done') + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.demo_user[1], context=context)) \ No newline at end of file diff --git a/addons/crm/crm_action_rule.py b/addons/crm/crm_action_rule.py index 72582a1f21e..74fc749f33c 100644 --- a/addons/crm/crm_action_rule.py +++ b/addons/crm/crm_action_rule.py @@ -43,8 +43,8 @@ class base_action_rule(osv.osv): 'act_categ_id': fields.many2one('crm.case.categ', 'Set Category to'), } - def do_check(self, cr, uid, action, obj, old_records=None, context=None): - ok = super(base_action_rule, self).do_check(cr, uid, action, obj, old_records=old_records, context=context) + def do_check(self, cr, uid, action, obj, precondition_ok=True, context=None): + ok = super(base_action_rule, self).do_check(cr, uid, action, obj, precondition_ok=precondition_ok, 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) diff --git a/addons/crm/crm_action_rule_view.xml b/addons/crm/crm_action_rule_view.xml index e21b74de382..347e54c46ac 100644 --- a/addons/crm/crm_action_rule_view.xml +++ b/addons/crm/crm_action_rule_view.xml @@ -7,16 +7,6 @@ base.action.rule - - - - - - - - - - diff --git a/addons/crm/tests/base_action_rule_test.py b/addons/crm/tests/base_action_rule_test.py deleted file mode 100644 index caca0c635da..00000000000 --- a/addons/crm/tests/base_action_rule_test.py +++ /dev/null @@ -1,69 +0,0 @@ -import tools -from openerp.tests import common - -class base_action_rule_test(common.TransactionCase): - - def setUp(self): - """*****setUp*****""" - super(base_action_rule_test, self).setUp() - cr, uid = self.cr, self.uid - - """*****TeamCreation******""" - self.team_pool = self.registry('crm.case.section') - self.team_1_id = self.team_pool.create(cr, uid, { - 'name' : "Team 1", - 'active' : 1, - 'user_id' : uid, - 'complete_name' : "Test / Team 1", - }, context=None) - self.team_2_id = self.team_pool.create(cr, uid, { - 'name' : "Team 2", - 'active' : 1, - 'user_id' : uid, - 'complete_name' : "Test / Team 2", - }, context=None) - - def create_rule_1(self, cr, uid, context=None): - """ - The "Rule 1" says that when a lead goes to the 'open' state, the team responsible for that lead changes to "Team 1" - """ - self.action_pool = self.registry('base.action.rule') - self.rule_1_id = self.action_pool.create(cr,uid,{ - 'name' : "Rule 1", - 'model_id' : self.registry('ir.model').search(cr, uid, [('model','=','crm.lead')], context=context)[0], - 'active' : 1, - 'trg_state_to' : 'open', - 'trg_date_type' : 'none', - 'act_section_id' : self.team_1_id, - }, context=context) - - def create_rule_2(self, cr, uid, context=None): - """ - The "Rule 2" says that when a lead goes from 'open' state to 'done' state, the team responsible for that lead changes to "Team 2" - """ - self.action_pool = self.registry('base.action.rule') - self.rule_2_id = self.action_pool.create(cr,uid,{ - 'name' : "Rule 2", - 'model_id' : self.registry('ir.model').search(cr, uid, [('model','=','crm.lead')], context=context)[0], - 'active' : 1, - 'trg_state_to' : 'done', - 'trg_state_from' : 'open', - 'trg_date_type' : 'none', - 'act_section_id' : self.team_2_id, - }, context=context) - - def test_00_check_to_state_draft(self): - """ - This test check that the team change when the state is open but doesn't change when the state is different - """ - cr, uid = self.cr, self.uid - context = {} - #First we need to create the Rule 1 - self.create_rule_1(cr, uid, context) - #Once it is done, we can create a new lead (with a state = 'draft') - #first we get our team 1 - self.team_1 = self.team_pool.browse(cr,uid,self.team_1_id,context=context) - #We get a lead - self.lead_1 = self.registry('ir.model.data').get_object_reference(cr, uid, 'crm', 'crm_case_1') - #We change the team of crm_case_1 - From 7e8533d278979a3f31c13c342aa9eafe757df493 Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Wed, 28 Nov 2012 13:01:00 +0100 Subject: [PATCH 15/87] [FIX] old actions based on filters still work bzr revid: api@openerp.com-20121128120100-58xrtp54txigz0b5 --- addons/base_action_rule/base_action_rule.py | 18 +++++++++--------- .../base_action_rule/base_action_rule_view.xml | 2 +- .../tests/base_action_rule_test.py | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 2b6877a7c9c..de3362c6644 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -97,8 +97,8 @@ trigger date, like sending a reminder 15 minutes before a meeting."), \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', 'Precondition Filter', required=False), #TODO: set domain [('model_id','=',model_id.model)] - 'filter_post_id': fields.many2one('ir.filters', 'Postcondition Filter', required=False), + 'filter_id':fields.many2one('ir.filters', 'Postcondition Filter', required=False), #TODO: set domain [('model_id','=',model_id.model)] + 'filter_pre_id': fields.many2one('ir.filters', 'Precondition Filter', required=False), 'last_run': fields.datetime('Last Run', readonly=1), } @@ -140,7 +140,7 @@ trigger date, like sending a reminder 15 minutes before a meeting."), precondition_ok = {} precondition_ok[new_id] = {} for action in self.browse(cr, uid, self.search(cr, uid, [], context=context), context=context): - if action.filter_id: + if action.filter_pre_id: precondition_ok[new_id][action.id] = False else: precondition_ok[new_id][action.id] = True @@ -167,12 +167,12 @@ trigger date, like sending a reminder 15 minutes before a meeting."), precondition_ok[id] = {} for action in self.browse(cr, uid, self.search(cr, uid, [], context=context), context=context): precondition_ok[id][action.id] = True - if action.filter_id and action.model_id.model == action.filter_id.model_id: + if action.filter_pre_id and action.model_id.model == action.filter_pre_id.model_id: ctx = dict(context) - ctx.update(eval(action.filter_id.context)) + ctx.update(eval(action.filter_pre_id.context)) obj_ids = [] if self.pool.get(action.model_id.model)!=None: - obj_ids = self.pool.get(action.model_id.model).search(cr, uid, eval(action.filter_id.domain), context=ctx) + obj_ids = self.pool.get(action.model_id.model).search(cr, uid, eval(action.filter_pre_id.domain), context=ctx) precondition_ok[id][action.id] = id in obj_ids old_write(cr, uid, ids, vals, context=context) if not context.get('action'): @@ -285,10 +285,10 @@ trigger date, like sending a reminder 15 minutes before a meeting."), if context is None: context = {} ok = precondition_ok - if action.filter_post_id and action.model_id.model == action.filter_post_id.model_id: + if action.filter_id and action.model_id.model == action.filter_id.model_id: ctx = dict(context) - ctx.update(eval(action.filter_post_id.context)) - obj_ids = self.pool.get(action.model_id.model).search(cr, uid, eval(action.filter_post_id.domain), context=ctx) + 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) diff --git a/addons/base_action_rule/base_action_rule_view.xml b/addons/base_action_rule/base_action_rule_view.xml index 3bd90457828..257465b490a 100644 --- a/addons/base_action_rule/base_action_rule_view.xml +++ b/addons/base_action_rule/base_action_rule_view.xml @@ -31,8 +31,8 @@

+ - diff --git a/addons/base_action_rule/tests/base_action_rule_test.py b/addons/base_action_rule/tests/base_action_rule_test.py index 6f1c885df69..0452401ce23 100644 --- a/addons/base_action_rule/tests/base_action_rule_test.py +++ b/addons/base_action_rule/tests/base_action_rule_test.py @@ -39,7 +39,7 @@ class base_action_rule_test(common.TransactionCase): 'user_id': self.admin_user[1], }, context=context) - def create_rule(self, cr, uid, filter_id=None, filter_post_id=None, context=None): + def create_rule(self, cr, uid, filter_id=None, filter_pre_id=None, context=None): """ The "Rule 1" says that when a lead goes to the 'draft' state, the responsible for that lead changes to "demo_user" """ @@ -49,7 +49,7 @@ class base_action_rule_test(common.TransactionCase): 'model_id': self.registry('ir.model').search(cr, uid, [('model','=','base.action.rule.lead.test')], context=context)[0], 'active' : 1, 'trg_date_type' : 'none', - 'filter_post_id' : filter_post_id, + 'filter_pre_id' : filter_pre_id, 'filter_id' : filter_id, 'act_user_id': self.demo_user[1], }, context=context) @@ -61,7 +61,7 @@ class base_action_rule_test(common.TransactionCase): cr, uid = self.cr, self.uid context = {} filter_draft = self.create_filter_draft(cr, uid, context=context) - rule_1_id = self.create_rule(cr, uid, filter_id=filter_draft, context=context) + rule_1_id = self.create_rule(cr, uid, filter_pre_id=filter_draft, context=context) new_lead_id = self.create_lead_test_1(cr,uid,context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='draft') @@ -74,7 +74,7 @@ class base_action_rule_test(common.TransactionCase): cr, uid = self.cr, self.uid context = {} filter_draft = self.create_filter_draft(cr, uid, context=context) - rule_1_id = self.create_rule(cr, uid, filter_post_id=filter_draft, context=context) + rule_1_id = self.create_rule(cr, uid, filter_id=filter_draft, context=context) new_lead_id = self.create_lead_test_1(cr,uid,context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='draft') @@ -92,7 +92,7 @@ class base_action_rule_test(common.TransactionCase): context = {} filter_draft = self.create_filter_draft(cr, uid, context=context) filter_done = self.create_filter_done(cr, uid, context=context) - self.create_rule(cr, uid, filter_id=filter_draft, filter_post_id=filter_done, context=context) + self.create_rule(cr, uid, filter_pre_id=filter_draft, filter_id=filter_done, context=context) new_lead_id = self.create_lead_test_1(cr,uid,context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='draft') @@ -130,7 +130,7 @@ class base_action_rule_test(common.TransactionCase): context = {} filter_draft = self.create_filter_draft(cr, uid, context=context) filter_done = self.create_filter_done(cr, uid, context=context) - self.create_rule(cr, uid, filter_id=filter_draft, filter_post_id=filter_done, context=context) + self.create_rule(cr, uid, filter_pre_id=filter_draft, filter_id=filter_done, context=context) new_lead_id = self.create_lead_test_1(cr,uid,context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='draft') From 2b0a7a577654a8d9db973a139f91e1bb31054f65 Mon Sep 17 00:00:00 2001 From: "Nimesh (Open ERP)" Date: Tue, 4 Dec 2012 15:31:06 +0530 Subject: [PATCH 16/87] [FIX] fix the problem of partial payment and purchase invoice will go to paid status even if the price is 0 in product. bzr revid: nco@tinyerp.com-20121204100106-64i2borf2dyj8jt8 --- addons/account/account_invoice.py | 7 +++++++ addons/account_voucher/account_voucher.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index f89f7828269..75ce4daef5b 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -674,6 +674,9 @@ class account_invoice(osv.osv): return super(account_invoice, self).copy(cr, uid, id, default, context) def test_paid(self, cr, uid, ids, *args): + for inv in self.browse(cr, uid, ids): + if inv.amount_total == 0.0: + return True res = self.move_line_id_payment_get(cr, uid, ids) if not res: return False @@ -851,6 +854,8 @@ class account_invoice(osv.osv): if context is None: context = {} for inv in self.browse(cr, uid, ids, context=context): + if inv.amount_total == 0.0: + continue if not inv.journal_id.sequence_id: raise osv.except_osv(_('Error!'), _('Please define sequence on the journal related to this invoice.')) if not inv.invoice_line: @@ -1037,6 +1042,8 @@ class account_invoice(osv.osv): self.write(cr, uid, ids, {}) for obj_inv in self.browse(cr, uid, ids, context=context): + if obj_inv.amount_total == 0.0: + continue id = obj_inv.id invtype = obj_inv.type number = obj_inv.number diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index ffc7cf1769d..f3a90b70f83 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -596,7 +596,7 @@ 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 + sign = 1 if ttype in ['payment', 'receipt'] else -1 if currency_id == line.currency_id.id: if line.amount_residual_currency * sign <= 0: return True From b04a134a16e2559b594a536276928ef9cc6f56a4 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Tue, 4 Dec 2012 11:23:04 +0100 Subject: [PATCH 17/87] [FIX] view_form: m2m_tags, when a user quick create a tag, select a tags in the list or blurred and click on the arrow, the last selection is persistant. Overwrite onSetInputData to reset suggestions if input is empty. bzr revid: chm@openerp.com-20121204102304-iru8pcjlca8zh5fj --- addons/web/static/src/js/view_form.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 21c3b3f43d9..5a573a1244e 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -3986,6 +3986,14 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in return item.name; }, }, + core: { + onSetInputData: function(e, data) { + if (data == '') { + this._plugins.autocomplete._suggestions = null; + } + this.input().val(data); + }, + }, }, }).bind('getSuggestions', function(e, data) { var _this = this; From 638dcfce3488a6849a109a7220d8164e15bbd777 Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Tue, 4 Dec 2012 16:36:17 +0100 Subject: [PATCH 18/87] [IMP] base_action_rule : delete unused fields and add explanation lp bug: https://launchpad.net/bugs/944197 fixed bzr revid: api@openerp.com-20121204153617-ju3yyj2tt3d84jl9 --- addons/base_action_rule/base_action_rule.py | 57 ------------------- .../base_action_rule_view.xml | 8 +-- addons/crm/crm_action_rule.py | 32 ----------- addons/crm/crm_action_rule_demo.xml | 1 - addons/crm/test/process/action_rule.yml | 20 ------- 5 files changed, 4 insertions(+), 114 deletions(-) delete mode 100644 addons/crm/test/process/action_rule.yml diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index de3362c6644..00b179655f0 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -84,18 +84,9 @@ 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'), \ ('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, 'and previously was', size=16), - 'trg_state_to': fields.selection(_state_get, 'Status changes to', 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', 'Postcondition Filter', required=False), #TODO: set domain [('model_id','=',model_id.model)] 'filter_pre_id': fields.many2one('ir.filters', 'Precondition Filter', required=False), @@ -290,26 +281,6 @@ trigger date, like sending a reminder 15 minutes before a meeting."), 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 [])) - ) - ) - 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 def do_action(self, cr, uid, action, obj, context=None): @@ -359,38 +330,10 @@ trigger date, like sending a reminder 15 minutes before a meeting."), 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: diff --git a/addons/base_action_rule/base_action_rule_view.xml b/addons/base_action_rule/base_action_rule_view.xml index 257465b490a..aab9ce0000c 100644 --- a/addons/base_action_rule/base_action_rule_view.xml +++ b/addons/base_action_rule/base_action_rule_view.xml @@ -23,10 +23,10 @@

- Select a filter or a timer as condition.
- To create a new filter,
- - Go to your Model's page and set the filter parameters in the "Search" view
- - In this same "Search" view, select the menu "Save Current Filter", enter the name and add the option "Share with all users"
+ Select a filter or a timer as condition.
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.
+ To create a new filter:
+ - 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)
+ - 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"
The filter must therefore be available in this page.

diff --git a/addons/crm/crm_action_rule.py b/addons/crm/crm_action_rule.py index 74fc749f33c..1344b170b9a 100644 --- a/addons/crm/crm_action_rule.py +++ b/addons/crm/crm_action_rule.py @@ -35,42 +35,10 @@ class base_action_rule(osv.osv): _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, precondition_ok=True, context=None): - ok = super(base_action_rule, self).do_check(cr, uid, action, obj, precondition_ok=precondition_ok, 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) diff --git a/addons/crm/crm_action_rule_demo.xml b/addons/crm/crm_action_rule_demo.xml index 83f5681f311..31f07e59034 100644 --- a/addons/crm/crm_action_rule_demo.xml +++ b/addons/crm/crm_action_rule_demo.xml @@ -72,7 +72,6 @@ Thanks, Set Auto Followers on leads which are urgent and come from USA. 2 - urgent.* diff --git a/addons/crm/test/process/action_rule.yml b/addons/crm/test/process/action_rule.yml deleted file mode 100644 index f76e6c250bc..00000000000 --- a/addons/crm/test/process/action_rule.yml +++ /dev/null @@ -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." From 5827f6c200dbf70e8b0e7aeb13facda32ac3c1bb Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Tue, 4 Dec 2012 16:52:10 +0100 Subject: [PATCH 19/87] [IMP] base_action_rule : clear state and stage lp bug: https://launchpad.net/bugs/944197 fixed bzr revid: api@openerp.com-20121204155210-3cjh1153idyax1sh --- addons/base_status/base_stage.py | 13 ------------- addons/base_status/base_state.py | 12 ------------ 2 files changed, 25 deletions(-) diff --git a/addons/base_status/base_stage.py b/addons/base_status/base_stage.py index 5710a31d6a0..72d58553092 100644 --- a/addons/base_status/base_stage.py +++ b/addons/base_status/base_stage.py @@ -217,7 +217,6 @@ class base_stage(object): self.write(cr, uid, [case.id], data, context=context) case.case_escalate_send_note(case.section_id.parent_id, context=context) cases = self.browse(cr, uid, ids, context=context) - self._action(cr, uid, cases, 'escalate', context=context) return True def case_open(self, cr, uid, ids, context=None): @@ -283,18 +282,6 @@ class base_stage(object): self.write(cr, uid, ids, values_to_update, context=context) return True - 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. diff --git a/addons/base_status/base_state.py b/addons/base_status/base_state.py index 97d70268bdf..e8a1dcc6b9f 100644 --- a/addons/base_status/base_state.py +++ b/addons/base_status/base_state.py @@ -107,7 +107,6 @@ class base_state(object): 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) case.case_escalate_send_note(parent_id, context=context) - self._action(cr, uid, cases, 'escalate', context=context) return True def case_open(self, cr, uid, ids, context=None): @@ -163,17 +162,6 @@ class base_state(object): update_values = {} update_values['state'] = state_name self.write(cr, uid, ids, update_values, context=context) - #self._action(cr, uid, cases, state_name, context=context) - - 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) # ****************************** # Notifications From 8381a0c92f5dd5d6c01e1dc53481509ad1ee7a1a Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Tue, 4 Dec 2012 17:20:52 +0100 Subject: [PATCH 20/87] [FIX] base_stage & state bzr revid: api@openerp.com-20121204162052-u3t2vvf4ttcppdic --- addons/base_status/base_stage.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/addons/base_status/base_stage.py b/addons/base_status/base_stage.py index 6e2313be4ac..f88a25c57b4 100644 --- a/addons/base_status/base_stage.py +++ b/addons/base_status/base_stage.py @@ -277,9 +277,6 @@ class base_stage(object): 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 From 0ca480a80d4e87883941f84508c58d36c97d6329 Mon Sep 17 00:00:00 2001 From: Arnaud Pineux Date: Tue, 4 Dec 2012 17:23:27 +0100 Subject: [PATCH 21/87] [FIX] base_stage & state bzr revid: api@openerp.com-20121204162327-uu941ixjjojgc15v --- addons/crm/__openerp__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py index 63d5d3da84b..a438dd2858e 100644 --- a/addons/crm/__openerp__.py +++ b/addons/crm/__openerp__.py @@ -111,7 +111,6 @@ Dashboard for CRM will include: 'test/process/lead2opportunity2win.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', From 87a7c9f8dd810f07a45202cde05de4a7d3e54340 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 19 Dec 2012 14:14:59 +0100 Subject: [PATCH 22/87] [IMP] crm: remove unused import bzr revid: rco@openerp.com-20121219131459-xtbfvfll740dq9rg --- addons/crm/crm_lead.py | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 309ef14efc6..11776f55756 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -29,7 +29,6 @@ from openerp.tools.translate import _ from openerp.tools import html2plaintext from base.res.res_partner import format_address -import pdb CRM_LEAD_FIELDS_TO_MERGE = ['name', 'partner_id', From 95b41b40e984e80a4228b761554fcacab39e8425 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 19 Dec 2012 14:21:38 +0100 Subject: [PATCH 23/87] [IMP] register_hook: improve a bit the code bzr revid: rco@openerp.com-20121219132138-npf12n5l5ug6ujvx --- openerp/modules/loading.py | 7 +++---- openerp/osv/orm.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/openerp/modules/loading.py b/openerp/modules/loading.py index aa756d51a57..fb68642f137 100644 --- a/openerp/modules/loading.py +++ b/openerp/modules/loading.py @@ -426,10 +426,9 @@ def load_modules(db, force_demo=False, status=None, update_module=False): else: _logger.info('Modules loaded.') - # STEP 7: for every model call _register_hook - for models in pool.models: - pool_mod = pool.get(models) - pool_mod._register_hook(cr) + # STEP 7: call _register_hook on every model + for model in pool.models.values(): + model._register_hook(cr) finally: cr.close() diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 7fbf2d90ad0..875723b0989 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -5229,7 +5229,7 @@ class BaseModel(object): resolve_o2m_commands_to_record_dicts = resolve_2many_commands def _register_hook(self, cr): - """ This method is called right after the registry is filled """ + """ stuff to do right after the registry is built """ pass # keep this import here, at top it will cause dependency cycle errors From 080761cb276136dbcc3c1c8a5b2a68c3ca91f3d0 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 19 Dec 2012 15:20:21 +0100 Subject: [PATCH 24/87] [FIX] base_action_rule: fix tests bzr revid: rco@openerp.com-20121219142021-15h9an87hhhkbhi8 --- .../tests/base_action_rule_test.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/addons/base_action_rule/tests/base_action_rule_test.py b/addons/base_action_rule/tests/base_action_rule_test.py index 0452401ce23..ebefebe3ff4 100644 --- a/addons/base_action_rule/tests/base_action_rule_test.py +++ b/addons/base_action_rule/tests/base_action_rule_test.py @@ -1,4 +1,4 @@ -import tools +from openerp import SUPERUSER_ID from openerp.tests import common from .. import test_models @@ -8,8 +8,8 @@ class base_action_rule_test(common.TransactionCase): """*****setUp*****""" super(base_action_rule_test, self).setUp() cr, uid = self.cr, self.uid - self.demo_user = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'user_demo') - self.admin_user = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'user_admin') + self.demo = self.registry('ir.model.data').get_object(cr, uid, 'base', 'user_demo').id + self.admin = SUPERUSER_ID def create_filter_done(self, cr, uid, context=None): filter_pool = self.registry('ir.filters') @@ -36,12 +36,12 @@ class base_action_rule_test(common.TransactionCase): lead_pool = self.registry('base.action.rule.lead.test') return lead_pool.create(cr, uid, { 'name': "Lead Test 1", - 'user_id': self.admin_user[1], + 'user_id': self.admin, }, context=context) def create_rule(self, cr, uid, filter_id=None, filter_pre_id=None, context=None): """ - The "Rule 1" says that when a lead goes to the 'draft' state, the responsible for that lead changes to "demo_user" + The "Rule 1" says that when a lead goes to the 'draft' state, the responsible for that lead changes to user "demo" """ self.action_pool = self.registry('base.action.rule') return self.action_pool.create(cr,uid,{ @@ -51,7 +51,7 @@ class base_action_rule_test(common.TransactionCase): 'trg_date_type' : 'none', 'filter_pre_id' : filter_pre_id, 'filter_id' : filter_id, - 'act_user_id': self.demo_user[1], + 'act_user_id': self.demo, }, context=context) def test_00_check_to_state_draft_pre(self): @@ -65,7 +65,7 @@ class base_action_rule_test(common.TransactionCase): new_lead_id = self.create_lead_test_1(cr,uid,context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='draft') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) def test_01_check_to_state_draft_post(self): """ @@ -78,7 +78,7 @@ class base_action_rule_test(common.TransactionCase): new_lead_id = self.create_lead_test_1(cr,uid,context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='draft') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.demo_user[1], context=context)) + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.demo, context=context)) def test_02_check_from_draft_to_done_with_steps(self): """ @@ -96,27 +96,27 @@ class base_action_rule_test(common.TransactionCase): new_lead_id = self.create_lead_test_1(cr,uid,context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='draft') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) """ change the state of new_lead to open and check that responsible doen't change""" new_lead.write({'state': 'open'}, context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='open') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) """ change the state of new_lead to pending and check that responsible doen't change""" new_lead.write({'state': 'pending'}, context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='pending') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) """ change the state of new_lead to cancel and check that responsible doen't change""" new_lead.write({'state': 'cancel'}, context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='cancel') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) """ change the state of new_lead to done and check that responsible doen't change """ new_lead.write({'state': 'done'}, context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='done') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) def test_02_check_from_draft_to_done_without_steps(self): """ @@ -124,7 +124,7 @@ class base_action_rule_test(common.TransactionCase): 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 "demo_user" + The responsible in that test will change to user "demo" """ cr, uid = self.cr, self.uid context = {} @@ -134,9 +134,9 @@ class base_action_rule_test(common.TransactionCase): new_lead_id = self.create_lead_test_1(cr,uid,context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='draft') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin_user[1], context=context)) + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) """ change the state of new_lead to done and check that responsible change to Demo_user""" new_lead.write({'state': 'done'}, context=context) new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) self.assertTrue(new_lead.state=='done') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.demo_user[1], context=context)) \ No newline at end of file + self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.demo, context=context)) From b35e1cb3217cdbb432d9bf93dc49ae214b4d9da9 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 19 Dec 2012 15:22:35 +0100 Subject: [PATCH 25/87] [IMP] base_action_rule: remove unused stuff bzr revid: rco@openerp.com-20121219142235-uqs7m7jmeqh38bdt --- addons/base_action_rule/base_action_rule.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index adcc0f25023..c401cf076d0 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -21,16 +21,11 @@ from datetime import datetime from datetime import timedelta -import re import time from openerp import SUPERUSER_ID -from openerp.osv import fields, osv, orm +from openerp.osv import fields, osv 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 def get_datetime(date_field): @@ -57,10 +52,6 @@ class base_action_rule(osv.osv): """ Get State """ return [('', ''), ('na','N/A (No previous state)')] - 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)]), @@ -101,7 +92,6 @@ trigger date, like sending a reminder 15 minutes before a meeting."), _order = 'sequence' - def post_action(self, cr, uid, ids, model, precondition_ok=None, context=None): # Searching for action rules cr.execute("SELECT model.model, rule.id FROM base_action_rule rule \ From e6cfbc5807dfa36154298c500e09f13480268e1c Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 19 Dec 2012 15:23:45 +0100 Subject: [PATCH 26/87] [IMP] base_action_rule: improve field definitions, add missing domains and restrictions bzr revid: rco@openerp.com-20121219142345-pya9tjhd9ilepxy7 --- addons/base_action_rule/base_action_rule.py | 38 +++++++++++++-------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index c401cf076d0..3b310519ed2 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -50,17 +50,18 @@ class base_action_rule(osv.osv): def state_get(self, cr, uid, context=None): """ Get State """ - return [('', ''), ('na','N/A (No previous state)')] + 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."), + '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_type': fields.selection([ ('none', 'None'), ('create', 'Creation Date'), @@ -69,18 +70,27 @@ when displaying a list of rules."), ('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'), \ + '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'), '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"), - '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', 'Postcondition Filter', required=False), #TODO: set domain [('model_id','=',model_id.model)] - 'filter_pre_id': fields.many2one('ir.filters', 'Precondition Filter', required=False), + 'server_action_ids': fields.one2many('ir.actions.server', 'action_rule_id', + domain="[('model_id', '=', model_id)]", + string='Server Action', + help="Example: email reminders, call object service, etc."), + 'filter_pre_id': fields.many2one('ir.filters', string='Before 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 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), } From 07a3cf28a017d6e7353d3e7e5245d14cd7776f29 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 19 Dec 2012 16:05:33 +0100 Subject: [PATCH 27/87] [IMP] base_action_rule: improve method _register_hook of base.action.rule bzr revid: rco@openerp.com-20121219150533-t513zasagd4a73sx --- addons/base_action_rule/base_action_rule.py | 45 ++++++++------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 3b310519ed2..0017a8d0794 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -117,7 +117,7 @@ class base_action_rule(osv.osv): self._action(cr, uid, [rule_id], model_pool.browse(cr, uid, ids, context=context), precondition_ok=precondition_ok, 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 `post_action`, in that order. @@ -140,7 +140,7 @@ class base_action_rule(osv.osv): 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 `post_action`, in that order. @@ -171,41 +171,31 @@ class base_action_rule(osv.osv): return True return wrapper - def _register_hook(self, cr): + 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 `Ǹone`.) """ - Wrap every `create` and `write` methods of the models specified by - the rules (given by `ids`). - """ - ids = self.search(cr,SUPERUSER_ID,[]) - return self._register_hook_(cr,SUPERUSER_ID,ids,context=None) - - def _register_hook_(self, cr, uid, ids, context=None): - """ - Wrap every `create` and `write` methods of the models specified by - the rules (given by `ids`). - """ - reg_ids = [] - if not isinstance(ids, list): - reg_ids.append(ids) - else: - reg_ids.extend(ids) - for action_rule in self.browse(cr, uid, reg_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=None) - obj_pool.write = self._write(obj_pool.write, model, context=None) - 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, \ @@ -214,7 +204,6 @@ class base_action_rule(osv.osv): 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): From 0228273ffb0e6bef4d8f1cbbf1b38d82508148cc Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 19 Dec 2012 16:07:23 +0100 Subject: [PATCH 28/87] [FIX] base_action_rule: fix in wrapper method bzr revid: rco@openerp.com-20121219150723-n25qqmczh3oau1cn --- addons/base_action_rule/base_action_rule.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 0017a8d0794..ed630248957 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -122,7 +122,7 @@ class base_action_rule(osv.osv): Return a wrapper around `old_create` calling both `old_create` and `post_action`, in that order. """ - def wrapper(cr, uid, vals, context=context): + def wrapper(cr, uid, vals, context=None): if context is None: context = {} new_id = old_create(cr, uid, vals, context=context) @@ -145,7 +145,7 @@ class base_action_rule(osv.osv): Return a wrapper around `old_write` calling both `old_write` and `post_action`, in that order. """ - def wrapper(cr, uid, ids, vals, context=context): + def wrapper(cr, uid, ids, vals, context=None): old_records = {} if context is None: context = {} From b0fd6e6ee744bb5a24739a60538b0f18ea5edada Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 20 Dec 2012 10:09:19 +0100 Subject: [PATCH 29/87] [IMP] base_action_rule: tests bzr revid: rco@openerp.com-20121220090919-eelugxam0kmyn7s2 --- .../tests/base_action_rule_test.py | 102 +++++++++--------- 1 file changed, 49 insertions(+), 53 deletions(-) diff --git a/addons/base_action_rule/tests/base_action_rule_test.py b/addons/base_action_rule/tests/base_action_rule_test.py index ebefebe3ff4..b0f169b3bdb 100644 --- a/addons/base_action_rule/tests/base_action_rule_test.py +++ b/addons/base_action_rule/tests/base_action_rule_test.py @@ -10,6 +10,7 @@ class base_action_rule_test(common.TransactionCase): 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') def create_filter_done(self, cr, uid, context=None): filter_pool = self.registry('ir.filters') @@ -33,13 +34,12 @@ class base_action_rule_test(common.TransactionCase): """ Create a new lead_test """ - lead_pool = self.registry('base.action.rule.lead.test') - return lead_pool.create(cr, uid, { + return self.model.create(cr, uid, { 'name': "Lead Test 1", 'user_id': self.admin, }, context=context) - def create_rule(self, cr, uid, filter_id=None, filter_pre_id=None, context=None): + 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" """ @@ -59,26 +59,24 @@ class base_action_rule_test(common.TransactionCase): 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 - context = {} - filter_draft = self.create_filter_draft(cr, uid, context=context) - rule_1_id = self.create_rule(cr, uid, filter_pre_id=filter_draft, context=context) - new_lead_id = self.create_lead_test_1(cr,uid,context=context) - new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) - self.assertTrue(new_lead.state=='draft') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) + filter_draft = self.create_filter_draft(cr, uid) + rule_1_id = 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) 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 - context = {} - filter_draft = self.create_filter_draft(cr, uid, context=context) - rule_1_id = self.create_rule(cr, uid, filter_id=filter_draft, context=context) - new_lead_id = self.create_lead_test_1(cr,uid,context=context) - new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) - self.assertTrue(new_lead.state=='draft') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.demo, context=context)) + filter_draft = self.create_filter_draft(cr, uid) + rule_1_id = 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) def test_02_check_from_draft_to_done_with_steps(self): """ @@ -89,34 +87,33 @@ class base_action_rule_test(common.TransactionCase): The responsible in that test will never change """ cr, uid = self.cr, self.uid - context = {} - filter_draft = self.create_filter_draft(cr, uid, context=context) - filter_done = self.create_filter_done(cr, uid, context=context) - self.create_rule(cr, uid, filter_pre_id=filter_draft, filter_id=filter_done, context=context) - new_lead_id = self.create_lead_test_1(cr,uid,context=context) - new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) - self.assertTrue(new_lead.state=='draft') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) + 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'}, context=context) - new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) - self.assertTrue(new_lead.state=='open') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) + 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'}, context=context) - new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) - self.assertTrue(new_lead.state=='pending') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) + 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'}, context=context) - new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) - self.assertTrue(new_lead.state=='cancel') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) + 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'}, context=context) - new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) - self.assertTrue(new_lead.state=='done') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) + 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) def test_02_check_from_draft_to_done_without_steps(self): """ @@ -127,16 +124,15 @@ class base_action_rule_test(common.TransactionCase): The responsible in that test will change to user "demo" """ cr, uid = self.cr, self.uid - context = {} - filter_draft = self.create_filter_draft(cr, uid, context=context) - filter_done = self.create_filter_done(cr, uid, context=context) - self.create_rule(cr, uid, filter_pre_id=filter_draft, filter_id=filter_done, context=context) - new_lead_id = self.create_lead_test_1(cr,uid,context=context) - new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) - self.assertTrue(new_lead.state=='draft') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.admin, context=context)) + 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'}, context=context) - new_lead = self.registry('base.action.rule.lead.test').browse(cr, uid, new_lead_id, context=context) - self.assertTrue(new_lead.state=='done') - self.assertTrue(new_lead.user_id==self.registry('res.users').browse(cr, uid, self.demo, context=context)) + 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) From da4226d8072dfb46c8b4d7d3d00fda14096e146c Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 20 Dec 2012 10:44:50 +0100 Subject: [PATCH 30/87] [FIX] base_action_rule: tests bzr revid: rco@openerp.com-20121220094450-wwkk1beszml5y9z6 --- .../tests/base_action_rule_test.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/addons/base_action_rule/tests/base_action_rule_test.py b/addons/base_action_rule/tests/base_action_rule_test.py index b0f169b3bdb..cc679a6b1a2 100644 --- a/addons/base_action_rule/tests/base_action_rule_test.py +++ b/addons/base_action_rule/tests/base_action_rule_test.py @@ -11,6 +11,7 @@ class base_action_rule_test(common.TransactionCase): 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') @@ -43,8 +44,7 @@ class base_action_rule_test(common.TransactionCase): """ The "Rule 1" says that when a lead goes to the 'draft' state, the responsible for that lead changes to user "demo" """ - self.action_pool = self.registry('base.action.rule') - return self.action_pool.create(cr,uid,{ + 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, @@ -54,17 +54,23 @@ class base_action_rule_test(common.TransactionCase): '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) - rule_1_id = self.create_rule(cr, uid, filter_pre_id=filter_draft) + 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): """ @@ -72,11 +78,12 @@ class base_action_rule_test(common.TransactionCase): """ cr, uid = self.cr, self.uid filter_draft = self.create_filter_draft(cr, uid) - rule_1_id = self.create_rule(cr, uid, filter_id=filter_draft) + 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): """ @@ -114,6 +121,7 @@ class base_action_rule_test(common.TransactionCase): 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): """ @@ -136,3 +144,4 @@ class base_action_rule_test(common.TransactionCase): 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) From 40b1c95e1a0932f4d1f87bf9894a169b2c9e57ff Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Thu, 20 Dec 2012 13:31:22 +0100 Subject: [PATCH 31/87] [IMP]mail: display checkbox for allow to add email (from email_from value) for creating partner and send email to this new partners. bzr revid: chm@openerp.com-20121220123122-qsqn8w97t1tre3cq --- addons/mail/mail_thread.py | 13 ++++-- addons/mail/res_users.py | 10 +++-- addons/mail/static/src/css/mail.css | 8 ++++ addons/mail/static/src/js/mail.js | 70 +++++++++++++++++++++++------ addons/mail/static/src/xml/mail.xml | 9 +++- 5 files changed, 88 insertions(+), 22 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 48bdef508d0..cb07cb91a6f 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -749,8 +749,8 @@ class mail_thread(osv.AbstractModel): return mail_message.create(cr, uid, values, context=context) def message_post_user_api(self, cr, uid, thread_id, body='', subject=False, parent_id=False, - attachment_ids=None, context=None, content_subtype='plaintext', - extra_email=[], **kwargs): + attachment_ids=None, extra_email=[], + context=None, content_subtype='plaintext', **kwargs): """ Wrapper on message_post, used for user input : - mail gateway - quick reply in Chatter (refer to mail.js), not @@ -771,13 +771,20 @@ class mail_thread(osv.AbstractModel): if content_subtype == 'plaintext': body = tools.plaintext2html(body) + partner_ids = kwargs.pop('partner_ids', []) + for partner in extra_email: part_ids = self.pool.get('res.partner').search(cr, uid, [('email', '=', partner)], context=context) if not part_ids: part_ids = [self.pool.get('res.partner').name_create(cr, uid, partner, context=context)[0]] self.message_subscribe(cr, uid, [thread_id], part_ids, context=context) - partner_ids = kwargs.pop('partner_ids', []) + message_ids = mail_message.search(cr, uid, [('email_from', '=', partner)], context=context) + if part_ids and message_ids: + mail_message.write(cr, uid, message_ids, {'email_from': None, 'author_id': part_ids[0]}, context=context) + + partner_ids = set(partner_ids) + set(part_ids) + if parent_id: parent_message = self.pool.get('mail.message').browse(cr, uid, parent_id, context=context) partner_ids += [(4, partner.id) for partner in parent_message.partner_ids] diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index 4c6bf6e150d..24c9c185d4d 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -116,8 +116,10 @@ class res_users(osv.Model): alias_pool.unlink(cr, uid, alias_ids, context=context) return res + def message_post_user_api(self, cr, uid, thread_id, body='', subject=False, parent_id=False, - attachment_ids=None, context=None, content_subtype='plaintext', **kwargs): + attachment_ids=None, extra_email=[], + context=None, content_subtype='plaintext', **kwargs): """ Redirect the posting of message on res.users to the related partner. This is done because when giving the context of Chatter on the various mailboxes, we do not have access to the current partner_id. @@ -128,8 +130,10 @@ class res_users(osv.Model): if isinstance(thread_id, (list, tuple)): thread_id = thread_id[0] partner_id = self.browse(cr, uid, thread_id).partner_id.id - return self.pool.get('res.partner').message_post_user_api(cr, uid, partner_id, body=body, subject=subject, - parent_id=parent_id, attachment_ids=attachment_ids, context=context, content_subtype=content_subtype, **kwargs) + return self.pool.get('res.partner').message_post_user_api(cr, uid, + partner_id, body=body, subject=subject, parent_id=parent_id, + attachment_ids=attachment_ids, extra_email=extra_email, + context=context, content_subtype=content_subtype, **kwargs) def message_post(self, cr, uid, thread_id, context=None, **kwargs): """ Redirect the posting of message on res.users to the related partner. diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index e154e285f39..886199a668b 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -253,6 +253,14 @@ .openerp .oe_mail .oe_msg_composer .oe_msg_attachment_list{ display: block; } +.openerp .oe_mail .oe_msg_composer .oe_emails_from label{ + vertical-align: middle; + display: block; + line-height: 16px; +} +.openerp .oe_mail .oe_msg_composer .oe_emails_from input{ + vertical-align: middle; +} .openerp .oe_mail .oe_attachment{ display: inline-block; width: 100px; diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index f873daff9c3..a770b07be17 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -369,6 +369,7 @@ openerp.mail = function (session) { this._super(parent, datasets, options); this.show_compact_message = false; this.show_delete_attachment = true; + this.emails_from = []; }, start: function () { @@ -478,9 +479,11 @@ openerp.mail = function (session) { this.$('.oe_cancel').on('click', _.bind( this.on_cancel, this) ); this.$('.oe_post').on('click', _.bind( this.on_message_post, this) ); this.$('.oe_full').on('click', _.bind( this.on_compose_fullmail, this, this.id ? 'reply' : 'comment') ); - + /* stack for don't close the compose form if the user click on a button */ - this.$('.oe_msg_footer').on('mousedown', _.bind( function () { this.stay_open = true; }, this)); + this.$('.oe_msg_left, .oe_msg_center').on('mousedown', _.bind( function () { this.stay_open = true; }, this)); + this.$('.oe_msg_left, .oe_msg_content').on('mouseup', _.bind( function () { this.$('textarea').focus(); }, this)); + var ev_stay = {}; ev_stay.mouseup = ev_stay.keydown = ev_stay.focus = function () { self.stay_open = false; }; this.$('textarea:not(.oe_compact)').on(ev_stay); @@ -491,6 +494,8 @@ openerp.mail = function (session) { // event: delete child attachments off the oe_msg_attachment_list box this.$(".oe_msg_attachment_list").on('click', '.oe_delete', this.on_attachment_delete); + + this.$(".oe_emails_from").on('change', 'input', this.on_checked_email_from); }, on_compose_fullmail: function (default_composition_mode) { @@ -568,21 +573,23 @@ openerp.mail = function (session) { if (this.do_check_attachment_upload() && (this.attachment_ids.length || body.match(/\S+/))) { //session.web.blockUI(); - this.parent_thread.ds_thread.call('message_post_user_api', [ - this.context.default_res_id, - body, - false, - this.context.default_parent_id, - _.map(this.attachment_ids, function (file) {return file.id;}), - this.parent_thread.context - ]).done(function (record) { + var values = [ + this.context.default_res_id, //thread_id + body, //body + false, //subject + this.context.default_parent_id, //parent_id + _.map(this.attachment_ids, function (file) {return file.id;}), //attachment_ids + _.map(_.filter(this.emails_from, function (f) {return f[1]}), function (f) {return f[0]}), //extra_email + this.parent_thread.context, // context + ]; + this.parent_thread.ds_thread.call('message_post_user_api', values).done(function (record) { var thread = self.parent_thread; var root = thread == self.options.root_thread; if (self.options.display_indented_thread < self.thread_level && thread.parent_message) { var thread = thread.parent_message.parent_thread; } // create object and attach to the thread object - thread.message_fetch([['id', 'child_of', [self.id]]], false, [record], function (arg, data) { + thread.message_fetch([["id", "=", record]], false, [record], function (arg, data) { var message = thread.create_message_object( data[0] ); // insert the message on dom thread.insert_message( message, root ? undefined : self.$el, root ); @@ -597,8 +604,8 @@ openerp.mail = function (session) { /* convert the compact mode into the compose message */ on_compose_expandable: function (event) { - - if ((!this.stay_open || (event && event.type == 'click')) && (!this.show_composer || !this.$('textarea:not(.oe_compact)').val().match(/\S+/))) { + this.get_emails_from(); + if ((!this.stay_open || (event && event.type == 'click')) && (!this.show_composer || !this.$('textarea:not(.oe_compact)').val().match(/\S+/) && !this.attachment_ids.length)) { this.show_composer = !this.show_composer || this.stay_open; this.reinit(); } @@ -620,6 +627,41 @@ openerp.mail = function (session) { if (!this.show_composer) { this.reinit(); } + }, + + get_emails_from: function () { + var self = this; + var messages = []; + + if (this.parent_thread.parent_message) { + // go to the parented message + var message = this.parent_thread.parent_message; + var parent_message = message.parent_id ? message.parent_thread.parent_message : message; + var messages = [parent_message].concat(parent_message.get_childs()); + } else { + // get all wall messages + _.each(this.options.root_thread.messages, function (msg) {messages.push(msg); messages.concat(msg.get_childs());}); + } + + var emails_from = _.map(_.filter(messages, + function (thread) {return thread.author_id && !thread.author_id[0];}), + function (thread) {return thread.author_id[1];}); + + return _.each(emails_from, function (email_from) { + if (!_.find(self.emails_from, function (from) {return from[0] == email_from;})) { + self.emails_from.push([email_from, true]); + } + }); + }, + + on_checked_email_from: function (event) { + var $input = $(event.target); + var email = $input.attr("data"); + _.each(this.emails_from, function (email_from) { + if (email_from[0] == email) { + email_from[1] = $input.is(":checked"); + } + }); } }); @@ -1477,7 +1519,7 @@ openerp.mail = function (session) { $(window).resize( _.bind(this.thread.on_scroll, this.thread) ); this.$el.resize( _.bind(this.thread.on_scroll, this.thread) ); window.setTimeout( _.bind(this.thread.on_scroll, this.thread), 500 ); - } + }, }); diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 3c510813029..168f32a9d7e 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -27,6 +27,11 @@
+
+ + + +
diff --git a/addons/mail/wizard/invite_view.xml b/addons/mail/wizard/invite_view.xml index b932f96dfe7..65463529948 100644 --- a/addons/mail/wizard/invite_view.xml +++ b/addons/mail/wizard/invite_view.xml @@ -11,8 +11,8 @@ - +
From ee5121a5906fb2f92bc3d5b77c318a2c44298686 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 21 Dec 2012 11:27:53 +0100 Subject: [PATCH 55/87] [IMP] base_action_rule: remove deprecated field act_state from base.action.rule bzr revid: rco@openerp.com-20121221102753-qejsv25g952gz8nx --- addons/base_action_rule/base_action_rule.py | 15 +-------------- addons/base_action_rule/base_action_rule_view.xml | 5 +---- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index b785726758a..304133a5926 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -60,14 +60,6 @@ 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 [('', '')] - _columns = { 'name': fields.char('Rule Name', size=64, required=True), 'model_id': fields.many2one('ir.model', 'Related Document Model', @@ -93,7 +85,6 @@ class base_action_rule(osv.osv): 'trg_date_range_type': fields.selection([('minutes', 'Minutes'), ('hour', 'Hours'), ('day', 'Days'), ('month', 'Months')], 'Delay type'), '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"), 'server_action_ids': fields.many2many('ir.actions.server', domain="[('model_id', '=', model_id)]", @@ -148,13 +139,9 @@ class base_action_rule(osv.osv): 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 action.act_state and 'state' in model._all_columns: - values['state'] = action.act_state - if values: model.write(cr, uid, record_ids, values, context=context) - if values.get('state') and hasattr(model, 'message_post'): - model.message_post(cr, uid, record_ids, _(action.act_state), context=context) + if action.act_followers and hasattr(model, 'message_subscribe'): model.message_subscribe(cr, uid, record_ids, map(int, action.act_followers), context=context) diff --git a/addons/base_action_rule/base_action_rule_view.xml b/addons/base_action_rule/base_action_rule_view.xml index aab9ce0000c..39096668fd3 100644 --- a/addons/base_action_rule/base_action_rule_view.xml +++ b/addons/base_action_rule/base_action_rule_view.xml @@ -42,12 +42,9 @@ - - - - + From fed5651f1cb6a6a38a3962b72d4aa8ff3f409ef0 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 21 Dec 2012 11:30:03 +0100 Subject: [PATCH 56/87] [IMP] base_action_rule: small code improvement bzr revid: rco@openerp.com-20121221103003-tl689nm2ser3u001 --- addons/base_action_rule/base_action_rule.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 304133a5926..5f337ef9db1 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -143,7 +143,8 @@ class base_action_rule(osv.osv): model.write(cr, uid, record_ids, values, context=context) if action.act_followers and hasattr(model, 'message_subscribe'): - model.message_subscribe(cr, uid, record_ids, map(int, action.act_followers), context=context) + follower_ids = map(int, action.act_followers) + model.message_subscribe(cr, uid, record_ids, follower_ids, context=context) return True From 306267ef55478b791326a6067f440e125e195a14 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 21 Dec 2012 11:32:33 +0100 Subject: [PATCH 57/87] [IMP] base_action_rule: improve field labels bzr revid: rco@openerp.com-20121221103233-lllh3c5f1azuffc7 --- addons/base_action_rule/base_action_rule.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 5f337ef9db1..1c66aeb1389 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -86,15 +86,14 @@ class base_action_rule(osv.osv): ('day', 'Days'), ('month', 'Months')], 'Delay type'), 'act_user_id': fields.many2one('res.users', 'Set Responsible to'), 'act_followers': fields.many2many("res.partner", string="Set Followers"), - 'server_action_ids': fields.many2many('ir.actions.server', + 'server_action_ids': fields.many2many('ir.actions.server', string='Server Actions', domain="[('model_id', '=', model_id)]", - string='Server Action', - help="Example: email reminders, call object service, etc."), - 'filter_pre_id': fields.many2one('ir.filters', string='Before Filter', + 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 Filter', + '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."), From d17d474fc706f0bd2263382b564131120a0c5d52 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 21 Dec 2012 11:38:12 +0100 Subject: [PATCH 58/87] [IMP] base_action_rule: remove unused import bzr revid: rco@openerp.com-20121221103812-u6ejn5rifw2ctjsx --- addons/base_action_rule/base_action_rule.py | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 1c66aeb1389..5df9d3ec6ee 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -26,7 +26,6 @@ import logging from openerp import SUPERUSER_ID from openerp.osv import fields, osv from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT -from openerp.tools.translate import _ _logger = logging.getLogger(__name__) From a7ef28fd94b0c3ca3c7296f2e9e9b26d1d3f2956 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 21 Dec 2012 11:54:36 +0100 Subject: [PATCH 59/87] [IMP] base_action_rule: make code more robust bzr revid: rco@openerp.com-20121221105436-3t553dlzdvjb5hfe --- addons/base_action_rule/base_action_rule.py | 27 ++++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 5df9d3ec6ee..678b1a4f555 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -107,9 +107,10 @@ class base_action_rule(osv.osv): _order = 'sequence' - def _filter(self, cr, uid, action_filter, record_ids=None, context=None): - """ filter the list record_ids that satisfy the action_filter """ + 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 {}) @@ -151,15 +152,17 @@ class base_action_rule(osv.osv): `_process`, in that order. """ def wrapper(cr, uid, vals, context=None): + if context and context.get('action'): + return old_create(cr, uid, vals, context=context) + new_id = old_create(cr, uid, vals, context=context) - if not (context and context.get('action')): - # as it is a new record, we do not consider the actions that have a prefilter - action_dom = [('model', '=', model), ('trg_date_type', 'in', ('none', 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.filter_id, [new_id], context=context): - self._process(cr, uid, action, [new_id], context=context) + # as it is a new record, we do not consider the actions that have a prefilter + action_dom = [('model', '=', model), ('trg_date_type', 'in', ('none', 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 @@ -180,12 +183,12 @@ class base_action_rule(osv.osv): # check preconditions pre_ids = {} for action in actions: - pre_ids[action] = self._filter(cr, uid, action.filter_pre_id, ids, context=context) + 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) # check postconditions, and execute actions on the records that satisfy them for action in actions: - post_ids = self._filter(cr, uid, action.filter_id, pre_ids[action], context=context) + 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 From 9fe900791d1388159e600a263bbadc5683267ece Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Fri, 21 Dec 2012 12:00:03 +0100 Subject: [PATCH 60/87] [IMP] follower: must have access write to invite follower bzr revid: chm@openerp.com-20121221110003-h7h9cie4vcejif7z --- addons/account/account_invoice_view.xml | 2 +- addons/mail/mail_thread.py | 13 +++++++++---- addons/mail/static/src/js/mail.js | 4 ++++ addons/mail/static/src/xml/mail_followers.xml | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index 6ca7c21a6e4..5f0f9002e2f 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -436,7 +436,7 @@
- +
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index fdd04aa1174..7611717bdc0 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -788,7 +788,7 @@ class mail_thread(osv.AbstractModel): mail_message.write(cr, uid, message_ids, {'email_from': None, 'author_id': part_ids[0]}, context=context) partner_ids = set(kwargs.pop('partner_ids', [])) | set(new_partner_ids) - + if parent_id: parent_message = self.pool.get('mail.message').browse(cr, uid, parent_id, context=context) partner_ids |= set([(4, partner.id) for partner in parent_message.partner_ids]) @@ -841,7 +841,12 @@ class mail_thread(osv.AbstractModel): def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None): """ Add partners to the records followers. """ - self.check_access_rights(cr, uid, 'read') + user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + if set(partner_ids) == set([user_pid]): + self.check_access_rights(cr, uid, 'read') + else: + self.check_access_rights(cr, uid, 'write') + self.write(cr, SUPERUSER_ID, ids, {'message_follower_ids': [(4, pid) for pid in partner_ids]}, context=context) # if subtypes are not specified (and not set to a void list), fetch default ones if subtype_ids is None: @@ -863,8 +868,8 @@ class mail_thread(osv.AbstractModel): def message_unsubscribe(self, cr, uid, ids, partner_ids, context=None): """ Remove partners from the records followers. """ - user_pids = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'] - if set(partner_ids) == set(user_pids): + user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + if set(partner_ids) == set([user_pid]): self.check_access_rights(cr, uid, 'read') else: self.check_access_rights(cr, uid, 'write') diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 8c8bb170453..b7592fd8b33 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -1587,6 +1587,10 @@ openerp.mail = function (session) { } this.domain = this.node.params && this.node.params.domain || []; + + if (!this.__parentedParent.is_action_enabled('edit')) { + this.node.params.show_link = false; + } }, start: function () { diff --git a/addons/mail/static/src/xml/mail_followers.xml b/addons/mail/static/src/xml/mail_followers.xml index 584fd94662c..df4f36544c9 100644 --- a/addons/mail/static/src/xml/mail_followers.xml +++ b/addons/mail/static/src/xml/mail_followers.xml @@ -19,7 +19,7 @@

Followers

- Invite others + Add others
From f080e91168861bc2b404a126029f625e1bb84d3e Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Fri, 21 Dec 2012 12:14:00 +0100 Subject: [PATCH 61/87] [IMP] mail: add message 'Add this contact's email on recipient and follower' bzr revid: chm@openerp.com-20121221111400-ngc62hepbgzqepy8 --- addons/mail/mail_thread.py | 11 +++++------ addons/mail/static/src/css/mail.css | 5 ++++- addons/mail/static/src/xml/mail.xml | 5 +++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 7611717bdc0..cd04de19bef 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -774,18 +774,17 @@ class mail_thread(osv.AbstractModel): new_partner_ids = set([]) for partner in extra_email: part_ids = self.pool.get('res.partner').search(cr, uid, [('email', '=', partner)], context=context) - # create a new partner if not exists if not part_ids: part_ids = [self.pool.get('res.partner').name_create(cr, uid, partner, context=dict())[0]] - - new_partner_ids |= set(part_ids) - self.message_subscribe(cr, uid, [thread_id], part_ids, context=context) - # link mail with this from mail to the new partner id message_ids = mail_message.search(cr, uid, [('email_from', '=', partner)], context=context) if part_ids and message_ids: mail_message.write(cr, uid, message_ids, {'email_from': None, 'author_id': part_ids[0]}, context=context) + new_partner_ids |= set(part_ids) + + if new_partner_ids: + self.message_subscribe(cr, uid, new_partner_ids, part_ids, context=context) partner_ids = set(kwargs.pop('partner_ids', [])) | set(new_partner_ids) @@ -846,7 +845,7 @@ class mail_thread(osv.AbstractModel): self.check_access_rights(cr, uid, 'read') else: self.check_access_rights(cr, uid, 'write') - + self.write(cr, SUPERUSER_ID, ids, {'message_follower_ids': [(4, pid) for pid in partner_ids]}, context=context) # if subtypes are not specified (and not set to a void list), fetch default ones if subtype_ids is None: diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index f41c5efc8bd..0bcf68d8b6b 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -253,10 +253,13 @@ .openerp .oe_mail .oe_msg_composer .oe_msg_attachment_list{ display: block; } +.openerp .oe_mail .oe_msg_composer .oe_emails_from{ + font-size: 12px; +} .openerp .oe_mail .oe_msg_composer .oe_emails_from label{ vertical-align: middle; display: block; - line-height: 16px; + line-height: 14px; } .openerp .oe_mail .oe_msg_composer .oe_emails_from input{ vertical-align: middle; diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 351e762c2f4..f878b4204f1 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -113,8 +113,8 @@ this document - and - + and + @@ -132,6 +132,7 @@
+ Add this contact's email on recipient and follower : From 2b8374f5eadfa0ada2151a179fad5a3879d53a97 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Fri, 21 Dec 2012 12:23:07 +0100 Subject: [PATCH 62/87] [IMP] mail: css for recipient on compose message bzr revid: chm@openerp.com-20121221112307-0hj05yw1nn3l3ux9 --- addons/mail/static/src/css/mail.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 0bcf68d8b6b..dbeb882d570 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -255,6 +255,7 @@ } .openerp .oe_mail .oe_msg_composer .oe_emails_from{ font-size: 12px; + margin-left: 20px; } .openerp .oe_mail .oe_msg_composer .oe_emails_from label{ vertical-align: middle; @@ -476,8 +477,8 @@ } .openerp .oe_mail .oe_msg_content .oe_mail_list_recipients{ font-size: 12px; - margin-top: 4px; - margin-bottom: 4px; + margin: 4px 0 4px 20px; + text-indent: -20px; } /* ---------------- HIDDEN MESSAGES ------------------ */ From 8972f7b334a569da26670040f19b7f793c1d2cd0 Mon Sep 17 00:00:00 2001 From: Christophe Matthieu Date: Fri, 21 Dec 2012 13:11:44 +0100 Subject: [PATCH 63/87] [IMP] mail: check if the new email recipient already exist or not, and fix _api bzr revid: chm@openerp.com-20121221121144-o8pbya2rc7v471y0 --- addons/mail/mail_thread.py | 2 +- addons/mail/static/src/js/mail.js | 39 ++++++++++++++++------------- addons/mail/static/src/xml/mail.xml | 3 +-- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index cd04de19bef..d27c9b6d435 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -784,7 +784,7 @@ class mail_thread(osv.AbstractModel): new_partner_ids |= set(part_ids) if new_partner_ids: - self.message_subscribe(cr, uid, new_partner_ids, part_ids, context=context) + self.message_subscribe(cr, uid, [thread_id], list(new_partner_ids), context=context) partner_ids = set(kwargs.pop('partner_ids', [])) | set(new_partner_ids) diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index b7592fd8b33..32b6e85f118 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -567,25 +567,30 @@ openerp.mail = function (session) { check_recipient_partners: function (emails) { var self = this; var deferreds = []; + var ds_partner = new session.web.DataSetSearch(this, 'res.partner'); _.each(emails, function (email) { - var deferred = $.Deferred(); - var pop = new session.web.form.FormOpenPopup(this); - pop.show_element( - 'res.partner', - 0, - { - 'default_email': email, - 'force_email': true, - 'ref': "compound_context", - }, - { - title: _t("Please complete partner's informations"), + ds_partner.call('search', [[['email', '=', email]]]).then(function (partner_ids) { + if (!partner_ids.length) { + var deferred = $.Deferred(); + var pop = new session.web.form.FormOpenPopup(this); + pop.show_element( + 'res.partner', + 0, + { + 'default_email': email, + 'force_email': true, + 'ref': "compound_context", + }, + { + title: _t("Please complete partner's informations"), + } + ); + pop.on('write_completed, closed', self, function () { + deferred.resolve(); + }); + deferreds.push(deferred); } - ); - pop.on('write_completed, closed', self, function () { - deferred.resolve(); }); - deferreds.push(deferred); }); return $.when.apply( $, deferreds ); }, @@ -1587,7 +1592,7 @@ openerp.mail = function (session) { } this.domain = this.node.params && this.node.params.domain || []; - + if (!this.__parentedParent.is_action_enabled('edit')) { this.node.params.show_link = false; } diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index f878b4204f1..41de6d6b40f 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -132,9 +132,8 @@
- Add this contact's email on recipient and follower : - +
From 84bef2751cc669e503746537891ec78224182349 Mon Sep 17 00:00:00 2001 From: "dle@openerp.com" <> Date: Fri, 21 Dec 2012 13:44:41 +0100 Subject: [PATCH 64/87] [FIX]Wrong condition and message bzr revid: dle@openerp.com-20121221124441-lklcl8b2bmp5wo27 --- addons/account/account_invoice.py | 3 --- addons/account_voucher/account_voucher.py | 7 ++++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 62ba9c179da..af26d1f4aed 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -690,9 +690,6 @@ class account_invoice(osv.osv): return super(account_invoice, self).copy(cr, uid, id, default, context) def test_paid(self, cr, uid, ids, *args): - for inv in self.browse(cr, uid, ids): - if inv.amount_total == 0.0: - return True res = self.move_line_id_payment_get(cr, uid, ids) if not res: return False diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 96a431e0a79..d95235b3803 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -26,6 +26,7 @@ from openerp import netsvc from openerp.osv import fields, osv import openerp.addons.decimal_precision as dp from openerp.tools.translate import _ +from openerp.tools import float_compare class res_company(osv.osv): _inherit = "res.company" @@ -1083,15 +1084,15 @@ class account_voucher(osv.osv): ctx.update({'date': voucher_brw.date}) for line in voucher_brw.line_ids: #create one move line per voucher line where amount is not 0.0 - if not line.amount: + if not line.amount and not float_compare(line.move_line_id.invoice.amount_total,0.0,precision_rounding=0.0): continue # convert the amount set on the voucher line into the currency of the voucher's company amount = self._convert_amount(cr, uid, line.untax_amount or line.amount, voucher_brw.id, context=ctx) # if the amount encoded in voucher is equal to the amount unreconciled, we need to compute the # currency rate difference if line.amount == line.amount_unreconciled: - if not line.move_line_id.amount_residual: - raise osv.except_osv(_('Wrong bank statement line'),_("You have to delete the bank statement line which the payment was reconciled to manually. Please check the payment of the partner %s by the amount of %s.")%(line.voucher_id.partner_id.name, line.voucher_id.amount)) + if not line.move_line_id: + raise osv.except_osv(_('Wrong voucher line'),_("The invoice you are willing to pay is not valid anymore.")) sign = voucher_brw.type in ('payment', 'purchase') and -1 or 1 currency_rate_difference = sign * (line.move_line_id.amount_residual - amount) else: From e9f9e1191ca4e782d8014a4ba13a1feedf3fc4d7 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Fri, 21 Dec 2012 14:03:02 +0100 Subject: [PATCH 65/87] [FIX] account_voucher: bug fixed in the supplier partial payment. There is no need to take care of the sign as the residual amount of account.move.line is already taking care of it bzr revid: qdp-launchpad@openerp.com-20121221130302-23v55ygcqekdlywi --- addons/account_voucher/account_voucher.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 4d510853660..842e50ebff1 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -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 From 63d993e45d34e2250734b861d5900fa74cb75ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 21 Dec 2012 14:39:28 +0100 Subject: [PATCH 66/87] [CLEAN] invite wizard: removed unused method onchange_partner_ids. Cleaned a bit code in invite_view. bzr revid: tde@openerp.com-20121221133928-lytc76vbs0bdnhuz --- addons/mail/wizard/invite.py | 11 ----------- addons/mail/wizard/invite_view.xml | 5 +++-- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py index 0b8b7bc7895..ee3053014af 100644 --- a/addons/mail/wizard/invite.py +++ b/addons/mail/wizard/invite.py @@ -49,17 +49,6 @@ class invite_wizard(osv.osv_memory): 'message': fields.html('Message'), } - def onchange_partner_ids(self, cr, uid, ids, value, context=None): - """ onchange_partner_ids (value format: [[6, 0, [3, 4]]]). The - basic purpose of this method is to check that destination partners - effectively have email addresses. Otherwise a warning is thrown. - """ - res = {'value': {}} - if not value or not value[0] or not value[0][0] == 6: - return - res.update(self.pool.get('mail.message').check_partners_email(cr, uid, value[0][2], context=context)) - return res - def add_followers(self, cr, uid, ids, context=None): for wizard in self.browse(cr, uid, ids, context=context): model_obj = self.pool.get(wizard.res_model) diff --git a/addons/mail/wizard/invite_view.xml b/addons/mail/wizard/invite_view.xml index 65463529948..5b3b679ae9f 100644 --- a/addons/mail/wizard/invite_view.xml +++ b/addons/mail/wizard/invite_view.xml @@ -11,8 +11,9 @@ - +