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.
Files changed (111) hide show
  1. wbhuman_resources/__init__.py +1 -0
  2. wbhuman_resources/admin/__init__.py +5 -0
  3. wbhuman_resources/admin/absence.py +113 -0
  4. wbhuman_resources/admin/calendars.py +37 -0
  5. wbhuman_resources/admin/employee.py +109 -0
  6. wbhuman_resources/admin/kpi.py +21 -0
  7. wbhuman_resources/admin/review.py +157 -0
  8. wbhuman_resources/apps.py +23 -0
  9. wbhuman_resources/dynamic_preferences_registry.py +119 -0
  10. wbhuman_resources/factories/__init__.py +38 -0
  11. wbhuman_resources/factories/absence.py +109 -0
  12. wbhuman_resources/factories/calendars.py +60 -0
  13. wbhuman_resources/factories/employee.py +80 -0
  14. wbhuman_resources/factories/kpi.py +155 -0
  15. wbhuman_resources/filters/__init__.py +20 -0
  16. wbhuman_resources/filters/absence.py +109 -0
  17. wbhuman_resources/filters/absence_graphs.py +85 -0
  18. wbhuman_resources/filters/calendars.py +28 -0
  19. wbhuman_resources/filters/employee.py +81 -0
  20. wbhuman_resources/filters/kpi.py +35 -0
  21. wbhuman_resources/filters/review.py +134 -0
  22. wbhuman_resources/filters/signals.py +27 -0
  23. wbhuman_resources/locale/de/LC_MESSAGES/django.mo +0 -0
  24. wbhuman_resources/locale/de/LC_MESSAGES/django.po +2207 -0
  25. wbhuman_resources/locale/de/LC_MESSAGES/django.po.translated +2456 -0
  26. wbhuman_resources/locale/en/LC_MESSAGES/django.mo +0 -0
  27. wbhuman_resources/locale/en/LC_MESSAGES/django.po +2091 -0
  28. wbhuman_resources/locale/fr/LC_MESSAGES/django.mo +0 -0
  29. wbhuman_resources/locale/fr/LC_MESSAGES/django.po +2093 -0
  30. wbhuman_resources/management/__init__.py +23 -0
  31. wbhuman_resources/migrations/0001_initial_squashed_squashed_0015_alter_absencerequest_calendaritem_ptr_and_more.py +949 -0
  32. wbhuman_resources/migrations/0016_alter_employeehumanresource_options.py +20 -0
  33. wbhuman_resources/migrations/0017_absencerequest_crossborder_country_and_more.py +55 -0
  34. wbhuman_resources/migrations/0018_remove_position_group_position_groups.py +32 -0
  35. wbhuman_resources/migrations/0019_alter_absencerequest_options_alter_kpi_options_and_more.py +44 -0
  36. wbhuman_resources/migrations/0020_alter_employeeyearbalance_year_alter_review_year.py +27 -0
  37. wbhuman_resources/migrations/0021_alter_position_color.py +18 -0
  38. wbhuman_resources/migrations/0022_remove_review_editable_mode.py +64 -0
  39. wbhuman_resources/migrations/__init__.py +0 -0
  40. wbhuman_resources/models/__init__.py +23 -0
  41. wbhuman_resources/models/absence.py +903 -0
  42. wbhuman_resources/models/calendars.py +370 -0
  43. wbhuman_resources/models/employee.py +1241 -0
  44. wbhuman_resources/models/kpi.py +199 -0
  45. wbhuman_resources/models/preferences.py +40 -0
  46. wbhuman_resources/models/review.py +982 -0
  47. wbhuman_resources/permissions/__init__.py +0 -0
  48. wbhuman_resources/permissions/backend.py +26 -0
  49. wbhuman_resources/serializers/__init__.py +49 -0
  50. wbhuman_resources/serializers/absence.py +308 -0
  51. wbhuman_resources/serializers/calendars.py +73 -0
  52. wbhuman_resources/serializers/employee.py +267 -0
  53. wbhuman_resources/serializers/kpi.py +80 -0
  54. wbhuman_resources/serializers/review.py +415 -0
  55. wbhuman_resources/signals.py +4 -0
  56. wbhuman_resources/tasks.py +195 -0
  57. wbhuman_resources/templates/review/review_report.html +322 -0
  58. wbhuman_resources/tests/__init__.py +1 -0
  59. wbhuman_resources/tests/conftest.py +96 -0
  60. wbhuman_resources/tests/models/__init__.py +0 -0
  61. wbhuman_resources/tests/models/test_absences.py +478 -0
  62. wbhuman_resources/tests/models/test_calendars.py +209 -0
  63. wbhuman_resources/tests/models/test_employees.py +502 -0
  64. wbhuman_resources/tests/models/test_review.py +103 -0
  65. wbhuman_resources/tests/models/test_utils.py +110 -0
  66. wbhuman_resources/tests/signals.py +108 -0
  67. wbhuman_resources/tests/test_permission.py +64 -0
  68. wbhuman_resources/tests/test_tasks.py +74 -0
  69. wbhuman_resources/urls.py +221 -0
  70. wbhuman_resources/utils.py +43 -0
  71. wbhuman_resources/viewsets/__init__.py +61 -0
  72. wbhuman_resources/viewsets/absence.py +312 -0
  73. wbhuman_resources/viewsets/absence_charts.py +328 -0
  74. wbhuman_resources/viewsets/buttons/__init__.py +7 -0
  75. wbhuman_resources/viewsets/buttons/absence.py +32 -0
  76. wbhuman_resources/viewsets/buttons/employee.py +44 -0
  77. wbhuman_resources/viewsets/buttons/kpis.py +16 -0
  78. wbhuman_resources/viewsets/buttons/review.py +195 -0
  79. wbhuman_resources/viewsets/calendars.py +103 -0
  80. wbhuman_resources/viewsets/display/__init__.py +39 -0
  81. wbhuman_resources/viewsets/display/absence.py +334 -0
  82. wbhuman_resources/viewsets/display/calendars.py +83 -0
  83. wbhuman_resources/viewsets/display/employee.py +254 -0
  84. wbhuman_resources/viewsets/display/kpis.py +92 -0
  85. wbhuman_resources/viewsets/display/review.py +429 -0
  86. wbhuman_resources/viewsets/employee.py +210 -0
  87. wbhuman_resources/viewsets/endpoints/__init__.py +42 -0
  88. wbhuman_resources/viewsets/endpoints/absence.py +57 -0
  89. wbhuman_resources/viewsets/endpoints/calendars.py +18 -0
  90. wbhuman_resources/viewsets/endpoints/employee.py +51 -0
  91. wbhuman_resources/viewsets/endpoints/kpis.py +53 -0
  92. wbhuman_resources/viewsets/endpoints/review.py +191 -0
  93. wbhuman_resources/viewsets/kpi.py +280 -0
  94. wbhuman_resources/viewsets/menu/__init__.py +22 -0
  95. wbhuman_resources/viewsets/menu/absence.py +50 -0
  96. wbhuman_resources/viewsets/menu/administration.py +15 -0
  97. wbhuman_resources/viewsets/menu/calendars.py +33 -0
  98. wbhuman_resources/viewsets/menu/employee.py +44 -0
  99. wbhuman_resources/viewsets/menu/kpis.py +18 -0
  100. wbhuman_resources/viewsets/menu/review.py +97 -0
  101. wbhuman_resources/viewsets/mixins.py +14 -0
  102. wbhuman_resources/viewsets/review.py +837 -0
  103. wbhuman_resources/viewsets/titles/__init__.py +18 -0
  104. wbhuman_resources/viewsets/titles/absence.py +30 -0
  105. wbhuman_resources/viewsets/titles/employee.py +18 -0
  106. wbhuman_resources/viewsets/titles/kpis.py +15 -0
  107. wbhuman_resources/viewsets/titles/review.py +62 -0
  108. wbhuman_resources/viewsets/utils.py +28 -0
  109. wbhuman_resources-1.58.4.dist-info/METADATA +8 -0
  110. wbhuman_resources-1.58.4.dist-info/RECORD +111 -0
  111. 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")