19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:32:12 +01:00
parent 79f83631d5
commit 73afc09215
6267 changed files with 1534193 additions and 1130106 deletions

View file

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import project_report
from . import timesheets_analysis_report

View file

@ -0,0 +1,24 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details
from odoo import fields, models
class ReportProjectTaskUser(models.Model):
_inherit = 'report.project.task.user'
remaining_hours_so = fields.Float('Time Remaining on SO', readonly=True, groups="hr_timesheet.group_hr_timesheet_user")
def _select(self):
return super()._select() + """,
sol.remaining_hours as remaining_hours_so
"""
def _group_by(self):
return super()._group_by() + """,
sol.remaining_hours
"""
def _from(self):
return super()._from() + """
LEFT JOIN sale_order_line sol ON t.sale_line_id = sol.id
"""

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_task_project_user_pivot_inherited" model="ir.ui.view">
<field name="name">report.project.task.user.pivot.inherited</field>
<field name="model">report.project.task.user</field>
<field name="inherit_id" ref="project.view_task_project_user_pivot"/>
<field name="arch" type="xml">
<field name="remaining_hours" position="after">
<field name="remaining_hours_so" widget="timesheet_uom"/>
</field>
</field>
</record>
<record id="view_task_project_user_fsm_pivot_base_inherited" model="ir.ui.view">
<field name="name">report.project.task.user.fsm.pivot.base.inherited</field>
<field name="model">report.project.task.user</field>
<field name="inherit_id" ref="project.view_task_project_user_fsm_pivot_base"/>
<field name="arch" type="xml">
<field name="remaining_hours_so" position="attributes">
<attribute name="invisible">1</attribute>
</field>
</field>
</record>
<record id="view_task_project_user_fsm_graph_base_inherited" model="ir.ui.view">
<field name="name">report.project.task.user.fsm.graph.base.inherited</field>
<field name="model">report.project.task.user</field>
<field name="inherit_id" ref="project.view_task_project_user_fsm_graph_base"/>
<field name="arch" type="xml">
<field name="remaining_hours" position="after">
<field name="remaining_hours_so" invisible="1"/>
</field>
</field>
</record>
</data>
</odoo>

View file

@ -4,19 +4,19 @@
<t t-set="show_project" t-value="true"/>
<t t-set="show_task" t-value="true"/>
<t t-call="web.html_container">
<t t-call="web.external_layout">
<t t-call="web.internal_layout">
<div class="page">
<t t-foreach="docs" t-as="doc">
<t t-set="doc_name" t-value="doc.name"/>
<t t-if="with_order_id" t-set="doc_name" t-value="str(doc.order_id.name) +' - '+ str(doc_name)"/>
<t t-elif="doc_name == '/'" t-set="doc_name" t-value="'Draft'"/>
<t t-elif="doc_name == '/'" t-set="doc_name">Draft</t>
<div class="oe_structure"/>
<div class="row mt8">
<div class="col-12">
<t t-if="doc.timesheet_ids">
<h2>
<br/>
<span>Timesheets for the <t t-out="doc_name"/> <t t-out="record_name"/>
<span>Timesheets for the <t t-out="doc_name">S0001</t> <t t-out="record_name">- Timesheet product</t>
</span>
</h2>
<t t-set='lines' t-value='doc.timesheet_ids'/>
@ -58,5 +58,6 @@
<field name="report_type">qweb-pdf</field>
<field name="report_name">sale_timesheet.report_timesheet_account_move</field>
<field name="binding_model_id" ref="model_account_move"/>
<field name="domain" eval="[('timesheet_ids', '!=', False)]"/>
</record>
</odoo>

View file

