diff --git a/hr_attendance_resume_anomaly/README.rst b/hr_attendance_resume_anomaly/README.rst new file mode 100644 index 00000000..cd711220 --- /dev/null +++ b/hr_attendance_resume_anomaly/README.rst @@ -0,0 +1,43 @@ +.. 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 + +============================ +Hr attendance resume anomaly +============================ + +* This module performs anomaly control in the attendances summary. These are + the anomalies that are controlled: + + - "There are attendances in day that do not apply". No turn or has + vacationed. + - "Have absence". The worker has worked, and has absent. + - "Festive worked". The worker has worked on festive day. + - "There is an approved absence and there is no attendance". The worker has + not imputed, and is absent. + - "Entry without exit". The worker forgot to impute in at the exit. + - "Attendances very often". Imputations very often, less than 1 minute. + - "There is no attendance in day that corresponds". The worker should to have + imputed on this day, and has not done so. + - "Real working day greater or less than the percentage over the planned day" + Actual day greater or lesser than the percentage of expected day + (provided there are no approved absences). The percentages are defined in + the calendar, one for excess, and another defect. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ +* Ana Juaristi +* Alfredo de la Fuente + +Do not contact contributors directly about support or help with technical issues. diff --git a/hr_attendance_resume_anomaly/__init__.py b/hr_attendance_resume_anomaly/__init__.py new file mode 100644 index 00000000..b6237f60 --- /dev/null +++ b/hr_attendance_resume_anomaly/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2019 Alfredo de la Fuente - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from . import models +from . import wizard diff --git a/hr_attendance_resume_anomaly/__manifest__.py b/hr_attendance_resume_anomaly/__manifest__.py new file mode 100644 index 00000000..61c77525 --- /dev/null +++ b/hr_attendance_resume_anomaly/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2019 Alfredo de la Fuente - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +{ + "name": "Hr Attendance Resume Anomaly", + "version": "12.0.1.0.0", + "license": "AGPL-3", + "depends": [ + "hr_attendance_control", + "hr_attendance_resume_holidays", + "hr_attendance_resume_absences" + ], + "author": "AvanzOSC", + "website": "http://www.avanzosc.es", + "category": "Human Resources", + "data": [ + "security/ir.model.access.csv", + "data/hr_attendance_resume_anomaly_data.xml", + "wizard/wiz_attendance_resume_put_resolved_view.xml", + "views/hr_attendance_resume_view.xml", + "views/resource_calendar_view.xml", + ], + "installable": True, +} diff --git a/hr_attendance_resume_anomaly/_common.py b/hr_attendance_resume_anomaly/_common.py new file mode 100644 index 00000000..3d4b7a87 --- /dev/null +++ b/hr_attendance_resume_anomaly/_common.py @@ -0,0 +1,64 @@ +# Copyright 2019 Alfredo de la Fuente - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from datetime import date as datetype +from dateutil.relativedelta import relativedelta +from pytz import timezone, utc +from odoo import fields, _ + +str2datetime = fields.Datetime.from_string +date2str = fields.Date.to_string + + +def _convert_to_local_date(date, tz=u'UTC'): + if not date: + return False + if not tz: + tz = u'UTC' + new_date = str2datetime(date) if isinstance(date, str) else date + new_date = new_date.replace(tzinfo=utc) + local_date = new_date.astimezone(timezone(tz)).replace(tzinfo=None) + return local_date + + +def _convert_to_utc_date(date, time=0.0, tz=u'UTC'): + if not date: + return False + if not tz: + tz = u'UTC' + date = date2str(date) if isinstance(date, datetype) else date + date = str2datetime(date) if isinstance(date, str) else date + date += relativedelta(hours=float(time)) + local = timezone(tz) + local_date = local.localize(date, is_dst=None) + utc_date = local_date.astimezone(utc).replace(tzinfo=None) + return utc_date + + +def _convert_time_to_float(date, tz=u'UTC'): + if not date: + return False + if not tz: + tz = u'UTC' + local_time = _convert_to_local_date(date, tz=tz) + hour = float(local_time.hour) + minutes = float(local_time.minute) / 60 + seconds = float(local_time.second) / 360 + return (hour + minutes + seconds) + + +def _catch_dayofweek(date): + day = str(date.weekday()) + if day == '0': + return _('Monday') + if day == '1': + return _('Tuesday') + if day == '2': + return _('Wednesday') + if day == '3': + return _('Thursday') + if day == '4': + return _('Friday') + if day == '5': + return _('Saturday') + if day == '6': + return _('Sunday') diff --git a/hr_attendance_resume_anomaly/data/hr_attendance_resume_anomaly_data.xml b/hr_attendance_resume_anomaly/data/hr_attendance_resume_anomaly_data.xml new file mode 100644 index 00000000..f7a4a7b6 --- /dev/null +++ b/hr_attendance_resume_anomaly/data/hr_attendance_resume_anomaly_data.xml @@ -0,0 +1,30 @@ + + + + There are attendances in day that do not apply + + + Have absence + + + Festive worked + + + There is an approved absence and there is no attendance + + + Entry without exit + + + Attendances very often + + + There is no attendance in day that corresponds + + + Real working day greater or less than the percentage over the planned day + + + Negative imputable time + + diff --git a/hr_attendance_resume_anomaly/i18n/es.po b/hr_attendance_resume_anomaly/i18n/es.po new file mode 100644 index 00000000..1f08c38c --- /dev/null +++ b/hr_attendance_resume_anomaly/i18n/es.po @@ -0,0 +1,264 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_attendance_resume_anomaly +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-11-18 09:09+0000\n" +"PO-Revision-Date: 2019-11-18 09:09+0000\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_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume__attendance_anomaly_ids +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.hr_attendance_resume_view_form +msgid "Anomalies" +msgstr "Anomalías" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__name +msgid "Anomaly" +msgstr "Anomalía" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume__anomaly_resolved +msgid "Anomaly resolved" +msgstr "Anomaía resuelta" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_hr_attendance +msgid "Attendance" +msgstr "Asistencia" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_hr_attendance_resume_anomaly +msgid "Attendances resumes anomalies" +msgstr "Anomalías de resumen de asistencias" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_very_often +msgid "Attendances very often" +msgstr "Fichajes muy seguidos" + +#. module: hr_attendance_resume_anomaly +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.wiz_attendance_resume_put_resolved_view_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__create_uid +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume__created_from_anomaly +msgid "Created from anomaly" +msgstr "Creado desde anomalía" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__create_date +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__display_name +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_without_exit +msgid "Entry without exit" +msgstr "Entrada sin salida" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:60 +#, python-format +msgid "Friday" +msgstr "Viernes" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_with_leave +msgid "Have absence" +msgstr "Tiene ausencia" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__id +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved__id +msgid "ID" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly____last_update +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved____last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__write_uid +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved__write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__write_date +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved__write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_hr_leave +msgid "Leave" +msgstr "Ausencia" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:52 +#, python-format +msgid "Monday" +msgstr "Lunes" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_negative_time +msgid "Negative imputable time" +msgstr "Tiempo imputable negativo" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_resource_calendar__percentage_defect_hours +msgid "Percentage defect hours" +msgstr "Porcentaje horas por defecto" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_resource_calendar__percentage_excess_hours +msgid "Percentage excess hours" +msgstr "Porcentaje horas por exceso" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_percentage_day +msgid "Real working day greater or less than the percentage over the planned day" +msgstr "Jornada real mayor o menor del porcentaje sobre jornada prevista" + +#. module: hr_attendance_resume_anomaly +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.wiz_attendance_resume_put_resolved_view_form +msgid "Resolved" +msgstr "Resuelto" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_resource_calendar +msgid "Resource Working Time" +msgstr "Tiempo de Trabajo de Recursos" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:62 +#, python-format +msgid "Saturday" +msgstr "Sábado" + +#. module: hr_attendance_resume_anomaly +#: model:ir.actions.act_window,name:hr_attendance_resume_anomaly.action_wiz_attendance_resume_put_resolved +msgid "Set as resolved anomalies" +msgstr "Poner como resueltas las anomalías" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:64 +#, python-format +msgid "Sunday" +msgstr "Domingo" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_without_calendar_day +msgid "There are attendances in day that do not apply" +msgstr "Hay asistencias en dia que no corresponde" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_with_approved_absence +msgid "There is an approved absence and there is no attendance" +msgstr "Hay ausencia aprovada y no hay asistencia" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_no_attendance +msgid "There is no attendance in day that corresponds" +msgstr "No hay asistencia en el día que le corresponde" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:58 +#, python-format +msgid "Thursday" +msgstr "Jueves" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/wizard/wiz_hr_attendance_resume_absence.py:18 +#, python-format +msgid "To close the absences, all anomalies must be in a 'resolved' state." +msgstr "Para cerrar las ausencias, todas las anomálias deben de estar en estado 'Resuelta'" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:54 +#, python-format +msgid "Tuesday" +msgstr "Martes" + +#. module: hr_attendance_resume_anomaly +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.wiz_attendance_resume_put_resolved_view_form +msgid "Uncheck treaties" +msgstr "Desmarcar tratados" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:56 +#, python-format +msgid "Wednesday" +msgstr "Miércoles" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume__with_anomaly +msgid "With anomaly" +msgstr "Con anomalía" + +#. module: hr_attendance_resume_anomaly +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.hr_attendance_resume_view_search +msgid "With resolved anomaly" +msgstr "Con anomalía resuelta" + +#. module: hr_attendance_resume_anomaly +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.hr_attendance_resume_view_search +msgid "Without resolved anomaly" +msgstr "Con anomalía sin resolver" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_wiz_attendance_resume_put_resolved +msgid "Wizard for put anomalies as resolved" +msgstr "Asistente para poner como resueltas las anomalías" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_wiz_hr_attendance_resume_absence +msgid "Wizard to impute absences from attendance resume" +msgstr "Asistente para imputar ausencias desde resumen de imputaciones" + +#. module: hr_attendance_resume_anomaly +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.wiz_attendance_resume_put_resolved_view_form +msgid "You are going to Set as resolved anomalies" +msgstr "Usted va a poner como resueltas las anomalías" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/models/hr_leave.py:33 +#, python-format +msgid "You cannot approve an attendance from resume with different days." +msgstr "No puede aprobar una ausencia desde resumen con días diferentes" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_hr_attendance_resume +msgid "hr.attendance.resume" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_festive_worked +msgid "Festive worked" +msgstr "Festivo trabajado" + diff --git a/hr_attendance_resume_anomaly/i18n/hr_attendance_resume_anomaly.pot b/hr_attendance_resume_anomaly/i18n/hr_attendance_resume_anomaly.pot new file mode 100644 index 00000000..781f1a07 --- /dev/null +++ b/hr_attendance_resume_anomaly/i18n/hr_attendance_resume_anomaly.pot @@ -0,0 +1,264 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_attendance_resume_anomaly +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-11-18 09:08+0000\n" +"PO-Revision-Date: 2019-11-18 09:08+0000\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_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume__attendance_anomaly_ids +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.hr_attendance_resume_view_form +msgid "Anomalies" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__name +msgid "Anomaly" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume__anomaly_resolved +msgid "Anomaly resolved" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_hr_attendance +msgid "Attendance" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_hr_attendance_resume_anomaly +msgid "Attendances resumes anomalies" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_very_often +msgid "Attendances very often" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.wiz_attendance_resume_put_resolved_view_form +msgid "Cancel" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__create_uid +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved__create_uid +msgid "Created by" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume__created_from_anomaly +msgid "Created from anomaly" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__create_date +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved__create_date +msgid "Created on" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__display_name +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved__display_name +msgid "Display Name" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_without_exit +msgid "Entry without exit" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:60 +#, python-format +msgid "Friday" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_with_leave +msgid "Have absence" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__id +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved__id +msgid "ID" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly____last_update +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved____last_update +msgid "Last Modified on" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__write_uid +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume_anomaly__write_date +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_wiz_attendance_resume_put_resolved__write_date +msgid "Last Updated on" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_hr_leave +msgid "Leave" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:52 +#, python-format +msgid "Monday" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_negative_time +msgid "Negative imputable time" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_resource_calendar__percentage_defect_hours +msgid "Percentage defect hours" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_resource_calendar__percentage_excess_hours +msgid "Percentage excess hours" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_percentage_day +msgid "Real working day greater or less than the percentage over the planned day" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.wiz_attendance_resume_put_resolved_view_form +msgid "Resolved" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_resource_calendar +msgid "Resource Working Time" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:62 +#, python-format +msgid "Saturday" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.actions.act_window,name:hr_attendance_resume_anomaly.action_wiz_attendance_resume_put_resolved +msgid "Set as resolved anomalies" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:64 +#, python-format +msgid "Sunday" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_without_calendar_day +msgid "There are attendances in day that do not apply" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_with_approved_absence +msgid "There is an approved absence and there is no attendance" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_no_attendance +msgid "There is no attendance in day that corresponds" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:58 +#, python-format +msgid "Thursday" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/wizard/wiz_hr_attendance_resume_absence.py:18 +#, python-format +msgid "To close the absences, all anomalies must be in a 'resolved' state." +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:54 +#, python-format +msgid "Tuesday" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.wiz_attendance_resume_put_resolved_view_form +msgid "Uncheck treaties" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/_common.py:56 +#, python-format +msgid "Wednesday" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model.fields,field_description:hr_attendance_resume_anomaly.field_hr_attendance_resume__with_anomaly +msgid "With anomaly" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.hr_attendance_resume_view_search +msgid "With resolved anomaly" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.hr_attendance_resume_view_search +msgid "Without resolved anomaly" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_wiz_attendance_resume_put_resolved +msgid "Wizard for put anomalies as resolved" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_wiz_hr_attendance_resume_absence +msgid "Wizard to impute absences from attendance resume" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model_terms:ir.ui.view,arch_db:hr_attendance_resume_anomaly.wiz_attendance_resume_put_resolved_view_form +msgid "You are going to Set as resolved anomalies" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: code:addons/hr_attendance_resume_anomaly/models/hr_leave.py:33 +#, python-format +msgid "You cannot approve an attendance from resume with different days." +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:ir.model,name:hr_attendance_resume_anomaly.model_hr_attendance_resume +msgid "hr.attendance.resume" +msgstr "" + +#. module: hr_attendance_resume_anomaly +#: model:hr.attendance.resume.anomaly,name:hr_attendance_resume_anomaly.hr_attendance_anomaly_festive_worked +msgid "Festive worked" +msgstr "" + diff --git a/hr_attendance_resume_anomaly/models/__init__.py b/hr_attendance_resume_anomaly/models/__init__.py new file mode 100644 index 00000000..8659e460 --- /dev/null +++ b/hr_attendance_resume_anomaly/models/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2019 Alfredo de la Fuente - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from . import hr_attendance +from . import resource_calendar +from . import hr_leave \ No newline at end of file diff --git a/hr_attendance_resume_anomaly/models/hr_attendance.py b/hr_attendance_resume_anomaly/models/hr_attendance.py new file mode 100644 index 00000000..c50034ef --- /dev/null +++ b/hr_attendance_resume_anomaly/models/hr_attendance.py @@ -0,0 +1,192 @@ +# Copyright 2019 Alfredo de la fuente - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from odoo import models, fields, api +from .._common import _catch_dayofweek +from dateutil.relativedelta import relativedelta + + +class HrAttendanceResumeAnomaly(models.Model): + _name = 'hr.attendance.resume.anomaly' + _description = 'Attendances resumes anomalies' + + name = fields.Char(string='Anomaly', required=True, translate=True) + + +class HrAttendance(models.Model): + _inherit = 'hr.attendance' + + def _impute_employee_attendances(self): + result = super(HrAttendance, self)._impute_employee_attendances() + without_calendar = self.filtered(lambda c: not c.imputation_date) + if without_calendar: + dates = set(without_calendar.mapped('check_in_without_hour')) + for date in dates: + attendances = without_calendar.filtered( + lambda c: c.check_in_without_hour == date) + attendances._impute_employee_attendances_without_day(date) + self.treat_without_signings_in_the_day() + return result + + def treat_without_signings_in_the_day(self): + festive_obj = self.env['hr.holidays.public.line'] + anomaly = self.env.ref( + 'hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_no_' + 'attendance') + resume_obj = self.env['hr.attendance.resume'] + min_check_in = min(self, key=lambda x: x.check_in) + min_date = min_check_in.check_in_without_hour + if (min_check_in.imputation_date and + min_check_in.imputation_date < min_date): + min_date = min_check_in.imputation_date + max_check_in = max(self, key=lambda x: x.check_in) + max_date = max_check_in.check_in_without_hour + if (max_check_in.imputation_date and + max_check_in.imputation_date < max_date): + max_date = max_check_in.imputation_date + while min_date <= max_date: + country = min_check_in[0].employee_id.company_id.country_id + cond = [('date', '=', min_date), + ('year_id.country_id', '=', country.id), + ('year_id.year', '=', min_date.year)] + festive = festive_obj.search(cond, limit=1) + if not festive: + cond = [('employee_id', '=', min_check_in[0].employee_id.id), + ('date', '=', min_date)] + resume = resume_obj.search(cond, limit=1) + if not resume: + emp = min_check_in[0].employee_id + calendar = emp._get_employee_calendar(min_date) + work_time, hour_from, hour_to = emp._get_day_info( + calendar, min_date) + if work_time != 0.0: + vals = { + 'employee_id': min_check_in[0].employee_id.id, + 'date': min_date, + 'theoretical_time': work_time, + 'created_from_anomaly': True, + 'with_anomaly': True, + 'day_week_literal': _catch_dayofweek(min_date), + 'attendance_anomaly_ids': [(6, 0, anomaly.ids)]} + resume = resume_obj.create(vals) + min_date += relativedelta(days=1) + + def _impute_employee_attendances_without_day(self, date): + resume_obj = self.env['hr.attendance.resume'] + anomalies = self.env['hr.attendance.resume.anomaly'] + anomaly = self.env.ref( + 'hr_attendance_resume_anomaly.hr_attendance_anomaly_without_' + 'calendar_day') + anomaly_without_exit = self.env.ref( + 'hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_without_' + 'exit') + anomaly_very_often = self.env.ref( + 'hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_very_' + 'often') + anomalies += anomaly + if any(p.entry_without_exit for p in self): + anomalies += anomaly_without_exit + if any(p.worked_hours < 0.0166666666666667 for p in self): + anomalies += anomaly_very_often + resume = resume_obj.create(self._catch_values_for_create_resume()) + if resume.imputable_time >= 6.0: + resume._put_personalized_planned_rest() + self.update({'treated': True, + 'attendance_resume_id': resume.id}) + resume.attendance_anomaly_ids = [(6, 0, anomalies.ids)] + + def _impute_employee_attendances_day(self): + festive_obj = self.env['hr.holidays.public.line'] + anomalies = self.env['hr.attendance.resume.anomaly'] + anomaly_with_leave = self.env.ref( + 'hr_attendance_resume_anomaly.hr_attendance_anomaly_with_leave') + anomaly_festive_worked = self.env.ref( + 'hr_attendance_resume_anomaly.hr_attendance_anomaly_festive_' + 'worked') + anomaly_without_exit = self.env.ref( + 'hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_without_' + 'exit') + anomaly_very_often = self.env.ref( + 'hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_very_' + 'often') + anomaly_percentage_day = self.env.ref( + 'hr_attendance_resume_anomaly.hr_attendance_anomaly_entry_' + 'percentage_day') + anomaly_negative_time = self.env.ref( + 'hr_attendance_resume_anomaly.hr_attendance_anomaly_negative_time') + resume = super(HrAttendance, self)._impute_employee_attendances_day() + if resume.leave_id: + anomalies += anomaly_with_leave + country = resume.employee_id.company_id.country_id + cond = [('date', '=', resume.date), + ('year_id.country_id', '=', country.id), + ('year_id.year', '=', resume.date.year)] + festive = festive_obj.search(cond, limit=1) + if festive: + anomalies += anomaly_festive_worked + if any(p.entry_without_exit for p in self): + anomalies += anomaly_without_exit + if any(p.worked_hours < 0.0166666666666667 for p in self): + anomalies += anomaly_very_often + if not resume.leave_id and resume.imputable_time: + calendar = resume.employee_id._get_employee_calendar(resume.date) + employee = resume.employee_id + work_time, hour_from, hour_to = employee._get_day_work_time( + calendar, resume.date) + error = False + if calendar.percentage_excess_hours: + hours = (work_time * calendar.percentage_excess_hours) / 100 + imputable_time = work_time + hours + if resume.imputable_time >= imputable_time: + error = True + if calendar.percentage_defect_hours: + hours = (work_time * calendar.percentage_defect_hours) / 100 + imputable_time = work_time - hours + if resume.imputable_time <= imputable_time: + error = True + if error: + anomalies += anomaly_percentage_day + if resume.imputable_time < 0: + anomalies += anomaly_negative_time + if anomalies: + resume.write({'with_anomaly': True, + 'attendance_anomaly_ids': [(6, 0, anomalies.ids)]}) + return resume + + def _create_resume_from_no_imputed_holidays(self, leave, employee, date): + anomaly = self.env.ref( + 'hr_attendance_resume_anomaly.hr_attendance_anomaly_with_approved_' + 'absence') + resume = super( + HrAttendance, self)._create_resume_from_no_imputed_holidays( + leave, employee, date) + if resume: + vals = {'with_anomaly': True, + 'attendance_anomaly_ids': [(6, 0, anomaly.ids)]} + resume.write(vals) + if resume.theoretical_time_final == 0.0: + resume.write({'anomaly_resolved': True}) + return resume + + +class HrAttendanceResume(models.Model): + _inherit = 'hr.attendance.resume' + + attendance_anomaly_ids = fields.Many2many( + string='Anomalies', comodel_name='hr.attendance.resume.anomaly', + relation='rel_attendance_resume_anomaly', + column1='attendance_resume_id', store=True, + column2='attendance_anomaly_id') + created_from_anomaly = fields.Boolean( + string='Created from anomaly', default=False) + with_anomaly = fields.Boolean( + string='With anomaly', default=False) + anomaly_resolved = fields.Boolean( + string='Anomaly resolved', default=False) + + @api.depends('theoretical_time', 'leave_id', 'attendance_anomaly_ids') + def _compute_theoretical_time_final(self): + return super( + HrAttendanceResume, self)._compute_theoretical_time_final() + + def _put_personalized_planned_rest(self): + self.planned_rest = 0.333333333333333 diff --git a/hr_attendance_resume_anomaly/models/hr_leave.py b/hr_attendance_resume_anomaly/models/hr_leave.py new file mode 100644 index 00000000..c5a37622 --- /dev/null +++ b/hr_attendance_resume_anomaly/models/hr_leave.py @@ -0,0 +1,53 @@ +# Copyright 2019 Alfredo de la fuente - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from odoo import models, api, exceptions, _ + + +class HrLeave(models.Model): + _inherit = 'hr.leave' + + @api.multi + def action_approve(self): + anomaly_with_leave = self.env.ref( + 'hr_attendance_resume_anomaly.hr_attendance_anomaly_with_leave') + result = super(HrLeave, self).action_approve() + if 'from_resume' not in self.env.context: + for leave in self.filtered( + lambda c: c.leave_type_request_unit != 'hour'): + cond = [('employee_id', '=', leave.employee_id.id), + ('imputation_date', '>=', leave.request_date_from), + ('imputation_date', '<=', leave.request_date_to)] + attendances = self.env['hr.attendance'].search(cond) + if attendances: + attendances.write({'treated': False}) + attendances._impute_in_attendance_resume() + else: + cond = [('employee_id', '=', + self.env.context.get('default_employee_id')), + ('date', '=', self.env.context.get('request_date'))] + resume = self.env['hr.attendance.resume'].search(cond, limit=1) + for leave in self.filtered( + lambda c: c.leave_type_request_unit != 'hour'): + if leave.request_date_from != leave.request_date_to: + raise exceptions.Warning( + _('You cannot approve an attendance from resume with ' + 'different days.')) + anomalies = resume.attendance_anomaly_ids + if anomaly_with_leave not in anomalies: + anomalies += anomaly_with_leave + resume.attendance_anomaly_ids = [(6, 0, anomalies.ids)] + return result + + @api.multi + def action_refuse(self): + result = super(HrLeave, self).action_refuse() + for leave in self.filtered( + lambda c: c.leave_type_request_unit != 'hour'): + cond = [('employee_id', '=', leave.employee_id.id), + ('imputation_date', '>=', leave.request_date_from), + ('imputation_date', '<=', leave.request_date_to)] + attendances = self.env['hr.attendance'].search(cond) + if attendances: + attendances.write({'treated': False}) + attendances._impute_in_attendance_resume() + return result diff --git a/hr_attendance_resume_anomaly/models/resource_calendar.py b/hr_attendance_resume_anomaly/models/resource_calendar.py new file mode 100644 index 00000000..5569c697 --- /dev/null +++ b/hr_attendance_resume_anomaly/models/resource_calendar.py @@ -0,0 +1,12 @@ +# Copyright 2019 Alfredo de la fuente - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from odoo import models, fields + + +class ResourceCalendar(models.Model): + _inherit = 'resource.calendar' + + percentage_excess_hours = fields.Float( + string='Percentage excess hours') + percentage_defect_hours = fields.Float( + string='Percentage defect hours') diff --git a/hr_attendance_resume_anomaly/security/ir.model.access.csv b/hr_attendance_resume_anomaly/security/ir.model.access.csv new file mode 100644 index 00000000..923ef220 --- /dev/null +++ b/hr_attendance_resume_anomaly/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 +access_hr_attendance_resume_anomaly_manager,hr.attendance.resume.anomaly,model_hr_attendance_resume_anomaly,hr_attendance.group_hr_attendance_manager,1,,, +access_hr_attendance_resume_anomaly_user,hr.attendance.resume.anomaly,model_hr_attendance_resume_anomaly,hr_attendance.group_hr_attendance_user,1,,, diff --git a/hr_attendance_resume_anomaly/views/hr_attendance_resume_view.xml b/hr_attendance_resume_anomaly/views/hr_attendance_resume_view.xml new file mode 100644 index 00000000..1a727d01 --- /dev/null +++ b/hr_attendance_resume_anomaly/views/hr_attendance_resume_view.xml @@ -0,0 +1,47 @@ + + + + hr.attendance.resume + + + + + + + + + + hr.attendance.resume + + + + + + + + + + + + hr.attendance.resume + + + + + + + + + + + + + + + + + + diff --git a/hr_attendance_resume_anomaly/views/resource_calendar_view.xml b/hr_attendance_resume_anomaly/views/resource_calendar_view.xml new file mode 100644 index 00000000..0de48ef6 --- /dev/null +++ b/hr_attendance_resume_anomaly/views/resource_calendar_view.xml @@ -0,0 +1,23 @@ + + + + resource.calendar + + + + + + + + + + resource.calendar + + + + + + + + + diff --git a/hr_attendance_resume_anomaly/wizard/__init__.py b/hr_attendance_resume_anomaly/wizard/__init__.py new file mode 100644 index 00000000..ec101f78 --- /dev/null +++ b/hr_attendance_resume_anomaly/wizard/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2019 Alfredo de la fuente - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from . import wiz_attendance_resume_put_resolved +from . import wiz_hr_attendance_resume_absence diff --git a/hr_attendance_resume_anomaly/wizard/wiz_attendance_resume_put_resolved.py b/hr_attendance_resume_anomaly/wizard/wiz_attendance_resume_put_resolved.py new file mode 100644 index 00000000..1fd25e90 --- /dev/null +++ b/hr_attendance_resume_anomaly/wizard/wiz_attendance_resume_put_resolved.py @@ -0,0 +1,17 @@ +# Copyright 2019 Alfredo de la fuente - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from odoo import models, api + + +class WizAttendanceResumePutResolved(models.TransientModel): + _name = "wiz.attendance.resume.put.resolved" + _description = "Wizard for put anomalies as resolved" + + @api.multi + def button_resolved(self): + context = dict(self._context or {}) + active_ids = context.get('active_ids', []) or [] + resumes = self.env['hr.attendance.resume'].browse(active_ids) + if resumes: + resumes.write({'anomaly_resolved': True}) + return {'type': 'ir.actions.act_window_close'} diff --git a/hr_attendance_resume_anomaly/wizard/wiz_attendance_resume_put_resolved_view.xml b/hr_attendance_resume_anomaly/wizard/wiz_attendance_resume_put_resolved_view.xml new file mode 100644 index 00000000..9e95ef13 --- /dev/null +++ b/hr_attendance_resume_anomaly/wizard/wiz_attendance_resume_put_resolved_view.xml @@ -0,0 +1,23 @@ + + + + wiz.attendance.resume.put.resolved + +
+

