From 8c3c96b8c9dab722819e8112c6cf4e538b188a9a Mon Sep 17 00:00:00 2001 From: Kelvin Jayanoris Date: Thu, 15 Oct 2020 13:07:34 +0300 Subject: [PATCH 1/6] Add a way to calculate accurate leave day counts --- small_small_hr/models.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/small_small_hr/models.py b/small_small_hr/models.py index 3e97c25..e17ee61 100644 --- a/small_small_hr/models.py +++ b/small_small_hr/models.py @@ -347,6 +347,15 @@ def duration(self): """Get duration as a property.""" return self.get_duration() + @cached_property + def day_count(self): + """ + Get the number of leave days as a property. + + This takes into account holidays and weekend policy. + """ + return get_real_leave_duration(leave_obj=self) + class OverTime(BaseStaffRequest): """Overtime model class.""" @@ -507,6 +516,24 @@ def get_days(start: object, end: object): yield local_start.date() + timedelta(days=i) +def get_real_leave_duration(leave_obj: Leave) -> Decimal: + """ + Get the real leave duration. + + Takes into account public holidays, weekends and weekend policy + """ + count = Decimal(0) + free_days = FreeDay.objects.filter( + date__gte=leave_obj.start.date(), date__lte=leave_obj.end.date() + ).values_list("date", flat=True) + days = get_days(start=leave_obj.start, end=leave_obj.end) + for day in days: + if day not in free_days: + day_value = settings.SSHR_DAY_LEAVE_VALUES[day.isoweekday()] + count = count + Decimal(day_value) + return count + + def get_taken_leave_days( staffprofile: object, status: str, leave_type: str, start_year: int, end_year: int ): From e11e47de47f53b2b4a93e61f1658929ed12d559f Mon Sep 17 00:00:00 2001 From: Kelvin Jayanoris Date: Thu, 15 Oct 2020 13:08:51 +0300 Subject: [PATCH 2/6] Add tests for leave day count --- tests/test_models.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/test_models.py b/tests/test_models.py index bc0b04f..9a319c5 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -11,6 +11,8 @@ from small_small_hr.models import FreeDay, Leave, get_taken_leave_days from small_small_hr.utils import create_free_days +# pylint: disable=hard-coded-auth-user + class TestModels(TestCase): """Test class for models.""" @@ -41,6 +43,47 @@ def test_freedays_str(self): ).__str__(), ) + @override_settings( + SSHR_DAY_LEAVE_VALUES={ + 1: 1, # Monday + 2: 1, # Tuesday + 3: 1, # Wednesday + 4: 1, # Thursday + 5: 1, # Friday + 6: 0.5, # Saturday + 7: 0, # Sunday + } + ) + def test_leave_day_count(self): + """Test leave object day_count.""" + user = mommy.make("auth.User", first_name="Mosh", last_name="Pitt") + staff = mommy.make("small_small_hr.StaffProfile", user=user) + mommy.make( + "small_small_hr.AnnualLeave", + staff=staff, + year=2017, + leave_type=Leave.REGULAR, + allowed_days=21, + ) + mommy.make( + "small_small_hr.FreeDay", + name="RANDOM HOLIDAY", + date=date(day=15, month=6, year=2017), + ) + # 9.5 days of leave ==> Sun not counted, Sat = 0.5 and 15/6/2017 is holiday + start = datetime(2017, 6, 5, 0, 0, 0, tzinfo=pytz.timezone(settings.TIME_ZONE)) + end = datetime(2017, 6, 16, 0, 0, 0, tzinfo=pytz.timezone(settings.TIME_ZONE)) + leave_obj = mommy.make( + "small_small_hr.Leave", + staff=staff, + start=start, + end=end, + leave_type=Leave.REGULAR, + review_status=Leave.APPROVED, + ) + + self.assertEqual(9.5, leave_obj.day_count) + @override_settings( SSHR_DAY_LEAVE_VALUES={ 1: 1, # Monday From 205fc13ef32fe94ef4f7f4fdc53d84f4a85946a0 Mon Sep 17 00:00:00 2001 From: Kelvin Jayanoris Date: Thu, 15 Oct 2020 13:19:40 +0300 Subject: [PATCH 3/6] Fix flaky test --- tests/test_forms.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/tests/test_forms.py b/tests/test_forms.py index 51d94eb..649be1d 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -1,5 +1,5 @@ """Module to test small_small_hr models.""" -# pylint: disable=too-many-lines +# pylint: disable=too-many-lines,hard-coded-auth-user import os from datetime import date, datetime, timedelta @@ -11,6 +11,7 @@ import pytz from model_mommy import mommy +from model_mommy.recipe import Recipe from model_reviews.models import ModelReview from small_small_hr.forms import ( @@ -39,6 +40,17 @@ class TestForms(TestCase): # pylint: disable=too-many-public-methods def setUp(self): """Set up test class.""" self.factory = RequestFactory() + StaffProfile.objects.rebuild() + self.manager = mommy.make( + "auth.User", first_name="Jane", last_name="Ndoe", email="jane@example.com" + ) + self.user = mommy.make( + "auth.User", first_name="Bob", last_name="Ndoe", email="bob@example.com" + ) + manager_mommy = Recipe(StaffProfile, lft=None, rght=None, user=self.manager) + staff_mommy = Recipe(StaffProfile, lft=None, rght=None, user=self.user) + self.manager_profile = manager_mommy.make() + self.staffprofile = staff_mommy.make() def test_annual_leave_form(self): """Test AnnualLeaveForm.""" @@ -1266,11 +1278,9 @@ def test_staff_profile_admin_create_form(self): def test_staff_profile_admin_form(self): """Test StaffProfileAdminForm.""" - manager = mommy.make("auth.User", username="manager") - managerprofile = mommy.make("small_small_hr.StaffProfile", user=manager) - - user = mommy.make("auth.User") - staffprofile = mommy.make("small_small_hr.StaffProfile", user=user) + managerprofile = self.manager_profile + user = self.user + staffprofile = self.staffprofile request = self.factory.get("/") request.session = {} From 47c34fd96607f5c5ca72ec10a76926b03cd329dd Mon Sep 17 00:00:00 2001 From: Kelvin Jayanoris Date: Thu, 15 Oct 2020 13:21:51 +0300 Subject: [PATCH 4/6] Silence pylint wanrings --- tests/test_emails.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_emails.py b/tests/test_emails.py index e5026e7..8c15a3c 100644 --- a/tests/test_emails.py +++ b/tests/test_emails.py @@ -1,4 +1,5 @@ """Module to test small_small_hr Emails.""" +# pylint: disable=hard-coded-auth-user from datetime import datetime from django.conf import settings From 3d9878f7e88b812cc54646b573f8d7371150d32d Mon Sep 17 00:00:00 2001 From: Kelvin Jayanoris Date: Thu, 15 Oct 2020 13:22:27 +0300 Subject: [PATCH 5/6] Use proper day count for leave --- .../small_small_hr/email/leave_application_email_body.html | 2 +- .../small_small_hr/email/leave_application_email_body.txt | 2 +- .../small_small_hr/email/leave_completed_email_body.html | 4 ++-- .../small_small_hr/email/leave_completed_email_body.txt | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/small_small_hr/templates/small_small_hr/email/leave_application_email_body.html b/small_small_hr/templates/small_small_hr/email/leave_application_email_body.html index c4ce072..0bca4c7 100644 --- a/small_small_hr/templates/small_small_hr/email/leave_application_email_body.html +++ b/small_small_hr/templates/small_small_hr/email/leave_application_email_body.html @@ -1,5 +1,5 @@ {{ object.content_object.staff.get_name }} requested time off:

