diff --git a/hr_timesheet_attendance_schedule/README.rst b/hr_timesheet_attendance_schedule/README.rst new file mode 100644 index 00000000..80f02052 --- /dev/null +++ b/hr_timesheet_attendance_schedule/README.rst @@ -0,0 +1,39 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +============================= +Timesheet Attendance Schedule +============================= + + +Installation +============ + +To install this module, you need to: + +1. Clone the branch 8.0 of the repository https://github.com/open-synergy/opnsynid-hr +2. Add the path to this repository in your configuration (addons-path) +3. Update the module list +4. Go to menu *Setting -> Modules -> Local Modules* +5. Search For *Timesheet Attendance Schedule* +6. Install the module + +Credits +======= + +Contributors +------------ + +* Michael Viriyananda +* Andhitia Rama +* Nurazmi + +Maintainer +---------- + +.. image:: https://opensynergy-indonesia.com/logo.png + :alt: OpenSynergy Indonesia + :target: https://opensynergy-indonesia.com + +This module is maintained by the OpenSynergy Indonesia. diff --git a/hr_timesheet_attendance_schedule/__init__.py b/hr_timesheet_attendance_schedule/__init__.py new file mode 100644 index 00000000..814c5234 --- /dev/null +++ b/hr_timesheet_attendance_schedule/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/hr_timesheet_attendance_schedule/__openerp__.py b/hr_timesheet_attendance_schedule/__openerp__.py new file mode 100644 index 00000000..050562bc --- /dev/null +++ b/hr_timesheet_attendance_schedule/__openerp__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Timesheet Attendance Schedule", + "version": "8.0.1.0.0", + "category": "Human Resource", + "website": "https://opensynergy-indonesia.com", + "author": "OpenSynergy Indonesia", + "license": "AGPL-3", + "installable": True, + "depends": [ + "hr_timesheet_sheet", + ], + "data": [ + "security/ir.model.access.csv", + "data/ir_filters_data.xml", + "data/ir_actions_server_data.xml", + "data/base_action_rule_data.xml", + "views/hr_timesheet_sheet_views.xml", + "views/hr_timesheet_attendance_schedule_views.xml", + "views/hr_attendance_config_setting_views.xml", + ], +} diff --git a/hr_timesheet_attendance_schedule/data/base_action_rule_data.xml b/hr_timesheet_attendance_schedule/data/base_action_rule_data.xml new file mode 100644 index 00000000..890ad61a --- /dev/null +++ b/hr_timesheet_attendance_schedule/data/base_action_rule_data.xml @@ -0,0 +1,17 @@ + + + + + + + + Create Attendance Schedule + + on_write + + + + + + diff --git a/hr_timesheet_attendance_schedule/data/ir_actions_server_data.xml b/hr_timesheet_attendance_schedule/data/ir_actions_server_data.xml new file mode 100644 index 00000000..12812abc --- /dev/null +++ b/hr_timesheet_attendance_schedule/data/ir_actions_server_data.xml @@ -0,0 +1,21 @@ + + + + + + + + Create Attendance Schedule + + ir.actions.server + + True + code + object.action_create_attendance_schedule() + + + + + + diff --git a/hr_timesheet_attendance_schedule/data/ir_filters_data.xml b/hr_timesheet_attendance_schedule/data/ir_filters_data.xml new file mode 100644 index 00000000..82f030be --- /dev/null +++ b/hr_timesheet_attendance_schedule/data/ir_filters_data.xml @@ -0,0 +1,16 @@ + + + + + + + + After Attendance Schedule Creation + hr_timesheet_sheet.sheet + [["state","=","draft"]] + + + + + diff --git a/hr_timesheet_attendance_schedule/models/__init__.py b/hr_timesheet_attendance_schedule/models/__init__.py new file mode 100644 index 00000000..a4216142 --- /dev/null +++ b/hr_timesheet_attendance_schedule/models/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import hr_attendance +from . import hr_timesheet_attendance_schedule +from . import hr_timesheet_sheet +from . import res_company +from . import res_config diff --git a/hr_timesheet_attendance_schedule/models/hr_attendance.py b/hr_timesheet_attendance_schedule/models/hr_attendance.py new file mode 100644 index 00000000..66a88ecd --- /dev/null +++ b/hr_timesheet_attendance_schedule/models/hr_attendance.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from dateutil.relativedelta import relativedelta +from openerp import models, fields, api + + +class HrAttendance(models.Model): + _inherit = "hr.attendance" + + @api.multi + @api.depends("name") + def _compute_schedule(self): + obj_schedule = self.env["hr.timesheet_attendance_schedule"] + for attn in self: + company = attn.employee_id.company_id + early_buffer = fields.Datetime.from_string(attn.name) + \ + relativedelta(hours=company.early_attendance_buffer) + early_buffer = fields.Datetime.to_string(early_buffer) + late_buffer = fields.Datetime.from_string(attn.name) + \ + relativedelta(hours=-company.late_attendance_buffer) + late_buffer = fields.Datetime.to_string(late_buffer) + + criteria = [ + ("employee_id", "=", attn.employee_id.id), + ("date_start", "<=", early_buffer), + ("date_end", ">=", late_buffer), + ] + schedules = obj_schedule.search(criteria, limit=1) + attn.schedule_id = schedules[0].id if len(schedules) > 0 else False + + schedule_id = fields.Many2one( + string="Attendance Schedule", + comodel_name="hr.timesheet_attendance_schedule", + compute="_compute_schedule", + store=True, + ) diff --git a/hr_timesheet_attendance_schedule/models/hr_timesheet_attendance_schedule.py b/hr_timesheet_attendance_schedule/models/hr_timesheet_attendance_schedule.py new file mode 100644 index 00000000..817259f3 --- /dev/null +++ b/hr_timesheet_attendance_schedule/models/hr_timesheet_attendance_schedule.py @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api + + +class HrTimesheetAttendanceSchedule(models.Model): + _name = "hr.timesheet_attendance_schedule" + _description = "Timesheet Attendance Schedule" + _order = "sheet_id, date_start" + + @api.multi + @api.depends("attendance_ids") + def _compute_attendance(self): + for schedule in self: + attendances = schedule.attendance_ids.filtered( + lambda r: r.action == "sign_in").sorted(key=lambda r: r.name) + schedule.start_attendance_id = attendances[ + 0].id if len(attendances) > 0 else False + + attendances = schedule.attendance_ids.\ + filtered(lambda r: r.action == "sign_out").\ + sorted(key=lambda r: r.name, reverse=True) + schedule.end_attendance_id = attendances[ + 0].id if len(attendances) > 0 else False + + @api.multi + @api.depends( + "start_attendance_id", "end_attendance_id", + ) + def _compute_state(self): + for attn in self: + if attn.start_attendance_id and \ + attn.end_attendance_id: + attn.state = "present" + elif (attn.start_attendance_id and not attn.end_attendance_id) or \ + (not attn.start_attendance_id and attn.end_attendance_id): + attn.state = "open" + elif not attn.start_attendance_id and \ + not attn.end_attendance_id: + attn.state = "absence" + + @api.multi + @api.depends( + "date_start", "date_end", + "start_attendance_id", "end_attendance_id", + "start_attendance_id.name", "end_attendance_id.name", + ) + def _compute_work_hour(self): + for attn in self: + schedule_work_hour = real_work_hour = early_start_hour = \ + late_start_hour = finish_early_hour = finish_late_hour = \ + 0.0 + dt_schedule_start = fields.Datetime.from_string(attn.date_start) \ + if attn.date_start else False + dt_schedule_end = fields.Datetime.from_string(attn.date_end) \ + if attn.date_end else False + dt_real_start = fields.Datetime.\ + from_string(attn.start_attendance_id.name) if attn.\ + start_attendance_id else False + dt_real_end = fields.Datetime.\ + from_string(attn.end_attendance_id.name) if attn.\ + end_attendance_id else False + + if dt_schedule_start and dt_schedule_end: + schedule_work_hour = \ + (dt_schedule_end - dt_schedule_start).total_seconds() / \ + 3600.0 + + if dt_real_start and dt_real_end: + real_work_hour = \ + (dt_real_end - dt_real_start).total_seconds() / \ + 3600.0 + + if dt_schedule_start and dt_real_start: + if dt_schedule_start > dt_real_start: + early_start_hour = (dt_schedule_start - dt_real_start).\ + total_seconds() / 3600.0 + + if dt_schedule_start < dt_real_start: + late_start_hour = (dt_real_start - dt_schedule_start).\ + total_seconds() / 3600.0 + + if dt_schedule_end and dt_real_end: + if dt_schedule_end > dt_real_end: + finish_early_hour = (dt_schedule_end - dt_real_end).\ + total_seconds() / 3600.0 + + if dt_schedule_end < dt_real_end: + finish_late_hour = (dt_real_end - dt_schedule_end).\ + total_seconds() / 3600.0 + + attn.schedule_work_hour = schedule_work_hour + attn.real_work_hour = real_work_hour + attn.early_start_hour = early_start_hour + attn.late_start_hour = late_start_hour + attn.finish_early_hour = finish_early_hour + attn.finish_late_hour = finish_late_hour + + sheet_id = fields.Many2one( + string="Timesheet", + comodel_name="hr_timesheet_sheet.sheet", + ) + employee_id = fields.Many2one( + string="Employee", + related="sheet_id.employee_id", + store=True, + ) + date_start = fields.Datetime( + string="Date Start", + required=False, + ) + date_end = fields.Datetime( + string="Date End", + required=False, + ) + attendance_ids = fields.One2many( + string="Attendances", + comodel_name="hr.attendance", + inverse_name="schedule_id", + ) + start_attendance_id = fields.Many2one( + string="Start Attendance", + comodel_name="hr.attendance", + compute="_compute_attendance", + store=True, + ) + end_attendance_id = fields.Many2one( + string="End Attendance", + comodel_name="hr.attendance", + compute="_compute_attendance", + store=True, + ) + real_date_start = fields.Datetime( + string="Real Date Start", + related="start_attendance_id.name", + ) + real_date_end = fields.Datetime( + string="Real Date End", + related="end_attendance_id.name", + ) + + state = fields.Selection( + string="State", + selection=[ + ("absence", "Absence"), + ("open", "Open"), + ("present", "Present"), + ], + default="absence", + required=True, + compute="_compute_state", + store=True, + ) + schedule_work_hour = fields.Float( + string="Schedule Work Hour", + compute="_compute_work_hour", + store=True, + ) + real_work_hour = fields.Float( + string="Real Work Hour", + compute="_compute_work_hour", + store=True, + ) + early_start_hour = fields.Float( + string="Early Start", + compute="_compute_work_hour", + store=True, + ) + late_start_hour = fields.Float( + string="Late Start", + compute="_compute_work_hour", + store=True, + ) + finish_early_hour = fields.Float( + string="Finish Early", + compute="_compute_work_hour", + store=True, + ) + finish_late_hour = fields.Float( + string="Finish Late", + compute="_compute_work_hour", + store=True, + ) diff --git a/hr_timesheet_attendance_schedule/models/hr_timesheet_sheet.py b/hr_timesheet_attendance_schedule/models/hr_timesheet_sheet.py new file mode 100644 index 00000000..0b8edd63 --- /dev/null +++ b/hr_timesheet_attendance_schedule/models/hr_timesheet_sheet.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api + + +class HrTimesheetSheet(models.Model): + _inherit = "hr_timesheet_sheet.sheet" + + attendance_schedule_ids = fields.One2many( + string="Attendance Schedule", + comodel_name="hr.timesheet_attendance_schedule", + inverse_name="sheet_id", + readonly=True, + states={ + "draft": [ + ("readonly", False), + ], + }, + ) + + @api.multi + def action_create_attendance_schedule(self): + for sheet in self: + sheet._create_attendance_schedule() + + @api.multi + def _create_attendance_schedule(self): + self.ensure_one() + obj_schedule = self.env["hr.timesheet_attendance_schedule"] + dt_start = fields.Datetime.from_string(self.date_from) + dt_end = fields.Datetime.from_string(self.date_to) + duration = abs((dt_end - dt_start).days) + 1 + if self.contract_ids: + contract = self.contract_ids.sorted( + lambda r: r.date_start, reverse=True)[0] + + schedules = contract.working_hours._schedule_days( + days=duration, + day_date=dt_start) + for schedule in schedules[0]: + str_start = fields.Datetime.to_string(schedule[0]) + str_end = fields.Datetime.to_string(schedule[1]) + if self._check_existing_schedule(str_start, str_end): + data = { + "sheet_id": self.id, + "date_start": str_start, + "date_end": str_end, + } + obj_schedule.create(data) + + @api.multi + def _check_existing_schedule(self, dt_start, dt_end): + self.ensure_one() + result = True + obj_schedule = self.env["hr.timesheet_attendance_schedule"] + criteria = [ + ("sheet_id", "=", self.id), + ("date_start", "=", dt_start), + ("date_end", "=", dt_end), + ] + schedule_count = obj_schedule.search_count(criteria) + if schedule_count > 0: + result = False + return result diff --git a/hr_timesheet_attendance_schedule/models/res_company.py b/hr_timesheet_attendance_schedule/models/res_company.py new file mode 100644 index 00000000..42bc3c18 --- /dev/null +++ b/hr_timesheet_attendance_schedule/models/res_company.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields + + +class ResCompany(models.Model): + _inherit = "res.company" + + early_attendance_buffer = fields.Float( + string="Early Attendance Buffer", + default=0.0, + required=True, + ) + late_attendance_buffer = fields.Float( + string="Late Attendance Buffer", + default=0.0, + required=True, + ) diff --git a/hr_timesheet_attendance_schedule/models/res_config.py b/hr_timesheet_attendance_schedule/models/res_config.py new file mode 100644 index 00000000..d41ba5f7 --- /dev/null +++ b/hr_timesheet_attendance_schedule/models/res_config.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 OpenSynergy Indonesia +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields + + +class ResConfig(models.TransientModel): + _inherit = "hr.attendance_config_setting" + + early_attendance_buffer = fields.Float( + string="Early Attendance Buffer", + related="company_id.early_attendance_buffer", + ) + late_attendance_buffer = fields.Float( + string="Late Attendance Buffer", + related="company_id.late_attendance_buffer", + ) diff --git a/hr_timesheet_attendance_schedule/security/ir.model.access.csv b/hr_timesheet_attendance_schedule/security/ir.model.access.csv new file mode 100644 index 00000000..94d4ee53 --- /dev/null +++ b/hr_timesheet_attendance_schedule/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +hr_timesheet_attendance_schedule_employee,hr.timesheet_attendance_schedule - employee,model_hr_timesheet_attendance_schedule,base.group_user,1,1,1,1 +hr_timesheet_attendance_schedule_all,hr.timesheet_attendance_schedule - all,model_hr_timesheet_attendance_schedule,,1,0,0,0 diff --git a/hr_timesheet_attendance_schedule/static/description/icon.png b/hr_timesheet_attendance_schedule/static/description/icon.png new file mode 100644 index 00000000..270415f7 Binary files /dev/null and b/hr_timesheet_attendance_schedule/static/description/icon.png differ diff --git a/hr_timesheet_attendance_schedule/views/hr_attendance_config_setting_views.xml b/hr_timesheet_attendance_schedule/views/hr_attendance_config_setting_views.xml new file mode 100644 index 00000000..68196b64 --- /dev/null +++ b/hr_timesheet_attendance_schedule/views/hr_attendance_config_setting_views.xml @@ -0,0 +1,31 @@ + + + + + + + + hr.attendance_config_setting form + hr.attendance_config_setting + + + + +
+
+
+
+
+
+
+
+ +
+
diff --git a/hr_timesheet_attendance_schedule/views/hr_timesheet_attendance_schedule_views.xml b/hr_timesheet_attendance_schedule/views/hr_timesheet_attendance_schedule_views.xml new file mode 100644 index 00000000..8355d548 --- /dev/null +++ b/hr_timesheet_attendance_schedule/views/hr_timesheet_attendance_schedule_views.xml @@ -0,0 +1,97 @@ + + + + + + + + hr.timesheet_attendance_schedule tree + hr.timesheet_attendance_schedule + + + + + + + + + + + + + + + + + + + hr.timesheet_attendance_schedule form + hr.timesheet_attendance_schedule + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + hr.timesheet_attendance_schedule search + hr.timesheet_attendance_schedule + + + + + + + + + + + + + Timesheet Attendance Schedule + ir.actions.act_window + hr.timesheet_attendance_schedule + form + tree,form + + + + + + +
+
diff --git a/hr_timesheet_attendance_schedule/views/hr_timesheet_sheet_views.xml b/hr_timesheet_attendance_schedule/views/hr_timesheet_sheet_views.xml new file mode 100644 index 00000000..80171cb1 --- /dev/null +++ b/hr_timesheet_attendance_schedule/views/hr_timesheet_sheet_views.xml @@ -0,0 +1,62 @@ + + + + + + + + hr_timesheet_sheet.sheet form + hr_timesheet_sheet.sheet + + + + + + + + + + + + + + + + + + + +
+
+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+ + +
+