@ -3,7 +3,7 @@
from odoo import fields, models, api
from odoo.addons.sale_timesheet.models.account import TIMESHEET_INVOICE_TYPES
from odoo.addons.sale_timesheet.models.hr_timesheet import TIMESHEET_INVOICE_TYPES
class TimesheetsAnalysisReport(models.Model):
@ -13,10 +13,10 @@ class TimesheetsAnalysisReport(models.Model):
so_line = fields.Many2one("sale.order.line", string="Sales Order Item", readonly=True)
timesheet_invoice_type = fields.Selection(TIMESHEET_INVOICE_TYPES, string="Billable Type", readonly=True)
timesheet_invoice_id = fields.Many2one("account.move", string="Invoice", readonly=True, help="Invoice created from the timesheet")
timesheet_revenues = fields.Float("Timesheet Revenues", readonly=True, help="Number of hours spent multiplied by the unit price per hour/day.")
margin = fields.Float("Margin", readonly=True, help="Timesheets revenues minus the costs")
billable_time = fields.Float("Billable Hours", readonly=True, help="Number of hours/days linked to a SOL.")
non_billable_time = fields.Float("Non-billable Hours", readonly=True, help="Number of hours/days not linked to a SOL.")
timesheet_revenues = fields.Monetary("Timesheet Revenues", currency_field="currency_id", readonly=True, help="Number of hours spent multiplied by the unit price per hour/day.")
margin = fields.Monetary("Margin", currency_field="currency_id", readonly=True, help="Timesheets revenues minus the costs")
billable_time = fields.Float("Billable Time", readonly=True, help="Number of hours/days linked to a SOL.")
non_billable_time = fields.Float("Non-billable Time", readonly=True, help="Number of hours/days not linked to a SOL.")
@property
def _table_query(self):
@ -36,13 +36,22 @@ class TimesheetsAnalysisReport(models.Model):
A.so_line AS so_line,
A.timesheet_invoice_type AS timesheet_invoice_type,
A.timesheet_invoice_id AS timesheet_invoice_id,
CASE WHEN A.order_id IS NULL THEN 0 ELSE A.unit_amount * SOL.price_unit * sol_product_uom.factor / a_product_uom.factor END AS timesheet_revenues,
CASE
WHEN A.order_id IS NULL OR T.service_type in ('manual', 'milestones')
THEN 0
WHEN T.invoice_policy = 'order' AND SOL.qty_delivered != 0
THEN (SOL.price_subtotal / SOL.qty_delivered) * (A.unit_amount / sol_product_uom.factor * a_product_uom.factor)
ELSE A.unit_amount * SOL.price_unit / sol_product_uom.factor * a_product_uom.factor
END AS timesheet_revenues,
CASE WHEN A.order_id IS NULL THEN 0 ELSE A.unit_amount END AS billable_time
"""
@api.model
def _from(self):
return super()._from() + """
LEFT JOIN sale_order_line SOL ON A.so_line = SOL.id
LEFT JOIN uom_uom sol_product_uom ON sol_product_uom.id=SOL.product_uom
INNER JOIN uom_uom a_product_uom ON a_product_uom.id=A.product_uom_id"""
LEFT JOIN sale_order_line SOL ON A.so_line = SOL.id
LEFT JOIN uom_uom sol_product_uom ON sol_product_uom.id = SOL.product_uom_id
INNER JOIN uom_uom a_product_uom ON a_product_uom.id = A.product_uom_id
LEFT JOIN product_product P ON P.id = SOL.product_id
LEFT JOIN product_template T ON T.id = P.product_tmpl_id
"""

View file

