[IMP]project_working_time_task_portal : add billable time

This commit is contained in:
Boris Gallet
2023-12-20 11:50:33 +01:00
parent ea5540c1b6
commit 82f0a2ef31
8 changed files with 188 additions and 12 deletions

View File

@@ -2,7 +2,7 @@
project_working_time_task_portal 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 Installation
============ ============
@@ -18,7 +18,7 @@ None yet.
Bug Tracker 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 trouble, please check there if your issue has already been
reported. If you spotted it first, help us smashing it by providing a reported. If you spotted it first, help us smashing it by providing a
detailed and welcomed feedback. detailed and welcomed feedback.

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -3,16 +3,18 @@
{ {
"name": "project_working_time_task_portal", "name": "project_working_time_task_portal",
"version": "14.0.0.0", "version": "14.0.2.0.0",
"author": "Elabore", "author": "Elabore",
"website": "https://elabore.coop", "website": "https://elabore.coop",
"maintainer": "Boris Gallet", "maintainer": "Boris Gallet",
"license": "AGPL-3", "license": "AGPL-3",
"category": "tools", "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 # any module necessary for this one to work correctly
"depends": [ "depends": [
"base", "base",
"project",
"sale_timesheet_line_exclude"
], ],
"qweb": [], "qweb": [],
"external_dependencies": { "external_dependencies": {
@@ -20,11 +22,9 @@
}, },
# always loaded # always loaded
"data": [ "data": [
# "security/security.xml",
# "security/ir.model.access.csv",
"views/hr_timesheet_portal.xml",
"views/assets.xml", "views/assets.xml",
# "data/data.xml", "views/hr_timesheet_portal.xml",
"views/hr_timesheet_view_task_form2.xml"
], ],
# only loaded in demonstration mode # only loaded in demonstration mode
"demo": [], "demo": [],

View File

@@ -33,4 +33,42 @@ msgstr "<strong>Heures Planifiées Sous-tâches: </strong>"
#. module: project_working_time_task_portal #. module: project_working_time_task_portal
#: model_terms:ir.ui.view,arch_db:project_working_time_task_portal.portal_my_task_remaining_hours #: model_terms:ir.ui.view,arch_db:project_working_time_task_portal.portal_my_task_remaining_hours
msgid "Remaining Hours:" msgid "Remaining Hours:"
msgstr "Heures restantes:" 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"

View File

@@ -0,0 +1 @@
from . import billable_time

View 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

View File

@@ -13,10 +13,10 @@
</div> </div>
</t> </t>
</div> </div>
<t t-if="task.progress > 0"> <t t-if="task.billable_progress > 0">
<div class="col-12 col-md-6 pb-2"> <div class="col-12 col-md-6 pb-2">
<strong>Progress: </strong> <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}%;"> <div class="oe-project_working_time_task_portal progressbar" t-attf-style="width: #{progress}%;">
<t t-esc="progress"/>% <t t-esc="progress"/>%
</div> </div>
@@ -27,13 +27,31 @@
</template> </template>
<template id="portal_my_task_remaining_hours" name="My Task: remaining hours" inherit_id="hr_timesheet.portal_timesheet_table" priority="40"> <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="{&quot;widget&quot;: &quot;float_time&quot;}"/>
</xpath>
<xpath expr="//t[@t-name='hr_timesheet.portal_timesheet_table']/table/tfoot/tr" position="inside"> <xpath expr="//t[@t-name='hr_timesheet.portal_timesheet_table']/table/tfoot/tr" position="inside">
<t t-if="task.planned_hours > 0"> <t t-if="task.planned_hours > 0">
<tr> <tr>
<th colspan="3"/> <th colspan="3"/>
<th class="text-right">Remaining Hours: <span t-field="task.remaining_hours" t-options="{&quot;widget&quot;: &quot;float_time&quot;}"/></th> <th class="text-right">Remaining Hours: <span t-field="task.billable_remaining_hours" t-options="{&quot;widget&quot;: &quot;float_time&quot;}"/></th>
</tr> </tr>
</t> </t>
</xpath> </xpath>
</template> </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> </odoo>

View File

@@ -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>