Sanjay Gohel (Open ERP) has proposed merging lp:~openerp-dev/openobject-addons/trunk-tracked-fields-chs into lp:openobject-addons.
Requested reviews: Thibault Delavallée (OpenERP) (tde-openerp) For more details, see: https://code.launchpad.net/~openerp-dev/openobject-addons/trunk-tracked-fields-chs/+merge/138647 Hello, I have added tracked_field.rst for documentation on tracked field. Thank You. Sanjay Gohel (SGO) -- https://code.launchpad.net/~openerp-dev/openobject-addons/trunk-tracked-fields-chs/+merge/138647 Your team OpenERP R&D Team is subscribed to branch lp:~openerp-dev/openobject-addons/trunk-tracked-fields-chs.
=== modified file 'account/account_invoice.py' --- account/account_invoice.py 2012-12-05 11:28:53 +0000 +++ account/account_invoice.py 2012-12-07 07:23:20 +0000 @@ -207,7 +207,7 @@ ('open','Open'), ('paid','Paid'), ('cancel','Cancelled'), - ],'Status', select=True, readonly=True, + ],'Status', select=True, readonly=True,tracked=True, help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed Invoice. \ \n* The \'Pro-forma\' when invoice is in Pro-forma status,invoice does not have an invoice number. \ \n* The \'Open\' status is used when user create invoice,a invoice number is generated.Its in open status till user does not pay invoice. \ @@ -275,7 +275,7 @@ help="Remaining amount due."), 'payment_ids': fields.function(_compute_lines, relation='account.move.line', type="many2many", string='Payments'), 'move_name': fields.char('Journal Entry', size=64, readonly=True, states={'draft':[('readonly',False)]}), - 'user_id': fields.many2one('res.users', 'Salesperson', readonly=True, states={'draft':[('readonly',False)]}), + 'user_id': fields.many2one('res.users', 'Salesperson', readonly=True, tracked=True, states={'draft':[('readonly',False)]}), 'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position', readonly=True, states={'draft':[('readonly',False)]}) } _defaults = { === modified file 'crm/crm.py' --- crm/crm.py 2012-11-29 22:26:45 +0000 +++ crm/crm.py 2012-12-07 07:23:20 +0000 @@ -78,6 +78,7 @@ 'state': fields.selection(AVAILABLE_STATES, 'Related Status', required=True, help="The status of your document will automatically change regarding the selected stage. " \ "For example, if a stage is related to the status 'Close', when your document reaches this stage, it is automatically closed."), + 'subtype': fields.char('Related Subtype', size=64), 'case_default': fields.boolean('Common to All Teams', help="If you check this field, this stage will be proposed by default on each sales team. It will not assign this stage to existing teams."), 'fold': fields.boolean('Hide in Views when Empty', === modified file 'crm/crm_lead.py' --- crm/crm_lead.py 2012-12-05 13:37:23 +0000 +++ crm/crm_lead.py 2012-12-07 07:23:20 +0000 @@ -197,7 +197,7 @@ '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.'), + select=True, tracked=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'), @@ -213,9 +213,9 @@ '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', + 'stage_id': fields.many2one('crm.case.stage', 'Stage',tracked=True, domain="['&', ('fold', '=', False), '&', '|', ('section_ids', '=', section_id), ('case_default', '=', True), '|', ('type', '=', type), ('type', '=', 'both')]"), - 'user_id': fields.many2one('res.users', 'Salesperson'), + 'user_id': fields.many2one('res.users', 'Salesperson',tracked=True), 'referred': fields.char('Referred By', size=64), 'date_open': fields.datetime('Opened', readonly=True), 'day_open': fields.function(_compute_day, string='Days to Open', \ @@ -228,7 +228,7 @@ # Only used for type opportunity 'probability': fields.float('Success Rate (%)',group_operator="avg"), - 'planned_revenue': fields.float('Expected Revenue'), + 'planned_revenue': fields.float('Expected Revenue',tracked=True), '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), @@ -386,7 +386,6 @@ 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) - self.case_mark_lost_send_note(cr, uid, ids, context=context) return True def case_mark_won(self, cr, uid, ids, context=None): @@ -395,7 +394,6 @@ 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) - self.case_mark_won_send_note(cr, uid, ids, context=context) return True def set_priority(self, cr, uid, ids, priority): @@ -847,11 +845,6 @@ # OpenChatter methods and notifications # ---------------------------------------- - def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): - """ Override of the (void) default notification method. """ - stage_name = self.pool.get('crm.case.stage').name_get(cr, uid, [stage_id], context=context)[0][1] - return self.message_post(cr, uid, ids, body=_("Stage changed to <b>%s</b>.") % (stage_name), subtype="mt_crm_stage", context=context) - def case_get_note_msg_prefix(self, cr, uid, lead, context=None): if isinstance(lead, (int, long)): lead = self.browse(cr, uid, [lead], context=context)[0] @@ -863,14 +856,6 @@ self.message_post(cr, uid, [id], body=message, context=context) return True - def case_mark_lost_send_note(self, cr, uid, ids, context=None): - message = _("Opportunity has been <b>lost</b>.") - return self.message_post(cr, uid, ids, body=message, subtype="mt_crm_lost", context=context) - - def case_mark_won_send_note(self, cr, uid, ids, context=None): - message = _("Opportunity has been <b>won</b>.") - return self.message_post(cr, uid, ids, body=message, subtype="mt_crm_won", context=context) - def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None): phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0] if action == 'log': prefix = 'Logged' === modified file 'crm/crm_lead_data.xml' --- crm/crm_lead_data.xml 2012-11-29 22:26:45 +0000 +++ crm/crm_lead_data.xml 2012-12-07 07:23:20 +0000 @@ -7,6 +7,7 @@ <field name="name">New</field> <field eval="1" name="case_default"/> <field name="state">draft</field> + <field name="subtype">mail.mt_crm_stage</field> <field eval="'10'" name="probability"/> <field eval="'10'" name="sequence"/> <field name="type">both</field> @@ -15,6 +16,7 @@ <field name="name">Opportunity</field> <field eval="1" name="case_default"/> <field name="state">open</field> + <field name="subtype">mail.mt_crm_stage</field> <field eval="'20'" name="probability"/> <field eval="'11'" name="sequence"/> <field name="type">lead</field> @@ -23,6 +25,7 @@ <field name="name">Qualification</field> <field eval="1" name="case_default"/> <field name="state">open</field> + <field name="subtype">mail.mt_crm_stage</field> <field eval="'20'" name="probability"/> <field eval="'12'" name="sequence"/> <field name="type">opportunity</field> @@ -31,6 +34,7 @@ <field name="name">Proposition</field> <field eval="1" name="case_default"/> <field name="state">open</field> + <field name="subtype">mail.mt_crm_stage</field> <field eval="'40'" name="probability"/> <field eval="'13'" name="sequence"/> <field name="type">opportunity</field> @@ -39,6 +43,7 @@ <field name="name">Negotiation</field> <field eval="1" name="case_default"/> <field name="state">open</field> + <field name="subtype">mail.mt_crm_stage</field> <field eval="'60'" name="probability"/> <field eval="'14'" name="sequence"/> <field name="type">opportunity</field> @@ -47,6 +52,7 @@ <field name="name">Won</field> <field eval="1" name="case_default"/> <field name="state">done</field> + <field name="subtype">mail.mt_crm_won</field> <field eval="'100'" name="probability"/> <field eval="'15'" name="sequence"/> <field eval="1" name="on_change"/> @@ -57,6 +63,7 @@ <field eval="1" name="case_default"/> <field eval="False" name="fold"/> <field name="state">cancel</field> + <field name="subtype">mail.mt_crm_stage</field> <field eval="'0'" name="probability"/> <field eval="'16'" name="sequence"/> <field name="type">lead</field> @@ -66,6 +73,7 @@ <field eval="1" name="case_default"/> <field eval="True" name="fold"/> <field name="state">cancel</field> + <field name="subtype">mail.mt_crm_lost</field> <field eval="'0'" name="probability"/> <field eval="'17'" name="sequence"/> <field name="type">opportunity</field> === modified file 'crm/crm_view.xml' --- crm/crm_view.xml 2012-11-29 22:26:45 +0000 +++ crm/crm_view.xml 2012-12-07 07:23:20 +0000 @@ -203,6 +203,7 @@ <group col="4"> <field name="name"/> <field name="state"/> + <field name="subtype"/> <field name="probability"/> <field name="type"/> <field name="on_change"/> === modified file 'crm/res_partner.py' --- crm/res_partner.py 2012-11-29 22:26:45 +0000 +++ crm/res_partner.py 2012-12-07 07:23:20 +0000 @@ -39,7 +39,7 @@ return res _columns = { - 'section_id': fields.many2one('crm.case.section', 'Sales Team'), + 'section_id': fields.many2one('crm.case.section', 'Sales Team',tracked=True), 'opportunity_ids': fields.one2many('crm.lead', 'partner_id',\ 'Leads and Opportunities', domain=[('state','in', ('draft','open','pending'))]), 'meeting_ids': fields.many2many('crm.meeting', 'crm_meeting_partner_rel','partner_id', 'meeting_id', === modified file 'hr_recruitment/hr_recruitment.py' --- hr_recruitment/hr_recruitment.py 2012-11-30 17:11:30 +0000 +++ hr_recruitment/hr_recruitment.py 2012-12-07 07:23:20 +0000 @@ -186,7 +186,7 @@ 'partner_id': fields.many2one('res.partner', 'Contact'), 'create_date': fields.datetime('Creation Date', readonly=True, select=True), 'write_date': fields.datetime('Update Date', readonly=True), - 'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage', + 'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage',tracked=True, domain="['&', ('fold', '=', False), '|', ('department_id', '=', department_id), ('department_id', '=', False)]"), 'state': fields.related('stage_id', 'state', type="selection", store=True, selection=AVAILABLE_STATES, string="Status", readonly=True, @@ -197,7 +197,7 @@ set to \'Pending\'.'), 'categ_ids': fields.many2many('hr.applicant_category', string='Tags'), 'company_id': fields.many2one('res.company', 'Company'), - 'user_id': fields.many2one('res.users', 'Responsible'), + 'user_id': fields.many2one('res.users', 'Responsible',tracked=True), # Applicant Columns 'date_closed': fields.datetime('Closed', readonly=True, select=True), 'date_open': fields.datetime('Opened', readonly=True, select=True), === added file 'mail/doc/tracked_field.rst' --- mail/doc/tracked_field.rst 1970-01-01 00:00:00 +0000 +++ mail/doc/tracked_field.rst 2012-12-07 07:23:20 +0000 @@ -0,0 +1,18 @@ +Tracked fields +--------------- +Tracked fields is generic logging system for the changes applied to some fields in open chatter. + - In the definition of a fields. you have to simply add tracked=True. + - When changing one of the fields having tracked=True on saved automatically write a log in openchatter for the changes in fields. + +How it works: ++++++++++++++ + - You have to add tracked=True in field defination as following. + - ``'stage_id': fields.many2one('project.task.type', 'Stage',tracked=True),`` + - For developed this feature we override write method of mail_thread. + - And make one mako template which shows old field and updated field. + +Open chatter log: ++++++++++++++++++ + - After changing field follower can show log of tracked field in open chatter as followed. + - Updated Fields: + - Stage: Analysis -> Specification \ No newline at end of file === modified file 'mail/mail_thread.py' --- mail/mail_thread.py 2012-11-29 22:26:45 +0000 +++ mail/mail_thread.py 2012-12-07 07:23:20 +0000 @@ -20,19 +20,25 @@ ############################################################################## import base64 +from collections import defaultdict import dateutil import email +from functools import partial import logging import pytz import time import tools import xmlrpclib +from mako.template import Template as MakoTemplate + from email.message import Message from mail_message import decode from openerp import SUPERUSER_ID from osv import osv, fields +from openerp.osv.orm import browse_record from tools.safe_eval import safe_eval as eval +from tools.translate import _ _logger = logging.getLogger(__name__) @@ -68,6 +74,14 @@ _name = 'mail.thread' _description = 'Email Thread' _mail_flat_thread = True + _TRACK_TEMPLATE = """ + <span>${updated_fields}</span> + <ul> + %for chg in changes: + <li><span>${chg[0]}</span>: ${chg[1]} -> ${chg[2]}</li> + %endfor + </ul> + """ def _get_message_data(self, cr, uid, ids, name, args, context=None): """ Computes: @@ -250,6 +264,87 @@ return super(mail_thread, self).copy(cr, uid, id, default=default, context=context) #------------------------------------------------------ + # Automatically log tracked fields + #------------------------------------------------------ + + def write(self, cr, uid, ids, values, context=None): + + if context is None: + context = {} + #import pudb;pudb.set_trace() + + def false_value(f): + if f._type == 'boolean': + return False + return f._symbol_set[1](False) + + def convert_for_comparison(v, f): + # It will convert value for comparison between current and new. + if not v: + return false_value(f) + if isinstance(v, browse_record): + return v.id + return v + + tracked = dict((n, f) for n, f in self._all_columns.items() if getattr(f.column, 'tracked', False)) + to_log = [k for k in values if k in tracked] + + from_ = None + changes = defaultdict(list) + if to_log: + for record in self.browse(cr, uid, ids, context): + for tl in to_log: + column = tracked[tl].column + current = convert_for_comparison(record[tl], column) + new = convert_for_comparison(values[tl], column) + if new != current: + changes[record].append(tl) + from_ = record[tl] + + result = super(mail_thread, self).write(cr, uid, ids, values, context=context) + + updated_fields = _('Updated Fields:') + + Trans = self.pool['ir.translation'] + def _t(c): + # translate field + model = c.parent_model or self._name + lang = context.get('lang') + return Trans._get_source(cr, uid, '{0},{1}'.format(model, c.name), 'field', lang, ci.column.string) + + def get_subtype(model, record): + # it will return subtype name(xml_id) for stage. + record_model = self.pool[model].browse(cr, SUPERUSER_ID, record) + if record_model.__hasattr__('subtype'): + return record_model.subtype + return False + + for record, changed_fields in changes.items(): + # TODO tpl changed_fields + chg = [] + subtype = False + for f in changed_fields: + to = self.browse(cr, uid, ids[0], context)[f] + ci = tracked[f] + if ci.column._type == "many2one": + if to: + to = to.name_get()[0][1] + else: + to = "Removed" + if isinstance(from_, browse_record): + from_ = from_.name_get()[0][1] + + subtype = get_subtype(ci.column._obj,values[f]) + chg.append((_t(ci), from_, to)) + + message = MakoTemplate(self._TRACK_TEMPLATE).render_unicode(updated_fields=updated_fields, + changes=chg) + + record.message_post(message,subtype=subtype) + + return result + + #------------------------------------------------------ # mail.message wrappers and tools #------------------------------------------------------ === modified file 'project/project.py' --- project/project.py 2012-11-30 17:11:30 +0000 +++ project/project.py 2012-12-07 07:23:20 +0000 @@ -46,6 +46,7 @@ 'state': fields.selection(_TASK_STATE, 'Related Status', required=True, help="The status of your document is automatically changed regarding the selected stage. " \ "For example, if a stage is related to the status 'Close', when your document reaches this stage, it is automatically closed."), + 'subtype': fields.char('Related Subtype', size=64), 'fold': fields.boolean('Hide in views if empty', help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."), } @@ -750,7 +751,7 @@ 'description': fields.text('Description'), 'priority': fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Important'), ('0','Very important')], 'Priority', select=True), 'sequence': fields.integer('Sequence', select=True, help="Gives the sequence order when displaying a list of tasks."), - 'stage_id': fields.many2one('project.task.type', 'Stage', + 'stage_id': fields.many2one('project.task.type', 'Stage',tracked=True, domain="['&', ('fold', '=', False), ('project_ids', '=', project_id)]"), 'state': fields.related('stage_id', 'state', type="selection", store=True, selection=_TASK_STATE, string="Status", readonly=True, @@ -770,7 +771,7 @@ 'date_start': fields.datetime('Starting Date',select=True), 'date_end': fields.datetime('Ending Date',select=True), 'date_deadline': fields.date('Deadline',select=True), - 'project_id': fields.many2one('project.project', 'Project', ondelete='set null', select="1"), + 'project_id': fields.many2one('project.project', 'Project', ondelete='set null', select="1",tracked=True), 'parent_ids': fields.many2many('project.task', 'project_task_parent_rel', 'task_id', 'parent_id', 'Parent Tasks'), 'child_ids': fields.many2many('project.task', 'project_task_parent_rel', 'parent_id', 'task_id', 'Delegated Tasks'), 'notes': fields.text('Notes'), @@ -796,7 +797,7 @@ 'project.task': (lambda self, cr, uid, ids, c={}: ids, ['work_ids', 'remaining_hours', 'planned_hours'], 10), 'project.task.work': (_get_task, ['hours'], 10), }), - 'user_id': fields.many2one('res.users', 'Assigned to'), + 'user_id': fields.many2one('res.users', 'Assigned to',tracked=True), 'delegated_user_id': fields.related('child_ids', 'user_id', type='many2one', relation='res.users', string='Delegated To'), 'partner_id': fields.many2one('res.partner', 'Customer'), 'work_ids': fields.one2many('project.task.work', 'task_id', 'Work done'), @@ -991,14 +992,12 @@ if not task.date_end: vals['date_end'] = fields.datetime.now() self.case_set(cr, uid, [task.id], 'done', vals, context=context) - self.case_close_send_note(cr, uid, [task.id], context=context) return True def do_reopen(self, cr, uid, ids, context=None): for task in self.browse(cr, uid, ids, context=context): project = task.project_id self.case_set(cr, uid, [task.id], 'open', {}, context=context) - self.case_open_send_note(cr, uid, [task.id], context) return True def do_cancel(self, cr, uid, ids, context=None): @@ -1010,7 +1009,6 @@ self._check_child_task(cr, uid, ids, context=context) for task in tasks: self.case_set(cr, uid, [task.id], 'cancelled', {'remaining_hours': 0.0}, context=context) - self.case_cancel_send_note(cr, uid, [task.id], context=context) return True def do_open(self, cr, uid, ids, context=None): @@ -1020,7 +1018,6 @@ def case_open(self, cr, uid, ids, context=None): if not isinstance(ids,list): ids = [ids] self.case_set(cr, uid, ids, 'open', {'date_start': fields.datetime.now()}, context=context) - self.case_open_send_note(cr, uid, ids, context) return True def do_draft(self, cr, uid, ids, context=None): @@ -1029,7 +1026,6 @@ def case_draft(self, cr, uid, ids, context=None): self.case_set(cr, uid, ids, 'draft', {}, context=context) - self.case_draft_send_note(cr, uid, ids, context=context) return True def do_pending(self, cr, uid, ids, context=None): @@ -1037,8 +1033,7 @@ return self.case_pending(cr, uid, ids, context=context) def case_pending(self, cr, uid, ids, context=None): - self.case_set(cr, uid, ids, 'pending', {}, context=context) - return self.case_pending_send_note(cr, uid, ids, context=context) + return self.case_set(cr, uid, ids, 'pending', {}, context=context) def _delegate_task_attachments(self, cr, uid, task_id, delegated_task_id, context=None): attachment = self.pool.get('ir.attachment') @@ -1078,7 +1073,6 @@ self.do_pending(cr, uid, [task.id], context=context) elif delegate_data['state'] == 'done': self.do_close(cr, uid, [task.id], context=context) - self.do_delegation_send_note(cr, uid, [task.id], context) delegated_tasks[task.id] = delegated_task_id return delegated_tasks @@ -1158,7 +1152,6 @@ self._subscribe_project_followers_to_task(cr, uid, task_id, context=context) self._store_history(cr, uid, [task_id], context=context) - self.create_send_note(cr, uid, [task_id], context=context) return task_id # Overridden to reset the kanban_state to normal whenever @@ -1176,7 +1169,6 @@ #raise osv.except_osv(_('Warning!'), _('Stage is not defined in the project.')) write_vals = vals_reset_kstate if t.stage_id != new_stage else vals super(task, self).write(cr, uid, [t.id], write_vals, context=context) - self.stage_set_send_note(cr, uid, [t.id], new_stage, context=context) result = True else: result = super(task, self).write(cr, uid, ids, vals, context=context) @@ -1286,24 +1278,8 @@ res = super(task, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) return res + ['user_id', 'manager_id'] - def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): - """ Override of the (void) default notification method. """ - stage_name = self.pool.get('project.task.type').name_get(cr, uid, [stage_id], context=context)[0][1] - return self.message_post(cr, uid, ids, body=_("Stage changed to <b>%s</b>.") % (stage_name), - context=context) - - def create_send_note(self, cr, uid, ids, context=None): - return self.message_post(cr, uid, ids, body=_("Task has been <b>created</b>."), context=context) - - def case_draft_send_note(self, cr, uid, ids, context=None): return self.message_post(cr, uid, ids, body=_('Task has been set as <b>draft</b>.'), context=context) - def do_delegation_send_note(self, cr, uid, ids, context=None): - for task in self.browse(cr, uid, ids, context=context): - msg = _('Task has been <b>delegated</b> to <em>%s</em>.') % (task.user_id.name) - self.message_post(cr, uid, [task.id], body=msg, context=context) - return True - def project_task_reevaluate(self, cr, uid, ids, context=None): if self.pool.get('res.users').has_group(cr, uid, 'project.group_time_work_estimation_tasks'): return { === modified file 'project/project_data.xml' --- project/project_data.xml 2012-11-29 22:26:45 +0000 +++ project/project_data.xml 2012-12-07 07:23:20 +0000 @@ -30,11 +30,13 @@ <field name="sequence">1</field> <field name="name">Analysis</field> <field name="state">draft</field> + <field name="subtype">project.mt_task_new</field> <field name="case_default" eval="False"/> </record> <record id="project_tt_specification" model="project.task.type"> <field name="sequence">2</field> <field name="name">Specification</field> + <field name="subtype">project.mt_task_change</field> <field name="state">pending</field> <field name="case_default" eval="True"/> </record> @@ -42,11 +44,13 @@ <field name="sequence">2</field> <field name="name">Design</field> <field name="state">open</field> + <field name="subtype">project.mt_task_change</field> <field name="case_default" eval="True"/> </record> <record id="project_tt_development" model="project.task.type"> <field name="sequence">3</field> <field name="name">Development</field> + <field name="subtype">project.mt_task_change</field> <field name="state">open</field> <field name="case_default" eval="True"/> </record> @@ -54,12 +58,14 @@ <field name="sequence">4</field> <field name="name">Testing</field> <field name="state">open</field> + <field name="subtype">project.mt_task_change</field> <field name="case_default" eval="True"/> </record> <record id="project_tt_merge" model="project.task.type"> <field name="sequence">5</field> <field name="name">Merge</field> <field name="state">open</field> + <field name="subtype">project.mt_task_change</field> <field name="case_default" eval="False"/> <field name="fold" eval="True"/> </record> @@ -67,6 +73,7 @@ <field name="sequence">6</field> <field name="name">Done</field> <field name="state">done</field> + <field name="subtype">project.mt_task_closed</field> <field name="case_default" eval="True"/> <field name="fold" eval="True"/> </record> @@ -74,6 +81,7 @@ <field name="sequence">7</field> <field name="name">Cancelled</field> <field name="state">cancelled</field> + <field name="subtype">project.mt_task_canceled</field> <field name="case_default" eval="True"/> <field name="fold" eval="True"/> </record> === modified file 'project/project_view.xml' --- project/project_view.xml 2012-11-29 22:26:45 +0000 +++ project/project_view.xml 2012-12-07 07:23:20 +0000 @@ -748,6 +748,7 @@ </group> <group> <field name="state"/> + <field name="subtype"/> <field name="sequence"/> <field name="fold"/> </group> === modified file 'project_issue/project_issue.py' --- project_issue/project_issue.py 2012-11-29 22:26:45 +0000 +++ project_issue/project_issue.py 2012-12-07 07:23:20 +0000 @@ -245,14 +245,14 @@ 'version_id': fields.many2one('project.issue.version', 'Version'), 'stage_id': fields.many2one ('project.task.type', 'Stage', domain="['&', ('fold', '=', False), ('project_ids', '=', project_id)]"), - 'project_id':fields.many2one('project.project', 'Project'), + 'project_id':fields.many2one('project.project', 'Project',tracked=True), 'duration': fields.float('Duration'), 'task_id': fields.many2one('project.task', 'Task', domain="[('project_id','=',project_id)]"), 'day_open': fields.function(_compute_day, string='Days to Open', \ multi='compute_day', type="float", store=True), 'day_close': fields.function(_compute_day, string='Days to Close', \ multi='compute_day', type="float", store=True), - 'user_id': fields.many2one('res.users', 'Assigned to', required=False, select=1), + 'user_id': fields.many2one('res.users', 'Assigned to', required=False, select=1,tracked=True), 'working_hours_open': fields.function(_compute_day, string='Working Hours to Open the Issue', \ multi='compute_day', type="float", store=True), 'working_hours_close': fields.function(_compute_day, string='Working Hours to Close the Issue', \ === modified file 'sale/sale.py' --- sale/sale.py 2012-12-04 14:46:49 +0000 +++ sale/sale.py 2012-12-07 07:23:20 +0000 @@ -187,7 +187,7 @@ ('manual', 'Sale to Invoice'), ('invoice_except', 'Invoice Exception'), ('done', 'Done'), - ], 'Status', readonly=True, help="Gives the status of the quotation or sales order. \nThe exception status is automatically set when a cancel operation occurs in the processing of a document linked to the sale order. \nThe 'Waiting Schedule' status is set when the invoice is confirmed but waiting for the scheduler to run on the order date.", select=True), + ], 'Status', readonly=True, tracked=True, help="Gives the status of the quotation or sales order. \nThe exception status is automatically set when a cancel operation occurs in the processing of a document linked to the sale order. \nThe 'Waiting Schedule' status is set when the invoice is confirmed but waiting for the scheduler to run on the order date.", select=True), 'date_order': fields.date('Date', required=True, readonly=True, select=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}), 'create_date': fields.datetime('Creation Date', readonly=True, select=True, help="Date on which sales order is created."), 'date_confirm': fields.date('Confirmation Date', readonly=True, select=True, help="Date on which sales order is confirmed."),
_______________________________________________ Mailing list: https://launchpad.net/~openerp-dev-gtk Post to : openerp-dev-gtk@lists.launchpad.net Unsubscribe : https://launchpad.net/~openerp-dev-gtk More help : https://help.launchpad.net/ListHelp