[IMP]project_working_time_task_portal : add billable time
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
project_working_time_task_portal
|
||||
===============
|
||||
|
||||
add planned hours and remaining hours in the task portal view
|
||||
add billable planned hours and billable remaining hours in the task portal view so the portal user only see billable timesheet lines
|
||||
|
||||
Installation
|
||||
============
|
||||
@@ -18,7 +18,7 @@ None yet.
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `our issues website <https://github.com/elabore-coop/project_working_time_task_portal/issues>`_. In case of
|
||||
Bugs are tracked on `our issues website <https://github.com/elabore-coop/project-tool/issues>`_. In case of
|
||||
trouble, please check there if your issue has already been
|
||||
reported. If you spotted it first, help us smashing it by providing a
|
||||
detailed and welcomed feedback.
|
||||
|
@@ -0,0 +1 @@
|
||||
from . import models
|
@@ -3,16 +3,18 @@
|
||||
|
||||
{
|
||||
"name": "project_working_time_task_portal",
|
||||
"version": "14.0.0.0",
|
||||
"version": "14.0.2.0.0",
|
||||
"author": "Elabore",
|
||||
"website": "https://elabore.coop",
|
||||
"maintainer": "Boris Gallet",
|
||||
"license": "AGPL-3",
|
||||
"category": "tools",
|
||||
"summary": "add planned hours and remaining hours in the task portal view",
|
||||
"summary": "add billable planned hours and billable remaining hours in the task portal view so the portal user only see billable timesheet lines",
|
||||
# any module necessary for this one to work correctly
|
||||
"depends": [
|
||||
"base",
|
||||
"project",
|
||||
"sale_timesheet_line_exclude"
|
||||
],
|
||||
"qweb": [],
|
||||
"external_dependencies": {
|
||||
@@ -20,11 +22,9 @@
|
||||
},
|
||||
# always loaded
|
||||
"data": [
|
||||
# "security/security.xml",
|
||||
# "security/ir.model.access.csv",
|
||||
"views/hr_timesheet_portal.xml",
|
||||
"views/assets.xml",
|
||||
# "data/data.xml",
|
||||
"views/hr_timesheet_portal.xml",
|
||||
"views/hr_timesheet_view_task_form2.xml"
|
||||
],
|
||||
# only loaded in demonstration mode
|
||||
"demo": [],
|
||||
|
@@ -34,3 +34,41 @@ msgstr "<strong>Heures Planifiées Sous-tâches: </strong>"
|
||||
#: model_terms:ir.ui.view,arch_db:project_working_time_task_portal.portal_my_task_remaining_hours
|
||||
msgid "Remaining Hours:"
|
||||
msgstr "Heures restantes:"
|
||||
|
||||
#. module: project_working_time_task_portal
|
||||
#: model:ir.model.fields,field_description:project_working_time_task_portal.field_project_task__billable_remaining_hours
|
||||
#: model_terms:ir.ui.view,arch_db:project_working_time_task_portal.project_working_time_view_task_form
|
||||
msgid "Billable Remaining Hours"
|
||||
msgstr "Heures restantes facturables"
|
||||
|
||||
#. module: project_working_time_task_portal
|
||||
#: model_terms:ir.ui.view,arch_db:project_working_time_task_portal.project_working_time_view_task_form
|
||||
msgid "Billable Remaining Days"
|
||||
msgstr "Jours restants facturables"
|
||||
|
||||
#. module: project_working_time_task_portal
|
||||
#: model_terms:ir.ui.view,arch_db:project_working_time_task_portal.project_working_time_view_task_form
|
||||
msgid "Billable Effective Days"
|
||||
msgstr "Jours passés facturables"
|
||||
|
||||
#. module: project_working_time_task_portal
|
||||
#: model:ir.model.fields,field_description:project_working_time_task_portal.field_project_task__billable_effective_hours
|
||||
#: model_terms:ir.ui.view,arch_db:project_working_time_task_portal.project_working_time_view_task_form
|
||||
msgid "Billable Effective Hours"
|
||||
msgstr "Heures passées facturables"
|
||||
|
||||
#. module: project_working_time_task_portal
|
||||
#: model:ir.model.fields,field_description:project_working_time_task_portal.field_project_task__subtask_billable_effective_hours
|
||||
msgid "Subtask Billable Effective Hours"
|
||||
msgstr "Heures passées facturables Sous-tâches"
|
||||
|
||||
#. module: project_working_time_task_portal
|
||||
#: model_terms:ir.ui.view,arch_db:project_working_time_task_portal.project_working_time_view_task_form
|
||||
msgid "Non Billable Effective Days"
|
||||
msgstr "Jours passés non facturables"
|
||||
|
||||
#. module: project_working_time_task_portal
|
||||
#: model:ir.model.fields,field_description:project_working_time_task_portal.field_project_task__non_billable_effective_hours
|
||||
#: model_terms:ir.ui.view,arch_db:project_working_time_task_portal.project_working_time_view_task_form
|
||||
msgid "Non Billable Effective Hours"
|
||||
msgstr "Heures passées non facturables"
|
1
project_working_time_task_portal/models/__init__.py
Normal file
1
project_working_time_task_portal/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import billable_time
|
86
project_working_time_task_portal/models/billable_time.py
Normal file
86
project_working_time_task_portal/models/billable_time.py
Normal file
@@ -0,0 +1,86 @@
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.tools.float_utils import float_compare
|
||||
|
||||
|
||||
class Task(models.Model):
|
||||
_inherit = "project.task"
|
||||
|
||||
billable_effective_hours = fields.Float(
|
||||
compute="_compute_billable_effective_hours",
|
||||
string="Billable Effective Hours",
|
||||
store=True,
|
||||
compute_sudo=True,
|
||||
)
|
||||
|
||||
non_billable_effective_hours = fields.Float(
|
||||
compute="_compute_non_billable_effective_hours",
|
||||
string="Non Billable Effective Hours",
|
||||
store=True,
|
||||
)
|
||||
|
||||
billable_remaining_hours = fields.Float(
|
||||
compute="_compute_billable_remaining_hours",
|
||||
string="Billable Remaining Hours",
|
||||
store=True,
|
||||
help="Total Billable remaining time (without exclude_from_sale_order timesheet lines), can be re-estimated periodically by the assignee of the task."
|
||||
)
|
||||
|
||||
billable_progress = fields.Float(
|
||||
compute="_compute_billable_progress_hours",
|
||||
string="Billable Progress",
|
||||
store=True,
|
||||
group_operator="avg",
|
||||
)
|
||||
|
||||
subtask_billable_effective_hours = fields.Float(
|
||||
compute="_compute_subtask_billable_effective_hours",
|
||||
string="Subtask Billable Effective Hours",
|
||||
store=True,
|
||||
compute_sudo=True,
|
||||
)
|
||||
|
||||
@api.depends('timesheet_ids.unit_amount')
|
||||
def _compute_billable_effective_hours(self):
|
||||
if not any(self._ids):
|
||||
for task in self:
|
||||
filtered_timesheets = task.timesheet_ids.filtered(lambda t: not t.exclude_from_sale_order)
|
||||
task.billable_effective_hours = sum(filtered_timesheets.mapped('unit_amount'))
|
||||
return
|
||||
|
||||
timesheet_read_group = self.env['account.analytic.line'].read_group(
|
||||
[('task_id', 'in', self.ids), ('exclude_from_sale_order', '=', False)], # We exclude the lines (timesheet) which are excluded from billing
|
||||
['unit_amount', 'task_id'],
|
||||
['task_id']
|
||||
)
|
||||
|
||||
timesheets_per_task = {res['task_id'][0]: res['unit_amount'] for res in timesheet_read_group}
|
||||
for task in self:
|
||||
task.billable_effective_hours = timesheets_per_task.get(task.id, 0.0)
|
||||
|
||||
@api.depends('effective_hours','billable_effective_hours')
|
||||
def _compute_non_billable_effective_hours(self):
|
||||
for task in self:
|
||||
task.non_billable_effective_hours = task.effective_hours - task.billable_effective_hours
|
||||
|
||||
@api.depends('billable_effective_hours', 'subtask_billable_effective_hours', 'planned_hours')
|
||||
def _compute_billable_remaining_hours(self):
|
||||
for task in self:
|
||||
task.billable_remaining_hours = task.planned_hours - task.billable_effective_hours - task.subtask_billable_effective_hours
|
||||
|
||||
|
||||
@api.depends('child_ids.billable_effective_hours', 'child_ids.subtask_billable_effective_hours')
|
||||
def _compute_subtask_billable_effective_hours(self):
|
||||
for task in self:
|
||||
task.subtask_billable_effective_hours = sum(child_task.billable_effective_hours + child_task.subtask_billable_effective_hours for child_task in task.child_ids)
|
||||
|
||||
@api.depends('billable_effective_hours', 'subtask_billable_effective_hours', 'planned_hours')
|
||||
def _compute_billable_progress_hours(self):
|
||||
for task in self:
|
||||
if (task.planned_hours > 0.0):
|
||||
task_total_hours = task.billable_effective_hours + task.subtask_billable_effective_hours
|
||||
if float_compare(task_total_hours, task.planned_hours, precision_digits=2) >= 0:
|
||||
task.billable_progress = 100
|
||||
else:
|
||||
task.billable_progress = round(100.0 * task_total_hours / task.planned_hours, 2)
|
||||
else:
|
||||
task.billable_progress = 0.0
|
@@ -13,10 +13,10 @@
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<t t-if="task.progress > 0">
|
||||
<t t-if="task.billable_progress > 0">
|
||||
<div class="col-12 col-md-6 pb-2">
|
||||
<strong>Progress: </strong>
|
||||
<t t-set="progress" t-value="task.progress"/>
|
||||
<t t-set="progress" t-value="task.billable_progress"/>
|
||||
<div class="oe-project_working_time_task_portal progressbar" t-attf-style="width: #{progress}%;">
|
||||
<t t-esc="progress"/>%
|
||||
</div>
|
||||
@@ -27,13 +27,31 @@
|
||||
</template>
|
||||
|
||||
<template id="portal_my_task_remaining_hours" name="My Task: remaining hours" inherit_id="hr_timesheet.portal_timesheet_table" priority="40">
|
||||
|
||||
<xpath expr="//tr[@t-foreach='timesheets']" position="attributes">
|
||||
<attribute name="t-if">not timesheet.exclude_from_sale_order</attribute>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//tfoot/tr/th/span" position="replace">
|
||||
<span t-esc="round(sum(timesheets.filtered(lambda t: not t.exclude_from_sale_order).mapped('unit_amount')), 2)"
|
||||
t-options="{"widget": "float_time"}"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//t[@t-name='hr_timesheet.portal_timesheet_table']/table/tfoot/tr" position="inside">
|
||||
<t t-if="task.planned_hours > 0">
|
||||
<tr>
|
||||
<th colspan="3"/>
|
||||
<th class="text-right">Remaining Hours: <span t-field="task.remaining_hours" t-options="{"widget": "float_time"}"/></th>
|
||||
<th class="text-right">Remaining Hours: <span t-field="task.billable_remaining_hours" t-options="{"widget": "float_time"}"/></th>
|
||||
</tr>
|
||||
</t>
|
||||
</xpath>
|
||||
|
||||
</template>
|
||||
|
||||
<template id="portal_my_task_subtask_timesheet_table" name="My Task Subtask Timesheet Table: hide not billable" inherit_id="hr_timesheet.portal_subtask_timesheet_tables" priority="40">
|
||||
<xpath expr="//tr[@t-foreach='timesheets']" position="attributes">
|
||||
<attribute name="t-if">not timesheet.exclude_from_sale_order</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="project_working_time_view_task_form" model="ir.ui.view">
|
||||
<field name="name">project.working.time.view.task.form</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.view_task_form2_inherited" />
|
||||
<field name="priority" eval="99" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='effective_hours']" position="after">
|
||||
<span>
|
||||
<label class="font-weight-bold" for="billable_effective_hours" string="Billable Effective Hours" attrs="{'invisible': ['|', ('planned_hours', '=', 0.0), ('encode_uom_in_days', '=', True)]}"/>
|
||||
<label class="font-weight-bold" for="billable_effective_hours" string="Billable Effective Days" attrs="{'invisible': ['|', ('planned_hours', '=', 0.0), ('encode_uom_in_days', '=', False)]}"/>
|
||||
</span>
|
||||
<field name="billable_effective_hours" widget="timesheet_uom" class="mt-2" attrs="{'invisible' : [('planned_hours', '=', 0.0)]}" nolabel="1"/>
|
||||
<span>
|
||||
<label class="font-weight-bold" for="non_billable_effective_hours" string="Non Billable Effective Hours" attrs="{'invisible': ['|', ('planned_hours', '=', 0.0), ('encode_uom_in_days', '=', True)]}"/>
|
||||
<label class="font-weight-bold" for="non_billable_effective_hours" string="Non Billable Effective Days" attrs="{'invisible': ['|', ('planned_hours', '=', 0.0), ('encode_uom_in_days', '=', False)]}"/>
|
||||
</span>
|
||||
<field name="non_billable_effective_hours" widget="timesheet_uom" class="mt-2" attrs="{'invisible' : [('planned_hours', '=', 0.0)]}" nolabel="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='remaining_hours']" position="after">
|
||||
<span>
|
||||
<label class="font-weight-bold" for="billable_remaining_hours" string="Billable Remaining Hours" attrs="{'invisible': ['|', ('planned_hours', '=', 0.0), ('encode_uom_in_days', '=', True)]}"/>
|
||||
<label class="font-weight-bold" for="billable_remaining_hours" string="Billable Remaining Days" attrs="{'invisible': ['|', ('planned_hours', '=', 0.0), ('encode_uom_in_days', '=', False)]}"/>
|
||||
</span>
|
||||
<field name="billable_remaining_hours" widget="timesheet_uom" class="oe_subtotal_footer_separator" attrs="{'invisible' : [('planned_hours', '=', 0.0)]}" nolabel="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
Reference in New Issue
Block a user