Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
adding service function

fixing sort order

adding error handleing

fixing error handler

adding basic tests
  • Loading branch information
DenisDDBT committed Feb 20, 2025
1 parent 9f5f2da commit 6016fe8
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 4 deletions.
48 changes: 47 additions & 1 deletion payroll/api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json

from django.conf import settings
from django.core.exceptions import ValidationError
from django.http import JsonResponse

from core.utils.generic_helpers import get_previous_months_data
Expand All @@ -11,12 +12,14 @@

class EditPayrollApiView(EditPayrollBaseView):
def get(self, request, *args, **kwargs):

employees = list(
payroll_service.get_payroll_data(
payroll_service.get_employee_data(
self.cost_centre,
self.financial_year,
)
)

vacancies = list(
payroll_service.get_vacancies_data(
self.cost_centre,
Expand Down Expand Up @@ -85,3 +88,46 @@ def post(self, request, *args, **kwargs):
)

return JsonResponse({})


class EmployeeNotesApi(EditPayrollBaseView):
def post(self, request, *args, **kwargs):
try:
data = json.loads(request.body)
if not data:
return JsonResponse({"error": "Missing request body"}, status=400)
notes = data.get("notes")
employee_no = data.get("employee_no")

if not notes or not employee_no:
return JsonResponse(
{"error": "Both 'notes' and 'employee_no' are required"}, status=400
)
employee_data = payroll_service.get_employee_data(
self.cost_centre,
self.financial_year,
)
employee = next(
(
item
for item in employee_data
if str(item["employee_no"]) == employee_no
),
None,
)
if employee:
payroll_service.update_employee_notes(
notes,
employee_no,
self.cost_centre,
self.financial_year,
)
return JsonResponse({}, status=204)
except json.JSONDecodeError:
return JsonResponse({"error": "Invalid JSON format"}, status=400)
except ValidationError:
return JsonResponse({"error": "Invalid data provided"}, status=400)
except Exception:
return JsonResponse(
{"error": "An error occurred while processing the request"}, status=500
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.1.5 on 2025-02-17 11:03

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("payroll", "0017_remove_payelementtype_group_and_more"),
]

operations = [
migrations.AddField(
model_name="employeepayperiods",
name="notes",
field=models.TextField(default=""),
),
migrations.AddField(
model_name="vacancypayperiods",
name="notes",
field=models.TextField(default=""),
),
]
1 change: 1 addition & 0 deletions payroll/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class Meta:
period_10 = models.BooleanField(default=True)
period_11 = models.BooleanField(default=True)
period_12 = models.BooleanField(default=True)
notes = models.TextField(default="")

@property
def periods(self) -> list[bool]:
Expand Down
25 changes: 23 additions & 2 deletions payroll/services/payroll.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ class EmployeePayroll(TypedDict):
pay_periods: list[bool]


def get_payroll_data(
def get_employee_data(
cost_centre: CostCentre,
financial_year: FinancialYear,
) -> Iterator[EmployeePayroll]:
Expand Down Expand Up @@ -274,6 +274,7 @@ def get_payroll_data(
basic_pay=obj.basic_pay,
# `first` is OK as there should only be one `pay_periods` with the filters.
pay_periods=obj.pay_periods.first().periods,
notes=obj.pay_periods.first().notes,
)


Expand Down Expand Up @@ -312,6 +313,23 @@ def update_employee_data(
pay_periods.save()


def update_employee_notes(
notes: str,
employee_no: str,
cost_centre: CostCentre,
financial_year: FinancialYear,
) -> None:
if not employee_no:
raise ValueError("employee_no is empty")
pay_period = EmployeePayPeriods.objects.get(
employee__employee_no=employee_no,
employee__cost_centre=cost_centre,
year=financial_year,
)
pay_period.notes = notes
pay_period.save()


class Vacancies(TypedDict):
id: int
grade: str
Expand All @@ -323,6 +341,7 @@ class Vacancies(TypedDict):
hiring_manager: str
hr_ref: str
pay_periods: list[bool]
notes: str


def get_vacancies_data(
Expand All @@ -343,6 +362,7 @@ def get_vacancies_data(
)
)
for obj in qs:
pay_periods = (obj.pay_periods.first(),)
yield Vacancies(
id=obj.pk,
grade=obj.grade.pk,
Expand All @@ -354,7 +374,8 @@ def get_vacancies_data(
hiring_manager=obj.hiring_manager,
hr_ref=obj.hr_ref,
# `first` is OK as there should only be one `pay_periods` with the filters.
pay_periods=obj.pay_periods.first().periods,
pay_periods=pay_periods.periods,
notes=pay_periods.notes,
)


Expand Down
30 changes: 30 additions & 0 deletions payroll/tests/services/test_payroll.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from random import randrange
from statistics import mean
import json

import pytest
from pytest_django.asserts import assertNumQueries
Expand Down Expand Up @@ -297,3 +298,32 @@ def test_scenario_update_forecast(db):
)

assert list(forecast_figures) == list(expected_forecast.values())

def update_notes_success(self, db, client):
url = "http://localhost:8000/payroll/api/888813/2024/employees/notes"
response = client.post(
url,
data=json.dumps(
{
"notes": "some notes",
"employee_no": "150892",
}
),
content_type="application/json",
)

assert response.status_code == 200

def update_notes_fail(self, db, client):
url = "http://localhost:8000/payroll/api/888813/2024/employees/notes"
response = client.post(
url,
data=json.dumps(
{
"notes": "some notes"
}
),
content_type="application/json",
)

assert response.status_code == 400
12 changes: 11 additions & 1 deletion payroll/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.urls import path

from forecast.views.edit_select_cost_centre import ChooseCostCentreView
from payroll.api import EditPayrollApiView, PayModifiersApiView
from payroll.api import EditPayrollApiView, EmployeeNotesApi, PayModifiersApiView

from . import views

Expand All @@ -19,6 +19,16 @@
EditPayrollApiView.as_view(),
name="api",
),
path(
"api/<str:cost_centre_code>/<int:financial_year>/vacancies/notes",
EmployeeNotesApi.as_view(),
name="vacancy_notes",
),
path(
"api/<str:cost_centre_code>/<int:financial_year>/employees/notes",
EmployeeNotesApi.as_view(),
name="employee_notes",
),
path(
"api/<str:cost_centre_code>/<int:financial_year>/pay_modifiers/",
PayModifiersApiView.as_view(),
Expand Down

0 comments on commit 6016fe8

Please sign in to comment.