+ You are going to Set as resolved anomalies +

+
+
+
+
+
+ +
+
diff --git a/hr_attendance_resume_anomaly/wizard/wiz_hr_attendance_resume_absence.py b/hr_attendance_resume_anomaly/wizard/wiz_hr_attendance_resume_absence.py new file mode 100644 index 00000000..9d44d0c3 --- /dev/null +++ b/hr_attendance_resume_anomaly/wizard/wiz_hr_attendance_resume_absence.py @@ -0,0 +1,21 @@ +# Copyright 2019 Alfredo de la fuente - AvanzOSC +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from odoo import models, api, exceptions, _ + + +class WizHrAttendanceResumeAbsence(models.TransientModel): + _inherit = "wiz.hr.attendance.resume.absence" + + @api.multi + def button_generate(self): + context = dict(self._context or {}) + active_ids = context.get('active_ids', []) or [] + resumes = self.env['hr.attendance.resume'].browse(active_ids) + if resumes: + resumes = resumes.filtered( + lambda c: not c.anomaly_resolved and c.attendance_anomaly_ids) + if resumes: + error = _(u"To close the absences, all anomalies must be in " + "a 'resolved' state.") + raise exceptions.Warning(error) + return super(WizHrAttendanceResumeAbsence, self).button_generate()