-{{ object.content_object.duration.days }} days of {{ object.content_object.get_leave_type_display}}
+{{ object.content_object.day_count }} days of {{ object.content_object.get_leave_type_display}}
{{ object.content_object.start|date:"D, d M Y" }} - {{ object.content_object.end|date:"D, d M Y" }}
Available Balance: {{ object.content_object.staff.get_available_leave_days|floatformat:2 }} days

Please log in to process the above: http://{{SITE.domain}}/reviews/{{ object.pk }} diff --git a/small_small_hr/templates/small_small_hr/email/leave_application_email_body.txt b/small_small_hr/templates/small_small_hr/email/leave_application_email_body.txt index c1f3028..33c7311 100644 --- a/small_small_hr/templates/small_small_hr/email/leave_application_email_body.txt +++ b/small_small_hr/templates/small_small_hr/email/leave_application_email_body.txt @@ -1,6 +1,6 @@ {{ object.content_object.staff.get_name }} requested time off: -{{ object.content_object.duration.days }} days of {{ object.content_object.get_leave_type_display}} +{{ object.content_object.day_count }} days of {{ object.content_object.get_leave_type_display}} {{ object.content_object.start|date:"D, d M Y" }} - {{ object.content_object.end|date:"D, d M Y" }} Available Balance: {{ object.content_object.staff.get_available_leave_days|floatformat:2 }} days diff --git a/small_small_hr/templates/small_small_hr/email/leave_completed_email_body.html b/small_small_hr/templates/small_small_hr/email/leave_completed_email_body.html index a2e32b4..5c8ae5d 100644 --- a/small_small_hr/templates/small_small_hr/email/leave_completed_email_body.html +++ b/small_small_hr/templates/small_small_hr/email/leave_completed_email_body.html @@ -1,8 +1,8 @@ {{ object.content_object.staff.get_name }},

