wbhuman_resources 1.51.2__py2.py3-none-any.whl → 1.61.5__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/factories/absence.py +1 -0
- wbhuman_resources/factories/calendars.py +1 -0
- wbhuman_resources/factories/employee.py +3 -0
- wbhuman_resources/factories/kpi.py +2 -0
- wbhuman_resources/filters/calendars.py +0 -1
- wbhuman_resources/locale/de/LC_MESSAGES/django.mo +0 -0
- wbhuman_resources/locale/de/LC_MESSAGES/django.po +252 -257
- wbhuman_resources/locale/en/LC_MESSAGES/django.po +248 -252
- wbhuman_resources/locale/fr/LC_MESSAGES/django.po +251 -256
- wbhuman_resources/migrations/0023_alter_employeehumanresource_weekly_off_periods.py +18 -0
- wbhuman_resources/migrations/0024_alter_absencerequestperiods_unique_together_and_more.py +49 -0
- wbhuman_resources/models/absence.py +19 -3
- wbhuman_resources/models/calendars.py +6 -6
- wbhuman_resources/models/employee.py +65 -18
- wbhuman_resources/models/kpi.py +17 -1
- wbhuman_resources/models/review.py +35 -26
- wbhuman_resources/permissions/backend.py +2 -4
- wbhuman_resources/serializers/__init__.py +1 -0
- wbhuman_resources/serializers/absence.py +5 -0
- wbhuman_resources/serializers/calendars.py +2 -1
- wbhuman_resources/serializers/employee.py +3 -3
- wbhuman_resources/signals.py +4 -0
- wbhuman_resources/tasks.py +37 -31
- wbhuman_resources/tests/test_permission.py +19 -15
- wbhuman_resources/utils.py +4 -1
- wbhuman_resources/viewsets/absence.py +18 -26
- wbhuman_resources/viewsets/absence_charts.py +4 -6
- wbhuman_resources/viewsets/buttons/kpis.py +3 -3
- wbhuman_resources/viewsets/buttons/review.py +3 -3
- wbhuman_resources/viewsets/display/calendars.py +2 -3
- wbhuman_resources/viewsets/display/kpis.py +1 -1
- wbhuman_resources/viewsets/display/review.py +5 -5
- wbhuman_resources/viewsets/endpoints/absence.py +4 -1
- wbhuman_resources/viewsets/kpi.py +2 -2
- wbhuman_resources/viewsets/menu/absence.py +11 -7
- wbhuman_resources/viewsets/menu/calendars.py +4 -5
- wbhuman_resources/viewsets/menu/employee.py +5 -6
- wbhuman_resources/viewsets/menu/kpis.py +2 -3
- wbhuman_resources/viewsets/menu/review.py +12 -13
- wbhuman_resources/viewsets/review.py +12 -14
- {wbhuman_resources-1.51.2.dist-info → wbhuman_resources-1.61.5.dist-info}/METADATA +2 -2
- {wbhuman_resources-1.51.2.dist-info → wbhuman_resources-1.61.5.dist-info}/RECORD +43 -40
- {wbhuman_resources-1.51.2.dist-info → wbhuman_resources-1.61.5.dist-info}/WHEEL +1 -1
wbhuman_resources/tasks.py
CHANGED
|
@@ -11,16 +11,19 @@ from django.template.loader import get_template
|
|
|
11
11
|
from django.utils.translation import gettext
|
|
12
12
|
from django.utils.translation import gettext_lazy as _
|
|
13
13
|
from dynamic_preferences.registries import global_preferences_registry
|
|
14
|
+
from wbcore.contrib.directory.models import Person
|
|
14
15
|
from wbcore.contrib.notifications.dispatch import send_notification
|
|
15
16
|
from wbcore.utils.date import current_month_date_end
|
|
16
17
|
from wbcore.utils.html import convert_html2text
|
|
18
|
+
from wbcore.workers import Queue
|
|
17
19
|
|
|
18
20
|
from wbhuman_resources.models import KPI, DayOffCalendar, EmployeeHumanResource, Review
|
|
19
21
|
|
|
20
22
|
from .models.preferences import get_previous_year_balance_expiration_date
|
|
23
|
+
from .signals import add_employee_activity_to_daily_brief
|
|
21
24
|
|
|
22
25
|
|
|
23
|
-
@shared_task
|
|
26
|
+
@shared_task(queue=Queue.BACKGROUND.value)
|
|
24
27
|
def create_future_public_holiday(today: date | None = None, forecast_year: int = 5):
|
|
25
28
|
if not today:
|
|
26
29
|
today = date.today()
|
|
@@ -29,31 +32,7 @@ def create_future_public_holiday(today: date | None = None, forecast_year: int =
|
|
|
29
32
|
calendar.create_public_holidays(year)
|
|
30
33
|
|
|
31
34
|
|
|
32
|
-
@shared_task
|
|
33
|
-
def daily_birthday(today=None):
|
|
34
|
-
"""
|
|
35
|
-
Cron task supposed to be ran every day. Check and notify employee about a colleague's birthday.
|
|
36
|
-
"""
|
|
37
|
-
day = "today"
|
|
38
|
-
if not today:
|
|
39
|
-
# Notify employee the day before
|
|
40
|
-
today = datetime.today() + timedelta(days=1)
|
|
41
|
-
day = "tomorrow"
|
|
42
|
-
for birthday_employee in EmployeeHumanResource.active_internal_employees.filter(
|
|
43
|
-
Q(profile__birthday__day=today.day) & Q(profile__birthday__month=today.month)
|
|
44
|
-
).all():
|
|
45
|
-
for employee in EmployeeHumanResource.active_internal_employees.exclude(id=birthday_employee.id):
|
|
46
|
-
send_notification(
|
|
47
|
-
code="wbhuman_resources.employeehumanresource.birthday",
|
|
48
|
-
title=_("{} {}'s birthday is {}!").format(
|
|
49
|
-
birthday_employee.profile.first_name, birthday_employee.profile.last_name, day
|
|
50
|
-
),
|
|
51
|
-
body=_("Wish her/him a happy birthday!"),
|
|
52
|
-
user=employee.profile.user_account,
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
@shared_task
|
|
35
|
+
@shared_task(queue=Queue.BACKGROUND.value)
|
|
57
36
|
def assign_balance(today=None):
|
|
58
37
|
"""
|
|
59
38
|
Yearly periodic cron tasks that increase for an employee
|
|
@@ -68,7 +47,7 @@ def assign_balance(today=None):
|
|
|
68
47
|
employee.assign_vacation_allowance_from_range(start_period.date(), end_period.date())
|
|
69
48
|
|
|
70
49
|
|
|
71
|
-
@shared_task
|
|
50
|
+
@shared_task(queue=Queue.BACKGROUND.value)
|
|
72
51
|
def check_and_warn_user_with_previous_year_available_balance(year=None):
|
|
73
52
|
"""
|
|
74
53
|
When this task run, it will send a reminder Notification to user with still available balance for the previous year
|
|
@@ -91,7 +70,7 @@ def check_and_warn_user_with_previous_year_available_balance(year=None):
|
|
|
91
70
|
)
|
|
92
71
|
|
|
93
72
|
|
|
94
|
-
@shared_task
|
|
73
|
+
@shared_task(queue=Queue.BACKGROUND.value)
|
|
95
74
|
def send_mail_to_accounting():
|
|
96
75
|
global_preferences = global_preferences_registry.manager()
|
|
97
76
|
accounting_company_emails = list(
|
|
@@ -133,7 +112,7 @@ def send_mail_to_accounting():
|
|
|
133
112
|
msg.send()
|
|
134
113
|
|
|
135
114
|
|
|
136
|
-
@shared_task
|
|
115
|
+
@shared_task(queue=Queue.BACKGROUND.value)
|
|
137
116
|
def daily_automatic_application_deadline():
|
|
138
117
|
for review in Review.objects.filter(
|
|
139
118
|
Q(status=Review.Status.FILL_IN_REVIEW) & Q(review_deadline__lte=datetime.now().date())
|
|
@@ -172,8 +151,8 @@ def daily_automatic_application_deadline():
|
|
|
172
151
|
)
|
|
173
152
|
|
|
174
153
|
|
|
175
|
-
@shared_task
|
|
176
|
-
def periodic_updating_kpi_task(kpi_id=
|
|
154
|
+
@shared_task(queue=Queue.BACKGROUND.value)
|
|
155
|
+
def periodic_updating_kpi_task(kpi_id: list | None = None, start=None, end=None):
|
|
177
156
|
intervals = {elt.name: KPI.Interval.get_frequence_correspondance(elt.name) for elt in KPI.Interval}
|
|
178
157
|
for key, value in intervals.items():
|
|
179
158
|
kpis = (
|
|
@@ -188,3 +167,30 @@ def periodic_updating_kpi_task(kpi_id=[], start=None, end=None):
|
|
|
188
167
|
end = kpi.period.upper
|
|
189
168
|
for date_evaluation in pd.date_range(start=start, end=end, freq=value):
|
|
190
169
|
kpi.generate_evaluation(date_evaluation.date())
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@shared_task(queue=Queue.BACKGROUND.value)
|
|
173
|
+
def daily_brief(today: date | None = None, **kwargs):
|
|
174
|
+
"""Creates a summary of the daily brief for all internal employees
|
|
175
|
+
Args:
|
|
176
|
+
today (date | None, optional): Date of today. Defaults to None.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
if not today:
|
|
180
|
+
today = date.today()
|
|
181
|
+
for employee in EmployeeHumanResource.active_internal_employees.filter(profile__user_account__isnull=False):
|
|
182
|
+
daily_brief = ""
|
|
183
|
+
for receiver, res in add_employee_activity_to_daily_brief.send( # noqa: B007
|
|
184
|
+
sender=Person, instance=employee.profile, val_date=today, **kwargs
|
|
185
|
+
):
|
|
186
|
+
if res:
|
|
187
|
+
title, html = res
|
|
188
|
+
daily_brief += f"<h2 text-align: center;>{title}</h2>\n<div style='margin-bottom: 1.5em; text-align: left;'>{html}</div>\n"
|
|
189
|
+
|
|
190
|
+
if daily_brief:
|
|
191
|
+
send_notification(
|
|
192
|
+
code="wbcrm.activity.daily_brief",
|
|
193
|
+
title=_("Your Daily Brief"),
|
|
194
|
+
body=daily_brief,
|
|
195
|
+
user=employee.profile.user_account,
|
|
196
|
+
)
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import pytest
|
|
2
|
-
from django.contrib.auth import get_user_model
|
|
3
2
|
from dynamic_preferences.registries import global_preferences_registry
|
|
4
3
|
from faker import Faker
|
|
5
4
|
from rest_framework.test import APIRequestFactory
|
|
6
5
|
from wbcore.contrib.authentication.factories import UserFactory
|
|
7
|
-
from wbcore.
|
|
6
|
+
from wbcore.contrib.permission.internal.registry import UserBackendRegistry
|
|
8
7
|
|
|
9
8
|
from wbhuman_resources.factories.employee import EmployeeHumanResourceFactory
|
|
10
9
|
from wbhuman_resources.models.employee import EmployeeHumanResource
|
|
11
10
|
|
|
12
|
-
User = get_user_model()
|
|
13
11
|
fake = Faker()
|
|
14
12
|
|
|
15
13
|
|
|
@@ -17,48 +15,54 @@ fake = Faker()
|
|
|
17
15
|
class TestPermissionTasks:
|
|
18
16
|
@pytest.fixture
|
|
19
17
|
def request_user_external(self):
|
|
20
|
-
request = APIRequestFactory()
|
|
21
18
|
user = UserFactory()
|
|
22
19
|
EmployeeHumanResourceFactory.create(
|
|
23
20
|
profile=user.profile, contract_type=EmployeeHumanResource.ContractType.EXTERNAL
|
|
24
21
|
)
|
|
22
|
+
UserBackendRegistry().refresh_users()
|
|
23
|
+
|
|
24
|
+
user.refresh_from_db()
|
|
25
|
+
request = APIRequestFactory()
|
|
25
26
|
request.user = user
|
|
26
27
|
return request
|
|
27
28
|
|
|
28
29
|
@pytest.fixture
|
|
29
30
|
def request_user_active_internal(self):
|
|
30
|
-
request = APIRequestFactory()
|
|
31
31
|
user = UserFactory()
|
|
32
32
|
EmployeeHumanResourceFactory.create(
|
|
33
33
|
profile=user.profile, contract_type=EmployeeHumanResource.ContractType.INTERNAL
|
|
34
34
|
)
|
|
35
|
+
UserBackendRegistry().refresh_users()
|
|
36
|
+
|
|
37
|
+
user.refresh_from_db()
|
|
38
|
+
request = APIRequestFactory()
|
|
35
39
|
request.user = user
|
|
36
40
|
return request
|
|
37
41
|
|
|
38
42
|
@pytest.fixture
|
|
39
43
|
def request_user_inactive_internal(self):
|
|
40
|
-
|
|
41
|
-
user = UserFactory()
|
|
44
|
+
user = UserFactory.create()
|
|
42
45
|
EmployeeHumanResourceFactory.create(
|
|
43
46
|
profile=user.profile, contract_type=EmployeeHumanResource.ContractType.INTERNAL, is_active=False
|
|
44
47
|
)
|
|
48
|
+
UserBackendRegistry().refresh_users()
|
|
49
|
+
user.refresh_from_db()
|
|
50
|
+
request = APIRequestFactory()
|
|
45
51
|
request.user = user
|
|
46
52
|
return request
|
|
47
53
|
|
|
48
54
|
def test_permission_active_internal(self, request_user_active_internal):
|
|
49
|
-
assert
|
|
55
|
+
assert request_user_active_internal.user.is_internal is True
|
|
50
56
|
|
|
51
57
|
def test_permission_inactive_internal(self, request_user_inactive_internal):
|
|
52
|
-
assert
|
|
58
|
+
assert request_user_inactive_internal.user.is_internal is False
|
|
53
59
|
|
|
54
60
|
def test_permission_external(self, request_user_external):
|
|
55
|
-
assert
|
|
61
|
+
assert request_user_external.user.is_internal is False
|
|
56
62
|
|
|
57
63
|
def test_permission_external_but_considered_internal(self, request_user_external):
|
|
58
|
-
from wbcore.permissions.registry import user_registry
|
|
59
|
-
|
|
60
64
|
user = request_user_external.user
|
|
61
65
|
global_preferences_registry.manager()["wbhuman_resources__is_external_considered_as_internal"] = True
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
assert
|
|
66
|
+
UserBackendRegistry().refresh_users()
|
|
67
|
+
user.refresh_from_db()
|
|
68
|
+
assert user.is_internal
|
wbhuman_resources/utils.py
CHANGED
|
@@ -6,8 +6,11 @@ from django.utils import timezone
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def get_number_of_hours_between_dates(
|
|
9
|
-
d1, d2, list_employee_dayoffs, list_public_holidays=False, hours_range=
|
|
9
|
+
d1, d2, list_employee_dayoffs, list_public_holidays=False, hours_range=None, granularity=12
|
|
10
10
|
):
|
|
11
|
+
if hours_range is None:
|
|
12
|
+
hours_range = range(0, 23)
|
|
13
|
+
|
|
11
14
|
def convert_days_from_hours(hours, granularity, hours_per_day):
|
|
12
15
|
return int(hours / granularity) * granularity / hours_per_day
|
|
13
16
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import wbcore.serializers as wb_serializers
|
|
2
1
|
from django.contrib.messages import info, warning
|
|
3
2
|
from django.db.models import Case, CharField, F, Q, Sum, Value, When
|
|
4
3
|
from django.db.models.functions import Concat, Extract
|
|
5
4
|
from django.shortcuts import get_object_or_404
|
|
6
5
|
from django.utils import timezone
|
|
7
6
|
from django.utils.functional import cached_property
|
|
8
|
-
from django.utils.translation import gettext
|
|
7
|
+
from django.utils.translation import gettext
|
|
9
8
|
from rest_framework import filters
|
|
10
9
|
from rest_framework.decorators import action
|
|
11
10
|
from rest_framework.response import Response
|
|
@@ -33,6 +32,7 @@ from wbhuman_resources.serializers import (
|
|
|
33
32
|
AbsenceRequestTypeModelSerializer,
|
|
34
33
|
AbsenceRequestTypeRepresentationSerializer,
|
|
35
34
|
EmployeeAbsenceDaysModelSerializer,
|
|
35
|
+
ReadOnlyAbsenceRequestModelSerializer,
|
|
36
36
|
)
|
|
37
37
|
from wbhuman_resources.viewsets.buttons import AbsenceRequestButtonConfig
|
|
38
38
|
from wbhuman_resources.viewsets.display import (
|
|
@@ -55,12 +55,7 @@ from wbhuman_resources.viewsets.titles import (
|
|
|
55
55
|
AbsenceTypeCountEmployeeTitleConfig,
|
|
56
56
|
)
|
|
57
57
|
|
|
58
|
-
from ..models.absence import can_validate_or_deny_request
|
|
59
|
-
from ..serializers.absence import (
|
|
60
|
-
CurrentUserDefaultPeriodDateTimeRange,
|
|
61
|
-
get_lower_time_choices,
|
|
62
|
-
get_upper_time_choices,
|
|
63
|
-
)
|
|
58
|
+
from ..models.absence import can_edit_request, can_validate_or_deny_request
|
|
64
59
|
from .mixins import EmployeeViewMixin
|
|
65
60
|
|
|
66
61
|
|
|
@@ -114,19 +109,16 @@ class AbsenceRequestModelViewSet(EmployeeViewMixin, viewsets.ModelViewSet):
|
|
|
114
109
|
return can_validate_or_deny_request(obj, self.request.user)
|
|
115
110
|
return False
|
|
116
111
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
default_timezone=employee.calendar.timezone,
|
|
123
|
-
default=CurrentUserDefaultPeriodDateTimeRange(),
|
|
124
|
-
lower_time_choices=get_lower_time_choices,
|
|
125
|
-
upper_time_choices=get_upper_time_choices,
|
|
126
|
-
)
|
|
112
|
+
@cached_property
|
|
113
|
+
def can_edit_request(self) -> bool:
|
|
114
|
+
if "pk" in self.kwargs and (obj := self.get_object()):
|
|
115
|
+
return can_edit_request(obj, self.request.user)
|
|
116
|
+
return True
|
|
127
117
|
|
|
128
|
-
|
|
129
|
-
|
|
118
|
+
def get_serializer_class(self):
|
|
119
|
+
if self.can_edit_request:
|
|
120
|
+
return AbsenceRequestModelSerializer
|
|
121
|
+
return ReadOnlyAbsenceRequestModelSerializer
|
|
130
122
|
|
|
131
123
|
def add_messages(
|
|
132
124
|
self,
|
|
@@ -143,7 +135,7 @@ class AbsenceRequestModelViewSet(EmployeeViewMixin, viewsets.ModelViewSet):
|
|
|
143
135
|
).exclude(id=instance.id)
|
|
144
136
|
activities_title = qs.values_list("title", flat=True)
|
|
145
137
|
if len(activities_title) > 0:
|
|
146
|
-
message =
|
|
138
|
+
message = gettext("<p>During this absence, you already have these events:</p><ul>")
|
|
147
139
|
for activity_title in activities_title:
|
|
148
140
|
message += f"<li>{activity_title}</li>"
|
|
149
141
|
message += "</ul>"
|
|
@@ -169,11 +161,11 @@ class AbsenceRequestModelViewSet(EmployeeViewMixin, viewsets.ModelViewSet):
|
|
|
169
161
|
available_hourly_balance_in_days = (
|
|
170
162
|
available_hourly_balance / instance.employee.calendar.get_daily_hours()
|
|
171
163
|
)
|
|
172
|
-
message =
|
|
164
|
+
message = gettext(
|
|
173
165
|
"After this request, you will have {} days ({} hours) left for the balance {}</b>"
|
|
174
166
|
).format(available_hourly_balance_in_days, available_hourly_balance, current_balance.year)
|
|
175
167
|
if other_pending_hours > 0:
|
|
176
|
-
message +=
|
|
168
|
+
message += gettext(
|
|
177
169
|
" (not including <b>{pending_hours}</b> hours from other pending/draft absence requests)"
|
|
178
170
|
).format(pending_hours=other_pending_hours)
|
|
179
171
|
if available_hourly_balance < 0:
|
|
@@ -185,7 +177,7 @@ class AbsenceRequestModelViewSet(EmployeeViewMixin, viewsets.ModelViewSet):
|
|
|
185
177
|
)
|
|
186
178
|
if day_offs:
|
|
187
179
|
day_offs_messages = [
|
|
188
|
-
|
|
180
|
+
gettext("{holiday} not counted ({title})").format(
|
|
189
181
|
holiday=holiday.date.strftime("%d.%m.%Y"), title=holiday.title
|
|
190
182
|
)
|
|
191
183
|
for holiday in day_offs
|
|
@@ -236,7 +228,7 @@ class AbsenceRequestModelViewSet(EmployeeViewMixin, viewsets.ModelViewSet):
|
|
|
236
228
|
def increaseday(self, request, pk=None):
|
|
237
229
|
absence_request = get_object_or_404(AbsenceRequest, id=pk)
|
|
238
230
|
if absence_request.type.is_extensible and (number_days := int(request.POST.get("number_days", 1))):
|
|
239
|
-
for
|
|
231
|
+
for _ in range(number_days):
|
|
240
232
|
if next_extensible_period := absence_request.next_extensible_period:
|
|
241
233
|
absence_request.period = next_extensible_period
|
|
242
234
|
absence_request.save()
|
|
@@ -254,7 +246,7 @@ class AbsenceRequestTypeModelViewSet(viewsets.ModelViewSet):
|
|
|
254
246
|
|
|
255
247
|
|
|
256
248
|
class AbsenceTypeCountEmployeeModelViewSet(viewsets.ModelViewSet):
|
|
257
|
-
|
|
249
|
+
ONLY_READ_ONLY_ENDPOINT = True
|
|
258
250
|
queryset = AbsenceRequestPeriods.objects.all()
|
|
259
251
|
serializer_class = EmployeeAbsenceDaysModelSerializer
|
|
260
252
|
|
|
@@ -10,8 +10,8 @@ from django.utils import timezone
|
|
|
10
10
|
from django.utils.dateparse import parse_date
|
|
11
11
|
from django.utils.translation import gettext as _
|
|
12
12
|
from wbcore import viewsets
|
|
13
|
-
from wbcore.pandas import fields as pf
|
|
14
|
-
from wbcore.pandas.views import PandasAPIViewSet
|
|
13
|
+
from wbcore.contrib.pandas import fields as pf
|
|
14
|
+
from wbcore.contrib.pandas.views import PandasAPIViewSet
|
|
15
15
|
from wbcore.utils.date import get_date_interval_from_request
|
|
16
16
|
|
|
17
17
|
from wbhuman_resources.filters import AbsenceRequestPlannerFilter, AbsenceTableFilter
|
|
@@ -104,8 +104,7 @@ def update_layoute(fig, start, end):
|
|
|
104
104
|
)
|
|
105
105
|
],
|
|
106
106
|
yaxis=dict(
|
|
107
|
-
title="",
|
|
108
|
-
titlefont=dict(color="#000000"),
|
|
107
|
+
title=dict(text="", font=dict(color="#000000")),
|
|
109
108
|
tickfont=dict(size=11, family="Courier", color="#000000"),
|
|
110
109
|
anchor="x",
|
|
111
110
|
side="left",
|
|
@@ -120,8 +119,7 @@ def update_layoute(fig, start, end):
|
|
|
120
119
|
spikethickness=1,
|
|
121
120
|
),
|
|
122
121
|
xaxis=dict(
|
|
123
|
-
title="",
|
|
124
|
-
titlefont=dict(color="#000000"),
|
|
122
|
+
title=dict(text="", font=dict(color="#000000")),
|
|
125
123
|
tickfont=dict(color="#000000"),
|
|
126
124
|
showline=False,
|
|
127
125
|
linewidth=0.5,
|
|
@@ -5,12 +5,12 @@ from wbcore.metadata.configs import buttons as bt
|
|
|
5
5
|
|
|
6
6
|
class KPIButtonConfig(bt.ButtonViewConfig):
|
|
7
7
|
def get_custom_instance_buttons(self):
|
|
8
|
-
|
|
8
|
+
buttons = []
|
|
9
9
|
if self.view.kwargs.get("pk", None):
|
|
10
|
-
|
|
10
|
+
buttons += [
|
|
11
11
|
bt.WidgetButton(
|
|
12
12
|
key="evaluationgraph", label=_("Evaluation Graph"), icon=WBIcon.CHART_BARS_HORIZONTAL.icon
|
|
13
13
|
),
|
|
14
14
|
bt.WidgetButton(key="kpievaluationpandas", label=_("Latest Evaluations"), icon=WBIcon.DATA_GRID.icon),
|
|
15
15
|
]
|
|
16
|
-
return {*
|
|
16
|
+
return {*buttons}
|
|
@@ -32,7 +32,7 @@ class ReviewButtonConfig(bt.ButtonViewConfig):
|
|
|
32
32
|
return self.get_custom_instance_buttons()
|
|
33
33
|
|
|
34
34
|
def get_custom_instance_buttons(self):
|
|
35
|
-
|
|
35
|
+
buttons = [
|
|
36
36
|
bt.WidgetButton(key="progress", label=_("Progress"), icon=WBIcon.CHART_BARS_HORIZONTAL.icon),
|
|
37
37
|
bt.ActionButton(
|
|
38
38
|
method=RequestType.PATCH,
|
|
@@ -132,7 +132,7 @@ class ReviewButtonConfig(bt.ButtonViewConfig):
|
|
|
132
132
|
"include_kpi",
|
|
133
133
|
)
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
buttons.append(
|
|
136
136
|
bt.ActionButton(
|
|
137
137
|
method=RequestType.PATCH,
|
|
138
138
|
identifiers=("wbhuman_resources:review",),
|
|
@@ -158,7 +158,7 @@ class ReviewButtonConfig(bt.ButtonViewConfig):
|
|
|
158
158
|
),
|
|
159
159
|
)
|
|
160
160
|
)
|
|
161
|
-
return {*
|
|
161
|
+
return {*buttons}
|
|
162
162
|
|
|
163
163
|
|
|
164
164
|
class ReviewGroupButtonConfig(bt.ButtonViewConfig):
|
|
@@ -73,12 +73,11 @@ class DefaultDailyPeriodDayOffCalendarDisplayConfig(DisplayViewConfig):
|
|
|
73
73
|
def get_list_display(self):
|
|
74
74
|
return dp.ListDisplay(
|
|
75
75
|
fields=[
|
|
76
|
-
dp.Field(key="
|
|
77
|
-
dp.Field(key="upper_time", label=_("End")),
|
|
76
|
+
dp.Field(key="timespan", label=_("Time Range")),
|
|
78
77
|
dp.Field(key="title", label=_("Title")),
|
|
79
78
|
dp.Field(key="total_hours", label=_("Total Hours")),
|
|
80
79
|
]
|
|
81
80
|
)
|
|
82
81
|
|
|
83
82
|
def get_instance_display(self) -> Display:
|
|
84
|
-
return create_simple_display([["
|
|
83
|
+
return create_simple_display([["title", "title"], ["timespan", "total_hours"]])
|
|
@@ -42,7 +42,7 @@ class KPIDisplayConfig(DisplayViewConfig):
|
|
|
42
42
|
create_simple_section("parameters_section", _("Parameters"), handler.get_display_grid()),
|
|
43
43
|
create_simple_section("evaluation_section", _("Evaluations"), [["evaluations"]], "evaluations"),
|
|
44
44
|
]
|
|
45
|
-
grid_fields.extend(["parameters_section", "evaluation_section"])
|
|
45
|
+
grid_fields.extend([["parameters_section", "evaluation_section"]])
|
|
46
46
|
return create_simple_display(grid_fields, sections)
|
|
47
47
|
|
|
48
48
|
|
|
@@ -303,24 +303,24 @@ class ReviewQuestionReviewDisplayConfig(ReviewQuestionDisplayConfig):
|
|
|
303
303
|
|
|
304
304
|
class ReviewAnswerDisplayConfig(DisplayViewConfig):
|
|
305
305
|
def get_list_display(self) -> Optional[dp.ListDisplay]:
|
|
306
|
-
|
|
306
|
+
fields = [
|
|
307
307
|
dp.Field(key="question_name", label=_("Question"), width=Unit.PIXEL(720)),
|
|
308
308
|
dp.Field(key="mandatory", label=_("Mandatory"), width=Unit.PIXEL(80)),
|
|
309
309
|
]
|
|
310
310
|
if self.view.get_queryset().filter(question__answer_type=ReviewQuestion.ANSWERTYPE.RATING):
|
|
311
|
-
|
|
311
|
+
fields += [dp.Field(key="answer_number", label=_("Rating"), width=Unit.PIXEL(170))]
|
|
312
312
|
|
|
313
|
-
|
|
313
|
+
fields += [dp.Field(key="answer_text", label=_("Comment"), width=Unit.PIXEL(720))]
|
|
314
314
|
|
|
315
315
|
if review_id := self.view.kwargs.get("review_id"):
|
|
316
316
|
review = Review.objects.get(id=review_id)
|
|
317
317
|
if review.status in [Review.Status.EVALUATION, Review.Status.VALIDATION]:
|
|
318
|
-
|
|
318
|
+
fields = [
|
|
319
319
|
dp.Field(key="question_name", label=_("Question"), width=Unit.PIXEL(740)),
|
|
320
320
|
dp.Field(key="answered_by", label=_("Answered By"), width=Unit.PIXEL(140)),
|
|
321
321
|
dp.Field(key="answer_text", label=_("Comment"), width=Unit.PIXEL(740)),
|
|
322
322
|
]
|
|
323
|
-
return dp.ListDisplay(fields=
|
|
323
|
+
return dp.ListDisplay(fields=fields)
|
|
324
324
|
|
|
325
325
|
def get_instance_display(self) -> Display:
|
|
326
326
|
grid_fields = [[repeat_field(2, "question_name")], ["mandatory", "."]]
|
|
@@ -2,6 +2,7 @@ from rest_framework.reverse import reverse
|
|
|
2
2
|
from wbcore.metadata.configs.endpoints import EndpointViewConfig
|
|
3
3
|
|
|
4
4
|
from wbhuman_resources.models import AbsenceRequest, EmployeeHumanResource
|
|
5
|
+
from wbhuman_resources.models.absence import get_valid_employee_human_resource
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class AbsenceRequestEndpointConfig(EndpointViewConfig):
|
|
@@ -18,7 +19,9 @@ class AbsenceRequestEndpointConfig(EndpointViewConfig):
|
|
|
18
19
|
return self.get_endpoint()
|
|
19
20
|
|
|
20
21
|
def get_create_endpoint(self, **kwargs):
|
|
21
|
-
|
|
22
|
+
if get_valid_employee_human_resource(self.request.user) is not None:
|
|
23
|
+
return self.get_endpoint()
|
|
24
|
+
return None
|
|
22
25
|
|
|
23
26
|
def get_delete_endpoint(self, **kwargs):
|
|
24
27
|
if self.instance:
|
|
@@ -9,9 +9,9 @@ from django.utils.translation import gettext
|
|
|
9
9
|
from django.utils.translation import gettext_lazy as _
|
|
10
10
|
from rest_framework import filters
|
|
11
11
|
from wbcore import viewsets
|
|
12
|
+
from wbcore.contrib.pandas import fields as pf
|
|
13
|
+
from wbcore.contrib.pandas.views import PandasAPIViewSet
|
|
12
14
|
from wbcore.filters import DjangoFilterBackend
|
|
13
|
-
from wbcore.pandas import fields as pf
|
|
14
|
-
from wbcore.pandas.views import PandasAPIViewSet
|
|
15
15
|
from wbcore.serializers.serializers import ModelSerializer
|
|
16
16
|
from wbcore.utils.strings import format_number
|
|
17
17
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from django.utils.translation import gettext as _
|
|
2
2
|
from wbcore.menus import ItemPermission, MenuItem
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
from wbhuman_resources.models.absence import get_valid_employee_human_resource
|
|
4
5
|
|
|
5
6
|
ABSENCEPLANNER_MENUITEM = MenuItem(
|
|
6
7
|
label=_("Absence Graph"),
|
|
7
8
|
endpoint="wbhuman_resources:absenceplanner-list",
|
|
8
9
|
permission=ItemPermission(
|
|
9
|
-
method=lambda request:
|
|
10
|
+
method=lambda request: request.user.is_internal, permissions=["wbhuman_resources.view_absencerequest"]
|
|
10
11
|
),
|
|
11
12
|
)
|
|
12
13
|
|
|
@@ -14,7 +15,7 @@ ABSENCETABLE_MENUITEM = MenuItem(
|
|
|
14
15
|
label=_("Presence Table"),
|
|
15
16
|
endpoint="wbhuman_resources:absencetable-list",
|
|
16
17
|
permission=ItemPermission(
|
|
17
|
-
method=lambda request:
|
|
18
|
+
method=lambda request: request.user.is_internal, permissions=["wbhuman_resources.view_absencerequest"]
|
|
18
19
|
),
|
|
19
20
|
)
|
|
20
21
|
|
|
@@ -22,13 +23,16 @@ ABSENCEREQUEST_MENUITEM = MenuItem(
|
|
|
22
23
|
label=_("Requests"),
|
|
23
24
|
endpoint="wbhuman_resources:absencerequest-list",
|
|
24
25
|
permission=ItemPermission(
|
|
25
|
-
method=lambda request:
|
|
26
|
+
method=lambda request: request.user.is_internal, permissions=["wbhuman_resources.view_absencerequest"]
|
|
26
27
|
),
|
|
27
28
|
add=MenuItem(
|
|
28
29
|
label=_("Add Requests"),
|
|
29
30
|
endpoint="wbhuman_resources:absencerequest-list",
|
|
30
31
|
permission=ItemPermission(
|
|
31
|
-
method=lambda request:
|
|
32
|
+
method=lambda request: request.user.is_internal
|
|
33
|
+
and get_valid_employee_human_resource(request.user) is not None,
|
|
34
|
+
permissions=["wbhuman_resources.add_absencerequest"],
|
|
35
|
+
include_superuser=False,
|
|
32
36
|
),
|
|
33
37
|
),
|
|
34
38
|
)
|
|
@@ -37,13 +41,13 @@ ABSENCEREQUESTTYPE_MENUITEM = MenuItem(
|
|
|
37
41
|
label=_("Request Types"),
|
|
38
42
|
endpoint="wbhuman_resources:absencerequesttype-list",
|
|
39
43
|
permission=ItemPermission(
|
|
40
|
-
method=lambda request:
|
|
44
|
+
method=lambda request: request.user.is_internal, permissions=["wbhuman_resources.view_absencerequesttpe"]
|
|
41
45
|
),
|
|
42
46
|
add=MenuItem(
|
|
43
47
|
label=_("Add Request Types"),
|
|
44
48
|
endpoint="wbhuman_resources:absencerequesttype-list",
|
|
45
49
|
permission=ItemPermission(
|
|
46
|
-
method=lambda request:
|
|
50
|
+
method=lambda request: request.user.is_internal,
|
|
47
51
|
permissions=["wbhuman_resources.add_absencerequesttype"],
|
|
48
52
|
),
|
|
49
53
|
),
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
from django.utils.translation import gettext as _
|
|
2
2
|
from wbcore.menus import ItemPermission, MenuItem
|
|
3
|
-
from wbcore.permissions.shortcuts import is_internal_user
|
|
4
3
|
|
|
5
4
|
DAYOFF_MENUITEM = MenuItem(
|
|
6
5
|
label=_("Days Off"),
|
|
7
6
|
endpoint="wbhuman_resources:dayoff-list",
|
|
8
7
|
permission=ItemPermission(
|
|
9
|
-
method=lambda request:
|
|
8
|
+
method=lambda request: request.user.is_internal, permissions=["wbhuman_resources.view_dayoff"]
|
|
10
9
|
),
|
|
11
10
|
add=MenuItem(
|
|
12
11
|
label=_("Add Day Off"),
|
|
13
12
|
endpoint="wbhuman_resources:dayoff-list",
|
|
14
13
|
permission=ItemPermission(
|
|
15
|
-
method=lambda request:
|
|
14
|
+
method=lambda request: request.user.is_internal, permissions=["wbhuman_resources.add_dayoff"]
|
|
16
15
|
),
|
|
17
16
|
),
|
|
18
17
|
)
|
|
@@ -21,13 +20,13 @@ DAYOFFCALENDAR_MENUITEM = MenuItem(
|
|
|
21
20
|
label=_("Calendars"),
|
|
22
21
|
endpoint="wbhuman_resources:dayoffcalendar-list",
|
|
23
22
|
permission=ItemPermission(
|
|
24
|
-
method=lambda request:
|
|
23
|
+
method=lambda request: request.user.is_internal, permissions=["wbhuman_resources.view_dayoffcalendar"]
|
|
25
24
|
),
|
|
26
25
|
add=MenuItem(
|
|
27
26
|
label=_("Add Calendar"),
|
|
28
27
|
endpoint="wbhuman_resources:dayoffcalendar-list",
|
|
29
28
|
permission=ItemPermission(
|
|
30
|
-
method=lambda request:
|
|
29
|
+
method=lambda request: request.user.is_internal, permissions=["wbhuman_resources.add_dayoffcalendar"]
|
|
31
30
|
),
|
|
32
31
|
),
|
|
33
32
|
)
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
from django.utils.translation import gettext as _
|
|
2
2
|
from wbcore.menus import ItemPermission, MenuItem
|
|
3
|
-
from wbcore.permissions.shortcuts import is_internal_user
|
|
4
3
|
|
|
5
4
|
EMPLOYEEHUMANRESOURCE_MENUITEM = MenuItem(
|
|
6
5
|
label=_("Balance & Usage"),
|
|
7
6
|
endpoint="wbhuman_resources:employeebalance-list",
|
|
8
7
|
permission=ItemPermission(
|
|
9
|
-
method=lambda request:
|
|
8
|
+
method=lambda request: request.user.is_internal,
|
|
10
9
|
permissions=["wbhuman_resources.view_employeehumanresource"],
|
|
11
10
|
),
|
|
12
11
|
)
|
|
@@ -15,14 +14,14 @@ EMPLOYEE_MENUITEM = MenuItem(
|
|
|
15
14
|
label=_("Employees"),
|
|
16
15
|
endpoint="wbhuman_resources:employee-list",
|
|
17
16
|
permission=ItemPermission(
|
|
18
|
-
method=lambda request:
|
|
17
|
+
method=lambda request: request.user.is_internal,
|
|
19
18
|
permissions=["wbhuman_resources.view_employeehumanresource"],
|
|
20
19
|
),
|
|
21
20
|
add=MenuItem(
|
|
22
21
|
label=_("Add Employee"),
|
|
23
22
|
endpoint="wbhuman_resources:employee-list",
|
|
24
23
|
permission=ItemPermission(
|
|
25
|
-
method=lambda request:
|
|
24
|
+
method=lambda request: request.user.is_internal,
|
|
26
25
|
permissions=["wbhuman_resources.add_employeehumanresource"],
|
|
27
26
|
),
|
|
28
27
|
),
|
|
@@ -32,13 +31,13 @@ POSITION_MENUITEM = MenuItem(
|
|
|
32
31
|
label=_("Positions"),
|
|
33
32
|
endpoint="wbhuman_resources:position-list",
|
|
34
33
|
permission=ItemPermission(
|
|
35
|
-
method=lambda request:
|
|
34
|
+
method=lambda request: request.user.is_internal, permissions=["wbhuman_resources.view_position"]
|
|
36
35
|
),
|
|
37
36
|
add=MenuItem(
|
|
38
37
|
label=_("Add Position"),
|
|
39
38
|
endpoint="wbhuman_resources:position-list",
|
|
40
39
|
permission=ItemPermission(
|
|
41
|
-
method=lambda request:
|
|
40
|
+
method=lambda request: request.user.is_internal, permissions=["wbhuman_resources.add_position"]
|
|
42
41
|
),
|
|
43
42
|
),
|
|
44
43
|
)
|