Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FFT-80 Integrate payroll into forecast #541

Merged
merged 6 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion forecast/templates/forecast/edit/choose_cost_centre.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
{% breadcrumb "Choose cost centre" "choose_cost_centre" %}
{% endblock %}

{% block title %}Edit Forecast - Choose Cost Centre{% endblock %}
{% block title %}Edit {{ view.get_page_header }} - Choose Cost Centre{% endblock %}

{% block content %}
<h3 class="govuk-heading-l">My cost centres</h3>
<div id="cost-centre-list-app"></div>
{% endblock %}
{% block scripts %}
<script>
window.nextPage = "{{ view.next_page }}";
window.costCentres = {{ view.get_user_cost_centres|safe }};
window.currentFinancialYear = {{ view.get_financial_year|safe }};
window.currentFinancialYearDisplay = "{{ view.get_financial_year_display|safe }}";
Expand Down
4 changes: 4 additions & 0 deletions forecast/views/edit_select_cost_centre.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class ChooseCostCentreView(
template_name = "forecast/edit/choose_cost_centre.html"
form_class = MyCostCentresForm
cost_centre = None
next_page = "forecast"

def test_func(self):
can_edit = can_edit_at_least_one_cost_centre(self.request.user)
Expand Down Expand Up @@ -80,6 +81,9 @@ def get_user_cost_centres(self):

return json.dumps(cost_centres)

def get_page_header(self):
return self.next_page.capitalize()

def get_form_kwargs(self):
kwargs = super(ChooseCostCentreView, self).get_form_kwargs()
kwargs["user"] = self.request.user
Expand Down
17 changes: 15 additions & 2 deletions front_end/src/Apps/Payroll.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useReducer } from "react";
import { useEffect, useReducer, useState } from "react";

import EditPayroll from "../Components/EditPayroll";
import * as api from "../Components/EditPayroll/api";
Expand All @@ -7,15 +7,27 @@ const initialPayrollState = [];

