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-174 Basic display functionality for pay uplift #613

Merged
merged 21 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
51 changes: 26 additions & 25 deletions front_end/src/Apps/Payroll.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import EmployeeRow from "../Components/EditPayroll/EmployeeRow";
import VacancyRow from "../Components/EditPayroll/VacancyRow";
import PayrollTable from "../Components/EditPayroll/PayrollTable";
import Tabs, { Tab } from "../Components/EditPayroll/Tabs";
import EditPayModifier from "../Components/EditPayroll/EditPayModifier";
import DisplayAttrition from "../Components/EditPayroll/DisplayAttrition";
import DisplayPayModifier from "../Components/EditPayroll/DisplayPayModifier";
import ToggleCheckbox from "../Components/Common/ToggleCheckbox";
import ErrorSummary from "../Components/Common/ErrorSummary";
import SuccessBanner from "../Components/Common/SuccessBanner";
Expand Down Expand Up @@ -72,7 +73,6 @@ export default function Payroll() {
() => allPayroll.employees.filter((payroll) => payroll.basic_pay <= 0),
[allPayroll],
);

const forecastAndActuals = useMemo(() => {
const total_results = [];

Expand Down Expand Up @@ -124,11 +124,11 @@ export default function Payroll() {
dispatch({ type: "updatePayPeriodsVacancies", id, index, enabled });
}

function handleUpdatePayModifiers(id, index, value) {
dispatch({ type: "updatePayModifiers", id, index, value });
function handleUpdateAttrition(index, value) {
dispatch({ type: "updateAttrition", index, value });
}

function handleCreatePayModifiers() {
function handleCreateAttrition() {
api.createPayModifiers().then((r) => {
getAllPayroll();
});
Expand Down Expand Up @@ -194,10 +194,15 @@ export default function Payroll() {
</a>
</Tab>
<Tab label="Pay modifiers" key="4">
<EditPayModifier
data={allPayroll.pay_modifiers}
onInputChange={handleUpdatePayModifiers}
onCreate={handleCreatePayModifiers}
<DisplayAttrition
attrition={allPayroll.pay_modifiers.attrition}
global_attrition={allPayroll.pay_modifiers.global_attrition}
onInputChange={handleUpdateAttrition}
onCreate={handleCreateAttrition}
/>
<DisplayPayModifier
modifier={allPayroll.pay_modifiers.pay_uplift}
title="Pay uplift"
/>
</Tab>
</Tabs>
Expand Down Expand Up @@ -230,22 +235,15 @@ function updatePayPeriods(data, action) {
});
}

function updatePayModifiers(data, action) {
return data.map((row) => {
if (row.id === action.id) {
const updatedPayModifier = row.pay_modifiers.map((modifier, index) => {
if (index === action.index) {
return parseFloat(action.value);
}
return modifier;
});
return {
...row,
pay_modifiers: updatedPayModifier,
};
function updateAttrition(data, action) {
const updatedAttrition = data.map((value, index) => {
if (index === action.index) {
return parseFloat(action.value);
}
return row;
return value;
});

return updatedAttrition;
}

const payrollReducer = (data, action) => {
Expand All @@ -265,10 +263,13 @@ const payrollReducer = (data, action) => {
vacancies: updatePayPeriods(data.vacancies, action),
};
}
case "updatePayModifiers": {
case "updateAttrition": {
return {
...data,
pay_modifiers: updatePayModifiers(data.pay_modifiers, action),
pay_modifiers: {
attrition: updateAttrition(data.pay_modifiers.attrition, action),
pay_uplift: data.pay_modifiers.pay_uplift,
},
};
}
}
Expand Down
74 changes: 74 additions & 0 deletions front_end/src/Components/EditPayroll/DisplayAttrition/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import PayModifierHeaders from "../PayModifierHeaders";
import DisplayPayModifier from "../DisplayPayModifier";

const DisplayAttrition = ({
attrition = [],
global_attrition = [],
onInputChange,
onCreate,
}) => {
if (attrition.length > 0) {
return (
<div className="govuk-form-group">
<h3 className="govuk-heading-s">Attrition</h3>
<table className="govuk-table">
<PayModifierHeaders />
<tbody className="govuk-table__body">
<tr className="govuk-table__row">
{attrition.map((value, index) => {
return (
<td className="govuk-table__cell" key={index}>
<input
className="govuk-input"
id={`modifier-${index}`}
name={`modifier-${index}`}
type="number"
defaultValue={value}
onChange={(e) => onInputChange(index, e.target.value)}
></input>
</td>
);
})}
</tr>
</tbody>
</table>
</div>
);
}

if (global_attrition.length > 0) {
return (
<>
<DisplayPayModifier
modifier={global_attrition}
title="Global attrition"
/>
<p className="govuk-body">
This attrition is for the current financial year. You can add one for
this specific cost centre instead.
</p>
<button
className="govuk-button govuk-button--secondary"
onClick={onCreate}
>
Add Attrition
</button>
</>
);
}

return (
<>
<h3 className="govuk-heading-s">Attrition</h3>
<p className="govuk-body">No attrition set</p>
<button
className="govuk-button govuk-button--secondary"
onClick={onCreate}
>
Add Attrition
</button>
</>
);
};

export default DisplayAttrition;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import PayModifierHeaders from "../PayModifierHeaders";

const DisplayPayModifier = ({ modifier = [], title }) => {
if (modifier.length === 0) {
return (
<>
<h3 className="govuk-heading-s">{title}</h3>
<p className="govuk-body">No {title.toLowerCase()} set</p>
</>
);
}

return (
<>
<h3 className="govuk-heading-s">{title}</h3>
<table className="govuk-table">
<PayModifierHeaders />
<tbody className="govuk-table__body">
<tr className="govuk-table__row">
{modifier.map((value, index) => {
return (
<td className="govuk-table__cell" key={index}>
{value}
</td>
);
})}
</tr>
</tbody>
</table>
</>
);
};

export default DisplayPayModifier;
58 changes: 0 additions & 58 deletions front_end/src/Components/EditPayroll/EditPayModifier/index.jsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { monthsToTitleCase } from "../../../Util";

const PayModifierHeaders = () => {
return (
<thead className="govuk-table__head">
<tr className="govuk-table__row">
{monthsToTitleCase.map((header) => {
return (
<th scope="col" className="govuk-table__header" key={header}>
{header}
</th>
);
})}
</tr>
</thead>
);
};

export default PayModifierHeaders;
7 changes: 4 additions & 3 deletions front_end/src/Components/EditPayroll/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@

/**
* @typedef {Object} PayModifierData
* @property {number} id - The pay modifier's pk.
* @property {number[]} pay_modifiers - The pay modifier's monthly percentages
* @property {float[]} global_attrition - The monthly percentages for attrition for that financial year
* @property {float[]} attrition - The monthly percentages for attrition scoped to the cost centre
* @property {float[]} pay_uplift - The monthly percentages for pay uplift
*/

/**
Expand Down Expand Up @@ -60,7 +61,7 @@
* @typedef {Object} PayrollData
* @property {EmployeeData[]} employees - A list of employees
* @property {VacancyData[]} vacancies - A list of vacancies
* @property {PayModifierData[]} pay_modifiers - A list of pay modifiers
* @property {PayModifierData[]} pay_modifiers - An object with attrition, global attrition and pay uplift
* @property {ForecastData[]} forecast - A list of forecast data
* @property {PreviousMonthsData[]} previous_months - A list of months with actuals loaded
*/
Expand Down
21 changes: 10 additions & 11 deletions payroll/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,11 @@ def get(self, request, *args, **kwargs):
self.financial_year,
)
)
pay_modifiers = list(
payroll_service.get_pay_modifiers_data(
self.cost_centre,
self.financial_year,
)
pay_modifiers = payroll_service.get_pay_modifiers_data(
self.cost_centre,
self.financial_year,
)

forecast = list(
payroll_service.payroll_forecast_report(
self.cost_centre, self.financial_year
Expand All @@ -52,7 +51,6 @@ def get(self, request, *args, **kwargs):

def post(self, request, *args, **kwargs):
data = json.loads(request.body)

payroll_service.update_employee_data(
self.cost_centre,
self.financial_year,
Expand All @@ -63,11 +61,12 @@ def post(self, request, *args, **kwargs):
self.financial_year,
data["vacancies"],
)
payroll_service.update_pay_modifiers_data(
self.cost_centre,
self.financial_year,
data["pay_modifiers"],
)
if data["pay_modifiers"]["attrition"]:
payroll_service.update_attrition_data(
self.cost_centre,
self.financial_year,
data["pay_modifiers"]["attrition"],
)

if settings.PAYROLL.ENABLE_FORECAST is True:
payroll_service.update_payroll_forecast(
Expand Down
Loading
Loading