diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 469acc2e317..2fe052f13a5 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -51,15 +51,6 @@ class account_invoice(models.Model): _inherit = ['mail.thread'] _description = "Invoice" _order = "number desc, id desc" - _track = { - 'type': { - }, - 'state': { - 'account.mt_invoice_paid': lambda self, cr, uid, obj, ctx=None: obj.state == 'paid' and obj.type in ('out_invoice', 'out_refund'), - 'account.mt_invoice_validated': lambda self, cr, uid, obj, ctx=None: obj.state == 'open' and obj.type in ('out_invoice', 'out_refund'), - 'account.mt_invoice_created': lambda self, cr, uid, obj, ctx=None: obj.state == 'draft' and obj.type in ('out_invoice', 'out_refund'), - }, - } @api.one @api.depends('invoice_line.price_subtotal', 'tax_line.amount') @@ -1206,6 +1197,18 @@ class account_invoice(models.Model): return recs.pay_and_reconcile(pay_amount, pay_account_id, period_id, pay_journal_id, writeoff_acc_id, writeoff_period_id, writeoff_journal_id, name=name) + @api.multi + def _track_subtype(self, init_values): + self.ensure_one() + if 'state' in init_values and self.state == 'paid' and self.type in ('out_invoice', 'out_refund'): + return 'account.mt_invoice_paid' + elif 'state' in init_values and self.state == 'open' and self.type in ('out_invoice', 'out_refund'): + return 'account.mt_invoice_validated' + elif 'state' in init_values and self.state == 'draft' and self.type in ('out_invoice', 'out_refund'): + return 'account.mt_invoice_created' + return super(account_invoice, self)._track_subtype(init_values) + + class account_invoice_line(models.Model): _name = "account.invoice.line" _description = "Invoice Line" diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 2f99792c606..c8f08436d46 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -287,12 +287,6 @@ class account_voucher(osv.osv): _description = 'Accounting Voucher' _inherit = ['mail.thread'] _order = "date desc, id desc" -# _rec_name = 'number' - _track = { - 'state': { - 'account_voucher.mt_voucher_state_change': lambda self, cr, uid, obj, ctx=None: True, - }, - } _columns = { 'type':fields.selection([ @@ -1414,6 +1408,12 @@ class account_voucher(osv.osv): reconcile = move_line_pool.reconcile_partial(cr, uid, rec_ids, writeoff_acc_id=voucher.writeoff_acc_id.id, writeoff_period_id=voucher.period_id.id, writeoff_journal_id=voucher.journal_id.id) return True + def _track_subtype(self, cr, uid, ids, init_values, context=None): + if 'state' in init_values: + return 'account_voucher.mt_voucher_state_change' + return super(account_voucher, self)._track_subtype(cr, uid, ids, init_values, context=context) + + class account_voucher_line(osv.osv): _name = 'account.voucher.line' _description = 'Voucher Lines' diff --git a/addons/analytic/models/analytic.py b/addons/analytic/models/analytic.py index 227683f8bd0..f1295fc9a02 100644 --- a/addons/analytic/models/analytic.py +++ b/addons/analytic/models/analytic.py @@ -32,13 +32,6 @@ class account_analytic_account(osv.osv): _name = 'account.analytic.account' _inherit = ['mail.thread'] _description = 'Analytic Account' - _track = { - 'state': { - 'analytic.mt_account_pending': lambda self, cr, uid, obj, ctx=None: obj.state == 'pending', - 'analytic.mt_account_closed': lambda self, cr, uid, obj, ctx=None: obj.state == 'close', - 'analytic.mt_account_opened': lambda self, cr, uid, obj, ctx=None: obj.state == 'open', - }, - } def _compute_level_tree(self, cr, uid, ids, child_ids, res, field_names, context=None): currency_obj = self.pool.get('res.currency') @@ -316,6 +309,17 @@ class account_analytic_account(osv.osv): account_ids = self.search(cr, uid, args, limit=limit, context=context) return self.name_get(cr, uid, account_ids, context=context) + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'state' in init_values and record.state == 'open': + return 'analytic.mt_account_opened' + elif 'state' in init_values and record.state == 'close': + return 'analytic.mt_account_closed' + elif 'state' in init_values and record.state == 'pending': + return 'analytic.mt_account_pending' + return super(account_analytic_account, self)._track_subtype(cr, uid, ids, init_values, context=context) + + class account_analytic_line(osv.osv): _name = 'account.analytic.line' _description = 'Analytic Line' diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index 90f81e8d884..3897397e13e 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -905,14 +905,6 @@ class calendar_event(osv.Model): duration = float(diff.days) * 24 + (float(diff.seconds) / 3600) values['duration'] = round(duration, 2) - _track = { - 'location': { - 'calendar.subtype_invitation': lambda self, cr, uid, obj, ctx=None: True, - }, - 'start': { - 'calendar.subtype_invitation': lambda self, cr, uid, obj, ctx=None: True, - }, - } _columns = { 'id': fields.integer('ID', readonly=True), 'state': fields.selection([('draft', 'Unconfirmed'), ('open', 'Confirmed')], string='Status', readonly=True, track_visibility='onchange'), @@ -1371,6 +1363,14 @@ class calendar_event(osv.Model): res[virtual_id] = result[real_id] return res + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'start' in init_values and record.start: + return 'calendar.subtype_invitation' + elif 'location' in init_values and record.location: + return 'calendar.subtype_invitation' + return super(calendar_event, self)._track_subtype(cr, uid, ids, init_values, context=context) + def onchange_partner_ids(self, cr, uid, ids, value, context=None): """ The basic purpose of this method is to check that destination partners effectively have email addresses. Otherwise a warning is thrown. diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 70239d01acb..42dfc8103cf 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -72,16 +72,6 @@ class crm_lead(format_address, osv.osv): _description = "Lead/Opportunity" _order = "priority desc,date_action,id desc" _inherit = ['mail.thread', 'ir.needaction_mixin', 'utm.mixin'] - - _track = { - 'stage_id': { - # this is only an heuristics; depending on your particular stage configuration it may not match all 'new' stages - 'crm.mt_lead_create': lambda self, cr, uid, obj, ctx=None: obj.probability == 0 and obj.stage_id and obj.stage_id.sequence <= 1, - 'crm.mt_lead_stage': lambda self, cr, uid, obj, ctx=None: (obj.stage_id and obj.stage_id.sequence > 1) and obj.probability < 100, - 'crm.mt_lead_won': lambda self, cr, uid, obj, ctx=None: obj.probability == 100 and obj.stage_id and obj.stage_id.fold, - 'crm.mt_lead_lost': lambda self, cr, uid, obj, ctx=None: obj.probability == 0 and obj.stage_id and obj.stage_id.fold and obj.stage_id.sequence > 1, - }, - } _mail_mass_mailing = _('Leads / Opportunities') def get_empty_list_help(self, cr, uid, help, context=None): @@ -979,6 +969,18 @@ class crm_lead(format_address, osv.osv): # Mail Gateway # ---------------------------------------- + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'stage_id' in init_values and record.probability == 100 and record.stage_id and record.stage_id.fold: + return 'crm.mt_lead_won' + elif 'stage_id' in init_values and record.probability == 0 and record.stage_id and record.stage_id.fold and record.stage_id.sequence > 1: + return 'crm.mt_lead_lost' + elif 'stage_id' in init_values and record.probability == 0 and record.stage_id and record.stage_id.sequence <= 1: + return 'crm.mt_lead_create' + elif 'stage_id' in init_values: + return 'crm.mt_lead_stage' + return super(crm_lead, self)._track_subtype(cr, uid, ids, init_values, context=context) + def message_get_reply_to(self, cr, uid, ids, context=None): """ Override to get the reply_to of the parent project. """ leads = self.browse(cr, SUPERUSER_ID, ids, context=context) diff --git a/addons/hr_contract/hr_contract.py b/addons/hr_contract/hr_contract.py index 2afdf069725..76ca3238460 100644 --- a/addons/hr_contract/hr_contract.py +++ b/addons/hr_contract/hr_contract.py @@ -73,13 +73,6 @@ class hr_contract(osv.osv): _description = 'Contract' _inherit = ['mail.thread', 'ir.needaction_mixin'] - _track = { - 'state': { - 'hr_contract.mt_contract_pending': lambda self, cr, uid, obj, ctx=None: obj.state == 'pending', - 'hr_contract.mt_contract_close': lambda self, cr, uid, obj, ctx=None: obj.state == 'close', - }, - } - _columns = { 'name': fields.char('Contract Reference', required=True), 'employee_id': fields.many2one('hr.employee', "Employee", required=True), @@ -139,3 +132,11 @@ class hr_contract(osv.osv): def set_as_close(self, cr, uid, ids, context=None): return self.write(cr, uid, ids, {'state': 'close'}, context=context) + + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'state' in init_values and record.state == 'pending': + return 'hr_contract.mt_contract_pending' + elif 'state' in init_values and record.state == 'close': + return 'hr_contract.mt_contract_close' + return super(hr_contract, self)._track_subtype(cr, uid, ids, init_values, context=context) diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index e260bf47a37..39dc9f74b96 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -57,13 +57,6 @@ class hr_expense_expense(osv.osv): _inherit = ['mail.thread', 'ir.needaction_mixin'] _description = "Expense" _order = "id desc" - _track = { - 'state': { - 'hr_expense.mt_expense_approved': lambda self, cr, uid, obj, ctx=None: obj.state == 'accepted', - 'hr_expense.mt_expense_refused': lambda self, cr, uid, obj, ctx=None: obj.state == 'cancelled', - 'hr_expense.mt_expense_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirm', - }, - } _columns = { 'name': fields.char('Description', required=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}), @@ -152,6 +145,16 @@ class hr_expense_expense(osv.osv): def expense_canceled(self, cr, uid, ids, context=None): return self.write(cr, uid, ids, {'state': 'cancelled'}, context=context) + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'state' in init_values and record.state == 'accepted': + return 'hr_expense.mt_expense_approved' + elif 'state' in init_values and record.state == 'confirm': + return 'hr_expense.mt_expense_confirmed' + elif 'state' in init_values and record.state == 'cancelled': + return 'hr_expense.mt_expense_refused' + return super(hr_expense_expense, self)._track_subtype(cr, uid, ids, init_values, context=context) + def account_move_get(self, cr, uid, expense_id, context=None): ''' This method prepare the creation of the account move related to the given expense. diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 4d3a0bc8182..757148ba3de 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -138,14 +138,6 @@ class hr_holidays(osv.osv): _description = "Leave" _order = "type desc, date_from asc" _inherit = ['mail.thread', 'ir.needaction_mixin'] - _track = { - 'state': { - 'hr_holidays.mt_holidays_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirm', - 'hr_holidays.mt_holidays_first_validated': lambda self, cr, uid, obj, ctx=None: obj.state == 'validate1', - 'hr_holidays.mt_holidays_approved': lambda self, cr, uid, obj, ctx=None: obj.state == 'validate', - 'hr_holidays.mt_holidays_refused': lambda self, cr, uid, obj, ctx=None: obj.state == 'refuse', - }, - } def _employee_get(self, cr, uid, context=None): emp_id = context.get('default_employee_id', False) @@ -488,6 +480,18 @@ class hr_holidays(osv.osv): ids_to_set_false = list(set(ids) - set(ids_to_set_true)) return self.write(cr, uid, ids_to_set_true, {'payslip_status': True}, context=context) and self.write(cr, uid, ids_to_set_false, {'payslip_status': False}, context=context) + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'state' in init_values and record.state == 'validate': + return 'hr_holidays.mt_holidays_approved' + elif 'state' in init_values and record.state == 'validate1': + return 'hr_holidays.mt_holidays_first_validated' + elif 'state' in init_values and record.state == 'confirm': + return 'hr_holidays.mt_holidays_confirmed' + elif 'state' in init_values and record.state == 'refuse': + return 'hr_holidays.mt_holidays_refused' + return super(hr_holidays, self)._track_subtype(cr, uid, ids, init_values, context=context) + class resource_calendar_leaves(osv.osv): _inherit = "resource.calendar.leaves" diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index e4fb4c7b629..876444858b2 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -132,17 +132,6 @@ class hr_applicant(osv.Model): _description = "Applicant" _order = "priority desc, id desc" _inherit = ['mail.thread', 'ir.needaction_mixin', 'utm.mixin'] - - _track = { - 'emp_id': { - 'hr_recruitment.mt_applicant_hired': lambda self, cr, uid, obj, ctx=None: obj.emp_id, - }, - 'stage_id': { - # this is only an heuristics; depending on your particular stage configuration it may not match all 'new' stages - 'hr_recruitment.mt_applicant_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence <= 1, - 'hr_recruitment.mt_applicant_stage_changed': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence > 1, - }, - } _mail_mass_mailing = _('Applicants') def _get_default_department_id(self, cr, uid, context=None): @@ -428,6 +417,16 @@ class hr_applicant(osv.Model): action['domain'] = str(['&', ('res_model', '=', self._name), ('res_id', 'in', ids)]) return action + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'emp_id' in init_values and record.emp_id: + return 'hr_recruitment.mt_applicant_hired' + elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence <= 1: + return 'hr_recruitment.mt_applicant_new' + elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence > 1: + return 'hr_recruitment.mt_applicant_stage_changed' + return super(hr_applicant, self)._track_subtype(cr, uid, ids, init_values, context=context) + def message_get_reply_to(self, cr, uid, ids, context=None): """ Override to get the reply_to of the parent project. """ applicants = self.browse(cr, SUPERUSER_ID, ids, context=context) diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py index e71514f1d32..1bbde4ad70e 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py @@ -37,13 +37,6 @@ class hr_timesheet_sheet(osv.osv): _order = "id desc" _description = "Timesheet" - _track = { - 'state': { - 'hr_timesheet_sheet.mt_timesheet_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirm', - 'hr_timesheet_sheet.mt_timesheet_approved': lambda self, cr, uid, obj, ctx=None: obj.state == 'done', - }, - } - def _total(self, cr, uid, ids, name, args, context=None): """ Compute the attendances, analytic lines timesheets and differences between them for all the days of a timesheet and the current day @@ -280,6 +273,14 @@ class hr_timesheet_sheet(osv.osv): # OpenChatter methods and notifications # ------------------------------------------------ + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'state' in init_values and record.state == 'confirm': + return 'hr_timesheet_sheet.mt_timesheet_confirmed' + elif 'state' in init_values and record.state == 'done': + return 'hr_timesheet_sheet.mt_timesheet_approved' + return super(hr_timesheet_sheet, self)._track_subtype(cr, uid, ids, init_values, context=context) + def _needaction_domain_get(self, cr, uid, context=None): emp_obj = self.pool.get('hr.employee') empids = emp_obj.search(cr, uid, [('parent_id.user_id', '=', uid)], context=context) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 52329c440c7..cf07bc9c0f1 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -86,23 +86,6 @@ class mail_thread(osv.AbstractModel): _mail_flat_thread = True _mail_post_access = 'write' - # Automatic logging system if mail installed - # _track = { - # 'field': { - # 'module.subtype_xml': lambda self, cr, uid, obj, context=None: obj[state] == done, - # 'module.subtype_xml2': lambda self, cr, uid, obj, context=None: obj[state] != done, - # }, - # 'field2': { - # ... - # }, - # } - # where - # :param string field: field name - # :param module.subtype_xml: xml_id of a mail.message.subtype (i.e. mail.mt_comment) - # :param obj: is a browse_record - # :param function lambda: returns whether the tracking should record using this subtype - _track = {} - # Mass mailing feature _mail_mass_mailing = False @@ -466,14 +449,26 @@ class mail_thread(osv.AbstractModel): """ tracked_fields = [] for name, field in self._fields.items(): - visibility = getattr(field, 'track_visibility', False) - if visibility == 'always' or (visibility == 'onchange' and name in updated_fields) or name in self._track: + if getattr(field, 'track_visibility', False): tracked_fields.append(name) if tracked_fields: return self.fields_get(cr, uid, tracked_fields, context=context) return {} + def _track_subtype(self, cr, uid, ids, init_values, context=None): + """ Give the subtypes triggered by the changes on the record according + to values that have been updated. + + :param ids: list of a single ID, the ID of the record being modified + :type ids: singleton list + :param init_values: the original values of the record; only modified fields + are present in the dict + :type init_values: dict + :returns: a subtype xml_id or False if no subtype is trigerred + """ + return False + def message_track(self, cr, uid, ids, tracked_fields, initial_values, context=None): def convert_for_display(value, col_info): @@ -530,28 +525,29 @@ class mail_thread(osv.AbstractModel): continue # find subtypes and post messages or log if no subtype found - subtypes = [] + subtype_xmlid = False # By passing this key, that allows to let the subtype empty and so don't sent email because partners_to_notify from mail_message._notify will be empty if not context.get('mail_track_log_only'): - for field, track_info in self._track.items(): - if field not in changes: - continue - for subtype, method in track_info.items(): - if method(self, cr, uid, browse_record, context): - subtypes.append(subtype) + subtype_xmlid = browse_record._track_subtype(dict((col_name, initial[col_name]) for col_name in changes)) + # compatibility: use the deprecated _track dict + if not subtype_xmlid and hasattr(self, '_track'): + for field, track_info in self._track.items(): + if field not in changes or subtype_xmlid: + continue + for subtype, method in track_info.items(): + if method(self, cr, uid, browse_record, context): + _logger.warning("Model %s still using deprecated _track dict; override _track_subtype method instead" % self._name) + subtype_xmlid = subtype - posted = False - for subtype in subtypes: - subtype_rec = self.pool.get('ir.model.data').xmlid_to_object(cr, uid, subtype, context=context) + if subtype_xmlid: + subtype_rec = self.pool['ir.model.data'].xmlid_to_object(cr, uid, subtype_xmlid, context=context) if not (subtype_rec and subtype_rec.exists()): - _logger.debug('subtype %s not found' % subtype) + _logger.debug('subtype %s not found' % subtype_xmlid) continue message = format_message(subtype_rec.description if subtype_rec.description else subtype_rec.name, tracked_values) - self.message_post(cr, uid, browse_record.id, body=message, subtype=subtype, context=context) - posted = True - if not posted: + else: message = format_message('', tracked_values) - self.message_post(cr, uid, browse_record.id, body=message, context=context) + self.message_post(cr, uid, browse_record.id, body=message, subtype=subtype_xmlid, context=context) return True #------------------------------------------------------ diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py index e2c65348d69..d8f2f90a7da 100644 --- a/addons/mail/tests/test_mail_features.py +++ b/addons/mail/tests/test_mail_features.py @@ -833,29 +833,26 @@ class test_mail(TestMail): mt_group_public_id = self.mail_message_subtype.create(cr, uid, {'name': 'group_public', 'description': 'Group changed'}) self.ir_model_data.create(cr, uid, {'name': 'mt_group_public', 'model': 'mail.message.subtype', 'module': 'mail', 'res_id': mt_group_public_id}) - # Data: alter mail_group model for testing purposes (test on classic, selection and many2one fields) - cls = type(self.mail_group) - self.assertNotIn('_track', cls.__dict__) - cls._track = { - 'public': { - 'mail.mt_private': lambda self, cr, uid, obj, ctx=None: obj.public == 'private', - }, - 'name': { - 'mail.mt_name_supername': lambda self, cr, uid, obj, ctx=None: obj.name == 'supername', - }, - 'group_public_id': { - 'mail.mt_group_public_set': lambda self, cr, uid, obj, ctx=None: obj.group_public_id, - 'mail.mt_group_public': lambda self, cr, uid, obj, ctx=None: True, - }, - } + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'public' in init_values and record.public == 'private': + return 'mail.mt_private' + elif 'name' in init_values and record.name == 'supername': + return 'mail.mt_name_supername' + elif 'group_public_id' in init_values and record.group_public_id: + return 'mail.mt_group_public_set' + elif 'group_public_id' in init_values and not record.group_public_id: + return 'mail.mt_group_public' + return False + self.registry('mail.group')._patch_method('_track_subtype', _track_subtype) visibility = {'public': 'onchange', 'name': 'always', 'group_public_id': 'onchange'} + cls = type(self.mail_group) for key in visibility: self.assertFalse(hasattr(getattr(cls, key), 'track_visibility')) getattr(cls, key).track_visibility = visibility[key] @self.addCleanup def cleanup(): - delattr(cls, '_track') for key in visibility: del getattr(cls, key).track_visibility @@ -863,51 +860,39 @@ class test_mail(TestMail): self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'public': 'public'}) self.group_pigs.refresh() self.assertEqual(len(self.group_pigs.message_ids), 1, 'tracked: a message should have been produced') - # Test: first produced message: no subtype, name change tracked + # Test: first produced message: group public tracking last_msg = self.group_pigs.message_ids[-1] - self.assertFalse(last_msg.subtype_id, 'tracked: message should not have been linked to a subtype') + self.assertFalse(last_msg.subtype_id.id, 'tracked: message should not have been linked to a subtype') self.assertIn(u"Selectedgroupofusers\u2192Everyone", _strip_string_spaces(last_msg.body), 'tracked: message body incorrect') self.assertIn('Pigs', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field') # Test: change name as supername, public as private -> 2 subtypes self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'name': 'supername', 'public': 'private'}) self.group_pigs.refresh() - self.assertEqual(len(self.group_pigs.message_ids), 3, 'tracked: two messages should have been produced') + self.assertEqual(len(self.group_pigs.message_ids), 2, 'tracked: one new tracking message should have been produced') # Test: first produced message: mt_name_supername last_msg = self.group_pigs.message_ids[-2] self.assertEqual(last_msg.subtype_id.id, mt_private_id, 'tracked: message should be linked to mt_private subtype') self.assertIn('Private public', last_msg.body, 'tracked: message body does not hold the subtype description') self.assertIn(u'Pigs\u2192supername', _strip_string_spaces(last_msg.body), 'tracked: message body incorrect') - # Test: second produced message: mt_name_supername - last_msg = self.group_pigs.message_ids[-3] - self.assertEqual(last_msg.subtype_id.id, mt_name_supername_id, 'tracked: message should be linked to mt_name_supername subtype') - self.assertIn('Supername name', last_msg.body, 'tracked: message body does not hold the subtype description') - self.assertIn(u"Everyone\u2192Invitedpeopleonly", _strip_string_spaces(last_msg.body), 'tracked: message body incorrect') - self.assertIn(u'Pigs\u2192supername', _strip_string_spaces(last_msg.body), 'tracked feature: message body does not hold always tracked field') - # Test: change public as public, group_public_id -> 2 subtypes, name always tracked + # Test: change public as public, group_public_id -> 1 subtype, name always tracked self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'public': 'public', 'group_public_id': group_system_id}) self.group_pigs.refresh() - self.assertEqual(len(self.group_pigs.message_ids), 5, 'tracked: one message should have been produced') + self.assertEqual(len(self.group_pigs.message_ids), 3, 'tracked: one message should have been produced') # Test: first produced message: mt_group_public_set_id, with name always tracked, public tracked on change - last_msg = self.group_pigs.message_ids[-4] + last_msg = self.group_pigs.message_ids[-3] self.assertEqual(last_msg.subtype_id.id, mt_group_public_set_id, 'tracked: message should be linked to mt_group_public_set_id') self.assertIn('Group set', last_msg.body, 'tracked: message body does not hold the subtype description') self.assertIn(u"Invitedpeopleonly\u2192Everyone", _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field') self.assertIn(u'HumanResources/Employee\u2192Administration/Settings', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field') - # Test: second produced message: mt_group_public_id, with name always tracked, public tracked on change - last_msg = self.group_pigs.message_ids[-5] - self.assertEqual(last_msg.subtype_id.id, mt_group_public_id, 'tracked: message should be linked to mt_group_public_id') - self.assertIn('Group changed', last_msg.body, 'tracked: message body does not hold the subtype description') - self.assertIn(u"Invitedpeopleonly\u2192Everyone", _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field') - self.assertIn(u'HumanResources/Employee\u2192Administration/Settings', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field') # Test: change group_public_id to False -> 1 subtype, name always tracked self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'group_public_id': False}) self.group_pigs.refresh() - self.assertEqual(len(self.group_pigs.message_ids), 6, 'tracked: one message should have been produced') + self.assertEqual(len(self.group_pigs.message_ids), 4, 'tracked: one message should have been produced') # Test: first produced message: mt_group_public_set_id, with name always tracked, public tracked on change - last_msg = self.group_pigs.message_ids[-6] + last_msg = self.group_pigs.message_ids[-4] self.assertEqual(last_msg.subtype_id.id, mt_group_public_id, 'tracked: message should be linked to mt_group_public_id') self.assertIn('Group changed', last_msg.body, 'tracked: message body does not hold the subtype description') self.assertIn(u'Administration/Settings\u2192', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field') @@ -915,4 +900,4 @@ class test_mail(TestMail): # Test: change not tracked field, no tracking message self.mail_group.write(cr, self.user_raoul_id, [self.group_pigs_id], {'description': 'Dummy'}) self.group_pigs.refresh() - self.assertEqual(len(self.group_pigs.message_ids), 6, 'tracked: No message should have been produced') + self.assertEqual(len(self.group_pigs.message_ids), 4, 'tracked: No message should have been produced') diff --git a/addons/project/project.py b/addons/project/project.py index 16ff6fc67e8..176aca004a5 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -612,22 +612,7 @@ class task(osv.osv): _description = "Task" _date_name = "date_start" _inherit = ['mail.thread', 'ir.needaction_mixin'] - _mail_post_access = 'read' - _track = { - 'stage_id': { - # this is only an heuristics; depending on your particular stage configuration it may not match all 'new' stages - 'project.mt_task_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence <= 1, - 'project.mt_task_stage': lambda self, cr, uid, obj, ctx=None: obj.stage_id.sequence > 1, - }, - 'user_id': { - 'project.mt_task_assigned': lambda self, cr, uid, obj, ctx=None: obj.user_id and obj.user_id.id, - }, - 'kanban_state': { - 'project.mt_task_blocked': lambda self, cr, uid, obj, ctx=None: obj.kanban_state == 'blocked', - 'project.mt_task_ready': lambda self, cr, uid, obj, ctx=None: obj.kanban_state == 'done', - }, - } def _get_default_partner(self, cr, uid, context=None): project_id = self._get_default_project_id(cr, uid, context) @@ -1137,6 +1122,20 @@ class task(osv.osv): # Mail gateway # --------------------------------------------------- + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'kanban_state' in init_values and record.kanban_state == 'blocked': + return 'project.mt_task_blocked' + elif 'kanban_state' in init_values and record.kanban_state == 'done': + return 'project.mt_task_ready' + elif 'user_id' in init_values and record.user_id: # assigned -> new + return 'project.mt_task_new' + elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence <= 1: # start stage -> new + return 'project.mt_task_new' + elif 'stage_id' in init_values: + return 'project.mt_task_stage' + return super(task, self)._track_subtype(cr, uid, ids, init_values, context=context) + def message_get_reply_to(self, cr, uid, ids, context=None): """ Override to get the reply_to of the parent project. """ tasks = self.browse(cr, SUPERUSER_ID, ids, context=context) diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml index 238b62cc2fc..58a787b28e9 100644 --- a/addons/project/project_data.xml +++ b/addons/project/project_data.xml @@ -62,17 +62,11 @@ - Task Created + Task Opened project.task - - Task created - - - Task Assigned - project.task - - Task Assigned + + Task opened Task Blocked @@ -81,10 +75,10 @@ Task blocked - Task Ready for Next Stage + Task Ready project.task - Task Ready for Next Stage + Task ready for Next Stage Stage Changed @@ -94,43 +88,36 @@ - Task Created + Task Opened 10 project.project project_id - - Task Assigned + + Task Blocked 11 project.project - + project_id - - Task Blocked + + Task Ready 12 project.project - + project_id Task Stage Changed 13 project.project + project_id - - Task Ready - 14 - project.project - - project_id - - diff --git a/addons/project/tests/test_project_flow.py b/addons/project/tests/test_project_flow.py index 72aabcf189b..a1a457b03ff 100644 --- a/addons/project/tests/test_project_flow.py +++ b/addons/project/tests/test_project_flow.py @@ -146,11 +146,9 @@ class TestProjectFlow(TestProjectBase): self.assertEqual(res[0], uid, 'project: message_process: task should have been created by uid as alias_user_id is False on the alias') # Test: messages - self.assertEqual(len(task.message_ids), 3, + self.assertEqual(len(task.message_ids), 2, 'project: message_process: newly created task should have 2 messages: creation and email') - self.assertEqual(task.message_ids[2].subtype_id.name, 'Task Created', - 'project: message_process: first message of new task should have Task Created subtype') - self.assertEqual(task.message_ids[1].subtype_id.name, 'Task Assigned', + self.assertEqual(task.message_ids[1].subtype_id.name, 'Task Opened', 'project: message_process: first message of new task should have Task Created subtype') self.assertEqual(task.message_ids[0].author_id.id, self.email_partner_id, 'project: message_process: second message should be the one from Agrolait (partner failed)') diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index eb5ce771a95..2a3ec26e891 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -50,22 +50,7 @@ class project_issue(osv.Model): _description = "Project Issue" _order = "priority desc, create_date desc" _inherit = ['mail.thread', 'ir.needaction_mixin'] - _mail_post_access = 'read' - _track = { - 'stage_id': { - # this is only an heuristics; depending on your particular stage configuration it may not match all 'new' stages - 'project_issue.mt_issue_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence <= 1, - 'project_issue.mt_issue_stage': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence > 1, - }, - 'user_id': { - 'project_issue.mt_issue_assigned': lambda self, cr, uid, obj, ctx=None: obj.user_id and obj.user_id.id, - }, - 'kanban_state': { - 'project_issue.mt_issue_blocked': lambda self, cr, uid, obj, ctx=None: obj.kanban_state == 'blocked', - 'project_issue.mt_issue_ready': lambda self, cr, uid, obj, ctx=None: obj.kanban_state == 'done', - }, - } def _get_default_partner(self, cr, uid, context=None): project_id = self._get_default_project_id(cr, uid, context) @@ -424,6 +409,20 @@ class project_issue(osv.Model): # Mail gateway # ------------------------------------------------------- + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'kanban_state' in init_values and record.kanban_state == 'blocked': + return 'project_issue.mt_issue_blocked' + elif 'kanban_state' in init_values and record.kanban_state == 'done': + return 'project_issue.mt_issue_ready' + elif 'user_id' in init_values and record.user_id: # assigned -> new + return 'project_issue.mt_issue_new' + elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence <= 1: # start stage -> new + return 'project_issue.mt_issue_new' + elif 'stage_id' in init_values: + return 'project_issue.mt_issue_stage' + return super(project_issue, self)._track_subtype(cr, uid, ids, init_values, context=context) + def message_get_reply_to(self, cr, uid, ids, context=None): """ Override to get the reply_to of the parent project. """ issues = self.browse(cr, SUPERUSER_ID, ids, context=context) diff --git a/addons/project_issue/project_issue_data.xml b/addons/project_issue/project_issue_data.xml index bcc80c006ee..2ed232be7a9 100644 --- a/addons/project_issue/project_issue_data.xml +++ b/addons/project_issue/project_issue_data.xml @@ -30,17 +30,11 @@ Access all issues from the top Project menu, and access the issues of a specific - Issue Created + Issue Opened project.issue - Issue created - - - Issue Assigned - project.issue - - Issue assigned + Issue opened Issue Blocked @@ -49,10 +43,10 @@ Access all issues from the top Project menu, and access the issues of a specific Issue blocked - Issue Ready for Next Stage + Issue Ready project.issue - Issue Ready for Next Stage + Issue ready for next sStage Stage Changed @@ -62,44 +56,37 @@ Access all issues from the top Project menu, and access the issues of a specific - Issue Created + Issue Opened 20 project.project project_id - - Issue Assigned + + Issue Blocked 21 project.project - + project_id - - Issue Blocked + + Issue Ready 22 project.project - + project_id Issue Stage Changed 23 project.project + Stage changed project_id - - Issue Ready - 24 - project.project - - project_id - - diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index a1f69fd0fe5..ff76b51d826 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -197,13 +197,7 @@ class purchase_order(osv.osv): ('done', 'Done'), ('cancel', 'Cancelled') ] - _track = { - 'state': { - 'purchase.mt_rfq_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirmed', - 'purchase.mt_rfq_approved': lambda self, cr, uid, obj, ctx=None: obj.state == 'approved', - 'purchase.mt_rfq_done': lambda self, cr, uid, obj, ctx=None: obj.state == 'done', - }, - } + _columns = { 'name': fields.char('Order Reference', required=True, select=True, copy=False, help="Unique number of the purchase order, " @@ -344,6 +338,16 @@ class purchase_order(osv.osv): return super(purchase_order, self).unlink(cr, uid, unlink_ids, context=context) + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'state' in init_values and record.state == 'approved': + return 'purchase.mt_rfq_approved' + elif 'state' in init_values and record.state == 'confirmed': + return 'purchase.mt_rfq_confirmed' + elif 'state' in init_values and record.state == 'done': + return 'purchase.mt_rfq_done' + return super(purchase_order, self)._track_subtype(cr, uid, ids, init_values, context=context) + def set_order_line_status(self, cr, uid, ids, status, context=None): line = self.pool.get('purchase.order.line') order_line_ids = [] diff --git a/addons/sale/sale.py b/addons/sale/sale.py index cce41a92050..bbd34bc2fe7 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -38,12 +38,6 @@ class sale_order(osv.osv): _name = "sale.order" _inherit = ['mail.thread', 'ir.needaction_mixin'] _description = "Sales Order" - _track = { - 'state': { - 'sale.mt_order_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state in ['manual'], - 'sale.mt_order_sent': lambda self, cr, uid, obj, ctx=None: obj.state in ['sent'] - }, - } def _amount_line_tax(self, cr, uid, line, context=None): val = 0.0 @@ -291,6 +285,14 @@ class sale_order(osv.osv): 'nodestroy': True, } + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'state' in init_values and record.state == 'manual': + return 'sale.mt_order_confirmed' + elif 'state' in init_values and record.state == 'sent': + return 'sale.mt_order_sent' + return super(sale_order, self)._track_subtype(cr, uid, ids, init_values, context=context) + def onchange_pricelist_id(self, cr, uid, ids, pricelist_id, order_lines, context=None): context = context or {} if not pricelist_id: diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index 1bae68dbab3..30f6db15016 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -31,12 +31,6 @@ class stock_landed_cost(osv.osv): _description = 'Stock Landed Cost' _inherit = 'mail.thread' - _track = { - 'state': { - 'stock_landed_costs.mt_stock_landed_cost_open': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done', - }, - } - def _total_amount(self, cr, uid, ids, name, args, context=None): result = {} for cost in self.browse(cr, uid, ids, context=context): @@ -273,6 +267,12 @@ class stock_landed_cost(osv.osv): line_obj.write(cr, uid, key, {'additional_landed_cost': value}, context=context) return True + def _track_subtype(self, cr, uid, ids, init_values, context=None): + record = self.browse(cr, uid, ids[0], context=context) + if 'state' in init_values and record.state == 'done': + return 'stock_landed_costs.mt_stock_landed_cost_open' + return super(stock_landed_cost, self)._track_subtype(cr, uid, ids, init_values, context=context) + class stock_landed_cost_lines(osv.osv): _name = 'stock.landed.cost.lines' diff --git a/addons/website_event/models/event.py b/addons/website_event/models/event.py index 31644790e25..eae2fabc97e 100644 --- a/addons/website_event/models/event.py +++ b/addons/website_event/models/event.py @@ -10,14 +10,9 @@ from openerp.addons.website.models.website import slug class event(models.Model): _name = 'event.event' _inherit = ['event.event', 'website.seo.metadata', 'website.published.mixin'] - _track = { - 'website_published': { - 'website_event.mt_event_published': lambda self, cr, uid, obj, ctx=None: obj.website_published, - 'website_event.mt_event_unpublished': lambda self, cr, uid, obj, ctx=None: not obj.website_published - }, - } twitter_hashtag = fields.Char('Twitter Hashtag', default=lambda self: self._default_hashtag()) + website_published = fields.Boolean(track_visibility='onchange') # TDE TODO FIXME: when website_mail/mail_thread.py inheritance work -> this field won't be necessary website_message_ids = fields.One2many( 'mail.message', 'res_id', @@ -89,3 +84,12 @@ class event(models.Model): if event.address_id: return self.browse(cr, SUPERUSER_ID, ids[0], context=context).address_id.google_map_link() return None + + @api.multi + def _track_subtype(self, init_values): + self.ensure_one() + if 'website_published' in init_values and self.website_published: + return 'website_event.mt_event_published' + elif 'website_published' in init_values and not self.website_published: + return 'website_event.mt_event_unpublished' + return super(event, self)._track_subtype(init_values)