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
111 changes: 111 additions & 0 deletions hr_timesheet_begin_end_break/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

===================================
Timesheet - Begin/End Hours + Break
===================================

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

.. |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/license-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/19.0/hr_timesheet_begin_end_break
:alt: OCA/timesheet
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/timesheet-19-0/timesheet-19-0-hr_timesheet_begin_end_break
: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=19.0
:alt: Try me on Runboat

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

This module extends
`hr_timesheet_begin_end <https://github.com/OCA/timesheet/tree/19.0/hr_timesheet_begin_end>`__
by adding a **break duration** field to timesheet lines that use
begin/end hours.

The worked time (``unit_amount``) is then computed as:

::

Unit Amount = (End Hour - Begin Hour) - Break Duration

For example, a line starting at 08:00, ending at 16:30 with a 30 minute
break results in 8.00 worked hours.

**Table of contents**

.. contents::
:local:

Usage
=====

To use this module you need to:

1. Go to *Timesheets* and open or create a timesheet line (or the
*Timesheets* tab of a project task).
2. Fill in the *Begin Hour* and *End Hour*.
3. Fill in the *Break* duration.
4. The *Duration* (worked hours) is automatically recomputed as the
difference between end and begin hours, minus the break.

The line is validated so that the duration always equals
``End Hour - Begin Hour - Break``, that the begin hour precedes the end
hour, and that lines do not overlap for the same employee on the same
day.

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_begin_end_break%0Aversion:%2019.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
-------

* Slivi-sliv

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

- Slivi-sliv <silvano999@gmail.com>
- Stefano Consolaro (`Mymage <https://www.mymage.it>`__)
- bosd <ebo@stefcy.com>

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.

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

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions hr_timesheet_begin_end_break/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
20 changes: 20 additions & 0 deletions hr_timesheet_begin_end_break/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2025 Slivi-sliv
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

{
"name": "Timesheet - Begin/End Hours + Break",
"version": "19.0.1.0.0",
"author": "Slivi-sliv, Odoo Community Association (OCA)",
"license": "AGPL-3",
"category": "Human Resources",
"depends": [
"hr_timesheet_begin_end",
],
"website": "https://github.com/OCA/timesheet",
"data": [
"views/hr_analytic_timesheet.xml",
"views/project_task_view.xml",
],
"installable": True,
"summary": "Adds break duration to begin/end timesheets",
}
50 changes: 50 additions & 0 deletions hr_timesheet_begin_end_break/i18n/hr_timesheet_begin_end_break.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * hr_timesheet_begin_end_break
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 19.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: hr_timesheet_begin_end_break
#: model:ir.model,name:hr_timesheet_begin_end_break.model_account_analytic_line
msgid "Analytic Line"
msgstr ""

#. module: hr_timesheet_begin_end_break
#: model_terms:ir.ui.view,arch_db:hr_timesheet_begin_end_break.hr_timesheet_line_form_break
#: model_terms:ir.ui.view,arch_db:hr_timesheet_begin_end_break.hr_timesheet_line_tree_break
#: model_terms:ir.ui.view,arch_db:hr_timesheet_begin_end_break.view_task_form2_inherited_break
msgid "Break"
msgstr ""

#. module: hr_timesheet_begin_end_break
#: model:ir.model.fields,field_description:hr_timesheet_begin_end_break.field_account_analytic_line__break_duration
msgid "Break Duration"
msgstr ""

#. module: hr_timesheet_begin_end_break
#: code:addons/hr_timesheet_begin_end_break/models/account_analytic_line.py:0
msgid "Lines can't overlap:\n"
msgstr ""

#. module: hr_timesheet_begin_end_break
#: code:addons/hr_timesheet_begin_end_break/models/account_analytic_line.py:0
msgid ""
"The beginning hour (%(html_start)s) must precede the ending hour "
"(%(html_stop)s)."
msgstr ""

#. module: hr_timesheet_begin_end_break
#: code:addons/hr_timesheet_begin_end_break/models/account_analytic_line.py:0
msgid ""
"The duration (%(html_unit_amount)s) must be equal to the difference between "
"the hours minus the break (%(html_hours)s)."
msgstr ""
1 change: 1 addition & 0 deletions hr_timesheet_begin_end_break/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import account_analytic_line
84 changes: 84 additions & 0 deletions hr_timesheet_begin_end_break/models/account_analytic_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Copyright 2025 Slivi-sliv
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from datetime import timedelta