-Your time off request for {{ object.content_object.duration.days }} days of {{ object.content_object.get_leave_type_display }} from {{ object.content_object.start|date:"d M" }} - {{ object.content_object.end|date:"d M" }} has been {{ object.content_object.get_review_status_display|lower }}.

+Your time off request for {{ object.content_object.day_count }} days of {{ object.content_object.get_leave_type_display }} from {{ object.content_object.start|date:"d M" }} - {{ object.content_object.end|date:"d M" }} has been {{ object.content_object.get_review_status_display|lower }}.

{{ object.content_object.start|date:"D, d M Y" }} - {{ object.content_object.end|date:"D, d M Y" }}
{{ object.content_object.get_leave_type_display}}
-{{ object.content_object.duration.days }} days
+{{ object.content_object.day_count }} days
Status: {{ object.content_object.get_review_status_display }}

Thank you,
diff --git a/small_small_hr/templates/small_small_hr/email/leave_completed_email_body.txt b/small_small_hr/templates/small_small_hr/email/leave_completed_email_body.txt index 955f22f..12bb225 100644 --- a/small_small_hr/templates/small_small_hr/email/leave_completed_email_body.txt +++ b/small_small_hr/templates/small_small_hr/email/leave_completed_email_body.txt @@ -1,10 +1,10 @@ {{ object.content_object.staff.get_name }}, -Your time off request for {{ object.content_object.duration.days }} days of {{ object.content_object.get_leave_type_display }} from {{ object.content_object.start|date:"d M" }} - {{ object.content_object.end|date:"d M" }} has been {{ object.content_object.get_review_status_display|lower }}. +Your time off request for {{ object.content_object.day_count }} days of {{ object.content_object.get_leave_type_display }} from {{ object.content_object.start|date:"d M" }} - {{ object.content_object.end|date:"d M" }} has been {{ object.content_object.get_review_status_display|lower }}. {{ object.content_object.start|date:"D, d M Y" }} - {{ object.content_object.end|date:"D, d M Y" }} {{ object.content_object.get_leave_type_display}} -{{ object.content_object.duration.days }} days +{{ object.content_object.day_count }} days Status: {{ object.content_object.get_review_status_display }} Thank you, From c9a4efc07bd4ac20e260c5f34c80b0c3da5a3cbb Mon Sep 17 00:00:00 2001 From: Kelvin Jayanoris Date: Thu, 15 Oct 2020 13:22:58 +0300 Subject: [PATCH 6/6] =?UTF-8?q?=E2=86=91=20bump=20to=20version=200.4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- small_small_hr/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/small_small_hr/__init__.py b/small_small_hr/__init__.py index 97e6b04..513c17e 100644 --- a/small_small_hr/__init__.py +++ b/small_small_hr/__init__.py @@ -1,5 +1,5 @@ """Main init file for small_small_hr.""" -VERSION = (0, 3, 0) +VERSION = (0, 4, 0) __version__ = ".".join(str(v) for v in VERSION) # pylint: disable=invalid-name default_app_config = "small_small_hr.apps.SmallSmallHrConfig" # noqa