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 125d39f commit 3105eeb
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 3 deletions.
46 changes: 46 additions & 0 deletions payroll/api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json

import waffle
from django.core.exceptions import ValidationError
from django.http import JsonResponse

from config import flags
Expand All @@ -12,12 +13,14 @@

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

employees = list(
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 @@ -86,3 +89,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
24 changes: 22 additions & 2 deletions payroll/services/payroll.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ def get_employee_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 @@ -320,6 +321,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 @@ -331,6 +349,7 @@ class Vacancies(TypedDict):
hiring_manager: str
hr_ref: str
pay_periods: list[bool]
notes: str


def get_vacancies_data(
Expand All @@ -352,7 +371,7 @@ def get_vacancies_data(
)
for obj in qs:
budget_type = obj.programme_code.budget_type

pay_periods = (obj.pay_periods.first(),)
yield Vacancies(
id=obj.pk,
grade=obj.grade.pk,
Expand All @@ -364,7 +383,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 @@ -315,3 +316,32 @@ def test_update_all_employee_pay_periods(db):

# then there are 2 pay periods
assert EmployeePayPeriods.objects.count() == 2

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 3105eeb

Please sign in to comment.