from odoo import api, exceptions, fields, models
from odoo.tools.float_utils import float_compare


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

break_duration = fields.Float(default=0.0)

@api.onchange("time_start", "time_stop", "break_duration")
def onchange_hours_start_stop(self):
res = super().onchange_hours_start_stop()
if self.time_start and self.time_stop:
# Re-compute the worked amount so that the break is deducted.
start = timedelta(hours=self.time_start)
stop = timedelta(hours=self.time_stop)
if stop >= start:
self.unit_amount = (stop - start).seconds / 3600 - self.break_duration
return res

@api.constrains("time_start", "time_stop", "unit_amount", "break_duration")
def _check_time_start_stop(self):
# This overrides (does not extend) the base ``hr_timesheet_begin_end``
# constraint of the same name: the base enforces
# ``unit_amount == time_stop - time_start``, which is always false once a
# break is deducted. We therefore re-check start/stop order and overlap
# here as well, mirroring the base implementation, and add
# ``break_duration`` to the duration expectation.
rounding = self.env.ref("uom.product_uom_hour").rounding
value_to_html = self.env["ir.qweb.field.float_time"].value_to_html
for line in self:
if not line.time_start and not line.time_stop:
continue
start = timedelta(hours=line.time_start)
stop = timedelta(hours=line.time_stop)
if stop < start:
raise exceptions.ValidationError(
self.env._(
"The beginning hour (%(html_start)s) must "
"precede the ending hour (%(html_stop)s).",
html_start=value_to_html(line.time_start, None),
html_stop=value_to_html(line.time_stop, None),
)
)
hours = (stop - start).seconds / 3600 - line.break_duration
if hours and float_compare(
hours, line.unit_amount, precision_rounding=rounding
):
raise exceptions.ValidationError(
self.env._(
"The duration (%(html_unit_amount)s) must be equal to the "
"difference between the hours minus the break "
"(%(html_hours)s).",
html_unit_amount=value_to_html(line.unit_amount, None),
html_hours=value_to_html(hours, None),
)
)
# check if lines overlap
others = self.search(
[
("id", "!=", line.id),
("employee_id", "=", line.employee_id.id),
("date", "=", line.date),
("time_start", "<", line.time_stop),
("time_stop", ">", line.time_start),
]
)
if others:
message = self.env._("Lines can't overlap:\n")
message += "\n".join(
[
f"{value_to_html(other.time_start, None)} - "
f"{value_to_html(other.time_stop, None)}"
for other in (line + others).sorted(
key=lambda item: item.time_start
)
]
)
raise exceptions.ValidationError(message)
3 changes: 3 additions & 0 deletions hr_timesheet_begin_end_break/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
3 changes: 3 additions & 0 deletions hr_timesheet_begin_end_break/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Slivi-sliv \<<silvano999@gmail.com>\>
- Stefano Consolaro ([Mymage](https://www.mymage.it))
- bosd \<<ebo@stefcy.com>\>
10 changes: 10 additions & 0 deletions hr_timesheet_begin_end_break/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
This module extends
[hr_timesheet_begin_end](https://github.com/OCA/timesheet/tree/19.0/hr_timesheet_begin_end)
by adding a **break duration** field to timesheet lines that use begin/end hours.

The worked time (`unit_amount`) is then computed as:

Unit Amount = (End Hour - Begin Hour) - Break Duration

For example, a line starting at 08:00, ending at 16:30 with a 30 minute break
results in 8.00 worked hours.
12 changes: 12 additions & 0 deletions hr_timesheet_begin_end_break/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
To use this module you need to:

1. Go to *Timesheets* and open or create a timesheet line (or the *Timesheets*
tab of a project task).
2. Fill in the *Begin Hour* and *End Hour*.
3. Fill in the *Break* duration.
4. The *Duration* (worked hours) is automatically recomputed as the difference
between end and begin hours, minus the break.

The line is validated so that the duration always equals
`End Hour - Begin Hour - Break`, that the begin hour precedes the end hour, and
that lines do not overlap for the same employee on the same day.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading