Skip to content

Commit

Permalink
Merge PR OCA#861 into 12.0
Browse files Browse the repository at this point in the history
Signed-off-by dreispt
  • Loading branch information
OCA-git-bot committed Aug 30, 2022
2 parents 4619c4e + 09148bf commit 90cd8a0
Show file tree
Hide file tree
Showing 28 changed files with 1,931 additions and 0 deletions.
121 changes: 121 additions & 0 deletions hr_attendance_sheet/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
===================
HR Attendance Sheet
===================

.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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%2Fhr-lightgray.png?logo=github
:target: https://github.com/OCA/hr/tree/12.0/hr_attendance_rfid
:alt: OCA/hr
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/hr-12-0/hr-12-0-hr_attendance_rfid
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/116/12.0
:alt: Try me on Runbot

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

This module extends attendance and adds sheets and other features.
Feature List:

* Attendance sheets, generated Daily, Weekly, Bi-Weekly or Monthly
* Auto lunch calculation in the duration if attendance duration surpasses time.
* Split attendance at midnight is attendance crosses to the next day.
* Approval process for 'HR', 'Manger' and 'Manager or HR'

**Table of contents**

.. contents::
:local:

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

Attendance Sheet Configuration:
#. Go to *Attendances -> Configuration*.
#. Set the Attendance Sheet Range to be used to calculate start/end dates
on the sheet when they are created.
#. Set the Attendance Sheet Review Policy for who cab review sheets.
#. Choose Split Overnight Attendance if you want attendances that cross
overnight to be split into two attendances at midnight.
#. Choose Auto Lunch if you want a lunch calculated automatically. Duration is
maximum hours where a lunch would be calculated with the lunch duration.
For example, duration set to 5hrs, lunch set to .5hrs, if attendance is 6hours
then the duration on the attendance would show 5.5hrs due to the auto lunch.

Employee Configuration:
#. Go to *Attendances -> Manage Attendances -> Employees*.
#. Open the Employee and then HR Settings
#. Check Manual Attendance to give them access to the attendance app.
#. Check Attendance Sheets if they will be having sheets created for them.
#. Set Hours to Work which is the expected work time during the sheet period,
this is used to calculates overtime values on the sheet.
#. If manager's are set to review, make sure a manager is set on the employee.

Usage
=====

#. Sheets will be auto created by a scheduled action for employees that have
the attendance sheet checked on their employee record.
#. Sheets will have a start/end time based on the period set in settings.
#. Attendances that have dates between the sheet start/end time will
automatically be put into the sheet.
#. Split overnight attendance (if enabled) is used if attendances shouldn't
cross overnight and will generate another attendance for the next day, split
at midnight of the timezone that is set on the employee record. A banner will
show on the attendance stating that a split has been applied.
#. Auto lunch (if enabled) will be calculated in the attendance duration if the
duration exceeds the auto lunch setting. A banner will show on the attendance
stating that an auto lunch has been applied.
#. After sheet period ends, Employee submits sheet to reviewer. Reviewer makes
changes if necessary and approves sheet.

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

Bugs are tracked on `GitHub Issues <https://github.com/OCA/hr/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/hr/issues/new?body=module:%20hr_attendance_rfid%0Aversion:%2012.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
~~~~~~~

* Pavlov Media

Contributors
~~~~~~~~~~~~

* Patrick Wilson <pwilson@pavlovmedia.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/hr <https://github.com/OCA/hr/tree/12.0/hr_attendance_sheet>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
5 changes: 5 additions & 0 deletions hr_attendance_sheet/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright 2017 Odoo S.A.
# Copyright 2020 Pavlov Media
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from . import models
30 changes: 30 additions & 0 deletions hr_attendance_sheet/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2020 Pavlov Media
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

{
"name": "HR Attendance Sheet",
"version": "12.0.1.0.0",
"category": "Human Resources",
"summary": "Group attendances into attendance sheets.",
"website": "https://github.com/OCA/hr",
"author": "Odoo S.A., Odoo Community Association (OCA)",
"license": "AGPL-3",
"installable": True,
"depends": [
"hr_attendance",
],
"data": [
"security/ir.model.access.csv",
"security/security_groups.xml",
"data/cron.xml",
"data/mail_data.xml",
"report/hr_attendance_sheet_report.xml",
"views/hr_attendance_sheet.xml",
"views/hr_attendance_view.xml",
"views/hr_department.xml",
"views/hr_employee.xml",
"views/res_config_settings_views.xml",
"views/res_company.xml",
],
"development_status": "Beta",
}
12 changes: 12 additions & 0 deletions hr_attendance_sheet/data/cron.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<odoo>
<record id="ir_cron_attendance_sheet_creation" model="ir.cron">
<field name="name">Attendance Sheets: Create Sheets</field>
<field name="interval_number">4</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False"/>
<field name="model_id" ref="model_hr_attendance_sheet"/>
<field name="code">model._create_sheet_id()</field>
<field name="state">code</field>
</record>
</odoo>
8 changes: 8 additions & 0 deletions hr_attendance_sheet/data/mail_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<odoo noupdate="1">
<!-- Leave specific activities -->
<record id="mail_act_attendance_sheet_approval" model="mail.activity.type">
<field name="name">Attendance Sheet Approval</field>
<field name="icon">fa-sun-o</field>
<field name="res_model_id" ref="hr_attendance_sheet.model_hr_attendance_sheet"/>
</record>
</odoo>
9 changes: 9 additions & 0 deletions hr_attendance_sheet/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright 2020 Pavlov Media
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from . import hr_attendance
from . import hr_attendance_sheet
from . import hr_department
from . import hr_employee
from . import res_company
from . import res_config
185 changes: 185 additions & 0 deletions hr_attendance_sheet/models/hr_attendance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Copyright 2020 Pavlov Media
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html

from odoo import api, fields, models, _
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
from odoo.exceptions import UserError

import pytz
from datetime import datetime


class HrAttendance(models.Model):
_inherit = "hr.attendance"

attendance_sheet_id = fields.Many2one(
"hr.attendance.sheet",
string="Sheet",
compute="_compute_attendance_sheet_id",
store=True,
)
duration = fields.Float(
string="Duration (Hrs)",
compute="_compute_duration",
)
auto_lunch = fields.Boolean(
string="Auto Lunch Applied",
help="If Auto Lunch is enabled and applied on this attendance.",
)
company_id = fields.Many2one(
"res.company", string="Company", related="attendance_sheet_id.company_id"
)
auto_lunch_enabled = fields.Boolean(
string="Auto Lunch Enabled", related="company_id.auto_lunch"
)
override_auto_lunch = fields.Boolean(
string="Override Auto Lunch",
help="Enable if you don't want the auto lunch to calculate.",
)
override_reason = fields.Text(
string="Override Reason",
help="State the reason you are overriding the auto lunch.",
)
attendance_admin = fields.Many2one(
"hr.employee",
string="Attendance Admin",
help="""In addition to the employees manager, this person can
administer attendances for all employees in the department. This field
is set on the department.""",
related="department_id.attendance_admin",
)

# Get Methods
def _get_attendance_employee_tz(self, date=None):
"""Convert date according to timezone of user"""
tz = False
if self.employee_id:
tz = self.employee_id.tz
if not date:
return False
time_zone = pytz.timezone(tz or "UTC")
attendance_dt = datetime.strptime(str(date), DEFAULT_SERVER_DATETIME_FORMAT)
attendance_tz_dt = pytz.UTC.localize(attendance_dt)
attendance_tz_dt = attendance_tz_dt.astimezone(time_zone)
attendance_tz_date_str = datetime.strftime(
attendance_tz_dt, DEFAULT_SERVER_DATE_FORMAT
)
return attendance_tz_date_str

def _get_attendance_state(self):
"""Check and raise error if related sheet is not in 'draft' state"""
if self.attendance_sheet_id and self.attendance_sheet_id.state == "locked":
raise UserError(_("You cannot modify an entry in a locked sheet."))
elif (
self.attendance_sheet_id.state == "done"
and self.env.user not in self.attendance_sheet_id._get_possible_reviewers()
):
raise UserError(_("You cannot modify an entry in a approved sheet"))
return True

# Compute Methods
@api.depends("employee_id", "check_in", "check_out")
def _compute_attendance_sheet_id(self):
"""Find and set current sheet in current attendance record"""
for attendance in self:
sheet_obj = self.env["hr.attendance.sheet"]
check_in = False
if attendance.check_in:
check_in = attendance._get_attendance_employee_tz(
date=attendance.check_in
)
domain = [("employee_id", "=", attendance.employee_id.id)]
if check_in:
domain += [
("date_start", "<=", check_in),
("date_end", ">=", check_in),
]
attendance_sheet_ids = sheet_obj.search(domain, limit=1)
if attendance_sheet_ids.state not in ("locked", "done"):
attendance.attendance_sheet_id = attendance_sheet_ids or False

@api.multi
def _compute_duration(self):
for rec in self:
if rec.check_in and rec.check_out:
delta = rec.check_out - rec.check_in
rec.duration = delta.total_seconds() / 3600

# If auto lunch is enabled for the company and time between
# other attendances < lunch period, then adjust the duration
# calculation for the first attendance.
if (
rec.company_id.auto_lunch
and rec.duration > rec.company_id.auto_lunch_duration != 0.0
and not rec.override_auto_lunch
):
day_start = rec.check_in.replace(
hour=0, minute=0, second=0, microsecond=0
)
day_end = rec.check_out.replace(
hour=23, minute=59, second=59, microsecond=59
)
first_attendance = self.env["hr.attendance"].search(
[
("check_in", ">=", day_start),
("check_in", "<=", day_end),
("employee_id", "=", rec.employee_id.id),
],
order="check_in asc",
limit=1,
)
today_attendances = self.env["hr.attendance"].search(
[
("check_in", ">=", day_start),
("check_in", "<=", day_end),
("employee_id", "=", rec.employee_id.id),
]
)
time_between_attendances = 0.0
if first_attendance and first_attendance.id == rec.id:
if len(today_attendances) > 1:
for attendance in today_attendances:
if attendance.id != first_attendance.id:
delta2 = (
attendance.check_in - first_attendance.check_out
)
time_between_attendances = (
delta2.total_seconds() / 3600
)
rec.write({"auto_lunch": False})
if (
time_between_attendances
< rec.company_id.auto_lunch_hours
):
rec.duration = (
delta.total_seconds() / 3600
) - rec.company_id.auto_lunch_hours
rec.write({"auto_lunch": True})
else:
rec.duration = (
delta.total_seconds() / 3600
) - rec.company_id.auto_lunch_hours
rec.write({"auto_lunch": True})
else:
rec.write({"auto_lunch": False})
elif rec.company_id.auto_lunch and rec.auto_lunch:
rec.write({"auto_lunch": False})

# Unlink/Write/Create Methods
@api.multi
def unlink(self):
"""Restrict to delete attendance from confirmed/locked sheet"""
for attendance in self:
attendance._get_attendance_state()
return super(HrAttendance, self).unlink()

@api.multi
def write(self, vals):
"""Restrict to write attendance from confirmed/locked sheet."""
protected_fields = ["employee_id", "check_in", "check_out"]
for attendance in self:
if attendance.attendance_sheet_id.state in ("locked", "done") and any(
f in vals.keys() for f in protected_fields
):
attendance._get_attendance_state()
return super(HrAttendance, self).write(vals)
Loading

0 comments on commit 90cd8a0

Please sign in to comment.