wbhuman_resources 2.2.1__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 +22 -0
- wbhuman_resources/dynamic_preferences_registry.py +118 -0
- wbhuman_resources/factories/__init__.py +38 -0
- wbhuman_resources/factories/absence.py +108 -0
- wbhuman_resources/factories/calendars.py +59 -0
- wbhuman_resources/factories/employee.py +79 -0
- wbhuman_resources/factories/kpi.py +154 -0
- wbhuman_resources/filters/__init__.py +20 -0
- wbhuman_resources/filters/absence.py +108 -0
- wbhuman_resources/filters/absence_graphs.py +84 -0
- wbhuman_resources/filters/calendars.py +28 -0
- wbhuman_resources/filters/employee.py +80 -0
- wbhuman_resources/filters/kpi.py +34 -0
- wbhuman_resources/filters/review.py +133 -0
- wbhuman_resources/filters/signals.py +26 -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 +889 -0
- wbhuman_resources/models/calendars.py +370 -0
- wbhuman_resources/models/employee.py +1211 -0
- wbhuman_resources/models/kpi.py +199 -0
- wbhuman_resources/models/preferences.py +39 -0
- wbhuman_resources/models/review.py +978 -0
- wbhuman_resources/permissions/__init__.py +0 -0
- wbhuman_resources/permissions/backend.py +25 -0
- wbhuman_resources/serializers/__init__.py +49 -0
- wbhuman_resources/serializers/absence.py +296 -0
- wbhuman_resources/serializers/calendars.py +71 -0
- wbhuman_resources/serializers/employee.py +266 -0
- wbhuman_resources/serializers/kpi.py +79 -0
- wbhuman_resources/serializers/review.py +414 -0
- wbhuman_resources/tasks.py +189 -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 +477 -0
- wbhuman_resources/tests/models/test_calendars.py +208 -0
- wbhuman_resources/tests/models/test_employees.py +501 -0
- wbhuman_resources/tests/models/test_review.py +102 -0
- wbhuman_resources/tests/models/test_utils.py +109 -0
- wbhuman_resources/tests/signals.py +107 -0
- wbhuman_resources/tests/test_permission.py +63 -0
- wbhuman_resources/tests/test_tasks.py +73 -0
- wbhuman_resources/urls.py +220 -0
- wbhuman_resources/utils.py +40 -0
- wbhuman_resources/viewsets/__init__.py +61 -0
- wbhuman_resources/viewsets/absence.py +327 -0
- wbhuman_resources/viewsets/absence_charts.py +327 -0
- wbhuman_resources/viewsets/buttons/__init__.py +7 -0
- wbhuman_resources/viewsets/buttons/absence.py +31 -0
- wbhuman_resources/viewsets/buttons/employee.py +43 -0
- wbhuman_resources/viewsets/buttons/kpis.py +16 -0
- wbhuman_resources/viewsets/buttons/review.py +194 -0
- wbhuman_resources/viewsets/calendars.py +102 -0
- wbhuman_resources/viewsets/display/__init__.py +39 -0
- wbhuman_resources/viewsets/display/absence.py +344 -0
- wbhuman_resources/viewsets/display/calendars.py +84 -0
- wbhuman_resources/viewsets/display/employee.py +253 -0
- wbhuman_resources/viewsets/display/kpis.py +91 -0
- wbhuman_resources/viewsets/display/review.py +427 -0
- wbhuman_resources/viewsets/employee.py +209 -0
- wbhuman_resources/viewsets/endpoints/__init__.py +42 -0
- wbhuman_resources/viewsets/endpoints/absence.py +74 -0
- wbhuman_resources/viewsets/endpoints/calendars.py +18 -0
- wbhuman_resources/viewsets/endpoints/employee.py +57 -0
- wbhuman_resources/viewsets/endpoints/kpis.py +58 -0
- wbhuman_resources/viewsets/endpoints/review.py +203 -0
- wbhuman_resources/viewsets/kpi.py +279 -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 +13 -0
- wbhuman_resources/viewsets/review.py +836 -0
- wbhuman_resources/viewsets/titles/__init__.py +18 -0
- wbhuman_resources/viewsets/titles/absence.py +29 -0
- wbhuman_resources/viewsets/titles/employee.py +18 -0
- wbhuman_resources/viewsets/titles/kpis.py +14 -0
- wbhuman_resources/viewsets/titles/review.py +61 -0
- wbhuman_resources/viewsets/utils.py +28 -0
- wbhuman_resources-2.2.1.dist-info/METADATA +9 -0
- wbhuman_resources-2.2.1.dist-info/RECORD +102 -0
- wbhuman_resources-2.2.1.dist-info/WHEEL +5 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from io import StringIO
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from django.contrib import admin
|
|
5
|
+
from django.shortcuts import redirect, render
|
|
6
|
+
from django.utils.translation import gettext_lazy as _
|
|
7
|
+
from reversion.errors import RevertError
|
|
8
|
+
from wbcore.admin import CsvImportForm, ImportCsvMixin
|
|
9
|
+
|
|
10
|
+
from ..models.absence import AbsenceRequest, AbsenceRequestPeriods, AbsenceRequestType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CustomImportCsvMixin(ImportCsvMixin):
|
|
14
|
+
def _import_csv(self, request, _sep=";"):
|
|
15
|
+
if request.method == "POST":
|
|
16
|
+
csv_file = request.FILES["csv_file"]
|
|
17
|
+
|
|
18
|
+
str_text = ""
|
|
19
|
+
for line in csv_file:
|
|
20
|
+
str_text = str_text + line.decode()
|
|
21
|
+
# Import csv as df
|
|
22
|
+
df = pd.read_csv(StringIO(str_text), sep=_sep)
|
|
23
|
+
# Sanitize dataframe
|
|
24
|
+
df = df.where(pd.notnull(df), None)
|
|
25
|
+
df = df.drop(df.columns.difference(self.get_import_fields()), axis=1)
|
|
26
|
+
|
|
27
|
+
# Overide this function if there is foreign key ids in the dataframe
|
|
28
|
+
df = self.manipulate_df(df)
|
|
29
|
+
errors = 0
|
|
30
|
+
revert_errors = 0
|
|
31
|
+
nb_added = 0
|
|
32
|
+
for model in df.to_dict("records"):
|
|
33
|
+
# by default, process the modela as a create request. Can be override to change the behavior
|
|
34
|
+
try:
|
|
35
|
+
nb_added += self.process_model(model)
|
|
36
|
+
# https://django-reversion.readthedocs.io/en/stable/common-problems.html
|
|
37
|
+
except RevertError:
|
|
38
|
+
revert_errors += 1
|
|
39
|
+
except Exception as e:
|
|
40
|
+
print(e) # noqa: T201
|
|
41
|
+
errors += 1
|
|
42
|
+
self.message_user(
|
|
43
|
+
request,
|
|
44
|
+
_(
|
|
45
|
+
"Your CSV file has been imported : {} imported ({} added, {} updated), {} errors, {} revert errors found due to failure to restore old versions"
|
|
46
|
+
).format(
|
|
47
|
+
df.shape[0] - errors - revert_errors,
|
|
48
|
+
nb_added,
|
|
49
|
+
df.shape[0] - errors - revert_errors - nb_added,
|
|
50
|
+
errors,
|
|
51
|
+
revert_errors,
|
|
52
|
+
),
|
|
53
|
+
)
|
|
54
|
+
return redirect("..")
|
|
55
|
+
form = CsvImportForm()
|
|
56
|
+
payload = {"form": form}
|
|
57
|
+
return render(request, "wbcore/admin/csv_form.html", payload)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@admin.register(AbsenceRequestType)
|
|
61
|
+
class AbsenceRequestTypeAdmin(admin.ModelAdmin):
|
|
62
|
+
list_display = [
|
|
63
|
+
"title",
|
|
64
|
+
"is_vacation",
|
|
65
|
+
"is_timeoff",
|
|
66
|
+
"is_extensible",
|
|
67
|
+
"auto_approve",
|
|
68
|
+
"days_in_advance",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
autocomplete_fields = ["crossborder_countries", "extra_notify_groups"]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class AbsenceRequestPeriodsInline(admin.TabularInline):
|
|
75
|
+
model = AbsenceRequestPeriods
|
|
76
|
+
ordering = ("timespan__startswith",)
|
|
77
|
+
extra = 0
|
|
78
|
+
raw_id_fields = ["balance", "request"]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@admin.register(AbsenceRequest)
|
|
82
|
+
class AbsenceRequestAdmin(admin.ModelAdmin):
|
|
83
|
+
list_display = ["status", "employee", "period", "type", "_total_hours", "_total_vacation_hours"]
|
|
84
|
+
|
|
85
|
+
fieldsets = (
|
|
86
|
+
(
|
|
87
|
+
"",
|
|
88
|
+
{
|
|
89
|
+
"fields": (
|
|
90
|
+
("status", "type"),
|
|
91
|
+
("period", "employee"),
|
|
92
|
+
"attachment",
|
|
93
|
+
)
|
|
94
|
+
},
|
|
95
|
+
),
|
|
96
|
+
(
|
|
97
|
+
_("Notes"),
|
|
98
|
+
{"fields": ("notes", "reason")},
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
inlines = (AbsenceRequestPeriodsInline,)
|
|
102
|
+
autocomplete_fields = ["employee"]
|
|
103
|
+
list_filter = ["type__is_vacation"]
|
|
104
|
+
|
|
105
|
+
def _total_hours(self, obj):
|
|
106
|
+
return obj._total_hours
|
|
107
|
+
|
|
108
|
+
def _total_vacation_hours(self, obj):
|
|
109
|
+
return obj._total_vacation_hours
|
|
110
|
+
|
|
111
|
+
#
|
|
112
|
+
# def get_queryset(self, request):
|
|
113
|
+
# return super().get_queryset().
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
|
|
3
|
+
from ..models import (
|
|
4
|
+
DayOff,
|
|
5
|
+
DayOffCalendar,
|
|
6
|
+
DefaultDailyPeriod,
|
|
7
|
+
EmployeeWeeklyOffPeriods,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@admin.register(DayOffCalendar)
|
|
12
|
+
class DayOffCalendarModelAdmin(admin.ModelAdmin):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@admin.register(DayOff)
|
|
17
|
+
class DayOffAdmin(admin.ModelAdmin):
|
|
18
|
+
list_display = ["date", "title", "count_as_holiday", "calendar"]
|
|
19
|
+
ordering = ["date"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@admin.register(DefaultDailyPeriod)
|
|
23
|
+
class DefaultDailyPeriodAdmin(admin.ModelAdmin):
|
|
24
|
+
list_display = ["lower_time", "upper_time", "title", "total_hours"]
|
|
25
|
+
ordering = ["lower_time"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class EmployeeWeeklyOffPeriodsInLine(admin.TabularInline):
|
|
29
|
+
model = EmployeeWeeklyOffPeriods
|
|
30
|
+
fields = [
|
|
31
|
+
"period",
|
|
32
|
+
"weekday",
|
|
33
|
+
]
|
|
34
|
+
fk_name = "employee"
|
|
35
|
+
extra = 0
|
|
36
|
+
raw_id_fields = ["employee"]
|
|
37
|
+
ordering = ("weekday", "period__lower_time")
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
|
|
3
|
+
from ..models.employee import (
|
|
4
|
+
BalanceHourlyAllowance,
|
|
5
|
+
EmployeeHumanResource,
|
|
6
|
+
EmployeeYearBalance,
|
|
7
|
+
Position,
|
|
8
|
+
)
|
|
9
|
+
from .calendars import EmployeeWeeklyOffPeriodsInLine
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PositionInline(admin.TabularInline):
|
|
13
|
+
model = Position
|
|
14
|
+
extra = 0
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@admin.register(Position)
|
|
18
|
+
class PositionAdmin(admin.ModelAdmin):
|
|
19
|
+
search_fields = ("name",)
|
|
20
|
+
list_display = ("name", "height", "manager")
|
|
21
|
+
|
|
22
|
+
raw_id_fields = ["groups", "manager"]
|
|
23
|
+
autocomplete_fields = ["groups", "manager"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BalanceHourlyAllowanceTabularAdmin(admin.TabularInline):
|
|
27
|
+
model = BalanceHourlyAllowance
|
|
28
|
+
fk_name = "balance"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@admin.register(EmployeeYearBalance)
|
|
32
|
+
class EmployeeYearBalanceAdmin(admin.ModelAdmin):
|
|
33
|
+
inlines = [BalanceHourlyAllowanceTabularAdmin]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class EmployeeYearBalanceInline(admin.TabularInline):
|
|
37
|
+
model = EmployeeYearBalance
|
|
38
|
+
fields = (
|
|
39
|
+
"year",
|
|
40
|
+
"balance",
|
|
41
|
+
"daily_hours",
|
|
42
|
+
"number_mandatory_days_off",
|
|
43
|
+
"total_vacation_hourly_usage",
|
|
44
|
+
"total_vacation_hourly_balance",
|
|
45
|
+
"balance_in_days",
|
|
46
|
+
"number_mandatory_days_off_in_days",
|
|
47
|
+
"total_vacation_hourly_usage_in_days",
|
|
48
|
+
"total_vacation_hourly_balance_in_days",
|
|
49
|
+
)
|
|
50
|
+
readonly_fields = [
|
|
51
|
+
"balance",
|
|
52
|
+
"daily_hours",
|
|
53
|
+
"number_mandatory_days_off",
|
|
54
|
+
"total_vacation_hourly_usage",
|
|
55
|
+
"total_vacation_hourly_balance",
|
|
56
|
+
"balance_in_days",
|
|
57
|
+
"number_mandatory_days_off_in_days",
|
|
58
|
+
"total_vacation_hourly_usage_in_days",
|
|
59
|
+
"total_vacation_hourly_balance_in_days",
|
|
60
|
+
]
|
|
61
|
+
extra = 0
|
|
62
|
+
ordering = ("-year",)
|
|
63
|
+
show_change_link = True
|
|
64
|
+
|
|
65
|
+
def _number_mandatory_days_off(self, obj):
|
|
66
|
+
return obj._number_mandatory_days_off
|
|
67
|
+
|
|
68
|
+
def _total_vacation_hourly_usage(self, obj):
|
|
69
|
+
return obj._total_vacation_hourly_usage
|
|
70
|
+
|
|
71
|
+
def _total_vacation_hourly_balance(self, obj):
|
|
72
|
+
return obj._total_vacation_hourly_balance
|
|
73
|
+
|
|
74
|
+
def _balance_in_days(self, obj):
|
|
75
|
+
return obj._balance_in_days
|
|
76
|
+
|
|
77
|
+
def _number_mandatory_days_off_in_days(self, obj):
|
|
78
|
+
return obj._number_mandatory_days_off_in_days
|
|
79
|
+
|
|
80
|
+
def _total_vacation_hourly_usage_in_days(self, obj):
|
|
81
|
+
return obj._total_vacation_hourly_usage_in_days
|
|
82
|
+
|
|
83
|
+
def _total_vacation_hourly_balance_in_days(self, obj):
|
|
84
|
+
return obj._total_vacation_hourly_balance_in_days
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@admin.register(EmployeeHumanResource)
|
|
88
|
+
class EmployeeHumanResourceAdmin(admin.ModelAdmin):
|
|
89
|
+
fieldsets = (
|
|
90
|
+
(
|
|
91
|
+
"",
|
|
92
|
+
{
|
|
93
|
+
"fields": (
|
|
94
|
+
"profile",
|
|
95
|
+
"is_active",
|
|
96
|
+
"extra_days_frequency",
|
|
97
|
+
"occupancy_rate",
|
|
98
|
+
"contract_type",
|
|
99
|
+
"position",
|
|
100
|
+
"enrollment_at",
|
|
101
|
+
"calendar",
|
|
102
|
+
)
|
|
103
|
+
},
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
raw_id_fields = ("profile",)
|
|
107
|
+
search_fields = ("profile__computed_str",)
|
|
108
|
+
list_display = ("profile", "contract_type", "position", "calendar")
|
|
109
|
+
inlines = (EmployeeYearBalanceInline, EmployeeWeeklyOffPeriodsInLine)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
|
|
3
|
+
from ..models.kpi import KPI, Evaluation
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@admin.register(Evaluation)
|
|
7
|
+
class KEvaluationAdmin(admin.ModelAdmin):
|
|
8
|
+
list_display = ["id", "person", "evaluated_score", "evaluation_date", "evaluated_period"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EvaluationInline(admin.TabularInline):
|
|
12
|
+
model = Evaluation
|
|
13
|
+
extra = 0
|
|
14
|
+
raw_id_fields = ["kpi", "person"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@admin.register(KPI)
|
|
18
|
+
class KPIAdmin(admin.ModelAdmin):
|
|
19
|
+
list_display = ["name", "period", "goal"]
|
|
20
|
+
inlines = (EvaluationInline,)
|
|
21
|
+
raw_id_fields = ["evaluated_persons"]
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
from io import StringIO
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from django.contrib import admin
|
|
5
|
+
from django.shortcuts import redirect, render
|
|
6
|
+
from django.utils.translation import gettext_lazy as _
|
|
7
|
+
from reversion.errors import RevertError
|
|
8
|
+
from wbcore.admin import CsvImportForm, ExportCsvMixin, ImportCsvMixin
|
|
9
|
+
|
|
10
|
+
from ..models.review import (
|
|
11
|
+
Review,
|
|
12
|
+
ReviewAnswer,
|
|
13
|
+
ReviewGroup,
|
|
14
|
+
ReviewQuestion,
|
|
15
|
+
ReviewQuestionCategory,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CustomImportCsvMixin(ImportCsvMixin):
|
|
20
|
+
def _import_csv(self, request, _sep=";"):
|
|
21
|
+
if request.method == "POST":
|
|
22
|
+
csv_file = request.FILES["csv_file"]
|
|
23
|
+
|
|
24
|
+
str_text = ""
|
|
25
|
+
for line in csv_file:
|
|
26
|
+
str_text = str_text + line.decode()
|
|
27
|
+
# Import csv as df
|
|
28
|
+
df = pd.read_csv(StringIO(str_text), sep=_sep)
|
|
29
|
+
# Sanitize dataframe
|
|
30
|
+
df = df.where(pd.notnull(df), None)
|
|
31
|
+
df = df.drop(df.columns.difference(self.get_import_fields()), axis=1)
|
|
32
|
+
|
|
33
|
+
# Overide this function if there is foreign key ids in the dataframe
|
|
34
|
+
df = self.manipulate_df(df)
|
|
35
|
+
errors = 0
|
|
36
|
+
revert_errors = 0
|
|
37
|
+
nb_added = 0
|
|
38
|
+
for model in df.to_dict("records"):
|
|
39
|
+
# by default, process the modela as a create request. Can be override to change the behavior
|
|
40
|
+
try:
|
|
41
|
+
nb_added += self.process_model(model)
|
|
42
|
+
# https://django-reversion.readthedocs.io/en/stable/common-problems.html
|
|
43
|
+
except RevertError:
|
|
44
|
+
revert_errors += 1
|
|
45
|
+
except Exception as e:
|
|
46
|
+
print(e) # noqa: T201
|
|
47
|
+
errors += 1
|
|
48
|
+
self.message_user(
|
|
49
|
+
request,
|
|
50
|
+
_(
|
|
51
|
+
"Your CSV file has been imported : {} imported ({} added, {} updated), {} errors, {} revert errors found due to failure to restore old versions"
|
|
52
|
+
).format(
|
|
53
|
+
df.shape[0] - errors - revert_errors,
|
|
54
|
+
nb_added,
|
|
55
|
+
df.shape[0] - errors - revert_errors - nb_added,
|
|
56
|
+
errors,
|
|
57
|
+
revert_errors,
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
return redirect("..")
|
|
61
|
+
form = CsvImportForm()
|
|
62
|
+
payload = {"form": form}
|
|
63
|
+
return render(request, "wbcore/admin/csv_form.html", payload)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@admin.register(ReviewGroup)
|
|
67
|
+
class ReviewGroupAdmin(admin.ModelAdmin):
|
|
68
|
+
list_display = ["name"]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@admin.register(Review)
|
|
72
|
+
class ReviewAdmin(admin.ModelAdmin):
|
|
73
|
+
list_display = [
|
|
74
|
+
"id",
|
|
75
|
+
"year",
|
|
76
|
+
"type",
|
|
77
|
+
"from_date",
|
|
78
|
+
"to_date",
|
|
79
|
+
"review_deadline",
|
|
80
|
+
"review",
|
|
81
|
+
"auto_apply_deadline",
|
|
82
|
+
"status",
|
|
83
|
+
"reviewee",
|
|
84
|
+
"reviewer",
|
|
85
|
+
"moderator",
|
|
86
|
+
"review_group",
|
|
87
|
+
"is_template",
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@admin.register(ReviewQuestionCategory)
|
|
92
|
+
class ReviewQuestionCategoryAdmin(ExportCsvMixin, CustomImportCsvMixin, admin.ModelAdmin):
|
|
93
|
+
list_display = ["name", "order", "weight"]
|
|
94
|
+
|
|
95
|
+
def manipulate_df(self, df):
|
|
96
|
+
return df
|
|
97
|
+
|
|
98
|
+
def get_import_fields(self):
|
|
99
|
+
return ["name", "order", "weight"]
|
|
100
|
+
|
|
101
|
+
def process_model(self, model):
|
|
102
|
+
if category_name := model.get("name"):
|
|
103
|
+
_, created = self.model.objects.update_or_create(
|
|
104
|
+
name=category_name,
|
|
105
|
+
defaults={
|
|
106
|
+
"order": model.get("order"),
|
|
107
|
+
"weight": model.get("weight"),
|
|
108
|
+
},
|
|
109
|
+
)
|
|
110
|
+
return 1 if created else 0
|
|
111
|
+
return 0
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@admin.register(ReviewQuestion)
|
|
115
|
+
class ReviewQuestion(ExportCsvMixin, CustomImportCsvMixin, admin.ModelAdmin):
|
|
116
|
+
list_display = [
|
|
117
|
+
"id",
|
|
118
|
+
"question",
|
|
119
|
+
"review",
|
|
120
|
+
"category",
|
|
121
|
+
"mandatory",
|
|
122
|
+
"answer_type",
|
|
123
|
+
"for_reviewee",
|
|
124
|
+
"for_reviewer",
|
|
125
|
+
"for_department_peers",
|
|
126
|
+
"for_company_peers",
|
|
127
|
+
"order",
|
|
128
|
+
"weight",
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
def manipulate_df(self, df):
|
|
132
|
+
df["review"] = df["review_id"].apply(lambda x: Review.objects.get(id=x))
|
|
133
|
+
df["category"] = df["category_name"].apply(lambda x: ReviewQuestionCategory.objects.filter(name=x).first())
|
|
134
|
+
return df
|
|
135
|
+
|
|
136
|
+
def get_import_fields(self):
|
|
137
|
+
return ["review_id", "category_name", "answer_type", "order", "weight", "question"]
|
|
138
|
+
|
|
139
|
+
def process_model(self, model):
|
|
140
|
+
if review := model.get("review"):
|
|
141
|
+
_, created = self.model.objects.update_or_create(
|
|
142
|
+
review=review,
|
|
143
|
+
question=model.get("question"),
|
|
144
|
+
defaults={
|
|
145
|
+
"category": model.get("category"),
|
|
146
|
+
"answer_type": model.get("answer_type"),
|
|
147
|
+
"order": model.get("order"),
|
|
148
|
+
"weight": model.get("weight"),
|
|
149
|
+
},
|
|
150
|
+
)
|
|
151
|
+
return 1 if created else 0
|
|
152
|
+
return 0
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@admin.register(ReviewAnswer)
|
|
156
|
+
class ReviewAnswerAdmin(admin.ModelAdmin):
|
|
157
|
+
list_display = ["question", "answered_by", "answer_number", "answered_anonymized"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from django.apps import AppConfig, apps
|
|
2
|
+
from django.db.models.signals import post_migrate
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class WbhumanResourcesConfig(AppConfig):
|
|
6
|
+
name = "wbhuman_resources"
|
|
7
|
+
|
|
8
|
+
def ready(self) -> None:
|
|
9
|
+
from wbcore.signals.filters import add_filters
|
|
10
|
+
from wbhuman_resources.management import initialize_task
|
|
11
|
+
|
|
12
|
+
from .filters.signals import add_position_filter
|
|
13
|
+
|
|
14
|
+
if apps.is_installed("wbcrm"):
|
|
15
|
+
from wbcrm.filters import ActivityFilter
|
|
16
|
+
|
|
17
|
+
add_filters.connect(add_position_filter, sender=ActivityFilter)
|
|
18
|
+
|
|
19
|
+
post_migrate.connect(
|
|
20
|
+
initialize_task,
|
|
21
|
+
dispatch_uid="wbhuman_resources.initialize_task",
|
|
22
|
+
)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.forms import ValidationError
|
|
3
|
+
from django.utils.translation import gettext as _
|
|
4
|
+
from dynamic_preferences.preferences import Section
|
|
5
|
+
from dynamic_preferences.registries import global_preferences_registry
|
|
6
|
+
from dynamic_preferences.types import (
|
|
7
|
+
BooleanPreference,
|
|
8
|
+
IntegerPreference,
|
|
9
|
+
LongStringPreference,
|
|
10
|
+
ModelChoicePreference,
|
|
11
|
+
StringPreference,
|
|
12
|
+
)
|
|
13
|
+
from wbhuman_resources.models.calendars import DayOffCalendar
|
|
14
|
+
|
|
15
|
+
human_resources = Section("wbhuman_resources")
|
|
16
|
+
|
|
17
|
+
# Employee dynamic preferences
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@global_preferences_registry.register
|
|
21
|
+
class EmployeeDefaultCalendarEntry(ModelChoicePreference):
|
|
22
|
+
section = human_resources
|
|
23
|
+
name = "employee_default_calendar"
|
|
24
|
+
queryset = DayOffCalendar.objects.all()
|
|
25
|
+
default = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@global_preferences_registry.register
|
|
29
|
+
class DefaultVacationDaysPreference(IntegerPreference):
|
|
30
|
+
section = human_resources
|
|
31
|
+
name = "default_vacation_days"
|
|
32
|
+
default = 25
|
|
33
|
+
|
|
34
|
+
verbose_name = _("Default employee vacation days")
|
|
35
|
+
help_text = _("The number of vacation days allocated to an employee working full time")
|
|
36
|
+
|
|
37
|
+
def validate(self, value):
|
|
38
|
+
if not isinstance(value, int) or value <= 1:
|
|
39
|
+
raise ValidationError(_("Only positive natural numbers allowed."))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@global_preferences_registry.register
|
|
43
|
+
class DefaultFromEmailAddressPreference(StringPreference):
|
|
44
|
+
section = human_resources
|
|
45
|
+
name = "default_from_email_address"
|
|
46
|
+
default = settings.DEFAULT_FROM_EMAIL
|
|
47
|
+
|
|
48
|
+
verbose_name = "The default from email address"
|
|
49
|
+
help_text = "The default from email address used to send hr related emails"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@global_preferences_registry.register
|
|
53
|
+
class NumberOfMonthsBeforeBalanceExpiration(IntegerPreference):
|
|
54
|
+
section = human_resources
|
|
55
|
+
name = "number_of_month_before_balance_expiration"
|
|
56
|
+
default = 12 * 5 # Default to 5 years
|
|
57
|
+
verbose_name = _("The number of month before yearly balance expiration")
|
|
58
|
+
help_text = _(
|
|
59
|
+
"The number of months before any yearly balance is considered expired. The count starts the first day of the next balance year"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@global_preferences_registry.register
|
|
64
|
+
class LongVacationNumberOfDaysPreference(IntegerPreference):
|
|
65
|
+
section = human_resources
|
|
66
|
+
name = "long_vacation_number_of_days"
|
|
67
|
+
default = 10
|
|
68
|
+
help_text = _("The number of days after which a vacation is considered a long vacation.")
|
|
69
|
+
verbose_name = _("Long Vacation Number of days")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# Monthly report accounting preference
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@global_preferences_registry.register
|
|
76
|
+
class AccountingCompanyEmails(LongStringPreference):
|
|
77
|
+
section = human_resources
|
|
78
|
+
name = "accounting_company_emails"
|
|
79
|
+
default = "no-reply@stainly-bench.com;"
|
|
80
|
+
verbose_name = _("Accounting email destination")
|
|
81
|
+
help_text = _("The accounting company emails to send automatic reports to, as a comma separated list")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# Calendar preferences
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@global_preferences_registry.register
|
|
88
|
+
class CalendarDefaultPublicHolidayPackagePreference(StringPreference):
|
|
89
|
+
section = human_resources
|
|
90
|
+
name = "calendar_default_public_holiday_package"
|
|
91
|
+
default = "europe.Switzerland"
|
|
92
|
+
|
|
93
|
+
verbose_name = _("Default calendar Public holiday package")
|
|
94
|
+
help_text = _("Package where the correct public holidays are found. Defaults to Europe Switzerland")
|
|
95
|
+
|
|
96
|
+
def validate(self, value):
|
|
97
|
+
if len(value.split(".")) != 2:
|
|
98
|
+
raise ValidationError(_("The preference has to be in the format of <continent>.<region>"))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@global_preferences_registry.register
|
|
102
|
+
class CalendarDefaultTimezonePreference(StringPreference):
|
|
103
|
+
section = human_resources
|
|
104
|
+
name = "calendar_default_timezone"
|
|
105
|
+
default = "UTC"
|
|
106
|
+
|
|
107
|
+
verbose_name = _("Default calendar timezone")
|
|
108
|
+
help_text = _("The default calendar timezone")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@global_preferences_registry.register
|
|
112
|
+
class AreExternalEmployeesConsideredAsInternal(BooleanPreference):
|
|
113
|
+
section = human_resources
|
|
114
|
+
name = "is_external_considered_as_internal"
|
|
115
|
+
default = False
|
|
116
|
+
|
|
117
|
+
verbose_name = _("Are external employee considered as internal?")
|
|
118
|
+
help_text = _("If True, will consider the external employee (and any related logic) as internal employee")
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from .absence import (
|
|
2
|
+
AbsenceRequestFactory,
|
|
3
|
+
AbsenceRequestPeriodsFactory,
|
|
4
|
+
AbsenceRequestTypeFactory,
|
|
5
|
+
TimeOffRequestFactory,
|
|
6
|
+
TimeOffTypeFactory,
|
|
7
|
+
VacationRequestFactory,
|
|
8
|
+
VacationTypeFactory,
|
|
9
|
+
)
|
|
10
|
+
from .calendars import (
|
|
11
|
+
BaseDayOffCalendarFactory,
|
|
12
|
+
DayOffCalendarFactory,
|
|
13
|
+
DayOffFactory,
|
|
14
|
+
DefaultDailyPeriodFactory,
|
|
15
|
+
)
|
|
16
|
+
from .employee import (
|
|
17
|
+
BalanceHourlyAllowanceFactory,
|
|
18
|
+
EmployeeHumanResourceFactory,
|
|
19
|
+
EmployeeWeeklyOffPeriodsFactory,
|
|
20
|
+
EmployeeYearBalanceFactory,
|
|
21
|
+
PositionFactory,
|
|
22
|
+
)
|
|
23
|
+
from .kpi import (
|
|
24
|
+
CompletedFilledReviewFactory,
|
|
25
|
+
DefaultPersonKPIFactory,
|
|
26
|
+
EvaluationFactory,
|
|
27
|
+
KPIFactory,
|
|
28
|
+
ReviewAbstractFactory,
|
|
29
|
+
ReviewAnswerFactory,
|
|
30
|
+
ReviewAnswerNoCategoryFactory,
|
|
31
|
+
ReviewFactory,
|
|
32
|
+
ReviewGroupFactory,
|
|
33
|
+
ReviewQuestionCategoryFactory,
|
|
34
|
+
ReviewQuestionFactory,
|
|
35
|
+
ReviewQuestionNoCategoryFactory,
|
|
36
|
+
ReviewTemplateFactory,
|
|
37
|
+
SignedReviewFactory,
|
|
38
|
+
)
|