Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions hr_timesheet_cost_account/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
===========================
HR - Timesheet Cost Account
===========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:403c4c2a80808e2a6770a0f406d3da28af7699d0ad3b13ee7b7c59a60b28ea24
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Ftimesheet-lightgray.png?logo=github
:target: https://github.com/OCA/timesheet/tree/18.0/hr_timesheet_cost_account
:alt: OCA/timesheet
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/timesheet-18-0/timesheet-18-0-hr_timesheet_cost_account
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/timesheet&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module allows recognizing timesheet-based labor costs in
accounting.

It introduces a **Timesheet Costing** document that collects timesheet
lines within a selected date range (optionally filtered by project
and/or employee), computes the total cost based on each employee's
hourly cost, and generates a journal entry with:

- **Debit** to the cost account for each timesheet line (including
analytic distribution)
- **Credit** to the suspense account for the total amount

**Table of contents**

.. contents::
:local:

Configuration
=============

Go to *Accounting > Configuration > Settings* and scroll to the
**Timesheets** section.

Configure the following options:

- **Cost Account**: account to debit for timesheet labor costs (e.g. WIP
or labor expense account)
- **Suspense Account**: account to credit as the counterpart entry
- **Journal**: journal used to generate timesheet cost entries

Ensure that each employee has an **Hourly Cost** defined under
*Employees > Settings*, as it is used to compute the cost amount.

Usage
=====

To use this module:

1. Go to *Timesheets > Timesheet Costing* or *Invoicing > Accounting >
Timesheet Costing*.
2. Click **New** and set the date range (*Date From* / *Date To*).
3. Optionally filter by **Projects** and/or **Employees** (leave empty
to include all).
4. Set the **Accounting Date**.
5. Click **Get Timesheets** to load matching, unprocessed timesheet
lines. The *Total Cost* is computed automatically based on hours and
employee hourly cost.
6. Click **Confirm**, then **Create Journal Entry** to generate the
accounting entry.

To review the result, use the **Journal Entry** smart button on the
costing form. The journal entry also provides a link back to the costing
document.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/timesheet/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/timesheet/issues/new?body=module:%20hr_timesheet_cost_account%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Ecosoft

Contributors
------------

- `Ecosoft <https://ecosoft.co.th>`__:

- Saran Lim. <saranl@ecosoft.co.th>

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-Saran440| image:: https://github.com/Saran440.png?size=40px
:target: https://github.com/Saran440
:alt: Saran440

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-Saran440|

This module is part of the `OCA/timesheet <https://github.com/OCA/timesheet/tree/18.0/hr_timesheet_cost_account>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
3 changes: 3 additions & 0 deletions hr_timesheet_cost_account/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from . import models
26 changes: 26 additions & 0 deletions hr_timesheet_cost_account/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2026 Ecosoft Co., Ltd. (<http://ecosoft.co.th>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

{
"name": "HR - Timesheet Cost Account",
"summary": "Accounting Cost for Timesheet",
"version": "18.0.1.0.0",
"category": "Tools",
"author": "Ecosoft, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/timesheet",
"license": "AGPL-3",
"depends": [
"analytic",
"account",
"hr_hourly_cost",
"hr_timesheet",
],
"data": [
"data/sequence.xml",
"security/ir.model.access.csv",
"views/res_config_settings_views.xml",
"views/hr_timesheet_costing_views.xml",
"views/account_move_views.xml",
],
"maintainers": ["Saran440"],
}
9 changes: 9 additions & 0 deletions hr_timesheet_cost_account/data/sequence.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="sequence_hr_timesheet_costing" model="ir.sequence">
<field name="name">Timesheet Costing</field>
<field name="code">hr.timesheet.costing</field>
<field name="prefix">TSC</field>
<field name="padding">5</field>
</record>
</odoo>
7 changes: 7 additions & 0 deletions hr_timesheet_cost_account/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

from . import account_move
from . import res_company
from . import res_config_settings
from . import hr_timesheet
from . import hr_timesheet_costing
24 changes: 24 additions & 0 deletions hr_timesheet_cost_account/models/account_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2026 Ecosoft Co., Ltd. (<http://ecosoft.co.th>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import fields, models


class AccountMove(models.Model):
_inherit = "account.move"

timesheet_costing_id = fields.Many2one(
comodel_name="hr.timesheet.costing",
readonly=True,
copy=False,
ondelete="set null",
)

def action_view_timesheet_costing(self):
self.ensure_one()
return {
"type": "ir.actions.act_window",
"res_model": "hr.timesheet.costing",
"view_mode": "form",
"res_id": self.timesheet_costing_id.id,
}
15 changes: 15 additions & 0 deletions hr_timesheet_cost_account/models/hr_timesheet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2026 Ecosoft Co., Ltd. (<http://ecosoft.co.th>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import fields, models


class AccountAnalyticLine(models.Model):
_inherit = "account.analytic.line"

timesheet_costing_id = fields.Many2one(
comodel_name="hr.timesheet.costing",
readonly=True,
copy=False,
ondelete="set null",
)
Loading
Loading