149 lines
7.3 KiB
Python
149 lines
7.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
|
|
|
from flectra import http, _
|
|
from flectra.http import request
|
|
from flectra.osv import expression
|
|
|
|
from flectra.tools import float_round
|
|
|
|
|
|
class SaleTimesheetController(http.Controller):
|
|
|
|
@http.route('/timesheet/plan', type='json', auth="user")
|
|
def plan(self, domain):
|
|
domain = expression.AND([domain, [('project_id', '!=', False)]]) # force timesheet and not AAL
|
|
values = self._prepare_plan_values(domain)
|
|
view = request.env.ref('sale_timesheet.timesheet_plan')
|
|
return {
|
|
'html_content': view.render(values)
|
|
}
|
|
|
|
def _prepare_plan_values(self, domain):
|
|
|
|
timesheet_lines = request.env['account.analytic.line'].search(domain)
|
|
currency = request.env.user.company_id.currency_id
|
|
|
|
values = {
|
|
'currency': currency,
|
|
'timesheet_lines': timesheet_lines,
|
|
'domain': domain,
|
|
}
|
|
hour_rounding = request.env.ref('product.product_uom_hour').rounding
|
|
billable_types = ['non_billable', 'non_billable_project', 'billable_time', 'billable_fixed']
|
|
|
|
# -- Stat Buttons
|
|
values['stat_buttons'] = self._plan_get_stat_button(timesheet_lines)
|
|
|
|
# -- Dashboard (per billable type)
|
|
dashboard_values = {
|
|
'hours': dict.fromkeys(billable_types + ['total'], 0.0),
|
|
'rates': dict.fromkeys(billable_types + ['total'], 0.0),
|
|
'money_amount': {
|
|
'invoiced': 0.0,
|
|
'to_invoiced': 0.0,
|
|
'cost': 0.0,
|
|
'total': 0.0,
|
|
}
|
|
}
|
|
dashboard_domain = domain + [('timesheet_invoice_type', '!=', False)] # force billable type
|
|
dashboard_data = request.env['account.analytic.line'].read_group(dashboard_domain, ['unit_amount', 'timesheet_revenue', 'timesheet_invoice_type'], ['timesheet_invoice_type'])
|
|
|
|
dashboard_total_hours = sum([data['unit_amount'] for data in dashboard_data])
|
|
for data in dashboard_data:
|
|
billable_type = data['timesheet_invoice_type']
|
|
# hours
|
|
dashboard_values['hours'][billable_type] = float_round(data.get('unit_amount'), precision_rounding=hour_rounding)
|
|
dashboard_values['hours']['total'] += float_round(data.get('unit_amount'), precision_rounding=hour_rounding)
|
|
# rates
|
|
dashboard_values['rates'][billable_type] = round(data.get('unit_amount') / dashboard_total_hours * 100, 2)
|
|
dashboard_values['rates']['total'] += round(data.get('unit_amount') / dashboard_total_hours * 100, 2)
|
|
|
|
# money_amount
|
|
so_lines = values['timesheet_lines'].mapped('so_line')
|
|
invoice_lines = so_lines.mapped('invoice_lines')
|
|
dashboard_values['money_amount']['invoiced'] = sum([inv_line.currency_id.with_context(date=inv_line.invoice_id.date_invoice).compute(inv_line.price_unit * inv_line.quantity, currency) for inv_line in invoice_lines.filtered(lambda line: line.invoice_id.state in ['open', 'paid'])])
|
|
dashboard_values['money_amount']['to_invoice'] = sum([sol.currency_id.compute(sol.price_unit * (1 - (sol.discount or 0.0) / 100.0) * sol.qty_to_invoice, currency) for sol in so_lines]) + sum([i.currency_id.with_context(date=i.invoice_id.date_invoice).compute(i.price_unit * i.quantity, currency) for i in invoice_lines.filtered(lambda line: line.invoice_id.state == 'draft')])
|
|
dashboard_values['money_amount']['cost'] = sum(values['timesheet_lines'].mapped('amount'))
|
|
dashboard_values['money_amount']['total'] = sum([dashboard_values['money_amount'][item] for item in dashboard_values['money_amount'].keys()])
|
|
|
|
values['dashboard'] = dashboard_values
|
|
|
|
# -- Time Repartition (per employee)
|
|
repartition_domain = domain + [('employee_id', '!=', False), ('timesheet_invoice_type', '!=', False)] # force billable type
|
|
repartition_data = request.env['account.analytic.line'].read_group(repartition_domain, ['employee_id', 'timesheet_invoice_type', 'unit_amount'], ['employee_id', 'timesheet_invoice_type'], lazy=False)
|
|
|
|
# set repartition per type per employee
|
|
repartition_employee = {}
|
|
for data in repartition_data:
|
|
employee_id = data['employee_id'][0]
|
|
repartition_employee.setdefault(employee_id, dict(
|
|
employee_id=data['employee_id'][0],
|
|
employee_name=data['employee_id'][1],
|
|
non_billable_project=0.0,
|
|
non_billable=0.0,
|
|
billable_time=0.0,
|
|
billable_fixed=0.0,
|
|
total=0.0,
|
|
))[data['timesheet_invoice_type']] = float_round(data.get('unit_amount', 0.0), precision_rounding=hour_rounding)
|
|
repartition_employee[employee_id]['__domain_'+data['timesheet_invoice_type']] = data['__domain']
|
|
|
|
# compute total
|
|
for employee_id, vals in repartition_employee.items():
|
|
repartition_employee[employee_id]['total'] = sum([vals[inv_type] for inv_type in billable_types])
|
|
|
|
hours_per_employee = [repartition_employee[employee_id]['total'] for employee_id in repartition_employee]
|
|
values['repartition_employee_max'] = max(hours_per_employee) if hours_per_employee else 1
|
|
values['repartition_employee'] = repartition_employee
|
|
|
|
return values
|
|
|
|
def _plan_get_stat_button(self, timesheet_lines):
|
|
stat_buttons = []
|
|
stat_buttons.append({
|
|
'name': _('Timesheets'),
|
|
'res_model': 'account.analytic.line',
|
|
'domain': [('id', 'in', timesheet_lines.ids)],
|
|
'icon': 'fa fa-calendar',
|
|
})
|
|
stat_project_ids = timesheet_lines.mapped('project_id').ids
|
|
stat_task_domain = [('project_id', 'in', stat_project_ids), '|', ('stage_id', '=', False), ('stage_id.fold', '=', False)]
|
|
stat_buttons.append({
|
|
'name': _('Tasks'),
|
|
'count': request.env['project.task'].search_count(stat_task_domain),
|
|
'res_model': 'project.task',
|
|
'domain': stat_task_domain,
|
|
'icon': 'fa fa-tasks',
|
|
})
|
|
return stat_buttons
|
|
|
|
@http.route('/timesheet/plan/action', type='json', auth="user")
|
|
def plan_stat_button(self, domain, res_model='account.analytic.line'):
|
|
action = {
|
|
'type': 'ir.actions.act_window',
|
|
'view_id': False,
|
|
'view_mode': 'tree,form',
|
|
'view_type': 'list',
|
|
'domain': domain,
|
|
}
|
|
if res_model == 'account.analytic.line':
|
|
ts_view_tree_id = request.env.ref('hr_timesheet.hr_timesheet_line_tree').id
|
|
ts_view_form_id = request.env.ref('hr_timesheet.hr_timesheet_line_form').id
|
|
action = {
|
|
'name': _('Timesheets'),
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': res_model,
|
|
'view_mode': 'tree,form',
|
|
'view_type': 'tree',
|
|
'views': [[ts_view_tree_id, 'list'], [ts_view_form_id, 'form']],
|
|
'domain': domain,
|
|
}
|
|
elif res_model == 'project.task':
|
|
action = request.env.ref('project.action_view_task').read()[0]
|
|
action.update({
|
|
'name': _('Tasks'),
|
|
'domain': domain,
|
|
'context': request.env.context, # erase original context to avoid default filter
|
|
})
|
|
return action
|