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
@@ -0,0 +1,109 @@
1
+ import random
2
+
3
+ import factory
4
+ from faker import Faker
5
+ from pandas.tseries.offsets import BDay
6
+ from psycopg.types.range import TimestamptzRange
7
+ from wbcore.contrib.icons import WBIcon
8
+
9
+ from wbhuman_resources.models import (
10
+ AbsenceRequest,
11
+ AbsenceRequestPeriods,
12
+ AbsenceRequestType,
13
+ )
14
+
15
+ from .calendars import DefaultDailyPeriodFactory
16
+ from .employee import EmployeeYearBalanceFactory
17
+
18
+ fake = Faker()
19
+
20
+
21
+ class AbsenceRequestTypeFactory(factory.django.DjangoModelFactory):
22
+ class Meta:
23
+ model = AbsenceRequestType
24
+ django_get_or_create = ["title"]
25
+
26
+ title = factory.Faker("text", max_nb_chars=64)
27
+ icon = factory.Iterator(WBIcon.values)
28
+ is_vacation = factory.Faker("boolean")
29
+ is_timeoff = factory.Faker("boolean")
30
+ is_extensible = factory.Faker("boolean")
31
+ auto_approve = False
32
+ days_in_advance = factory.Faker("pyint", min_value=0, max_value=5)
33
+
34
+ @factory.post_generation
35
+ def crossborder_countries(self, create, extracted, **kwargs):
36
+ if not create:
37
+ return
38
+
39
+ if extracted:
40
+ for country in extracted:
41
+ self.crossborder_countries.add(country)
42
+
43
+
44
+ def _get_random_period(calendar):
45
+ lower = Faker().future_datetime() + BDay(0)
46
+ upper = lower + BDay(random.randint(1, 7))
47
+ return TimestamptzRange(
48
+ lower=lower.to_pydatetime().astimezone(calendar.timezone),
49
+ upper=upper.to_pydatetime().astimezone(calendar.timezone),
50
+ )
51
+
52
+
53
+ class VacationTypeFactory(AbsenceRequestTypeFactory):
54
+ title = "Vacation"
55
+ is_vacation = True
56
+ is_timeoff = True
57
+ is_extensible = False
58
+ auto_approve = False
59
+
60
+
61
+ class TimeOffTypeFactory(AbsenceRequestTypeFactory):
62
+ title = "TimeOff"
63
+ is_vacation = False
64
+ is_timeoff = True
65
+ is_extensible = True
66
+ auto_approve = True
67
+
68
+
69
+ class AbsenceRequestFactory(factory.django.DjangoModelFactory):
70
+ class Meta:
71
+ model = AbsenceRequest
72
+
73
+ period = factory.LazyAttribute(lambda o: _get_random_period(o.employee.calendar))
74
+ attachment = factory.django.FileField()
75
+ type = factory.SubFactory("wbhuman_resources.factories.AbsenceRequestTypeFactory")
76
+ employee = factory.SubFactory("wbhuman_resources.factories.EmployeeHumanResourceFactory")
77
+ notes = factory.Faker("text")
78
+ reason = factory.Faker("text")
79
+
80
+
81
+ class VacationRequestFactory(AbsenceRequestFactory):
82
+ type = factory.SubFactory(VacationTypeFactory)
83
+ status = AbsenceRequest.Status.APPROVED
84
+
85
+
86
+ class TimeOffRequestFactory(AbsenceRequestFactory):
87
+ type = factory.SubFactory(TimeOffTypeFactory)
88
+ status = AbsenceRequest.Status.APPROVED
89
+
90
+
91
+ class AbsenceRequestPeriodsFactory(factory.django.DjangoModelFactory):
92
+ class Meta:
93
+ model = AbsenceRequestPeriods
94
+ django_get_or_create = ["employee", "default_period", "date"]
95
+
96
+ date = factory.LazyAttribute(lambda o: (fake.date_object() + BDay(0)).date())
97
+ employee = factory.SubFactory("wbhuman_resources.factories.EmployeeHumanResourceFactory")
98
+ default_period = factory.LazyAttribute(lambda o: DefaultDailyPeriodFactory.create(calendar=o.employee.calendar))
99
+ request = factory.LazyAttribute(
100
+ lambda o: AbsenceRequestFactory.create(
101
+ status=AbsenceRequest.Status.APPROVED,
102
+ type=VacationTypeFactory.create(),
103
+ employee=o.employee,
104
+ period=TimestamptzRange(
105
+ lower=o.default_period.get_lower_datetime(o.date), upper=o.default_period.get_upper_datetime(o.date)
106
+ ),
107
+ )
108
+ )
109
+ balance = factory.LazyAttribute(lambda o: EmployeeYearBalanceFactory.create(year=o.date.year, employee=o.employee))
@@ -0,0 +1,60 @@
1
+ import zoneinfo
2
+ from datetime import time
3
+
4
+ import factory
5
+ from faker import Faker
6
+
7
+ from wbhuman_resources.models.calendars import (
8
+ DayOff,
9
+ DayOffCalendar,
10
+ DefaultDailyPeriod,
11
+ )
12
+
13
+ fake = Faker()
14
+
15
+
16
+ class BaseDayOffCalendarFactory(factory.django.DjangoModelFactory):
17
+ title = "Default Calendar"
18
+ resource = "europe.Germany"
19
+ timezone = zoneinfo.ZoneInfo("UTC")
20
+
21
+ class Meta:
22
+ model = DayOffCalendar
23
+ django_get_or_create = ["resource"]
24
+
25
+
26
+ class DayOffCalendarFactory(BaseDayOffCalendarFactory):
27
+ @factory.post_generation
28
+ def post_gen(self, create, extracted, **kwargs):
29
+ if create:
30
+ DefaultDailyPeriodFactory.create(calendar=self)
31
+ DefaultDailyPeriodFactory.create(
32
+ calendar=self,
33
+ lower_time=time(
34
+ 14,
35
+ 0,
36
+ 0,
37
+ ),
38
+ upper_time=time(18, 0, 0),
39
+ title="afternoon",
40
+ )
41
+
42
+
43
+ class DefaultDailyPeriodFactory(factory.django.DjangoModelFactory):
44
+ lower_time = time(9, 0, 0)
45
+ upper_time = time(13, 0, 0)
46
+ title = "morning"
47
+ calendar = factory.SubFactory("wbhuman_resources.factories.BaseDayOffCalendarFactory")
48
+
49
+ class Meta:
50
+ model = DefaultDailyPeriod
51
+ django_get_or_create = ["lower_time", "upper_time", "calendar"]
52
+
53
+
54
+ class DayOffFactory(factory.django.DjangoModelFactory):
55
+ class Meta:
56
+ model = DayOff
57
+
58
+ title = factory.Faker("text", max_nb_chars=64)
59
+ date = factory.Faker("date_object")
60
+ calendar = factory.SubFactory("wbhuman_resources.factories.DayOffCalendarFactory")
@@ -0,0 +1,80 @@
1
+ import factory
2
+ from django.contrib.auth.models import Group
3
+ from django.db.models.signals import post_save
4
+ from faker import Faker
5
+
6
+ from wbhuman_resources.models import (
7
+ BalanceHourlyAllowance,
8
+ EmployeeHumanResource,
9
+ EmployeeWeeklyOffPeriods,
10
+ EmployeeYearBalance,
11
+ Position,
12
+ )
13
+
14
+ fake = Faker()
15
+
16
+
17
+ class PositionFactory(factory.django.DjangoModelFactory):
18
+ class Meta:
19
+ model = Position
20
+
21
+ name = factory.Faker("text", max_nb_chars=64)
22
+ manager = None
23
+
24
+ @factory.post_generation
25
+ def post_gen(self, create, extracted, **kwargs):
26
+ group = Group.objects.get_or_create(name="Test Group")[0]
27
+ self.groups.add(group)
28
+
29
+
30
+ @factory.django.mute_signals(post_save)
31
+ class EmployeeHumanResourceFactory(factory.django.DjangoModelFactory):
32
+ class Meta:
33
+ model = EmployeeHumanResource
34
+ django_get_or_create = ["profile"]
35
+
36
+ is_active = True
37
+ profile = factory.SubFactory("wbcore.contrib.authentication.factories.AuthenticatedPersonFactory")
38
+ direct_manager = factory.SubFactory("wbcore.contrib.directory.factories.PersonFactory")
39
+ calendar = factory.SubFactory("wbhuman_resources.factories.DayOffCalendarFactory")
40
+ position = factory.SubFactory(PositionFactory)
41
+ enrollment_at = factory.Faker("past_date")
42
+ extra_days_frequency = EmployeeHumanResource.ExtraDaysBalanceFrequency.YEARLY
43
+
44
+ contract_type = EmployeeHumanResource.ContractType.INTERNAL
45
+ occupancy_rate = factory.Faker("pyfloat", min_value=0, max_value=1)
46
+
47
+
48
+ class EmployeeYearBalanceFactory(factory.django.DjangoModelFactory):
49
+ class Meta:
50
+ model = EmployeeYearBalance
51
+ django_get_or_create = ["employee", "year"]
52
+
53
+ employee = factory.SubFactory("wbhuman_resources.factories.EmployeeHumanResourceFactory")
54
+ year = factory.Faker("pyint", min_value=1970, max_value=2022)
55
+ extra_balance = factory.Faker("pyint", min_value=0, max_value=20)
56
+
57
+ @factory.post_generation
58
+ def post_gen(self, create, extracted, **kwargs):
59
+ if create:
60
+ BalanceHourlyAllowanceFactory.create(balance=self)
61
+
62
+
63
+ class BalanceHourlyAllowanceFactory(factory.django.DjangoModelFactory):
64
+ balance = factory.SubFactory("wbhuman_resources.factories.EmployeeYearBalanceFactory")
65
+ period_index = 1
66
+ hourly_allowance = factory.Faker("pyint", min_value=160, max_value=200)
67
+
68
+ class Meta:
69
+ model = BalanceHourlyAllowance
70
+ django_get_or_create = ["balance", "period_index"]
71
+
72
+
73
+ class EmployeeWeeklyOffPeriodsFactory(factory.django.DjangoModelFactory):
74
+ employee = factory.SubFactory("wbhuman_resources.factories.EmployeeHumanResourceFactory")
75
+ period = factory.LazyAttribute(lambda x: x.employee.calendar.default_periods.order_by("?")[0])
76
+ weekday = factory.Faker("pyint", min_value=0, max_value=7)
77
+
78
+ class Meta:
79
+ model = EmployeeWeeklyOffPeriods
80
+ django_get_or_create = ["employee", "period", "weekday"]
@@ -0,0 +1,155 @@
1
+ import random
2
+ from datetime import timedelta
3
+
4
+ import factory
5
+ import pytz
6
+ from django.utils import timezone
7
+ from faker import Faker
8
+ from psycopg.types.range import DateRange
9
+ from wbcore.contrib.authentication.factories import InternalUserFactory
10
+ from wbcore.contrib.directory.factories.entries import PersonFactory
11
+
12
+ from wbhuman_resources.models import (
13
+ KPI,
14
+ Evaluation,
15
+ Review,
16
+ ReviewAnswer,
17
+ ReviewGroup,
18
+ ReviewQuestion,
19
+ ReviewQuestionCategory,
20
+ )
21
+
22
+ fake = Faker()
23
+
24
+
25
+ class ReviewGroupFactory(factory.django.DjangoModelFactory):
26
+ class Meta:
27
+ model = ReviewGroup
28
+
29
+ name = factory.Faker("text", max_nb_chars=64)
30
+
31
+ @factory.post_generation
32
+ def employees(self, create, extracted, **kwargs):
33
+ if not create:
34
+ return
35
+
36
+ if extracted:
37
+ for employee in extracted:
38
+ self.employees.add(employee)
39
+
40
+
41
+ class ReviewAbstractFactory(factory.django.DjangoModelFactory):
42
+ class Meta:
43
+ model = Review
44
+ django_get_or_create = ["moderator"]
45
+
46
+ review_group = factory.SubFactory(ReviewGroupFactory)
47
+ review = factory.Faker("date_time_between", start_date="+5d", end_date="+6d", tzinfo=pytz.utc)
48
+ moderator = factory.LazyAttribute(lambda o: InternalUserFactory.create().profile)
49
+ year = factory.Faker("pyint", min_value=1000, max_value=9999)
50
+
51
+
52
+ class ReviewFactory(ReviewAbstractFactory):
53
+ class Meta:
54
+ model = Review
55
+ django_get_or_create = ["moderator", "reviewee", "reviewer"]
56
+
57
+ from_date = factory.Faker("date_between", start_date="+2d", end_date="+3d")
58
+ to_date = factory.Faker("date_between", start_date="+4d", end_date="+5d")
59
+ review_deadline = factory.Faker("date_between", start_date="+5d", end_date="+6d")
60
+ reviewee = factory.LazyAttribute(lambda o: InternalUserFactory.create().profile)
61
+ reviewer = factory.LazyAttribute(lambda o: InternalUserFactory.create().profile)
62
+ feedback_reviewee = factory.Faker("text")
63
+ feedback_reviewer = factory.Faker("text")
64
+
65
+ signed_reviewee = factory.Faker("date_time", tzinfo=pytz.utc)
66
+ signed_reviewer = factory.Faker("date_time", tzinfo=pytz.utc)
67
+ completely_filled_reviewee = factory.Faker("date_time", tzinfo=pytz.utc)
68
+ completely_filled_reviewer = factory.Faker("date_time", tzinfo=pytz.utc)
69
+
70
+
71
+ class CompletedFilledReviewFactory(ReviewFactory):
72
+ completely_filled_reviewee = factory.Faker("date_time", tzinfo=pytz.utc)
73
+ completely_filled_reviewer = factory.Faker("date_time", tzinfo=pytz.utc)
74
+
75
+
76
+ class SignedReviewFactory(CompletedFilledReviewFactory):
77
+ signed_reviewee = factory.Faker("date_time", tzinfo=pytz.utc)
78
+ signed_reviewer = factory.Faker("date_time", tzinfo=pytz.utc)
79
+
80
+
81
+ class ReviewTemplateFactory(ReviewAbstractFactory):
82
+ is_template = True
83
+
84
+
85
+ class ReviewQuestionCategoryFactory(factory.django.DjangoModelFactory):
86
+ class Meta:
87
+ model = ReviewQuestionCategory
88
+
89
+ name = factory.Faker("text", max_nb_chars=64)
90
+ order = factory.Faker("pyint", min_value=0, max_value=9999)
91
+ weight = factory.Faker("pydecimal", right_digits=1, min_value=0, max_value=9999)
92
+
93
+
94
+ class ReviewQuestionNoCategoryFactory(factory.django.DjangoModelFactory):
95
+ class Meta:
96
+ model = ReviewQuestion
97
+
98
+ review = factory.SubFactory(ReviewFactory)
99
+ question = factory.Faker("text")
100
+ order = factory.Faker("pyint", min_value=0, max_value=9999)
101
+ weight = factory.Faker("pydecimal", right_digits=1, min_value=0, max_value=9999)
102
+
103
+
104
+ class ReviewQuestionFactory(ReviewQuestionNoCategoryFactory):
105
+ category = factory.SubFactory(ReviewQuestionCategoryFactory)
106
+
107
+
108
+ class ReviewAnswerFactory(factory.django.DjangoModelFactory):
109
+ class Meta:
110
+ model = ReviewAnswer
111
+
112
+ question = factory.SubFactory(ReviewQuestionFactory)
113
+ answered_by = factory.LazyAttribute(lambda o: InternalUserFactory.create().profile)
114
+ answered_anonymized = hash(factory.SelfAttribute("answered_by"))
115
+ answer_number = factory.Faker("pyint", min_value=0, max_value=9999)
116
+ answer_text = factory.Faker("text")
117
+
118
+
119
+ class ReviewAnswerNoCategoryFactory(ReviewAnswerFactory):
120
+ question = factory.SubFactory(ReviewQuestionNoCategoryFactory)
121
+
122
+
123
+ class KPIFactory(factory.django.DjangoModelFactory):
124
+ class Meta:
125
+ model = KPI
126
+
127
+ name = factory.Faker("pystr")
128
+ goal = factory.Faker("pyint", min_value=0, max_value=9999)
129
+ period = DateRange(timezone.now().date(), timezone.now().date() + timedelta(days=random.randint(4, 5)))
130
+ handler = "wbcrm.kpi_handlers.activities.NumberOfActivityKPI"
131
+
132
+ @factory.post_generation
133
+ def evaluated_persons(self, create, extracted, **kwargs):
134
+ if not create:
135
+ return
136
+
137
+ if extracted:
138
+ for person in extracted:
139
+ self.evaluated_persons.add(person)
140
+
141
+
142
+ class DefaultPersonKPIFactory(KPIFactory):
143
+ @factory.post_generation
144
+ def evaluated_persons(self, create, extracted, **kwargs):
145
+ self.evaluated_persons.add(PersonFactory())
146
+
147
+
148
+ class EvaluationFactory(factory.django.DjangoModelFactory):
149
+ class Meta:
150
+ model = Evaluation
151
+
152
+ kpi = factory.SubFactory(KPIFactory)
153
+ person = factory.SubFactory("wbcore.contrib.directory.factories.PersonFactory")
154
+ evaluated_period = DateRange(timezone.now().date(), timezone.now().date() + timedelta(days=random.randint(4, 5)))
155
+ evaluation_date = timezone.now().date()
@@ -0,0 +1,20 @@
1
+ from .absence import (
2
+ AbsenceRequestEmployeeHumanResourceFilterSet,
3
+ AbsenceRequestFilter,
4
+ AbsenceTypeCountEmployeeModelFilterSet,
5
+ )
6
+ from .absence_graphs import AbsenceRequestPlannerFilter, AbsenceTableFilter
7
+ from .calendars import DayOffFilter
8
+ from .employee import EmployeeBalanceFilterSet, EmployeeFilterSet, PositionFilterSet
9
+ from .kpi import KPIFilterSet, KPIEvaluationFilterSet, KPIEvaluationPandasFilter
10
+ from .review import (
11
+ ReviewGroupFilter,
12
+ ReviewTemplateFilter,
13
+ ReviewFilter,
14
+ ReviewQuestionCategoryFilter,
15
+ ReviewQuestionFilter,
16
+ ReviewAnswerFilter,
17
+ ReviewProgressReviewFilter,
18
+ RatingReviewAnswerReviewFilter,
19
+ )
20
+ from .signals import *
@@ -0,0 +1,109 @@
1
+ from datetime import date, timedelta
2
+
3
+ import pandas as pd
4
+ from django.utils.timezone import localdate
5
+ from django.utils.translation import gettext_lazy as _
6
+ from psycopg.types.range import TimestamptzRange
7
+ from wbcore import filters as wb_filters
8
+ from wbcore.contrib.agenda.filters import CalendarItemPeriodBaseFilterSet
9
+
10
+ from wbhuman_resources.models import (
11
+ AbsenceRequest,
12
+ AbsenceRequestPeriods,
13
+ AbsenceRequestType,
14
+ Position,
15
+ )
16
+
17
+
18
+ def current_year_date_start(*args, **kwargs):
19
+ d = localdate()
20
+ return date(d.year, 1, 1)
21
+
22
+
23
+ def current_year_date_end(*args, **kwargs):
24
+ d = localdate()
25
+ return max((d + pd.tseries.offsets.YearEnd(1)).date(), d + timedelta(days=60))
26
+
27
+
28
+ class AbsenceRequestFilter(CalendarItemPeriodBaseFilterSet):
29
+ conference_room = boolean_conference_room = None
30
+ department = wb_filters.ModelChoiceFilter(
31
+ label=_("Department"),
32
+ queryset=Position.objects.all(),
33
+ endpoint=Position.get_representation_endpoint(),
34
+ value_key=Position.get_representation_value_key(),
35
+ label_key=Position.get_representation_label_key(),
36
+ method="filter_position",
37
+ )
38
+ is_active_employee = wb_filters.BooleanFilter(
39
+ label=_("Is Employee Active"), method="boolean_is_active_employee", initial=True
40
+ )
41
+ _total_hours_in_days__gte = wb_filters.NumberFilter(
42
+ lookup_expr="gte", label="Total hours", field_name="_total_hours_in_days"
43
+ )
44
+ _total_vacation_hours_in_days__gte = wb_filters.NumberFilter(
45
+ lookup_expr="gte", label="Total hours", field_name="_total_vacation_hours_in_days"
46
+ )
47
+ _total_hours_in_days__lte = wb_filters.NumberFilter(
48
+ lookup_expr="lte", label="Total hours", field_name="_total_hours_in_days"
49
+ )
50
+ _total_vacation_hours_in_days__lte = wb_filters.NumberFilter(
51
+ lookup_expr="lte", label="Total hours", field_name="_total_vacation_hours_in_days"
52
+ )
53
+
54
+ def get_default_period(self):
55
+ return TimestamptzRange(lower=current_year_date_start(), upper=current_year_date_end())
56
+
57
+ def boolean_is_active_employee(self, queryset, name, value):
58
+ if value:
59
+ return queryset.filter(employee__is_active=True)
60
+ # return queryset.filter(employee__in=EmployeeHumanResource.active_internal_employees.all())
61
+ return queryset
62
+
63
+ def filter_position(self, queryset, name, value):
64
+ if value:
65
+ return queryset.filter(department__in=value.get_descendants(include_self=True)).distinct()
66
+ return queryset
67
+
68
+ class Meta:
69
+ model = AbsenceRequest
70
+ fields = {
71
+ "employee": ["exact"],
72
+ "status": ["exact"],
73
+ "type": ["exact"],
74
+ "created": ["gte", "exact", "lte"],
75
+ }
76
+
77
+
78
+ class AbsenceTypeCountEmployeeModelFilterSet(wb_filters.FilterSet):
79
+ year = wb_filters.YearFilter(field_name="year", lookup_expr="exact")
80
+
81
+ absence_type = wb_filters.ModelChoiceFilter(
82
+ label=_("Type"),
83
+ queryset=AbsenceRequestType.objects.all(),
84
+ endpoint=AbsenceRequestType.get_representation_endpoint(),
85
+ value_key=AbsenceRequestType.get_representation_value_key(),
86
+ label_key=AbsenceRequestType.get_representation_label_key(),
87
+ method="filter_absence_type",
88
+ )
89
+
90
+ def filter_absence_type(self, queryset, label, value):
91
+ if value:
92
+ return queryset.filter(request__type=value)
93
+ return queryset
94
+
95
+ class Meta:
96
+ model = AbsenceRequestPeriods
97
+ fields = {}
98
+
99
+
100
+ class AbsenceRequestEmployeeHumanResourceFilterSet(AbsenceRequestFilter):
101
+ department = is_active_employee = None
102
+
103
+ class Meta:
104
+ model = AbsenceRequest
105
+ fields = {
106
+ "status": ["exact"],
107
+ "type": ["exact"],
108
+ "created": ["gte", "exact", "lte"],
109
+ }
@@ -0,0 +1,85 @@
1
+ from datetime import timedelta
2
+
3
+ import pandas as pd
4
+ from django.utils.timezone import localdate
5
+ from django.utils.translation import gettext_lazy as _
6
+ from dynamic_preferences.registries import global_preferences_registry
7
+ from psycopg.types.range import TimestamptzRange
8
+ from wbcore import filters as wb_filters
9
+
10
+ from wbhuman_resources.models import AbsenceRequestPeriods, DayOffCalendar, Position
11
+
12
+
13
+ def current_year_date_range(*args, **kwargs):
14
+ d = localdate()
15
+ return TimestamptzRange(
16
+ (localdate() - pd.tseries.offsets.Week(weekday=0)).date(),
17
+ max((d + pd.tseries.offsets.YearEnd(1)).date(), d + timedelta(days=60)),
18
+ )
19
+
20
+
21
+ def monday_of_current_week(*args, **kwargs):
22
+ today = localdate()
23
+ return today - timedelta(days=today.weekday())
24
+
25
+
26
+ def get_calendar_default(field, request, view, **kwargs) -> int | None:
27
+ if (profile := request.user.profile) and (employee := getattr(profile, "human_resources", None)):
28
+ return employee.calendar.id
29
+ if calendar := global_preferences_registry.manager()["wbhuman_resources__employee_default_calendar"]:
30
+ return calendar.id
31
+ try:
32
+ return DayOffCalendar.objects.first().id
33
+ except AttributeError:
34
+ return None
35
+
36
+
37
+ class AbsenceRequestPlannerFilter(wb_filters.FilterSet):
38
+ calendar = wb_filters.ModelChoiceFilter(
39
+ label=_("Calendar"),
40
+ required=True,
41
+ clearable=False,
42
+ queryset=DayOffCalendar.objects.all(),
43
+ endpoint=DayOffCalendar.get_representation_endpoint(),
44
+ value_key=DayOffCalendar.get_representation_value_key(),
45
+ label_key=DayOffCalendar.get_representation_label_key(),
46
+ initial=get_calendar_default,
47
+ method=lambda queryset, label, value: queryset,
48
+ )
49
+
50
+ date = wb_filters.DateRangeFilter(
51
+ label=_("Date Range"),
52
+ method=lambda queryset, label, value: queryset,
53
+ required=True,
54
+ clearable=False,
55
+ initial=current_year_date_range,
56
+ )
57
+ only_employee_with_absence_periods = wb_filters.BooleanFilter(
58
+ initial=False,
59
+ label=_("Only Employee With Absence periods"),
60
+ method=lambda queryset, label, value: queryset,
61
+ )
62
+
63
+ position = wb_filters.ModelChoiceFilter(
64
+ label=_("Position"),
65
+ queryset=Position.objects.all(),
66
+ endpoint=Position.get_representation_endpoint(),
67
+ value_key=Position.get_representation_value_key(),
68
+ label_key=Position.get_representation_label_key(),
69
+ method=lambda queryset, label, value: queryset,
70
+ )
71
+
72
+ class Meta:
73
+ model = AbsenceRequestPeriods
74
+ fields = {}
75
+
76
+
77
+ class AbsenceTableFilter(AbsenceRequestPlannerFilter):
78
+ date_gte = date_lte = None
79
+ date = wb_filters.DateFilter(
80
+ label=_("Week Day"),
81
+ method=lambda queryset, label, value: queryset,
82
+ initial=monday_of_current_week,
83
+ required=True,
84
+ help_text="Change this date to any day on the week you are interested in seeing the presence table",
85
+ )
@@ -0,0 +1,28 @@
1
+ from django.utils.translation import gettext_lazy as _
2
+ from wbcore import filters as wb_filters
3
+ from wbcore.filters.defaults import current_year_date_range
4
+
5
+ from wbhuman_resources.models import DayOff
6
+
7
+
8
+ class DayOffFilter(wb_filters.FilterSet):
9
+ date = wb_filters.DateRangeFilter(
10
+ label=_("Date Range"),
11
+ required=True,
12
+ clearable=False,
13
+ initial=current_year_date_range,
14
+ )
15
+
16
+ def start_filter(self, queryset, name, value):
17
+ if value:
18
+ return queryset.filter(date__gte=value)
19
+ return queryset
20
+
21
+ def end_filter(self, queryset, name, value):
22
+ if value:
23
+ return queryset.filter(date__lte=value)
24
+ return queryset
25
+
26
+ class Meta:
27
+ model = DayOff
28
+ fields = {"count_as_holiday": ["exact"], "calendar": ["exact"]}