- Odoo allows you to create default invoicing types. You might
- have to regularly assign discounts because of a specific
- contract or agreement with a customer. From this menu, you can
- create additional types of invoicing to speed up your
- invoicing.
-
-
-
-
+
+ Odoo allows you to create default invoicing types. You might
+ have to regularly assign discounts because of a specific
+ contract or agreement with a customer. From this menu, you can
+ create additional types of invoicing to speed up your
+ invoicing.
+
+
+
+
{'invoice_on_timesheets':1, 'default_type': 'contract'}
diff --git a/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice.yml b/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice.yml
index 3d852a53540..29bb6363ebe 100644
--- a/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice.yml
+++ b/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice.yml
@@ -28,7 +28,7 @@
-
I assign account on analytic account line.
-
- !python {model: hr.analytic.timesheet}: |
+ !python {model: account.analytic.line}: |
self.on_change_account_id(cr, uid, [ref('account_analytic_line_developyamlforhrmodule0')], ref('analytic.analytic_agrolait'))
-
diff --git a/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice_no_prod_tax.yml b/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice_no_prod_tax.yml
index 15b243d0ac8..6c2adc1862a 100644
--- a/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice_no_prod_tax.yml
+++ b/addons/hr_timesheet_invoice/test/test_hr_timesheet_invoice_no_prod_tax.yml
@@ -28,7 +28,7 @@
-
I assign account on analytic account line.
-
- !python {model: hr.analytic.timesheet}: |
+ !python {model: account.analytic.line}: |
self.on_change_account_id(cr, uid, [ref('account_analytic_line_developyamlforhrmodule1')], ref('analytic.analytic_agrolait'))
-
I create a Tax Code
diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py
index 953cb1942aa..90a9c8550bf 100644
--- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py
+++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py
@@ -139,16 +139,12 @@ class hr_timesheet_sheet(osv.osv):
if sheet.employee_id.id not in employee_ids: employee_ids.append(sheet.employee_id.id)
return hr_employee.attendance_action_change(cr, uid, employee_ids, context=context)
- def _count_all(self, cr, uid, ids, field_name, arg, context=None):
- Timesheet = self.pool['hr.analytic.timesheet']
- Attendance = self.pool['hr.attendance']
- return {
- sheet_id: {
- 'timesheet_activity_count': Timesheet.search_count(cr,uid, [('sheet_id','=', sheet_id)], context=context),
- 'attendance_count': Attendance.search_count(cr,uid, [('sheet_id', '=', sheet_id)], context=context)
- }
- for sheet_id in ids
- }
+ def _count_attendances(self, cr, uid, ids, field_name, arg, context=None):
+ res = dict.fromkeys(ids, 0)
+ attendances_groups = self.pool['hr.attendance'].read_group(cr, uid, [('sheet_id' , 'in' , ids)], ['sheet_id'], 'sheet_id', context=context)
+ for attendances in attendances_groups:
+ res[attendances['sheet_id'][0]] = attendances['sheet_id_count']
+ return res
_columns = {
'name': fields.char('Note', select=1,
@@ -157,7 +153,7 @@ class hr_timesheet_sheet(osv.osv):
'user_id': fields.related('employee_id', 'user_id', type="many2one", relation="res.users", store=True, string="User", required=False, readonly=True),#fields.many2one('res.users', 'User', required=True, select=1, states={'confirm':[('readonly', True)], 'done':[('readonly', True)]}),
'date_from': fields.date('Date from', required=True, select=1, readonly=True, states={'new':[('readonly', False)]}),
'date_to': fields.date('Date to', required=True, select=1, readonly=True, states={'new':[('readonly', False)]}),
- 'timesheet_ids' : fields.one2many('hr.analytic.timesheet', 'sheet_id',
+ 'timesheet_ids' : fields.one2many('account.analytic.line', 'sheet_id',
'Timesheet lines',
readonly=True, states={
'draft': [('readonly', False)],
@@ -181,8 +177,7 @@ class hr_timesheet_sheet(osv.osv):
'account_ids': fields.one2many('hr_timesheet_sheet.sheet.account', 'sheet_id', 'Analytic accounts', readonly=True),
'company_id': fields.many2one('res.company', 'Company'),
'department_id':fields.many2one('hr.department','Department'),
- 'timesheet_activity_count': fields.function(_count_all, type='integer', string='Timesheet Activities', multi=True),
- 'attendance_count': fields.function(_count_all, type='integer', string="Attendances", multi=True),
+ 'attendance_count': fields.function(_count_attendances, type='integer', string="Attendances"),
}
def _default_date_from(self, cr, uid, context=None):
@@ -260,7 +255,7 @@ class hr_timesheet_sheet(osv.osv):
raise UserError(_('You cannot delete a timesheet which have attendance entries.'))
toremove = []
- analytic_timesheet = self.pool.get('hr.analytic.timesheet')
+ analytic_timesheet = self.pool.get('account.analytic.line')
for sheet in self.browse(cr, uid, ids, context=context):
for timesheet in sheet.timesheet_ids:
toremove.append(timesheet.id)
@@ -298,26 +293,9 @@ class hr_timesheet_sheet(osv.osv):
return dom
-class account_analytic_line(osv.osv):
- _inherit = "account.analytic.line"
-
- def _get_default_date(self, cr, uid, context=None):
- if context is None:
- context = {}
- #get the default date (should be: today)
- res = super(account_analytic_line, self)._get_default_date(cr, uid, context=context)
- #if we got the dates from and to from the timesheet and if the default date is in between, we use the default
- #but if the default isn't included in those dates, we use the date start of the timesheet as default
- if context.get('timesheet_date_from') and context.get('timesheet_date_to'):
- if context['timesheet_date_from'] <= res <= context['timesheet_date_to']:
- return res
- return context.get('timesheet_date_from')
- #if we don't get the dates from the timesheet, we return the default value from super()
- return res
-
class account_analytic_account(osv.osv):
_inherit = "account.analytic.account"
-
+
def name_create(self, cr, uid, name, context=None):
if context is None:
context = {}
@@ -326,9 +304,24 @@ class account_analytic_account(osv.osv):
return super(account_analytic_account, self).name_create(cr, uid, name, context=context)
rec_id = self.create(cr, uid, {self._rec_name: name}, context)
return self.name_get(cr, uid, [rec_id], context)[0]
-
-class hr_timesheet_line(osv.osv):
- _inherit = "hr.analytic.timesheet"
+
+
+class account_analytic_line(osv.osv):
+ _inherit = "account.analytic.line"
+
+ def _get_default_date(self, cr, uid, context=None):
+ if context is None:
+ context = {}
+ # get the default date (should be: today)
+ res = super(account_analytic_line, self)._get_default_date(cr, uid, context=context)
+ # if we got the dates from and to from the timesheet and if the default date is in between, we use the default
+ # but if the default isn't included in those dates, we use the date start of the timesheet as default
+ if context.get('timesheet_date_from') and context.get('timesheet_date_to'):
+ if context['timesheet_date_from'] <= res <= context['timesheet_date_to']:
+ return res
+ return context.get('timesheet_date_from')
+ # if we don't get the dates from the timesheet, we return the default value from super()
+ return res
def _sheet(self, cursor, user, ids, name, args, context=None):
sheet_obj = self.pool.get('hr_timesheet_sheet.sheet')
@@ -349,29 +342,22 @@ class hr_timesheet_line(osv.osv):
for ts in self.browse(cr, uid, ids, context=context):
cr.execute("""
SELECT l.id
- FROM hr_analytic_timesheet l
- INNER JOIN account_analytic_line al
- ON (l.line_id = al.id)
- WHERE %(date_to)s >= al.date
- AND %(date_from)s <= al.date
- AND %(user_id)s = al.user_id
+ FROM account_analytic_line l
+ WHERE %(date_to)s >= l.date
+ AND %(date_from)s <= l.date
+ AND %(user_id)s = l.user_id
GROUP BY l.id""", {'date_from': ts.date_from,
'date_to': ts.date_to,
'user_id': ts.employee_id.user_id.id,})
ts_line_ids.extend([row[0] for row in cr.fetchall()])
return ts_line_ids
- def _get_account_analytic_line(self, cr, uid, ids, context=None):
- ts_line_ids = self.pool.get('hr.analytic.timesheet').search(cr, uid, [('line_id', 'in', ids)])
- return ts_line_ids
-
_columns = {
'sheet_id': fields.function(_sheet, string='Sheet', select="1",
type='many2one', relation='hr_timesheet_sheet.sheet', ondelete="cascade",
store={
'hr_timesheet_sheet.sheet': (_get_hr_timesheet_sheet, ['employee_id', 'date_from', 'date_to'], 10),
- 'account.analytic.line': (_get_account_analytic_line, ['user_id', 'date'], 10),
- 'hr.analytic.timesheet': (lambda self,cr,uid,ids,context=None: ids, None, 10),
+ 'account.analytic.line': (lambda self,cr,uid,ids,context=None: ids, ['user_id', 'date'], 10),
},
),
}
@@ -380,13 +366,13 @@ class hr_timesheet_line(osv.osv):
if isinstance(ids, (int, long)):
ids = [ids]
self._check(cr, uid, ids)
- return super(hr_timesheet_line, self).write(cr, uid, ids, values, context=context)
+ return super(account_analytic_line, self).write(cr, uid, ids, values,context=context)
def unlink(self, cr, uid, ids, *args, **kwargs):
if isinstance(ids, (int, long)):
ids = [ids]
self._check(cr, uid, ids)
- return super(hr_timesheet_line,self).unlink(cr, uid, ids,*args, **kwargs)
+ return super(account_analytic_line,self).unlink(cr, uid, ids,*args, **kwargs)
def _check(self, cr, uid, ids):
for att in self.browse(cr, uid, ids):
@@ -395,8 +381,7 @@ class hr_timesheet_line(osv.osv):
return True
def multi_on_change_account_id(self, cr, uid, ids, account_ids, context=None):
- return dict([(el, self.on_change_account_id(cr, uid, ids, el, context.get('user_id', uid))) for el in account_ids])
-
+ return dict([(account_id, self.on_change_account_id(cr, uid, ids, account_id, user_id=context.get('user_id', uid), is_timesheet=True, context=context)) for account_id in account_ids])
class hr_attendance(osv.osv):
@@ -550,7 +535,6 @@ class hr_timesheet_sheet_sheet_day(osv.osv):
}
_depends = {
'account.analytic.line': ['date', 'unit_amount'],
- 'hr.analytic.timesheet': ['line_id', 'sheet_id'],
'hr.attendance': ['action', 'name', 'sheet_id'],
}
@@ -583,7 +567,7 @@ class hr_timesheet_sheet_sheet_day(osv.osv):
FROM
((
select
- min(hrt.id) as id,
+ min(l.id) as id,
p.tz as timezone,
l.date::date as name,
s.id as sheet_id,
@@ -591,9 +575,8 @@ class hr_timesheet_sheet_sheet_day(osv.osv):
0 as orphan_attendances,
0.0 as total_attendance
from
- hr_analytic_timesheet hrt
- JOIN account_analytic_line l ON l.id = hrt.line_id
- LEFT JOIN hr_timesheet_sheet_sheet s ON s.id = hrt.sheet_id
+ account_analytic_line l
+ LEFT JOIN hr_timesheet_sheet_sheet s ON s.id = l.sheet_id
JOIN hr_employee e ON s.employee_id = e.id
JOIN resource_resource r ON e.resource_id = r.id
LEFT JOIN res_users u ON r.user_id = u.id
@@ -642,26 +625,23 @@ class hr_timesheet_sheet_sheet_account(osv.osv):
_depends = {
'account.analytic.line': ['account_id', 'date', 'to_invoice', 'unit_amount', 'user_id'],
- 'hr.analytic.timesheet': ['line_id'],
'hr_timesheet_sheet.sheet': ['date_from', 'date_to', 'user_id'],
}
def init(self, cr):
cr.execute("""create or replace view hr_timesheet_sheet_sheet_account as (
select
- min(hrt.id) as id,
+ min(l.id) as id,
l.account_id as name,
s.id as sheet_id,
sum(l.unit_amount) as total,
l.to_invoice as invoice_rate
from
- hr_analytic_timesheet hrt
- left join (account_analytic_line l
+ account_analytic_line l
LEFT JOIN hr_timesheet_sheet_sheet s
ON (s.date_to >= l.date
AND s.date_from <= l.date
- AND s.user_id = l.user_id))
- on (l.id = hrt.line_id)
+ AND s.user_id = l.user_id)
group by l.account_id, s.id, l.to_invoice
)""")
diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml
index aa5685d3d4a..daedf0686f0 100644
--- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml
+++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml
@@ -42,9 +42,9 @@
- {'search_default_sheet_id': [active_id]}
+ {'search_default_sheet_id': [active_id], 'default_is_timesheet' : 1}Timesheet Activities
- hr.analytic.timesheet
+ account.analytic.linehr_timesheet_sheet.sheet
@@ -56,6 +56,23 @@
hr_timesheet_sheet.sheet
+
+ hr.timesheet.sheet.tree
+ hr_timesheet_sheet.sheet
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hr.timesheet.sheet.form
@@ -107,10 +124,10 @@
-
+
-
+
@@ -120,10 +137,11 @@
+
@@ -198,7 +217,7 @@
hr_timesheet_sheet.sheetform
- {'search_default_to_approve':1}
+ {'search_default_to_approve':1, 'default_is_timesheet':1}
@@ -214,9 +233,15 @@
-
-
+
+
+ tree
+
+
+
+
+
- hr.analytic.timesheet.form
- hr.analytic.timesheet
+ account.analytic.line.form
+ account.analytic.line
@@ -252,8 +277,8 @@
- hr.analytic.timesheet.search
- hr.analytic.timesheet
+ account.analytic.line.search
+ account.analytic.line
@@ -324,24 +349,6 @@
res_model="hr_timesheet_sheet.sheet.day"
src_model="hr_timesheet_sheet.sheet"/>
-
- hr.timesheet.sheet.tree
- hr_timesheet_sheet.sheet
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- hr.timesheet.day.tree
@@ -363,7 +370,7 @@
formTimesheetstree,form
- {'search_default_employee_id': [active_id], 'default_employee_id': active_id}
+ {'search_default_employee_id': [active_id], 'default_employee_id': active_id , 'default_is_timesheet' : 1}
diff --git a/addons/hr_timesheet_sheet/report/hr_timesheet_report.py b/addons/hr_timesheet_sheet/report/hr_timesheet_report.py
index 6d41cc6443d..8aaea5d2606 100644
--- a/addons/hr_timesheet_sheet/report/hr_timesheet_report.py
+++ b/addons/hr_timesheet_sheet/report/hr_timesheet_report.py
@@ -52,11 +52,11 @@ class hr_timesheet_report(osv.osv):
sum(total_attendance) / coalesce(sum(j.count),1) as total_attendance
FROM hr_timesheet_sheet_sheet_day d left join (
SELECT
- h.sheet_id,
+ a.sheet_id,
a.date,
count(*)
- FROM account_analytic_line a inner join hr_analytic_timesheet h ON (h.line_id=a.id)
- GROUP BY h.sheet_id, a.date
+ FROM account_analytic_line a
+ GROUP BY a.sheet_id, a.date
) j ON (d.sheet_id = j.sheet_id AND d.name = j.date)
GROUP BY d.sheet_id, d.name
)
@@ -73,7 +73,7 @@ class hr_timesheet_report(osv.osv):
htss.state"""
def _from(self):
- return super(hr_timesheet_report, self)._from() + "left join hr_timesheet_sheet_sheet as htss ON (hat.sheet_id=htss.id) join totals as t on (t.sheet_id = hat.sheet_id and t.date = aal.date)"
+ return super(hr_timesheet_report, self)._from() + "left join hr_timesheet_sheet_sheet as htss ON (aal.sheet_id=htss.id) join totals as t on (t.sheet_id = aal.sheet_id and t.date = aal.date)"
def _group_by(self):
return super(hr_timesheet_report, self)._group_by() + """,
diff --git a/addons/hr_timesheet_sheet/security/ir.model.access.csv b/addons/hr_timesheet_sheet/security/ir.model.access.csv
index 2ad3c7d5ca2..d3185141612 100644
--- a/addons/hr_timesheet_sheet/security/ir.model.access.csv
+++ b/addons/hr_timesheet_sheet/security/ir.model.access.csv
@@ -3,5 +3,4 @@ access_hr_timesheet_sheet_sheet_user,hr_timesheet_sheet.sheet.user,model_hr_time
access_hr_timesheet_sheet_sheet_system_employee,hr_timesheet_sheet.sheet.system.employee,model_hr_timesheet_sheet_sheet,base.group_user,1,1,1,0
access_hr_timesheet_sheet_sheet_day,hr_timesheet_sheet.sheet.day,model_hr_timesheet_sheet_sheet_day,base.group_hr_user,1,1,1,1
access_hr_timesheet_sheet_sheet_account,hr_timesheet_sheet.sheet.account,model_hr_timesheet_sheet_sheet_account,base.group_hr_user,1,1,1,1
-access_hr_analytic_timesheet_system_user,hr.analytic.timesheet.system.user,model_hr_analytic_timesheet,base.group_user,1,0,0,0
access_hr_timesheet_sheet_sheet_day,hr.timesheet.sheet.sheet.day.user,model_hr_timesheet_sheet_sheet_day,base.group_user,1,1,1,0
diff --git a/addons/hr_timesheet_sheet/static/src/js/timesheet.js b/addons/hr_timesheet_sheet/static/src/js/timesheet.js
index 805333795e4..c99a3593d7e 100644
--- a/addons/hr_timesheet_sheet/static/src/js/timesheet.js
+++ b/addons/hr_timesheet_sheet/static/src/js/timesheet.js
@@ -107,9 +107,9 @@ var WeeklyTimesheet = form_common.FormWidget.extend(form_common.ReinitializeWidg
var accounts;
var account_names;
var default_get;
- return this.render_drop.add(new Model("hr.analytic.timesheet").call("default_get", [
- ['account_id','general_account_id', 'journal_id','date','name','user_id','product_id','product_uom_id','to_invoice','amount','unit_amount'],
- new data.CompoundContext({'user_id': self.get('user_id')})]).then(function(result) {
+ return this.render_drop.add(new Model("account.analytic.line").call("default_get", [
+ ['account_id','general_account_id', 'journal_id','date','name','user_id','product_id','product_uom_id','to_invoice','amount','unit_amount', 'is_timesheet'],
+ new data.CompoundContext({'user_id': self.get('user_id'), 'default_is_timesheet':true})]).then(function(result) {
default_get = result;
// calculating dates
dates = [];
@@ -132,7 +132,7 @@ var WeeklyTimesheet = form_common.FormWidget.extend(form_common.ReinitializeWidg
var account_ids = _.map(_.keys(accounts), function(el) { return el === "false" ? false : Number(el); });
- return new Model("hr.analytic.timesheet").call("multi_on_change_account_id", [[], account_ids,
+ return new Model("account.analytic.line").call("multi_on_change_account_id", [[], account_ids,
new data.CompoundContext({'user_id': self.get('user_id')})]).then(function(accounts_defaults) {
accounts = _(accounts).chain().map(function(lines, account_id) {
var account_defaults = _.extend({}, default_get, (accounts_defaults[account_id] || {}).value || {});
@@ -216,7 +216,7 @@ var WeeklyTimesheet = form_common.FormWidget.extend(form_common.ReinitializeWidg
account.days[day_count].lines[0].unit_amount += num - self.sum_box(account, day_count);
var product = (account.days[day_count].lines[0].product_id instanceof Array) ? account.days[day_count].lines[0].product_id[0] : account.days[day_count].lines[0].product_id;
var journal = (account.days[day_count].lines[0].journal_id instanceof Array) ? account.days[day_count].lines[0].journal_id[0] : account.days[day_count].lines[0].journal_id;
- self.defs.push(new Model("hr.analytic.timesheet").call("on_change_unit_amount", [[], product, account.days[day_count].lines[0].unit_amount, false, false, journal]).then(function(res) {
+ self.defs.push(new Model("account.analytic.line").call("on_change_unit_amount", [[], product, account.days[day_count].lines[0].unit_amount, false, false, journal]).then(function(res) {
account.days[day_count].lines[0]['amount'] = res.value.amount || 0;
self.display_totals();
self.sync();
@@ -271,7 +271,7 @@ var WeeklyTimesheet = form_common.FormWidget.extend(form_common.ReinitializeWidg
return;
}
var ops = self.generate_o2m_value();
- new Model("hr.analytic.timesheet").call("multi_on_change_account_id", [[], [id],
+ new Model("account.analytic.line").call("multi_on_change_account_id", [[], [id],
new data.CompoundContext({'user_id': self.get('user_id')})]).then(function(res) {
res = res[id];
var def = _.extend({}, self.default_get, res.value, {
diff --git a/addons/portal_project/security/ir.model.access.csv b/addons/portal_project/security/ir.model.access.csv
index d2a7287e355..45998a38841 100644
--- a/addons/portal_project/security/ir.model.access.csv
+++ b/addons/portal_project/security/ir.model.access.csv
@@ -2,12 +2,10 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_project,project,project.model_project_project,base.group_portal,1,0,0,0
access_task,task,project.model_project_task,base.group_portal,1,0,0,0
access_task_type,task_type,project.model_project_task_type,base.group_portal,1,0,0,0
-access_task_work,task_work,project.model_project_task_work,base.group_portal,1,0,0,0
access_project_category,project_category,project.model_project_category,base.group_portal,1,0,0,0
access_account_analytic_account,account_analytic_account,analytic.model_account_analytic_account,base.group_portal,1,0,0,0
access_project_public,project,project.model_project_project,base.group_public,1,0,0,0
access_task_public,task,project.model_project_task,base.group_public,1,0,0,0
access_task_type_public,task_type,project.model_project_task_type,base.group_public,1,0,0,0
-access_task_work_public,task_work,project.model_project_task_work,base.group_public,1,0,0,0
access_project_category_public,project_category,project.model_project_category,base.group_public,1,0,0,0
access_account_analytic_account_public,account_analytic_account,analytic.model_account_analytic_account,base.group_public,1,0,0,0
\ No newline at end of file
diff --git a/addons/project/project.py b/addons/project/project.py
index af4789fd0b5..a02947c7387 100644
--- a/addons/project/project.py
+++ b/addons/project/project.py
@@ -112,73 +112,6 @@ class project(osv.osv):
val['pricelist_id'] = pricelist_id
return {'value': val}
- def _get_projects_from_tasks(self, cr, uid, task_ids, context=None):
- tasks = self.pool.get('project.task').browse(cr, uid, task_ids, context=context)
- project_ids = [task.project_id.id for task in tasks if task.project_id]
- return self.pool.get('project.project')._get_project_and_parents(cr, uid, project_ids, context)
-
- def _get_project_and_parents(self, cr, uid, ids, context=None):
- """ return the project ids and all their parent projects """
- res = set(ids)
- while ids:
- cr.execute("""
- SELECT DISTINCT parent.id
- FROM project_project project, project_project parent, account_analytic_account account
- WHERE project.analytic_account_id = account.id
- AND parent.analytic_account_id = account.parent_id
- AND project.id IN %s
- """, (tuple(ids),))
- ids = [t[0] for t in cr.fetchall()]
- res.update(ids)
- return list(res)
-
- def _get_project_and_children(self, cr, uid, ids, context=None):
- """ retrieve all children projects of project ids;
- return a dictionary mapping each project to its parent project (or None)
- """
- res = dict.fromkeys(ids, None)
- while ids:
- cr.execute("""
- SELECT project.id, parent.id
- FROM project_project project, project_project parent, account_analytic_account account
- WHERE project.analytic_account_id = account.id
- AND parent.analytic_account_id = account.parent_id
- AND parent.id IN %s
- """, (tuple(ids),))
- dic = dict(cr.fetchall())
- res.update(dic)
- ids = dic.keys()
- return res
-
- def _progress_rate(self, cr, uid, ids, names, arg, context=None):
- child_parent = self._get_project_and_children(cr, uid, ids, context)
- # compute planned_hours, total_hours, effective_hours specific to each project
- cr.execute("""
- SELECT project_id, COALESCE(SUM(planned_hours), 0.0),
- COALESCE(SUM(total_hours), 0.0), COALESCE(SUM(effective_hours), 0.0)
- FROM project_task
- LEFT JOIN project_task_type ON project_task.stage_id = project_task_type.id
- WHERE project_task.project_id IN %s AND project_task_type.fold = False
- GROUP BY project_id
- """, (tuple(child_parent.keys()),))
- # aggregate results into res
- res = dict([(id, {'planned_hours':0.0, 'total_hours':0.0, 'effective_hours':0.0}) for id in ids])
- for id, planned, total, effective in cr.fetchall():
- # add the values specific to id to all parent projects of id in the result
- while id:
- if id in ids:
- res[id]['planned_hours'] += planned
- res[id]['total_hours'] += total
- res[id]['effective_hours'] += effective
- id = child_parent[id]
- # compute progress rates
- for id in ids:
- if res[id]['total_hours']:
- res[id]['progress_rate'] = round(100.0 * res[id]['effective_hours'] / res[id]['total_hours'], 2)
- else:
- res[id]['progress_rate'] = 0.0
- return res
-
def unlink(self, cr, uid, ids, context=None):
alias_ids = []
mail_alias = self.pool.get('mail.alias')
@@ -284,26 +217,6 @@ class project(osv.osv):
'members': fields.many2many('res.users', 'project_user_rel', 'project_id', 'uid', 'Project Members',
help="Project's members are users who can have an access to the tasks related to this project.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'tasks': fields.one2many('project.task', 'project_id', "Task Activities"),
- 'planned_hours': fields.function(_progress_rate, multi="progress", string='Planned Time', help="Sum of planned hours of all tasks related to this project and its child projects.",
- store = {
- 'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
- 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'stage_id'], 20),
- }),
- 'effective_hours': fields.function(_progress_rate, multi="progress", string='Time Spent', help="Sum of spent hours of all tasks related to this project and its child projects.",
- store = {
- 'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
- 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'stage_id'], 20),
- }),
- 'total_hours': fields.function(_progress_rate, multi="progress", string='Total Time', help="Sum of total hours of all tasks related to this project and its child projects.",
- store = {
- 'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
- 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'stage_id'], 20),
- }),
- 'progress_rate': fields.function(_progress_rate, multi="progress", string='Progress', type='float', group_operator="avg", help="Percent of tasks closed according to the total of tasks todo.",
- store = {
- 'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
- 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'stage_id'], 20),
- }),
'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report", states={'close':[('readonly',True)]} ),
'type_ids': fields.many2many('project.task.type', 'project_task_type_rel', 'project_id', 'type_id', 'Tasks Stages', states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'task_count': fields.function(_task_count, type='integer', string="Tasks",),
@@ -759,12 +672,6 @@ class task(osv.osv):
res[task.id] = False
return res
- def _get_task(self, cr, uid, ids, context=None):
- result = {}
- for work in self.pool.get('project.task.work').browse(cr, uid, ids, context=context):
- if work.task_id: result[work.task_id.id] = True
- return result.keys()
-
_columns = {
'active': fields.function(_is_template, store=True, string='Not a Template Task', type='boolean', help="This field is computed automatically and have the same behavior than the boolean 'active' field: if the task is linked to a template or unactivated project, it will be hidden unless specifically asked."),
'name': fields.char('Task Summary', track_visibility='onchange', size=128, required=True, select=True),
@@ -792,31 +699,10 @@ class task(osv.osv):
'child_ids': fields.many2many('project.task', 'project_task_parent_rel', 'parent_id', 'task_id', 'Delegated Tasks'),
'notes': fields.text('Notes'),
'planned_hours': fields.float('Initially Planned Hours', help='Estimated time to do the task, usually set by the project manager when the task is in draft state.'),
- 'effective_hours': fields.function(_hours_get, string='Hours Spent', multi='hours', help="Computed using the sum of the task work done.",
- store = {
- 'project.task': (lambda self, cr, uid, ids, c={}: ids, ['work_ids', 'remaining_hours', 'planned_hours'], 10),
- 'project.task.work': (_get_task, ['hours'], 10),
- }),
'remaining_hours': fields.float('Remaining Hours', digits=(16,2), help="Total remaining time, can be re-estimated periodically by the assignee of the task."),
- 'total_hours': fields.function(_hours_get, string='Total', multi='hours', help="Computed as: Time Spent + Remaining Time.",
- store = {
- 'project.task': (lambda self, cr, uid, ids, c={}: ids, ['work_ids', 'remaining_hours', 'planned_hours'], 10),
- 'project.task.work': (_get_task, ['hours'], 10),
- }),
- 'progress': fields.function(_hours_get, string='Working Time Progress (%)', multi='hours', group_operator="avg", help="If the task has a progress of 99.99% you should close the task if it's finished or reevaluate the time",
- store = {
- 'project.task': (lambda self, cr, uid, ids, c={}: ids, ['work_ids', 'remaining_hours', 'planned_hours', 'state', 'stage_id'], 10),
- 'project.task.work': (_get_task, ['hours'], 10),
- }),
- 'delay_hours': fields.function(_hours_get, string='Delay Hours', multi='hours', help="Computed as difference between planned hours by the project manager and the total hours of the task.",
- store = {
- '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', select=True, track_visibility='onchange'),
'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'),
'manager_id': fields.related('project_id', 'analytic_account_id', 'user_id', type='many2one', relation='res.users', string='Project Manager'),
'company_id': fields.many2one('res.company', 'Company'),
'id': fields.integer('ID', readonly=True),
@@ -829,7 +715,6 @@ class task(osv.osv):
'date_last_stage_update': fields.datetime.now,
'kanban_state': 'normal',
'priority': '0',
- 'progress': 0,
'sequence': 10,
'active': True,
'user_id': lambda obj, cr, uid, ctx=None: uid,
@@ -889,6 +774,10 @@ class task(osv.osv):
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
users_obj = self.pool.get('res.users')
if context is None: context = {}
+ # read uom as admin to avoid access rights issues, e.g. for portal/share users,
+ # this should be safe (no context passed to avoid side-effects)
+ obj_tm = users_obj.browse(cr, SUPERUSER_ID, uid, context=context).company_id.project_time_mode_id
+ tm = obj_tm and obj_tm.name or 'Hours'
res = super(task, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu=submenu)
@@ -984,16 +873,14 @@ class task(osv.osv):
new_attachment_ids.append(attachment.copy(cr, uid, attachment_id, default={'res_id': delegated_task_id}, context=context))
return new_attachment_ids
- def do_delegate(self, cr, uid, ids, delegate_data=None, context=None):
- """
- Delegate Task to another users.
- """
- if delegate_data is None:
- delegate_data = {}
- assert delegate_data['user_id'], _("Delegated User should be specified")
- delegated_tasks = {}
+ def _get_effective_hours(self, task):
+ # this method is override in project_timesheet modules.
+ return 0.0
+
+ def _prepare_delegate_values(self, cr, uid, ids, delegate_data, context=None):
+ delegate_values = {}
for task in self.browse(cr, uid, ids, context=context):
- delegated_task_id = self.copy(cr, uid, task.id, {
+ delegate_values[task.id] = {
'name': delegate_data['name'],
'project_id': delegate_data['project_id'] and delegate_data['project_id'][0] or False,
'stage_id': delegate_data.get('stage_id') and delegate_data.get('stage_id')[0] or False,
@@ -1002,14 +889,40 @@ class task(osv.osv):
'parent_ids': [(6, 0, [task.id])],
'description': delegate_data['new_task_description'] or '',
'child_ids': [],
- 'work_ids': []
+ 'remaining_hours': delegate_data['planned_hours_me'],
+ # The values below will be written on the old task.
+ 'task_planned_hours': delegate_data['planned_hours_me'],
+ 'task_name': delegate_data['prefix'] or ''
+ }
+ return delegate_values
+
+ def do_delegate(self, cr, uid, ids, delegate_data=None, context=None):
+ """
+ Delegate Task to another users.
+ """
+ if delegate_data is None:
+ delegate_data = {}
+ assert delegate_data['user_id'], _("Delegated User should be specified")
+ delegated_tasks = {}
+ vals = self._prepare_delegate_values(cr, uid, ids, delegate_data, context)
+ for task in self.browse(cr, uid, ids, context=context):
+
+ delegated_task_id = self.copy(cr, uid, task.id, {
+ 'name': vals[task.id]['name'],
+ 'project_id': vals[task.id]['project_id'],
+ 'stage_id': vals[task.id]['stage_id'],
+ 'user_id': vals[task.id]['user_id'],
+ 'planned_hours': vals[task.id]['planned_hours'],
+ 'parent_ids': vals[task.id]['parent_ids'],
+ 'description': vals[task.id]['description'],
+ 'child_ids': vals[task.id]['child_ids'],
}, context=context)
self._delegate_task_attachments(cr, uid, task.id, delegated_task_id, context=context)
- newname = delegate_data['prefix'] or ''
+
task.write({
- 'remaining_hours': delegate_data['planned_hours_me'],
- 'planned_hours': delegate_data['planned_hours_me'] + (task.effective_hours or 0.0),
- 'name': newname,
+ 'remaining_hours': vals[task.id]['remaining_hours'],
+ 'planned_hours': vals[task.id]['task_planned_hours'],
+ 'name': vals[task.id]['task_name']
}, context=context)
delegated_tasks[task.id] = delegated_task_id
return delegated_tasks
@@ -1100,6 +1013,9 @@ class task(osv.osv):
res = super(task, self).unlink(cr, uid, ids, context)
return res
+ def _get_total_hours(self, task):
+ return self._get_effective_hours(task) + task.remaining_hours
+
def _generate_task(self, cr, uid, tasks, ident=4, context=None):
context = context or {}
result = ""
@@ -1110,7 +1026,7 @@ class task(osv.osv):
result += '''
%sdef Task_%s():
%s todo = \"%.2fH\"
-%s effort = \"%.2fH\"''' % (ident,task.id, ident,task.remaining_hours, ident,task.total_hours)
+%s effort = \"%.2fH\"''' % (ident,task.id, ident,task.remaining_hours, ident, self._get_total_hours(task))
start = []
for t2 in task.parent_ids:
start.append("up.Task_%s.end" % (t2.id,))
@@ -1192,50 +1108,6 @@ class task(osv.osv):
pass
return super(task, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context)
-class project_work(osv.osv):
- _name = "project.task.work"
- _description = "Project Task Work"
- _columns = {
- 'name': fields.char('Work summary'),
- 'date': fields.datetime('Date', select="1"),
- 'task_id': fields.many2one('project.task', 'Task', ondelete='cascade', required=True, select="1"),
- 'hours': fields.float('Time Spent'),
- 'user_id': fields.many2one('res.users', 'Done by', required=True, select="1"),
- 'company_id': fields.related('task_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True)
- }
-
- _defaults = {
- 'user_id': lambda obj, cr, uid, context: uid,
- 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S')
- }
-
- _order = "date desc"
- def create(self, cr, uid, vals, context=None):
- if 'hours' in vals and (not vals['hours']):
- vals['hours'] = 0.00
- if 'task_id' in vals:
- cr.execute('update project_task set remaining_hours=remaining_hours - %s where id=%s', (vals.get('hours',0.0), vals['task_id']))
- self.pool.get('project.task').invalidate_cache(cr, uid, ['remaining_hours'], [vals['task_id']], context=context)
- return super(project_work,self).create(cr, uid, vals, context=context)
-
- def write(self, cr, uid, ids, vals, context=None):
- if 'hours' in vals and (not vals['hours']):
- vals['hours'] = 0.00
- if 'hours' in vals:
- task_obj = self.pool.get('project.task')
- for work in self.browse(cr, uid, ids, context=context):
- cr.execute('update project_task set remaining_hours=remaining_hours - %s + (%s) where id=%s', (vals.get('hours',0.0), work.hours, work.task_id.id))
- task_obj.invalidate_cache(cr, uid, ['remaining_hours'], [work.task_id.id], context=context)
- return super(project_work,self).write(cr, uid, ids, vals, context)
-
- def unlink(self, cr, uid, ids, context=None):
- task_obj = self.pool.get('project.task')
- for work in self.browse(cr, uid, ids):
- cr.execute('update project_task set remaining_hours=remaining_hours + %s where id=%s', (work.hours, work.task_id.id))
- task_obj.invalidate_cache(cr, uid, ['remaining_hours'], [work.task_id.id], context=context)
- return super(project_work,self).unlink(cr, uid, ids, context=context)
-
-
class account_analytic_account(osv.osv):
_inherit = 'account.analytic.account'
_description = 'Analytic Account'
diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml
index 09f17030249..8e78ee03a18 100644
--- a/addons/project/project_view.xml
+++ b/addons/project/project_view.xml
@@ -158,10 +158,6 @@
attrs="{'readonly':[('state','in',['close', 'cancelled'])]}"
context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'project.group_project_manager']}"/>
-
-
@@ -230,10 +226,6 @@
-
-
-
-
@@ -244,8 +236,6 @@
project.project
-
-
@@ -348,35 +338,6 @@
-
- project.task.work.form
- project.task.work
-
-
-
-
-
-
- project.task.work.tree
- project.task.work
-
-
-
-
-
-
-
-
-
-
@@ -410,36 +371,16 @@
context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'project.group_project_user']}"/>
+ on_change="onchange_planned(planned_hours)"/>
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-