[MERGE] improvement project-issue
bzr revid: tfr@openerp.com-20110407114145-xvr27pjquxwk9ff0
This commit is contained in:
parent
1a45ff614d
commit
b11a71fd87
|
@ -205,7 +205,7 @@ class account_analytic_account(osv.osv):
|
|||
def check_recursion(self, cr, uid, ids, parent=None):
|
||||
return super(account_analytic_account, self)._check_recursion(cr, uid, ids, parent=parent)
|
||||
|
||||
_order = 'date_start desc,parent_id desc,code'
|
||||
_order = 'name asc'
|
||||
_constraints = [
|
||||
(check_recursion, 'Error! You can not create recursive analytic accounts.', ['parent_id']),
|
||||
]
|
||||
|
|
|
@ -279,10 +279,22 @@ class document_file(osv.osv):
|
|||
else:
|
||||
if vals.get('file_size'):
|
||||
del vals['file_size']
|
||||
if not self._check_duplication(cr, uid, vals):
|
||||
raise osv.except_osv(_('ValidateError'), _('File name must be unique!'))
|
||||
result = super(document_file, self).create(cr, uid, vals, context)
|
||||
cr.commit() # ?
|
||||
result = self._check_duplication(cr, uid, vals)
|
||||
if not result:
|
||||
domain = [
|
||||
('res_id', '=', vals['res_id']),
|
||||
('res_model', '=', vals['res_model']),
|
||||
('datas_fname', '=', vals['datas_fname']),
|
||||
]
|
||||
attach_ids = self.search(cr, uid, domain, context=context)
|
||||
super(document_file, self).write(cr, uid, attach_ids,
|
||||
{'datas' : vals['datas']},
|
||||
context=context)
|
||||
result = attach_ids[0]
|
||||
else:
|
||||
#raise osv.except_osv(_('ValidateError'), _('File name must be unique!'))
|
||||
result = super(document_file, self).create(cr, uid, vals, context)
|
||||
cr.commit() # ?
|
||||
return result
|
||||
|
||||
def __get_partner_id(self, cr, uid, res_model, res_id, context=None):
|
||||
|
|
|
@ -92,6 +92,7 @@ class email_template_mailbox(osv.osv):
|
|||
self.unlink(cr, uid, [id], context=context)
|
||||
# Remove attachments for this mail
|
||||
attachment_pool.unlink(cr, uid, values['attachments_ids'], context=context)
|
||||
return result
|
||||
else:
|
||||
self.write(cr, uid, id, {'folder':'sent', 'state':'na', 'date_mail':time.strftime("%Y-%m-%d %H:%M:%S")}, context)
|
||||
self.historise(cr, uid, [id], "Email sent successfully", context)
|
||||
|
|
|
@ -201,7 +201,7 @@
|
|||
</record>
|
||||
|
||||
<menuitem name="Email Templates" id="menu_email_template_all"
|
||||
parent="menu_email_template_configuration" action="action_email_template_tree_all" />
|
||||
parent="menu_email_template" action="action_email_template_tree_all" />
|
||||
|
||||
<!-- Email Template menu in Tools -->
|
||||
<menuitem name="Email Templates" id="menu_email_template_all_tools"
|
||||
|
|
|
@ -93,7 +93,17 @@ class marketing_campaign(osv.osv):
|
|||
this campaign to be run"),
|
||||
'partner_field_id': fields.many2one('ir.model.fields', 'Partner Field',
|
||||
domain="[('model_id', '=', object_id), ('ttype', '=', 'many2one'), ('relation', '=', 'res.partner')]",
|
||||
help="The generated workitems will be linked to the partner related to the record. If the record is the partner itself leave this field empty."),
|
||||
help="The generated workitems will be linked to the partner related to the record. "\
|
||||
"If the record is the partner itself leave this field empty. "\
|
||||
"This is useful for reporting purposes, via the Campaign Analysis or Campaign Follow-up views."),
|
||||
'unique_field_id': fields.many2one('ir.model.fields', 'Unique Field',
|
||||
domain="[('model_id', '=', object_id), ('ttype', 'in', ['char','int','many2one','text','selection'])]",
|
||||
help='If set, this field will help segments that work in "no duplicates" mode to avoid '\
|
||||
'selecting similar records twice. Similar records are records that have the same value for '\
|
||||
'this unique field. For example by choosing the "email_from" field for CRM Leads you would prevent '\
|
||||
'sending the same campaign to the same email address again. If not set, the "no duplicates" segments '\
|
||||
"will only avoid selecting the same record again if it entered the campaign previously. "\
|
||||
"Only easily comparable fields like textfields, integers, selections or single relationships may be used."),
|
||||
'mode': fields.selection([('test', 'Test Directly'),
|
||||
('test_realtime', 'Test in Realtime'),
|
||||
('manual', 'With Manual Confirmation'),
|
||||
|
@ -209,11 +219,36 @@ Normal - the campaign runs normally and automatically sends all emails and repor
|
|||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
raise osv.except_osv(_("Operation not supported"), _("Sorry, campaign duplication is not supported at the moment."))
|
||||
|
||||
def _find_duplicate_workitems(self, cr, uid, record, campaign_rec, context=None):
|
||||
"""Finds possible duplicates workitems for a record in this campaign, based on a uniqueness
|
||||
field.
|
||||
|
||||
:param record: browse_record to find duplicates workitems for.
|
||||
:param campaign_rec: browse_record of campaign
|
||||
"""
|
||||
Workitems = self.pool.get('marketing.campaign.workitem')
|
||||
duplicate_workitem_domain = [('res_id','=', record.id),
|
||||
('campaign_id','=', campaign_rec.id)]
|
||||
unique_field = campaign_rec.unique_field_id
|
||||
if unique_field:
|
||||
unique_value = getattr(record, unique_field.name, None)
|
||||
if unique_value:
|
||||
if unique_field.ttype == 'many2one':
|
||||
unique_value = unique_value.id
|
||||
similar_res_ids = self.pool.get(campaign_rec.object_id.model).search(cr, uid,
|
||||
[(unique_field.name, '=', unique_value)], context=context)
|
||||
if similar_res_ids:
|
||||
duplicate_workitem_domain = [('res_id','in', similar_res_ids),
|
||||
('campaign_id','=', campaign_rec.id)]
|
||||
return Workitems.search(cr, uid, duplicate_workitem_domain, context=context)
|
||||
|
||||
|
||||
marketing_campaign()
|
||||
|
||||
class marketing_campaign_segment(osv.osv):
|
||||
_name = "marketing.campaign.segment"
|
||||
_description = "Campaign Segment"
|
||||
_order = "name"
|
||||
|
||||
def _get_next_sync(self, cr, uid, ids, fn, args, context=None):
|
||||
# next auto sync date is same for all segments
|
||||
|
@ -226,13 +261,19 @@ class marketing_campaign_segment(osv.osv):
|
|||
'campaign_id': fields.many2one('marketing.campaign', 'Campaign', required=True, select=1, ondelete="cascade"),
|
||||
'object_id': fields.related('campaign_id','object_id', type='many2one', relation='ir.model', string='Resource'),
|
||||
'ir_filter_id': fields.many2one('ir.filters', 'Filter', ondelete="restrict",
|
||||
help="Filter to select the matching resource records that belong to this segment. New filters can be created and saved using the advanced search on the list view of the Resource. If no filter is set, all records are selected without filtering. The synchronization mode may also add a criterion to the filter."),
|
||||
help="Filter to select the matching resource records that belong to this segment. "\
|
||||
"New filters can be created and saved using the advanced search on the list view of the Resource. "\
|
||||
"If no filter is set, all records are selected without filtering. "\
|
||||
"The synchronization mode may also add a criterion to the filter."),
|
||||
'sync_last_date': fields.datetime('Last Synchronization', help="Date on which this segment was synchronized last time (automatically or manually)"),
|
||||
'sync_mode': fields.selection([('create_date', 'Only records created after last sync'),
|
||||
('write_date', 'Only records modified after last sync (no duplicates)'),
|
||||
('all', 'All records (no duplicates)')],
|
||||
'Synchronization mode',
|
||||
help="Determines an additional criterion to add to the filter when selecting new records to inject in the campaign."),
|
||||
help="Determines an additional criterion to add to the filter when selecting new records to inject in the campaign. "\
|
||||
'"No duplicates" prevents selecting records which have already entered the campaign previously.'\
|
||||
'If the campaign has a "unique field" set, "no duplicates" will also prevent selecting records which have '\
|
||||
'the same value for the unique field as other records that already entered the campaign.'),
|
||||
'state': fields.selection([('draft', 'Draft'),
|
||||
('running', 'Running'),
|
||||
('done', 'Done'),
|
||||
|
@ -301,6 +342,7 @@ class marketing_campaign_segment(osv.osv):
|
|||
|
||||
def process_segment(self, cr, uid, segment_ids=None, context=None):
|
||||
Workitems = self.pool.get('marketing.campaign.workitem')
|
||||
Campaigns = self.pool.get('marketing.campaign')
|
||||
if not segment_ids:
|
||||
segment_ids = self.search(cr, uid, [('state', '=', 'running')], context=context)
|
||||
|
||||
|
@ -323,21 +365,20 @@ class marketing_campaign_segment(osv.osv):
|
|||
object_ids = model_obj.search(cr, uid, criteria, context=context)
|
||||
|
||||
# XXX TODO: rewrite this loop more efficiently without doing 1 search per record!
|
||||
for o_ids in model_obj.browse(cr, uid, object_ids, context=context):
|
||||
# avoid duplicated workitem for the same resource
|
||||
for record in model_obj.browse(cr, uid, object_ids, context=context):
|
||||
# avoid duplicate workitem for the same resource
|
||||
if segment.sync_mode in ('write_date','all'):
|
||||
wi_ids = Workitems.search(cr, uid, [('res_id','=',o_ids.id),('segment_id','=',segment.id)], context=context)
|
||||
if wi_ids:
|
||||
if Campaigns._find_duplicate_workitems(cr, uid, record, segment.campaign_id, context=context):
|
||||
continue
|
||||
|
||||
wi_vals = {
|
||||
'segment_id': segment.id,
|
||||
'date': action_date,
|
||||
'state': 'todo',
|
||||
'res_id': o_ids.id
|
||||
'res_id': record.id
|
||||
}
|
||||
|
||||
partner = self.pool.get('marketing.campaign')._get_partner_for(segment.campaign_id, o_ids)
|
||||
partner = self.pool.get('marketing.campaign')._get_partner_for(segment.campaign_id, record)
|
||||
if partner:
|
||||
wi_vals['partner_id'] = partner.id
|
||||
|
||||
|
@ -353,6 +394,7 @@ marketing_campaign_segment()
|
|||
|
||||
class marketing_campaign_activity(osv.osv):
|
||||
_name = "marketing.campaign.activity"
|
||||
_order = "name"
|
||||
_description = "Campaign Activity"
|
||||
|
||||
_action_types = [
|
||||
|
@ -454,10 +496,11 @@ class marketing_campaign_activity(osv.osv):
|
|||
action_context = dict(context,
|
||||
active_id=workitem.res_id,
|
||||
active_ids=[workitem.res_id],
|
||||
active_model=workitem.object_id.model)
|
||||
active_model=workitem.object_id.model,
|
||||
workitem=workitem)
|
||||
res = server_obj.run(cr, uid, [activity.server_action_id.id],
|
||||
context=action_context)
|
||||
# server action return False if the action is perfomed
|
||||
# server action return False if the action is performed
|
||||
# except client_action, other and python code
|
||||
return res == False and True or res
|
||||
|
||||
|
@ -557,6 +600,8 @@ class marketing_campaign_workitem(osv.osv):
|
|||
continue
|
||||
|
||||
proxy = self.pool.get(wi.object_id.model)
|
||||
if not proxy.exists(cr, uid, [wi.res_id]):
|
||||
continue
|
||||
ng = proxy.name_get(cr, uid, [wi.res_id], context=context)
|
||||
if ng:
|
||||
res[wi.id] = ng[0][1]
|
||||
|
@ -567,8 +612,14 @@ class marketing_campaign_workitem(osv.osv):
|
|||
if not len(args):
|
||||
return []
|
||||
|
||||
condition = []
|
||||
final_ids = []
|
||||
condition_name = None
|
||||
for domain_item in args:
|
||||
# we only use the first domain criterion and ignore all the rest including operators
|
||||
if isinstance(domain_item, (list,tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_name':
|
||||
condition_name = [None, domain_item[1], domain_item[2]]
|
||||
break
|
||||
|
||||
assert condition_name, "Invalid search domain for marketing_campaign_workitem.res_name. It should use 'res_name'"
|
||||
|
||||
cr.execute("""select w.id, w.res_id, m.model \
|
||||
from marketing_campaign_workitem w \
|
||||
|
@ -577,15 +628,17 @@ class marketing_campaign_workitem(osv.osv):
|
|||
left join ir_model m on (m.id=c.object_id)
|
||||
""")
|
||||
res = cr.fetchall()
|
||||
workitem_map = {}
|
||||
matching_workitems = []
|
||||
for id, res_id, model in res:
|
||||
workitem_map.setdefault(model,{}).setdefault(res_id,set()).add(id)
|
||||
for model, id_map in workitem_map.iteritems():
|
||||
model_pool = self.pool.get(model)
|
||||
for arg in args:
|
||||
if arg[1] == 'ilike':
|
||||
condition.append((model_pool._rec_name, 'ilike', arg[2]))
|
||||
res_ids = model_pool.search(cr, uid, condition, context=context)
|
||||
if res_id in res_ids:
|
||||
final_ids.append(id)
|
||||
return [('id', 'in', final_ids)]
|
||||
condition_name[0] = model_pool._rec_name
|
||||
condition = [('id', 'in', id_map.keys()), condition_name]
|
||||
for res_id in model_pool.search(cr, uid, condition, context=context):
|
||||
matching_workitems.extend(id_map[res_id])
|
||||
return [('id', 'in', list(set(matching_workitems)))]
|
||||
|
||||
_columns = {
|
||||
'segment_id': fields.many2one('marketing.campaign.segment', 'Segment', readonly=True),
|
||||
|
@ -723,7 +776,7 @@ class marketing_campaign_workitem(osv.osv):
|
|||
# manual states are not processed automatically
|
||||
continue
|
||||
while True:
|
||||
domain = [('state', '=', 'todo'), ('date', '!=', False)]
|
||||
domain = [('campaign_id', '=', camp.id), ('state', '=', 'todo'), ('date', '!=', False)]
|
||||
if camp.mode in ('test_realtime', 'active'):
|
||||
domain += [('date','<=', time.strftime('%Y-%m-%d %H:%M:%S'))]
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
<separator string="Resource" colspan="2" />
|
||||
<field name="object_id"/>
|
||||
<field name="partner_field_id"/>
|
||||
<field name="unique_field_id"/>
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Cost" colspan="2" />
|
||||
|
|
|
@ -26,6 +26,10 @@ from datetime import datetime, date
|
|||
from tools.translate import _
|
||||
from osv import fields, osv
|
||||
|
||||
class project_project(osv.osv):
|
||||
_name = 'project.project'
|
||||
|
||||
project_project()
|
||||
|
||||
class project_task_type(osv.osv):
|
||||
_name = 'project.task.type'
|
||||
|
@ -35,6 +39,7 @@ class project_task_type(osv.osv):
|
|||
'name': fields.char('Stage Name', required=True, size=64, translate=True),
|
||||
'description': fields.text('Description'),
|
||||
'sequence': fields.integer('Sequence'),
|
||||
'project_ids': fields.many2many('project.project', 'project_task_type_rel', 'type_id', 'project_id', 'Projects'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
|
|
|
@ -43,9 +43,9 @@ project_issue_version()
|
|||
class project_issue(crm.crm_case, osv.osv):
|
||||
_name = "project.issue"
|
||||
_description = "Project Issue"
|
||||
_order = "priority, id desc"
|
||||
_order = "priority, create_date desc"
|
||||
_inherit = ['mailgate.thread']
|
||||
|
||||
|
||||
def case_open(self, cr, uid, ids, *args):
|
||||
"""
|
||||
@param self: The object pointer
|
||||
|
@ -56,7 +56,7 @@ class project_issue(crm.crm_case, osv.osv):
|
|||
"""
|
||||
|
||||
res = super(project_issue, self).case_open(cr, uid, ids, *args)
|
||||
self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
|
||||
self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S'), 'assigned_to' : uid})
|
||||
for (id, name) in self.name_get(cr, uid, ids):
|
||||
message = _("Issue '%s' has been opened.") % name
|
||||
self.log(cr, uid, id, message)
|
||||
|
@ -175,7 +175,7 @@ class project_issue(crm.crm_case, osv.osv):
|
|||
'section_id': fields.many2one('crm.case.section', 'Sales Team', \
|
||||
select=True, help='Sales team to which Case belongs to.\
|
||||
Define Responsible user and Email account for mail gateway.'),
|
||||
'user_id': fields.many2one('res.users', 'Responsible'),
|
||||
'user_id': fields.related('project_id', 'user_id', type='many2one', relation='res.users', store=True, select=1, string='Responsible'),
|
||||
'partner_id': fields.many2one('res.partner', 'Partner'),
|
||||
'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \
|
||||
domain="[('partner_id','=',partner_id)]"),
|
||||
|
@ -200,19 +200,19 @@ class project_issue(crm.crm_case, osv.osv):
|
|||
'partner_name': fields.char("Employee's Name", size=64),
|
||||
'partner_mobile': fields.char('Mobile', size=32),
|
||||
'partner_phone': fields.char('Phone', size=32),
|
||||
'type_id': fields.many2one ('project.task.type', 'Resolution'),
|
||||
'type_id': fields.many2one ('project.task.type', 'Resolution', domain="[('project_ids', '=', project_id)]"),
|
||||
'project_id':fields.many2one('project.project', 'Project'),
|
||||
'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', \
|
||||
method=True, multi='day_open', type="float", store=True),
|
||||
method=True, multi='compute_day', type="float", store=True),
|
||||
'day_close': fields.function(_compute_day, string='Days to Close', \
|
||||
method=True, multi='day_close', type="float", store=True),
|
||||
'assigned_to': fields.related('task_id', 'user_id', string = 'Assigned to', type="many2one", relation="res.users", store=True, help='This is the current user to whom the related task have been assigned'),
|
||||
method=True, multi='compute_day', type="float", store=True),
|
||||
'assigned_to': fields.many2one('res.users', 'Assigned to', required=False, select=1),
|
||||
'working_hours_open': fields.function(_compute_day, string='Working Hours to Open the Issue', \
|
||||
method=True, multi='working_days_open', type="float", store=True),
|
||||
method=True, multi='compute_day', type="float", store=True),
|
||||
'working_hours_close': fields.function(_compute_day, string='Working Hours to Close the Issue', \
|
||||
method=True, multi='working_days_close', type="float", store=True),
|
||||
method=True, multi='compute_day', type="float", store=True),
|
||||
'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
|
||||
'date_action_last': fields.datetime('Last Action', readonly=1),
|
||||
'date_action_next': fields.datetime('Next Action', readonly=1),
|
||||
|
@ -230,17 +230,30 @@ class project_issue(crm.crm_case, osv.osv):
|
|||
return user.context_project_id.id
|
||||
return False
|
||||
|
||||
def on_change_project(self, cr, uid, ids, project_id, context=None):
|
||||
result = {}
|
||||
|
||||
if project_id:
|
||||
project = self.pool.get('project.project').browse(cr, uid, project_id, context=context)
|
||||
if project.user_id:
|
||||
result['value'] = {'user_id' : project.user_id.id}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
_defaults = {
|
||||
'active': 1,
|
||||
'user_id': crm.crm_case._get_default_user,
|
||||
#'user_id': crm.crm_case._get_default_user,
|
||||
'partner_id': crm.crm_case._get_default_partner,
|
||||
'partner_address_id': crm.crm_case._get_default_partner_address,
|
||||
'email_from': crm.crm_case. _get_default_email,
|
||||
'email_from': crm.crm_case._get_default_email,
|
||||
'state': 'draft',
|
||||
'section_id': crm.crm_case. _get_section,
|
||||
'section_id': crm.crm_case._get_section,
|
||||
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c),
|
||||
'priority': crm.AVAILABLE_PRIORITIES[2][0],
|
||||
'project_id':_get_project,
|
||||
'categ_id' : lambda *a: False,
|
||||
#'assigned_to' : lambda obj, cr, uid, context: uid,
|
||||
}
|
||||
|
||||
def convert_issue_task(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -52,13 +52,14 @@
|
|||
<field name="arch" type="xml">
|
||||
<form string="Issue Tracker Form">
|
||||
<group colspan="4" col="6">
|
||||
<field name="name"/>
|
||||
<field name="project_id" required="True"/>
|
||||
<field name="categ_id" widget="selection" domain="[('object_id.model', '=', 'project.issue')]"/>
|
||||
<field name="user_id"/>
|
||||
<field name="assigned_to" />
|
||||
<field name="name"/>
|
||||
<field name="project_id" required="True" on_change="on_change_project(project_id)"/>
|
||||
<field name="categ_id" widget="selection" domain="[('object_id.model', '=', 'project.issue')]"/>
|
||||
<field name="user_id" invisible="1" />
|
||||
<field name="assigned_to"/>
|
||||
<field name="version_id" colspan="2" widget="selection"/>
|
||||
<group colspan="2" col="4">
|
||||
<field name="type_id" readonly="1"/>
|
||||
<field name="type_id" string="Resolution" />
|
||||
<button name="prev_type" string="Previous" type="object" icon="gtk-go-back" help="Change to Previous Stage"/>
|
||||
<button name="next_type" string="Next" type="object" icon="gtk-go-forward" help="Change to Next Stage"/>
|
||||
</group>
|
||||
|
@ -73,7 +74,6 @@
|
|||
</group>
|
||||
<group col="3" colspan="2">
|
||||
<separator colspan="3" string="Status"/>
|
||||
<field name="version_id" colspan="3" widget="selection"/>
|
||||
<field name="priority" colspan="3"/>
|
||||
<field name="task_id" on_change="onchange_task_id(task_id)"/>
|
||||
<button string="Convert To Task" name="convert_issue_task" icon="gtk-index" type="object"
|
||||
|
@ -184,12 +184,11 @@
|
|||
<field name="partner_id" groups="base.group_extended"/>
|
||||
<field name="project_id" />
|
||||
<field name="priority" string="Priority"/>
|
||||
<field name="type_id" widget="selection" readonly="1"/>
|
||||
<field name="type_id" widget="selection" readonly="1" string="Resolution" />
|
||||
<button name="prev_type" string="Previous" type="object" icon="gtk-go-back" help="Change to Previous Stage"/>
|
||||
<button name="next_type" string="Next" type="object" icon="gtk-go-forward" help="Change to Next Stage"/>
|
||||
<field name="version_id" widget="selection"/>
|
||||
<field name="user_id"/>
|
||||
<field name="assigned_to" attrs="{'readonly':[('task_id','=',False)]}"/>
|
||||
<field name="assigned_to"/>
|
||||
<field name="progress" widget="progressbar" attrs="{'invisible':[('task_id','=',False)]}"/>
|
||||
<field name="state"/>
|
||||
<button name="case_cancel" string="Cancel" states="draft,open,pending" type="object" icon="gtk-cancel"/>
|
||||
|
@ -218,8 +217,8 @@
|
|||
<separator orientation="vertical"/>
|
||||
<field name="name"/>
|
||||
<field name="partner_id" groups="base.group_extended"/>
|
||||
<field name="user_id">
|
||||
<filter domain="[('user_id','=',False)]" help="Unassigned Issues" icon="terp-personal-" separator="1"/>
|
||||
<field name="assigned_to">
|
||||
<filter domain="[('assigned_to','=',False)]" help="Unassigned Issues" icon="terp-personal-" separator="1"/>
|
||||
</field>
|
||||
<field name="project_id"/>
|
||||
</group>
|
||||
|
@ -233,7 +232,7 @@
|
|||
<newline/>
|
||||
<group expand="0" string="Group By..." groups="base.group_extended">
|
||||
<filter string="Responsible" icon="terp-personal"
|
||||
domain="[]" context="{'group_by':'user_id'}" />
|
||||
domain="[]" context="{'group_by':'assigned_to'}" />
|
||||
<filter string="Partner" icon="terp-partner" domain="[]"
|
||||
context="{'group_by':'partner_id'}" />
|
||||
<separator orientation="vertical"/>
|
||||
|
@ -264,7 +263,7 @@
|
|||
<field name="type">calendar</field>
|
||||
<field name="priority" eval="2"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar string="Issues" date_start="date" color="user_id" date_delay="duration">
|
||||
<calendar string="Issues" date_start="date" color="assigned_to" date_delay="duration">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
</calendar>
|
||||
|
@ -285,11 +284,11 @@
|
|||
<field name="name" string="Feature description"/>
|
||||
<field name="partner_id" groups="base.group_extended"/>
|
||||
<field name="priority" string="Priority"/>
|
||||
<field name="type_id" widget="selection" readonly="1"/>
|
||||
<button name="prev_type" string="Previous" type="object" icon="gtk-go-back" help="Change to Previous Stage"/>
|
||||
<button name="next_type" string="Next" type="object" icon="gtk-go-forward" help="Change to Next Stage"/>
|
||||
<field name="type_id" widget="selection" readonly="1"/>
|
||||
<button name="prev_type" string="Previous" type="object" icon="gtk-go-back" help="Change to Previous Stage"/>
|
||||
<button name="next_type" string="Next" type="object" icon="gtk-go-forward" help="Change to Next Stage"/>
|
||||
<field name="version_id"/>
|
||||
<field name="user_id"/>
|
||||
<field name="assigned_to"/>
|
||||
<field name="state"/>
|
||||
<button name="case_cancel" string="Cancel" states="draft,open,pending" type="object" icon="gtk-cancel"/>
|
||||
<button name="case_close" string="Done" states="open,draft,pending" type="object" icon="gtk-jump-to"/>
|
||||
|
@ -312,7 +311,7 @@
|
|||
<separator orientation="vertical"/>
|
||||
<group>
|
||||
<field name="name" select='1' string="Feature description"/>
|
||||
<field name="user_id" select="1"/>
|
||||
<field name="assigned_to" select="1"/>
|
||||
<field name="state" select="1">
|
||||
<filter icon="terp-check" domain="[('state','in',('open','draft'))]" help="Current Features" name="current_feature"/>
|
||||
<filter icon="terp-camera_test" domain="[('state','=','open')]" help="Open Features"/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
|
@ -16,7 +16,7 @@
|
|||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
@ -101,10 +101,11 @@ class project_issue_report(osv.osv):
|
|||
c.task_id,
|
||||
date_trunc('day',c.create_date) as create_date,
|
||||
extract('epoch' from (c.date_open-c.create_date))/(3600*24) as delay_open,
|
||||
extract('epoch' from (c.date_closed-c.create_date))/(3600*24) as delay_close,
|
||||
extract('epoch' from (c.date_closed-c.date_open))/(3600*24) as delay_close,
|
||||
(SELECT count(id) FROM mailgate_message WHERE model='project.issue' AND res_id=c.id) AS email
|
||||
FROM
|
||||
project_issue c
|
||||
WHERE c.categ_id IN (select id from crm_case_categ where object_id in (select id from ir_model where model = 'project.issue'))
|
||||
)""")
|
||||
|
||||
project_issue_report()
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
<field name="nbr" string="#Project Issues" sum='#Number of Project Issues'/>
|
||||
<field name="delay_open" avg='Avg Opening Delay'/>
|
||||
<field name="delay_close" avg='Avg Closing Delay'/>
|
||||
<field name="working_hours_open" sum='Open Working Hours'/>
|
||||
<field name="working_hours_close" sum='Close Working hours'/>
|
||||
<field name="working_hours_open" avg='Open Working Hours'/>
|
||||
<field name="working_hours_close" avg='Close Working hours'/>
|
||||
<field name="email" sum='# Emails'/>
|
||||
</tree>
|
||||
</field>
|
||||
|
|
|
@ -27,8 +27,36 @@ class project_issue(osv.osv):
|
|||
_description = 'project issue'
|
||||
_columns = {
|
||||
'timesheet_ids': fields.one2many('hr.analytic.timesheet', 'issue_id', 'Timesheets'),
|
||||
'analytic_account_id': fields.related('project_id', 'analytic_account_id', type='many2one', relation='account.analytic.account',string='Analytic Account')
|
||||
'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account'),
|
||||
}
|
||||
|
||||
def on_change_project(self, cr, uid, ids, project_id, context=None):
|
||||
if not project_id:
|
||||
return {}
|
||||
|
||||
result = super(project_issue, self).on_change_project(cr, uid, ids, project_id, context=context)
|
||||
|
||||
project = self.pool.get('project.project').browse(cr, uid, project_id, context=context)
|
||||
if 'value' not in result:
|
||||
result['value'] = {}
|
||||
|
||||
account = project.analytic_account_id
|
||||
if account:
|
||||
result['value']['analytic_account_id'] = account.id
|
||||
|
||||
return result
|
||||
|
||||
def on_change_account_id(self, cr, uid, ids, account_id, context=None):
|
||||
if not account_id:
|
||||
return {}
|
||||
|
||||
account = self.pool.get('account.analytic.account').browse(cr, uid, account_id, context=context)
|
||||
result = {}
|
||||
|
||||
if account and account.state == 'pending':
|
||||
result = {'warning' : {'title' : _('Analytic Account'), 'message' : _('The Analytic Account is in pending !')}}
|
||||
|
||||
return result
|
||||
|
||||
project_issue()
|
||||
|
||||
|
|
|
@ -7,14 +7,22 @@
|
|||
<field name="inherit_id" ref="project_issue.project_issue_form_view" />
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="project_id" position="attributes">
|
||||
<attribute name="on_change">on_change_project(project_id)</attribute>
|
||||
</field>
|
||||
<xpath expr="/form/notebook" position="before">
|
||||
<field name="analytic_account_id"
|
||||
domain="[('parent_id','!=',False),('partner_id', '=', partner_id),('type', '!=', 'view')]"
|
||||
on_change='on_change_account_id(analytic_account_id)'
|
||||
groups="base.group_extended" />
|
||||
</xpath>
|
||||
<notebook colspan="4">
|
||||
<page string="Worklogs">
|
||||
<field name="analytic_account_id" invisible="1" domain="[('parent_id','!=',False)]" groups="base.group_extended"/>
|
||||
<field name="timesheet_ids" colspan="4" nolabel="1" context="{'default_user_id' : user_id, 'default_account_id' : analytic_account_id}">
|
||||
<field name="timesheet_ids" colspan="4" nolabel="1" context="{'default_user_id' : assigned_to, 'default_account_id' : analytic_account_id}">
|
||||
<tree editable="top" string="Timesheet">
|
||||
<field name="name"/>
|
||||
<field name="unit_amount" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" widget="float_time"/>
|
||||
<field name="account_id" invisible="0" domain="[('partner_id', '=', parent.partner_id)]" on_change="on_change_account_id(account_id)"/>
|
||||
<field name="name"/>
|
||||
<field name="unit_amount" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)" widget="float_time"/>
|
||||
<field name="account_id" invisible="0" domain="[('partner_id', '=', parent.partner_id)]" on_change="on_change_account_id(account_id)"/>
|
||||
<field name="date"/>
|
||||
<field name="user_id"/>
|
||||
<field invisible="1" name="journal_id"/>
|
||||
|
|
Loading…
Reference in New Issue