[IMP]project_working_time_task_portal : add billable time
This commit is contained in:
@@ -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.
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
from . import models
|
@@ -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": [],
|
||||||
|
@@ -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
|
#: 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"
|
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>
|
</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="{"widget": "float_time"}"/>
|
||||||
|
</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="{"widget": "float_time"}"/></th>
|
<th class="text-right">Remaining Hours: <span t-field="task.billable_remaining_hours" t-options="{"widget": "float_time"}"/></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>
|
@@ -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