export default function Payroll() {
const [payroll, dispatch] = useReducer(payrollReducer, initialPayrollState);
const [saveSuccess, setSaveSuccess] = useState(false);

useEffect(() => {
const savedSuccessFlag = localStorage.getItem("saveSuccess");
if (savedSuccessFlag === "true") {
setSaveSuccess(true);
localStorage.removeItem("saveSuccess");
}

api.getPayrollData().then((data) => dispatch({ type: "fetched", data }));
}, []);

// Handlers
async function handleSavePayroll() {
try {
api.postPayrollData(payroll);
await api.postPayrollData(payroll);

setSaveSuccess(true);
localStorage.setItem("saveSuccess", "true");

window.location.reload();
} catch (error) {
console.error("Error saving payroll: ", error);
}
Expand All @@ -30,6 +42,7 @@ export default function Payroll() {
payroll={payroll}
onSavePayroll={handleSavePayroll}
onTogglePayPeriods={handleTogglePayPeriods}
saveSuccess={saveSuccess}
/>
);
}
Expand Down
4 changes: 3 additions & 1 deletion front_end/src/Components/CostCentreList/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
const [financialYears, setFinancialYears] = useState([])
const [forecastFinYearDisplay, setForecastFinYearDisplay] = useState(null)
const [forecastFinYear, setForecastFinYear] = useState(null)
const [nextPage, setNextPage] = useState(null)

useEffect(() => {
const timer = () => {
Expand All @@ -18,6 +19,7 @@
if (window.financialYears){
setFinancialYears(window.financialYears)
}
setNextPage(window.nextPage)
} else {
timer()
}
Expand Down Expand Up @@ -68,7 +70,7 @@
<ul className="cost-centre-list">
{displayedCentres.map((costCentre, index) => {
return <li key={index}>
<a href={ `/forecast/edit/${costCentre.code}/${forecastFinYear}` } className="govuk-link">{costCentre.code} - {costCentre.name}</a>
<a href={ `/${nextPage}/edit/${costCentre.code}/${forecastFinYear}` } className="govuk-link">{costCentre.code} - {costCentre.name}</a>

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.
</li>
})}
</ul>
Expand Down
13 changes: 13 additions & 0 deletions front_end/src/Components/EditPayroll/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default function EditPayroll({
payroll,
onSavePayroll,
onTogglePayPeriods,
saveSuccess,
}) {
const headers = [
"Name",
Expand All @@ -30,6 +31,18 @@ export default function EditPayroll({
];
return (
<>
{saveSuccess && (
<div className="govuk-notification-banner govuk-notification-banner--success">
<div className="govuk-notification-banner__header">
<h2
className="govuk-notification-banner__title"
id="govuk-notification-banner-title"
>
Success
</h2>
</div>
</div>
)}
<PayrollTable
headers={headers}
payroll={payroll}
Expand Down
25 changes: 25 additions & 0 deletions payroll/migrations/0003_employee_programme_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 4.2.15 on 2024-10-30 12:40

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("chartofaccountDIT", "0015_alter_simplehistoryanalysis1_options_and_more"),
("payroll", "0002_alter_employeepayperiods_options"),
]

operations = [
migrations.AddField(
model_name="employee",
name="programme_code",
field=models.ForeignKey(
default=338887,
on_delete=django.db.models.deletion.PROTECT,
to="chartofaccountDIT.programmecode",
),
preserve_default=False,
),
]
14 changes: 13 additions & 1 deletion payroll/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,19 @@


class Employee(models.Model):
cost_centre = models.ForeignKey("costcentre.CostCentre", models.PROTECT)
cost_centre = models.ForeignKey(
"costcentre.CostCentre",
models.PROTECT,
)
# I've been informed that an employee should only be associated to a single
# programme code. However, programme codes are actually assigned on a per pay
# element basis and in some cases an employee can be associated to multiple. This is
# seen as an edge case and we want to model it such that an employee only has a
# single programme code. We will have to handle this discrepancy somewhere.
programme_code = models.ForeignKey(
"chartofaccountDIT.ProgrammeCode",
models.PROTECT,
)
employee_no = models.CharField(max_length=8, unique=True)
first_name = models.CharField(max_length=32)
last_name = models.CharField(max_length=32)
Expand Down
12 changes: 10 additions & 2 deletions payroll/services/payroll.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ def create_employee_pay_periods(employee: Employee) -> None:


def payroll_forecast_report(cost_centre: CostCentre, financial_year: FinancialYear):

period_sum_annotations = {
f"period_{i+1}_sum": Sum(
F("pay_element__debit_amount") - F("pay_element__credit_amount"),
Expand All @@ -44,7 +43,16 @@ def payroll_forecast_report(cost_centre: CostCentre, financial_year: FinancialYe
cost_centre=cost_centre,
pay_periods__year=financial_year,
)
.values("pay_element__type__group", "pay_element__type__group__name")
.order_by(
"programme_code",
"pay_element__type__group",
)
.values(
"programme_code",
"pay_element__type__group__natural_code",
"pay_element__type__group",
"pay_element__type__group__name",
)
.annotate(**period_sum_annotations)
)

Expand Down
7 changes: 6 additions & 1 deletion payroll/templates/payroll/page/edit_payroll.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

{% block breadcrumbs %}
{{ block.super }}
{% breadcrumb "Choose cost centre" "payroll:choose_cost_centre" %}
{% breadcrumb "Edit payroll" "edit_payroll" %}
{% endblock breadcrumbs %}

Expand All @@ -14,12 +15,14 @@ <h1 class="govuk-heading-l">Edit payroll</h1>

<h1 class="govuk-heading-l">Forecast</h1>
<p class="govuk-body-s">
This is a temporary table to demostrate the forecast figures. Eventually these
This is a temporary table to demonstrate the forecast figures. Eventually these
figures would end up in the "Edit forecast" table.
</p>
<table class="govuk-table">
<thead class="govuk-table__head">
<tr class="govuk-table__row">
<th scope="col" class="govuk-table__header">Programme code</th>
<th scope="col" class="govuk-table__header">Natural code</th>
<th scope="col" class="govuk-table__header">Pay type</th>
{% for month in months %}
<th scope="col" class="govuk-table__header">{{ month }}</th>
Expand All @@ -29,6 +32,8 @@ <h1 class="govuk-heading-l">Forecast</h1>
<tbody class="govuk-table__body">
{% for row in payroll_forecast_report %}
<tr class="govuk-table__row">
<th scope="row" class="govuk-table__header">{{ row.programme_code }}</th>
<th scope="row" class="govuk-table__header">{{ row.pay_element__type__group__natural_code }}</th>
<th scope="row" class="govuk-table__header">{{ row.pay_element__type__group__name }}</th>
<td class="govuk-table__cell">{{ row.period_1_sum }}</td>
<td class="govuk-table__cell">{{ row.period_2_sum }}</td>
Expand Down
8 changes: 7 additions & 1 deletion payroll/urls.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from django.urls import path

from forecast.views.edit_select_cost_centre import ChooseCostCentreView

from . import views


app_name = "payroll"

urlpatterns = [
# TODO: Add choose financial year and cost centre url.
path(
"edit/<str:cost_centre_code>/<int:financial_year>/",
views.edit_payroll_page,
Expand All @@ -17,4 +18,9 @@
views.PayrollView.as_view(),
name="api",
),
path(
"edit/choose-cost-centre/",
ChooseCostCentreView.as_view(next_page="payroll"),
name="choose_cost_centre",
),
]