@ -1,5 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="timesheets_analysis_report_list_inherited" model="ir.ui.view">
<field name="name">timesheets.analysis.report.list.inherited</field>
<field name="model">timesheets.analysis.report</field>
<field name="inherit_id" ref="hr_timesheet.timesheets_analysis_report_list"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='task_id']" position="after">
<field name="so_line"
optional="show"
options="{'no_open': True}"
placeholder="Non-billable"
/>
<field name="timesheet_invoice_type" optional="hide"/>
<field name="timesheet_invoice_id" optional="hide"/>
<field name="timesheet_revenues" optional="hide" sum="Total"/>
<field name="margin" optional="hide" sum="Total"/>
</xpath>
</field>
</record>
<record id="timesheete_analysis_report_form" model="ir.ui.view">
<field name="name">timesheets.analysis.report.form</field>
<field name="model">timesheets.analysis.report</field>
<field name="inherit_id" ref="hr_timesheet.timesheets_analysis_report_form"/>
<field name="arch" type="xml">
<xpath expr="//sheet/group/group" position="inside">
<field name="so_line" widget="so_line_field" placeholder="Non-billable"/>
</xpath>
</field>
</record>
<record id="timesheets_analysis_report_pivot_inherit" model="ir.ui.view">
<field name="name">timesheets.analysis.report.pivot</field>
@ -91,7 +120,7 @@
<field name="name">timesheets.analysis.report.pivot</field>
<field name="model">timesheets.analysis.report</field>
<field name="arch" type="xml">
<pivot string="Timesheets Analysis" sample="1" disable_linking="True">
<pivot string="Timesheets Analysis" sample="1">
<field name="date" interval="month" type="row"/>
<field name="timesheet_invoice_type" type="col"/>
<field name="amount" string="Timesheet Costs"/>
@ -106,7 +135,7 @@
<field name="name">timesheets.analysis.report.graph</field>
<field name="model">timesheets.analysis.report</field>
<field name="arch" type="xml">
<graph string="Timesheets" sample="1" js_class="hr_timesheet_graphview" disable_linking="True">
<graph string="Timesheets" sample="1" js_class="hr_timesheet_graphview">
<field name="amount" string="Timesheet Costs"/>
<field name="unit_amount" type="measure" widget="timesheet_uom"/>
<field name="billable_time" widget="timesheet_uom"/>
@ -119,34 +148,18 @@
<record id="hr_timesheet_report_search_sale_timesheet" model="ir.ui.view">
<field name="name">timesheets.analysis.report.search</field>
<field name="model">timesheets.analysis.report</field>
<field name="inherit_id" ref="hr_timesheet.hr_timesheet_report_search"/>
<field name="inherit_id" ref="sale_timesheet.timesheet_view_search"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='task_id']" position="after">
<field name="order_id" string="Sales Order" groups="sales_team.group_sale_salesman"/>
<search position="attributes">
<attribute name="string">Timesheet Report</attribute>
</search>
<xpath expr="//field[@name='order_id']" position="after">
<field name="so_line" groups="sales_team.group_sale_salesman"/>
</xpath>
<xpath expr="//filter[@name='month']" position="before">
<filter name="billable_fixed" string="Billed at a Fixed Price" domain="[('timesheet_invoice_type', '=', 'billable_fixed')]"
groups="sales_team.group_sale_salesman"/>
<filter name="billable_time" string="Billed on Timesheets" domain="[('timesheet_invoice_type', '=', 'billable_time')]"
groups="sales_team.group_sale_salesman"/>
<filter name="billable_milestones" string="Billed on Milestones" domain="[('timesheet_invoice_type', '=', 'billable_milestones')]"
groups="sales_team.group_sale_salesman"/>
<filter name="billable_manual" string="Billed Manually" domain="[('timesheet_invoice_type', '=', 'billable_manual')]"
groups="sales_team.group_sale_salesman"/>
<filter name="non_billable" string="Non-Billable" domain="[('timesheet_invoice_type', '=', 'non_billable')]"
groups="sales_team.group_sale_salesman"/>
<separator/>
</xpath>
<xpath expr="//filter[@name='groupby_employee']" position="after">
<xpath expr="//filter[@name='groupby_sale_order_item']" position="before">
<filter string="Sales Order" name="groupby_sale_order" domain="[]"
context="{'group_by': 'order_id'}" groups="sales_team.group_sale_salesman"/>
<filter string="Sales Order Item" name="groupby_sale_order_item" domain="[]"
context="{'group_by': 'so_line'}" groups="sales_team.group_sale_salesman"/>
<filter string="Invoice" name="groupby_invoice" domain="[]"
context="{'group_by': 'timesheet_invoice_id'}" groups="sales_team.group_sale_salesman"/>
<filter string="Billable Type" name="groupby_timesheet_invoice_type" domain="[]"
context="{'group_by': 'timesheet_invoice_type'}" groups="sales_team.group_sale_salesman"/>
</xpath>
</field>
</record>
@ -154,6 +167,7 @@
<record id="timesheet_action_billing_report" model="ir.actions.act_window">
<field name="name">Timesheets by Billing Type</field>
<field name="res_model">timesheets.analysis.report</field>
<field name="path">timesheets-billing</field>
<field name="domain">[('project_id', '!=', False)]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
@ -179,6 +193,13 @@
<field name="act_window_id" ref="timesheet_action_billing_report"/>
</record>
<record id="timesheet_action_view_report_by_billing_rate_list" model="ir.actions.act_window.view">
<field name="sequence" eval="10"/>
<field name="view_mode">list</field>
<field name="view_id" ref="hr_timesheet.timesheets_analysis_report_list"/>
<field name="act_window_id" ref="timesheet_action_billing_report"/>
</record>
<menuitem id="menu_timesheet_billing_analysis"
parent="hr_timesheet.menu_timesheets_reports_timesheet"
action="timesheet_action_billing_report"