Skip to content

Commit

Permalink
Merge pull request #2 from miikanissi/HR-Kanban-Attendance-Initial-Ve…
Browse files Browse the repository at this point in the history
…rsion

[IMP] hr_attendance_kanban: Minor improvements
  • Loading branch information
dbruehlmeier authored Jan 17, 2024
2 parents 1510f2f + 73ee60d commit a59d6c9
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 15 deletions.
1 change: 1 addition & 0 deletions hr_attendance_kanban/models/hr_employee.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class HrEmployee(models.Model):
@api.depends(
"last_attendance_id.check_in",
"last_attendance_id.check_out",
"last_attendance_id.attendance_type_id",
"last_attendance_id",
)
def _compute_attendance_type_id(self):
Expand Down
40 changes: 40 additions & 0 deletions hr_attendance_kanban/tests/test_hr_attendance_kanban.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,43 @@ def test_attendance_type(self):
# Test absent type unlink raises UserError
with self.assertRaises(UserError):
new_att_type_absent.unlink()

@users("test-user-admin")
def test_attendance_manual(self):
"""Test employee attendance type when attendance is created, checked out and
deleted manually"""
public_employee = self.env["hr.employee.public"].browse(self.employee.ids)

# Create attendance manually
attendance = self.env["hr.attendance"].create(
{
"employee_id": public_employee.employee_id.id,
"check_in": datetime.now().strftime(DF),
"attendance_type_id": self.att_type_office.id,
}
)

# Ensure employee attendance type matches new ongoing attendance
self.assertEqual(
attendance.attendance_type_id, public_employee.attendance_type_id
)

# Check out attendance
attendance.check_out = datetime.now().strftime(DF)

# Ensure employee attendance went back to absent
self.assertEqual(self.att_type_absent, public_employee.attendance_type_id)

# Modify attendance to force it back to checked in
attendance.check_out = False

# Ensure employee attendance type matches new ongoing attendance
self.assertEqual(
attendance.attendance_type_id, public_employee.attendance_type_id
)

# Delete the ongoing attendance
attendance.unlink()

# Ensure employee attendance went back to absent
self.assertEqual(self.att_type_absent, public_employee.attendance_type_id)
2 changes: 1 addition & 1 deletion hr_attendance_kanban/views/hr_attendance_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<field name="priority">500</field>
<field name="arch" type="xml">
<field name="worked_hours" position="after">
<field name="attendance_type_id" />
<field name="attendance_type_id" domain="[('absent', '!=', True)]" />
<field name="comment" />
</field>
</field>
Expand Down
50 changes: 37 additions & 13 deletions hr_attendance_kanban/wizard/hr_attendance_kanban_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ class HrAttendanceKanbanWizard(models.Model):
attendance_state = fields.Selection(related="public_employee_id.attendance_state")

next_attendance_type_id = fields.Many2one("hr.attendance.type", "Attendance Type")
comment = fields.Char(help="Optional comment for attendance")

manual_mode = fields.Boolean(
help=(
Expand All @@ -29,35 +28,56 @@ class HrAttendanceKanbanWizard(models.Model):
)
)

start_time = fields.Datetime(compute="_compute_times", store=True, readonly=False)
end_time = fields.Datetime(compute="_compute_times", store=True, readonly=False)
start_time = fields.Datetime(
compute="_compute_start_time", store=True, readonly=False
)
end_time = fields.Datetime(compute="_compute_end_time", store=True, readonly=False)
comment = fields.Char(compute="_compute_comment", store=True, readonly=False)

@api.depends("last_attendance_id.check_in")
@api.depends("last_attendance_id.check_out")
@api.depends("last_attendance_id")
def _compute_times(self):
"""Computes the default start and end times for check in/out wizard rounded
def _compute_start_time(self):
"""Computes the default start time for check in/out wizard rounded
down to the closest 5 minutes. If employee is checked in, get the checked in
time for start time."""
for wizard in self:
time_now = fields.Datetime.now()
time_now_rounded = time_now - datetime.timedelta(
minutes=time_now.minute % 5,
seconds=time_now.second,
microseconds=time_now.microsecond,
)
time_now = fields.Datetime.now()
time_now_rounded = time_now - datetime.timedelta(
minutes=time_now.minute % 5,
seconds=time_now.second,
microseconds=time_now.microsecond,
)

for wizard in self:
if (
wizard.public_employee_id
and wizard.last_attendance_id
and not wizard.last_attendance_id.check_out
):
wizard.start_time = wizard.last_attendance_id.check_in
wizard.end_time = time_now_rounded
else:
wizard.start_time = time_now_rounded

@api.depends("last_attendance_id.check_in")
@api.depends("last_attendance_id.check_out")
@api.depends("last_attendance_id")
def _compute_end_time(self):
"""Computes the end time for check in/out wizard rounded
down to the closest 5 minutes when checking out."""
time_now = fields.Datetime.now()
time_now_rounded = time_now - datetime.timedelta(
minutes=time_now.minute % 5,
seconds=time_now.second,
microseconds=time_now.microsecond,
)
for wizard in self:
if (
wizard.public_employee_id
and wizard.last_attendance_id
and not wizard.last_attendance_id.check_out
):
wizard.end_time = time_now_rounded

@api.depends("last_attendance_id.comment")
@api.depends("last_attendance_id.check_out")
@api.depends("last_attendance_id")
Expand Down Expand Up @@ -130,6 +150,10 @@ def action_change(self):
limit=1,
)
if attendance:
if self.end_time < attendance.check_in:
raise UserError(
_('"Check Out" time cannot be earlier than "Check In" time.')
)
attendance.check_out = self.end_time
else:
raise UserError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
/>
<field
name="next_attendance_type_id"
attrs="{'required': [('manual_mode', '=', True), ('attendance_state', '!=', 'checked_in')] , 'readonly': ['|', ('manual_mode', '!=', True), ('attendance_state', '!=', 'checked_out')], 'invisible': ['|', ('manual_mode', '!=', True), ('attendance_state', '!=', 'checked_out')]}"
attrs="{'required': [('manual_mode', '=', True), ('attendance_state', '!=', 'checked_in')] , 'readonly': ['|', ('manual_mode', '!=', True), ('attendance_state', '!=', 'checked_out')], 'invisible': [('attendance_state', '!=', 'checked_out')]}"
domain="[('absent', '!=', True)]"
options="{'no_create': True}"
/>
Expand Down

0 comments on commit a59d6c9

Please sign in to comment.