wbhuman_resources 1.58.4__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wbhuman_resources/__init__.py +1 -0
- wbhuman_resources/admin/__init__.py +5 -0
- wbhuman_resources/admin/absence.py +113 -0
- wbhuman_resources/admin/calendars.py +37 -0
- wbhuman_resources/admin/employee.py +109 -0
- wbhuman_resources/admin/kpi.py +21 -0
- wbhuman_resources/admin/review.py +157 -0
- wbhuman_resources/apps.py +23 -0
- wbhuman_resources/dynamic_preferences_registry.py +119 -0
- wbhuman_resources/factories/__init__.py +38 -0
- wbhuman_resources/factories/absence.py +109 -0
- wbhuman_resources/factories/calendars.py +60 -0
- wbhuman_resources/factories/employee.py +80 -0
- wbhuman_resources/factories/kpi.py +155 -0
- wbhuman_resources/filters/__init__.py +20 -0
- wbhuman_resources/filters/absence.py +109 -0
- wbhuman_resources/filters/absence_graphs.py +85 -0
- wbhuman_resources/filters/calendars.py +28 -0
- wbhuman_resources/filters/employee.py +81 -0
- wbhuman_resources/filters/kpi.py +35 -0
- wbhuman_resources/filters/review.py +134 -0
- wbhuman_resources/filters/signals.py +27 -0
- wbhuman_resources/locale/de/LC_MESSAGES/django.mo +0 -0
- wbhuman_resources/locale/de/LC_MESSAGES/django.po +2207 -0
- wbhuman_resources/locale/de/LC_MESSAGES/django.po.translated +2456 -0
- wbhuman_resources/locale/en/LC_MESSAGES/django.mo +0 -0
- wbhuman_resources/locale/en/LC_MESSAGES/django.po +2091 -0
- wbhuman_resources/locale/fr/LC_MESSAGES/django.mo +0 -0
- wbhuman_resources/locale/fr/LC_MESSAGES/django.po +2093 -0
- wbhuman_resources/management/__init__.py +23 -0
- wbhuman_resources/migrations/0001_initial_squashed_squashed_0015_alter_absencerequest_calendaritem_ptr_and_more.py +949 -0
- wbhuman_resources/migrations/0016_alter_employeehumanresource_options.py +20 -0
- wbhuman_resources/migrations/0017_absencerequest_crossborder_country_and_more.py +55 -0
- wbhuman_resources/migrations/0018_remove_position_group_position_groups.py +32 -0
- wbhuman_resources/migrations/0019_alter_absencerequest_options_alter_kpi_options_and_more.py +44 -0
- wbhuman_resources/migrations/0020_alter_employeeyearbalance_year_alter_review_year.py +27 -0
- wbhuman_resources/migrations/0021_alter_position_color.py +18 -0
- wbhuman_resources/migrations/0022_remove_review_editable_mode.py +64 -0
- wbhuman_resources/migrations/__init__.py +0 -0
- wbhuman_resources/models/__init__.py +23 -0
- wbhuman_resources/models/absence.py +903 -0
- wbhuman_resources/models/calendars.py +370 -0
- wbhuman_resources/models/employee.py +1241 -0
- wbhuman_resources/models/kpi.py +199 -0
- wbhuman_resources/models/preferences.py +40 -0
- wbhuman_resources/models/review.py +982 -0
- wbhuman_resources/permissions/__init__.py +0 -0
- wbhuman_resources/permissions/backend.py +26 -0
- wbhuman_resources/serializers/__init__.py +49 -0
- wbhuman_resources/serializers/absence.py +308 -0
- wbhuman_resources/serializers/calendars.py +73 -0
- wbhuman_resources/serializers/employee.py +267 -0
- wbhuman_resources/serializers/kpi.py +80 -0
- wbhuman_resources/serializers/review.py +415 -0
- wbhuman_resources/signals.py +4 -0
- wbhuman_resources/tasks.py +195 -0
- wbhuman_resources/templates/review/review_report.html +322 -0
- wbhuman_resources/tests/__init__.py +1 -0
- wbhuman_resources/tests/conftest.py +96 -0
- wbhuman_resources/tests/models/__init__.py +0 -0
- wbhuman_resources/tests/models/test_absences.py +478 -0
- wbhuman_resources/tests/models/test_calendars.py +209 -0
- wbhuman_resources/tests/models/test_employees.py +502 -0
- wbhuman_resources/tests/models/test_review.py +103 -0
- wbhuman_resources/tests/models/test_utils.py +110 -0
- wbhuman_resources/tests/signals.py +108 -0
- wbhuman_resources/tests/test_permission.py +64 -0
- wbhuman_resources/tests/test_tasks.py +74 -0
- wbhuman_resources/urls.py +221 -0
- wbhuman_resources/utils.py +43 -0
- wbhuman_resources/viewsets/__init__.py +61 -0
- wbhuman_resources/viewsets/absence.py +312 -0
- wbhuman_resources/viewsets/absence_charts.py +328 -0
- wbhuman_resources/viewsets/buttons/__init__.py +7 -0
- wbhuman_resources/viewsets/buttons/absence.py +32 -0
- wbhuman_resources/viewsets/buttons/employee.py +44 -0
- wbhuman_resources/viewsets/buttons/kpis.py +16 -0
- wbhuman_resources/viewsets/buttons/review.py +195 -0
- wbhuman_resources/viewsets/calendars.py +103 -0
- wbhuman_resources/viewsets/display/__init__.py +39 -0
- wbhuman_resources/viewsets/display/absence.py +334 -0
- wbhuman_resources/viewsets/display/calendars.py +83 -0
- wbhuman_resources/viewsets/display/employee.py +254 -0
- wbhuman_resources/viewsets/display/kpis.py +92 -0
- wbhuman_resources/viewsets/display/review.py +429 -0
- wbhuman_resources/viewsets/employee.py +210 -0
- wbhuman_resources/viewsets/endpoints/__init__.py +42 -0
- wbhuman_resources/viewsets/endpoints/absence.py +57 -0
- wbhuman_resources/viewsets/endpoints/calendars.py +18 -0
- wbhuman_resources/viewsets/endpoints/employee.py +51 -0
- wbhuman_resources/viewsets/endpoints/kpis.py +53 -0
- wbhuman_resources/viewsets/endpoints/review.py +191 -0
- wbhuman_resources/viewsets/kpi.py +280 -0
- wbhuman_resources/viewsets/menu/__init__.py +22 -0
- wbhuman_resources/viewsets/menu/absence.py +50 -0
- wbhuman_resources/viewsets/menu/administration.py +15 -0
- wbhuman_resources/viewsets/menu/calendars.py +33 -0
- wbhuman_resources/viewsets/menu/employee.py +44 -0
- wbhuman_resources/viewsets/menu/kpis.py +18 -0
- wbhuman_resources/viewsets/menu/review.py +97 -0
- wbhuman_resources/viewsets/mixins.py +14 -0
- wbhuman_resources/viewsets/review.py +837 -0
- wbhuman_resources/viewsets/titles/__init__.py +18 -0
- wbhuman_resources/viewsets/titles/absence.py +30 -0
- wbhuman_resources/viewsets/titles/employee.py +18 -0
- wbhuman_resources/viewsets/titles/kpis.py +15 -0
- wbhuman_resources/viewsets/titles/review.py +62 -0
- wbhuman_resources/viewsets/utils.py +28 -0
- wbhuman_resources-1.58.4.dist-info/METADATA +8 -0
- wbhuman_resources-1.58.4.dist-info/RECORD +111 -0
- wbhuman_resources-1.58.4.dist-info/WHEEL +5 -0
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from django.contrib.auth import get_user_model
|
|
2
|
+
from django.db.models import Q, QuerySet
|
|
3
|
+
from wbcore.permissions.backend import UserBackend as BaseUserBackend
|
|
4
|
+
|
|
5
|
+
from wbhuman_resources.models.employee import EmployeeHumanResource
|
|
6
|
+
|
|
7
|
+
User = get_user_model()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class UserBackend(BaseUserBackend):
|
|
11
|
+
"""
|
|
12
|
+
The UserBackend proposed by the human resources module
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def get_internal_users(self) -> "QuerySet[User]":
|
|
16
|
+
"""
|
|
17
|
+
Get internal users defined by the human resources module
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
A queryset of users
|
|
21
|
+
"""
|
|
22
|
+
internal_employee_profiles = EmployeeHumanResource.active_internal_employees.all()
|
|
23
|
+
base_internal_users = super().get_internal_users()
|
|
24
|
+
return User.objects.filter(
|
|
25
|
+
Q(profile__in=internal_employee_profiles.values("profile")) | Q(id__in=base_internal_users.values("id"))
|
|
26
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from .absence import (
|
|
2
|
+
AbsenceRequestCrossBorderCountryModelSerializer,
|
|
3
|
+
AbsenceRequestModelSerializer,
|
|
4
|
+
AbsenceRequestPeriodsModelSerializer,
|
|
5
|
+
AbsenceRequestTypeModelSerializer,
|
|
6
|
+
AbsenceRequestTypeRepresentationSerializer,
|
|
7
|
+
EmployeeAbsenceDaysModelSerializer,
|
|
8
|
+
IncreaseDaySerializer,
|
|
9
|
+
)
|
|
10
|
+
from .calendars import (
|
|
11
|
+
DayOffCalendarModelSerializer,
|
|
12
|
+
DayOffCalendarRepresentationSerializer,
|
|
13
|
+
DayOffModelSerializer,
|
|
14
|
+
DayOffRepresentationSerializer,
|
|
15
|
+
DefaultDailyPeriodModelSerializer,
|
|
16
|
+
DefaultDailyPeriodRepresentationSerializer,
|
|
17
|
+
EmployeeWeeklyOffPeriodsRepresentationSerializer,
|
|
18
|
+
)
|
|
19
|
+
from .employee import (
|
|
20
|
+
DeactivateEmployeeSerializer,
|
|
21
|
+
EmployeeBalanceModelSerializer,
|
|
22
|
+
EmployeeHumanResourceRepresentationSerializer,
|
|
23
|
+
EmployeeModelSerializer,
|
|
24
|
+
EmployeeWeeklyOffPeriodsModelSerializer,
|
|
25
|
+
EmployeeYearBalanceModelSerializer,
|
|
26
|
+
EmployeeYearBalanceRepresentationSerializer,
|
|
27
|
+
PositionModelSerializer,
|
|
28
|
+
PositionRepresentationSerializer,
|
|
29
|
+
)
|
|
30
|
+
from .kpi import (
|
|
31
|
+
EvaluationModelSerializer,
|
|
32
|
+
EvaluationRepresentationSerializer,
|
|
33
|
+
KPIModelSerializer,
|
|
34
|
+
KPIRepresentationSerializer,
|
|
35
|
+
)
|
|
36
|
+
from .review import (
|
|
37
|
+
ReviewAnswerModelSerializer,
|
|
38
|
+
ReviewAnswerRepresentationSerializer,
|
|
39
|
+
ReviewGroupModelSerializer,
|
|
40
|
+
ReviewGroupRepresentationSerializer,
|
|
41
|
+
ReviewListModelSerializer,
|
|
42
|
+
ReviewModelSerializer,
|
|
43
|
+
ReviewQuestionCategoryModelSerializer,
|
|
44
|
+
ReviewQuestionCategoryRepresentationSerializer,
|
|
45
|
+
ReviewQuestionModelSerializer,
|
|
46
|
+
ReviewQuestionRepresentationSerializer,
|
|
47
|
+
ReviewReadOnlyModelSerializer,
|
|
48
|
+
ReviewRepresentationSerializer,
|
|
49
|
+
)
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from datetime import date
|
|
3
|
+
|
|
4
|
+
from django.contrib.messages import info
|
|
5
|
+
from django.db import models
|
|
6
|
+
from django.utils import timezone
|
|
7
|
+
from django.utils.translation import gettext
|
|
8
|
+
from django.utils.translation import gettext_lazy as _
|
|
9
|
+
from rest_framework import serializers
|
|
10
|
+
from rest_framework.reverse import reverse
|
|
11
|
+
from wbcore import serializers as wb_serializers
|
|
12
|
+
from wbcore.contrib.authentication.serializers import GroupRepresentationSerializer
|
|
13
|
+
from wbcore.contrib.geography.serializers import CountryRepresentationSerializer
|
|
14
|
+
|
|
15
|
+
from wbhuman_resources.models import (
|
|
16
|
+
AbsenceRequest,
|
|
17
|
+
AbsenceRequestPeriods,
|
|
18
|
+
AbsenceRequestType,
|
|
19
|
+
EmployeeHumanResource,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from .calendars import DefaultDailyPeriodRepresentationSerializer
|
|
23
|
+
from .employee import (
|
|
24
|
+
EmployeeHumanResourceRepresentationSerializer,
|
|
25
|
+
EmployeeYearBalanceRepresentationSerializer,
|
|
26
|
+
PositionRepresentationSerializer,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AbsenceRequestTypeModelSerializer(wb_serializers.ModelSerializer):
|
|
31
|
+
_extra_notify_groups = GroupRepresentationSerializer(source="extra_notify_groups", many=True)
|
|
32
|
+
|
|
33
|
+
@wb_serializers.register_only_instance_resource()
|
|
34
|
+
def crossborder_countries_additional_resources(self, instance, request, user, **kwargs):
|
|
35
|
+
return {
|
|
36
|
+
"crossbordercountries": reverse(
|
|
37
|
+
"wbhuman_resources:absencerequesttype-crossbordercountry-list",
|
|
38
|
+
args=[instance.id],
|
|
39
|
+
request=request,
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
class Meta:
|
|
44
|
+
model = AbsenceRequestType
|
|
45
|
+
fields = [
|
|
46
|
+
"id",
|
|
47
|
+
"title",
|
|
48
|
+
"is_vacation",
|
|
49
|
+
"is_timeoff",
|
|
50
|
+
"is_extensible",
|
|
51
|
+
"auto_approve",
|
|
52
|
+
"days_in_advance",
|
|
53
|
+
"is_country_necessary",
|
|
54
|
+
"extra_notify_groups",
|
|
55
|
+
"_extra_notify_groups",
|
|
56
|
+
"icon",
|
|
57
|
+
"color",
|
|
58
|
+
"_additional_resources",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class AbsenceRequestTypeRepresentationSerializer(wb_serializers.RepresentationSerializer):
|
|
63
|
+
endpoint = "wbhuman_resources:absencerequesttyperepresentation-list"
|
|
64
|
+
_detail = wb_serializers.HyperlinkField(reverse_name="wbhuman_resources:absencerequesttype-detail")
|
|
65
|
+
|
|
66
|
+
class Meta:
|
|
67
|
+
model = AbsenceRequestType
|
|
68
|
+
fields = [
|
|
69
|
+
"id",
|
|
70
|
+
"title",
|
|
71
|
+
"_detail",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class AbsenceRequestCrossBorderCountryModelSerializer(wb_serializers.ModelSerializer):
|
|
76
|
+
geography_repr = wb_serializers.CharField(read_only=True)
|
|
77
|
+
_geography = CountryRepresentationSerializer(source="geography")
|
|
78
|
+
|
|
79
|
+
class Meta:
|
|
80
|
+
model = AbsenceRequestType.crossborder_countries.through
|
|
81
|
+
fields = ("id", "geography_repr", "_geography", "geography", "absencerequesttype")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class CurrentUserDefaultPeriodDateTimeRange:
|
|
86
|
+
requires_context = True
|
|
87
|
+
user_attr = "profile.human_resources"
|
|
88
|
+
|
|
89
|
+
def __call__(self, serializer_instance):
|
|
90
|
+
employee = wb_serializers.CurrentUserDefault("profile.human_resources")(serializer_instance)
|
|
91
|
+
if employee:
|
|
92
|
+
return employee.calendar.get_default_fullday_period(date.today())
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def get_lower_time_choices(field, request):
|
|
96
|
+
if (profile := request.user.profile) and (employee := getattr(profile, "human_resources", None)):
|
|
97
|
+
return employee.calendar.get_period_start_choices()
|
|
98
|
+
return []
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def get_upper_time_choices(field, request):
|
|
102
|
+
if (profile := request.user.profile) and (employee := getattr(profile, "human_resources", None)):
|
|
103
|
+
return employee.calendar.get_period_end_choices()
|
|
104
|
+
return []
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class AbsenceRequestModelSerializer(wb_serializers.ModelSerializer):
|
|
108
|
+
type_icon = wb_serializers.IconSelectField(read_only=True)
|
|
109
|
+
_type = AbsenceRequestTypeRepresentationSerializer(source="type")
|
|
110
|
+
employee = wb_serializers.PrimaryKeyRelatedField(
|
|
111
|
+
default=wb_serializers.CurrentUserDefault(user_attr="profile.human_resources"),
|
|
112
|
+
queryset=EmployeeHumanResource.active_internal_employees.all(),
|
|
113
|
+
read_only=lambda view: not view.request.user.has_perm("wbhuman_resources.administrate_absencerequest"),
|
|
114
|
+
)
|
|
115
|
+
_employee = EmployeeHumanResourceRepresentationSerializer(source="employee")
|
|
116
|
+
period = wb_serializers.DateTimeRangeField(
|
|
117
|
+
default=CurrentUserDefaultPeriodDateTimeRange(),
|
|
118
|
+
lower_time_choices=get_lower_time_choices,
|
|
119
|
+
upper_time_choices=get_upper_time_choices,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
created = wb_serializers.DateTimeField(read_only=True)
|
|
123
|
+
reason = wb_serializers.TextField(label="Reason for refusal", read_only=lambda view: not view.can_administrate)
|
|
124
|
+
|
|
125
|
+
_total_hours = wb_serializers.FloatField(
|
|
126
|
+
label=_("Total Hours"),
|
|
127
|
+
required=False,
|
|
128
|
+
read_only=True,
|
|
129
|
+
help_text=_("The total number of hours this request spans (i.e. only actual working days)"),
|
|
130
|
+
)
|
|
131
|
+
_total_vacation_hours = wb_serializers.FloatField(
|
|
132
|
+
label=_("Vacation Hours"),
|
|
133
|
+
required=False,
|
|
134
|
+
read_only=True,
|
|
135
|
+
help_text=_("The number of vacation hours this request uses"),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
_total_hours_in_days = wb_serializers.FloatField(
|
|
139
|
+
label=_("Total Days"),
|
|
140
|
+
required=False,
|
|
141
|
+
read_only=True,
|
|
142
|
+
help_text=_("The total number of days this request spans (i.e. only actual working days)"),
|
|
143
|
+
)
|
|
144
|
+
_total_vacation_hours_in_days = wb_serializers.FloatField(
|
|
145
|
+
label=_("Vacation Days"),
|
|
146
|
+
required=False,
|
|
147
|
+
read_only=True,
|
|
148
|
+
help_text=_("The number of vacation days this request uses"),
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
department = wb_serializers.PrimaryKeyRelatedField(read_only=True)
|
|
152
|
+
_department = PositionRepresentationSerializer(source="department")
|
|
153
|
+
|
|
154
|
+
_crossborder_country = CountryRepresentationSerializer(source="crossborder_country")
|
|
155
|
+
|
|
156
|
+
@wb_serializers.register_only_instance_resource()
|
|
157
|
+
def additional_resources(self, instance, request, user, **kwargs):
|
|
158
|
+
res = {}
|
|
159
|
+
res["periods"] = reverse(
|
|
160
|
+
"wbhuman_resources:request-periods-list",
|
|
161
|
+
args=[instance.id],
|
|
162
|
+
request=request,
|
|
163
|
+
)
|
|
164
|
+
if instance and instance.type.is_extensible and instance.status == AbsenceRequest.Status.APPROVED.name:
|
|
165
|
+
if not instance.next_extensible_period:
|
|
166
|
+
info(
|
|
167
|
+
request,
|
|
168
|
+
_(
|
|
169
|
+
"This request cannot be extended because the next available date is already taken. Please amend this request first"
|
|
170
|
+
),
|
|
171
|
+
)
|
|
172
|
+
else:
|
|
173
|
+
res["increase_days"] = reverse(
|
|
174
|
+
"wbhuman_resources:absencerequest-increaseday",
|
|
175
|
+
args=[instance.id],
|
|
176
|
+
request=request,
|
|
177
|
+
)
|
|
178
|
+
return res
|
|
179
|
+
|
|
180
|
+
def validate(self, data):
|
|
181
|
+
errors = {}
|
|
182
|
+
obj = self.instance
|
|
183
|
+
request_type = data.get("type", obj.type if obj else None)
|
|
184
|
+
if not request_type:
|
|
185
|
+
raise serializers.ValidationError({"type": [_("A type needs to be provided")]})
|
|
186
|
+
# check crossborder rules
|
|
187
|
+
try:
|
|
188
|
+
request_type.validate_country(data.get("crossborder_country", obj.crossborder_country if obj else None))
|
|
189
|
+
except ValueError as e:
|
|
190
|
+
errors["crossborder_country"] = e.args[0]
|
|
191
|
+
|
|
192
|
+
# If the user does not have the permission to administrate all the vacation requests,
|
|
193
|
+
# then we need to set the profile here, because this is, in this case, a read only field.
|
|
194
|
+
# if obj already exist the owner employee is not updated, important for keep the same employee when the manager update the object
|
|
195
|
+
if (
|
|
196
|
+
data
|
|
197
|
+
and (request := self.context.get("request"))
|
|
198
|
+
and (user_employee := getattr(request.user.profile, "human_resources", None))
|
|
199
|
+
):
|
|
200
|
+
if not request.user.has_perm("wbhuman_resources.administrate_absencerequest") and not obj:
|
|
201
|
+
data["employee"] = user_employee
|
|
202
|
+
|
|
203
|
+
employee = data.get("employee", obj.employee if obj else None)
|
|
204
|
+
period = data.get("period", obj.period if obj else None)
|
|
205
|
+
|
|
206
|
+
# Check if the period is a valid datetime range
|
|
207
|
+
if period.lower >= period.upper:
|
|
208
|
+
errors["period"] = [gettext("End date cannot be before start date")]
|
|
209
|
+
|
|
210
|
+
# If requester is not a manager of the request's employee, we check if the request can be taken given the time rules
|
|
211
|
+
if not user_employee.is_manager_of(employee) and not EmployeeHumanResource.is_administrator(request.user):
|
|
212
|
+
now = timezone.now()
|
|
213
|
+
if period.lower < now or period.upper < now:
|
|
214
|
+
errors["period"] = [
|
|
215
|
+
gettext("You cannot save or modify an absence already started or already finished")
|
|
216
|
+
]
|
|
217
|
+
if (period.lower.date() - now.date()).days < request_type.days_in_advance:
|
|
218
|
+
errors["period"] = [
|
|
219
|
+
gettext("The request needs to start at least {days_in_advance} days from now").format(
|
|
220
|
+
days_in_advance=request_type.days_in_advance
|
|
221
|
+
)
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
# Check if overlapping requests exists
|
|
225
|
+
if (
|
|
226
|
+
AbsenceRequest.objects.exclude(
|
|
227
|
+
status__in=[AbsenceRequest.Status.CANCELLED, AbsenceRequest.Status.DENIED]
|
|
228
|
+
)
|
|
229
|
+
.filter(
|
|
230
|
+
models.Q(employee=employee)
|
|
231
|
+
& ~models.Q(id=obj.id if obj else -1) # TODO: Remove later - Not really needed (probably)
|
|
232
|
+
& models.Q(period__overlap=period)
|
|
233
|
+
)
|
|
234
|
+
.exists()
|
|
235
|
+
):
|
|
236
|
+
errors["non_field_errors"] = [
|
|
237
|
+
_("Overlaping requests already exists at the specified periods. Please change the dates.")
|
|
238
|
+
]
|
|
239
|
+
if len(errors.keys()) > 0:
|
|
240
|
+
raise serializers.ValidationError(errors)
|
|
241
|
+
|
|
242
|
+
return data
|
|
243
|
+
|
|
244
|
+
class Meta:
|
|
245
|
+
model = AbsenceRequest
|
|
246
|
+
fields = [
|
|
247
|
+
"id",
|
|
248
|
+
"type_icon",
|
|
249
|
+
"status",
|
|
250
|
+
"employee",
|
|
251
|
+
"_employee",
|
|
252
|
+
"period",
|
|
253
|
+
"type",
|
|
254
|
+
"_type",
|
|
255
|
+
"notes",
|
|
256
|
+
"reason",
|
|
257
|
+
"_total_hours",
|
|
258
|
+
"_total_vacation_hours",
|
|
259
|
+
"_total_hours_in_days",
|
|
260
|
+
"_total_vacation_hours_in_days",
|
|
261
|
+
"created",
|
|
262
|
+
"attachment",
|
|
263
|
+
"department",
|
|
264
|
+
"_department",
|
|
265
|
+
"_additional_resources",
|
|
266
|
+
"is_cancelled",
|
|
267
|
+
"_crossborder_country",
|
|
268
|
+
"crossborder_country",
|
|
269
|
+
]
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class EmployeeAbsenceDaysModelSerializer(wb_serializers.ModelSerializer):
|
|
273
|
+
year = wb_serializers.YearField(read_only=True)
|
|
274
|
+
absence_type = wb_serializers.ChoiceField(read_only=True, choices=AbsenceRequestType.get_choices())
|
|
275
|
+
hours_count = wb_serializers.FloatField(read_only=True)
|
|
276
|
+
days_count = wb_serializers.FloatField(read_only=True)
|
|
277
|
+
id = wb_serializers.PrimaryKeyCharField()
|
|
278
|
+
|
|
279
|
+
class Meta:
|
|
280
|
+
model = AbsenceRequestPeriods
|
|
281
|
+
fields = ["id", "absence_type", "year", "hours_count", "days_count"]
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class AbsenceRequestPeriodsModelSerializer(wb_serializers.ModelSerializer):
|
|
285
|
+
_balance = EmployeeYearBalanceRepresentationSerializer(source="balance")
|
|
286
|
+
_default_period = DefaultDailyPeriodRepresentationSerializer(source="default_period")
|
|
287
|
+
_total_hours = wb_serializers.FloatField(read_only=True, required=False)
|
|
288
|
+
|
|
289
|
+
class Meta:
|
|
290
|
+
model = AbsenceRequestPeriods
|
|
291
|
+
fields = [
|
|
292
|
+
"id",
|
|
293
|
+
"request",
|
|
294
|
+
"employee",
|
|
295
|
+
"default_period",
|
|
296
|
+
"_default_period",
|
|
297
|
+
"date",
|
|
298
|
+
"timespan",
|
|
299
|
+
"_total_hours",
|
|
300
|
+
"balance",
|
|
301
|
+
"_balance",
|
|
302
|
+
"consecutive_hours_count",
|
|
303
|
+
]
|
|
304
|
+
read_only_fields = fields
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class IncreaseDaySerializer(wb_serializers.Serializer):
|
|
308
|
+
number_days = wb_serializers.IntegerField(label=_("Increase absence by"), default=1)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from rest_framework.reverse import reverse
|
|
2
|
+
from wbcore import serializers as wb_serializers
|
|
3
|
+
|
|
4
|
+
from wbhuman_resources.models import (
|
|
5
|
+
DayOff,
|
|
6
|
+
DefaultDailyPeriod,
|
|
7
|
+
EmployeeWeeklyOffPeriods,
|
|
8
|
+
)
|
|
9
|
+
from wbhuman_resources.models.calendars import DayOffCalendar
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DayOffCalendarRepresentationSerializer(wb_serializers.RepresentationSerializer):
|
|
13
|
+
class Meta:
|
|
14
|
+
model = DayOffCalendar
|
|
15
|
+
fields = ("id", "title")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DefaultDailyPeriodRepresentationSerializer(wb_serializers.RepresentationSerializer):
|
|
19
|
+
class Meta:
|
|
20
|
+
model = DefaultDailyPeriod
|
|
21
|
+
fields = ("id", "lower_time", "upper_time", "title", "total_hours")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class EmployeeWeeklyOffPeriodsRepresentationSerializer(wb_serializers.RepresentationSerializer):
|
|
25
|
+
class Meta:
|
|
26
|
+
model = EmployeeWeeklyOffPeriods
|
|
27
|
+
fields = ("id", "computed_str")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DayOffRepresentationSerializer(wb_serializers.RepresentationSerializer):
|
|
31
|
+
class Meta:
|
|
32
|
+
model = DayOff
|
|
33
|
+
fields = ("id", "title", "date")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class DayOffCalendarModelSerializer(wb_serializers.ModelSerializer):
|
|
37
|
+
timezone = wb_serializers.TimeZoneField()
|
|
38
|
+
|
|
39
|
+
@wb_serializers.register_resource()
|
|
40
|
+
def additional_resources(self, instance, request, user):
|
|
41
|
+
return {
|
|
42
|
+
"days_off": reverse(
|
|
43
|
+
"wbhuman_resources:calendar-dayoff-list",
|
|
44
|
+
args=[instance.id],
|
|
45
|
+
request=request,
|
|
46
|
+
),
|
|
47
|
+
"default_periods": reverse(
|
|
48
|
+
"wbhuman_resources:calendar-defaultperiod-list",
|
|
49
|
+
args=[instance.id],
|
|
50
|
+
request=request,
|
|
51
|
+
),
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
class Meta:
|
|
55
|
+
model = DayOffCalendar
|
|
56
|
+
fields = ("id", "title", "resource", "timezone", "_additional_resources")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class DefaultDailyPeriodModelSerializer(wb_serializers.ModelSerializer):
|
|
60
|
+
_calendar = DayOffCalendarRepresentationSerializer(source="calendar")
|
|
61
|
+
timespan = wb_serializers.TimeRange(timerange_fields=("lower_time", "upper_time"))
|
|
62
|
+
|
|
63
|
+
class Meta:
|
|
64
|
+
model = DefaultDailyPeriod
|
|
65
|
+
fields = ("id", "timespan", "title", "total_hours", "calendar", "_calendar")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class DayOffModelSerializer(wb_serializers.ModelSerializer):
|
|
69
|
+
_calendar = DayOffCalendarRepresentationSerializer(source="calendar")
|
|
70
|
+
|
|
71
|
+
class Meta:
|
|
72
|
+
model = DayOff
|
|
73
|
+
fields = ("id", "title", "date", "count_as_holiday", "calendar", "_calendar")
|