Skip to content

Commit

Permalink
feat: partially waive user's fines (#344)
Browse files Browse the repository at this point in the history
* feat: add amount to waiveFine debtor service method

* test: add tests for debtor service

* feat: make endpoint backwards compatible with old spec

* docs: add deprecation comments

* fix: build error
  • Loading branch information
Yoronex authored Oct 15, 2024
1 parent c435c57 commit 4d9a271
Show file tree
Hide file tree
Showing 7 changed files with 461 additions and 107 deletions.
16 changes: 8 additions & 8 deletions src/controller/debtor-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export default class DebtorController extends BaseController {
}

try {
res.json(await DebtorService.getFineHandoutEvents({ take, skip }));
res.json(await new DebtorService().getFineHandoutEvents({ take, skip }));
} catch (error) {
this.logger.error('Could not return all fine handout event:', error);
res.status(500).json('Internal server error.');
Expand All @@ -152,7 +152,7 @@ export default class DebtorController extends BaseController {
this.logger.trace('Get fine handout event', id, 'by', req.token.user);

try {
res.json(await DebtorService.getSingleFineHandoutEvent(Number.parseInt(id, 10)));
res.json(await new DebtorService().getSingleFineHandoutEvent(Number.parseInt(id, 10)));
} catch (error) {
this.logger.error('Could not return fine handout event:', error);
res.status(500).json('Internal server error.');
Expand Down Expand Up @@ -182,7 +182,7 @@ export default class DebtorController extends BaseController {
return;
}

await DebtorService.deleteFine(parsedId);
await new DebtorService().deleteFine(parsedId);
res.status(204).send();
} catch (error) {
this.logger.error('Could not return fine handout event:', error);
Expand Down Expand Up @@ -223,7 +223,7 @@ export default class DebtorController extends BaseController {
}

try {
res.json(await DebtorService.calculateFinesOnDate(params));
res.json(await new DebtorService().calculateFinesOnDate(params));
} catch (error) {
this.logger.error('Could not calculate fines:', error);
res.status(500).json('Internal server error.');
Expand Down Expand Up @@ -261,7 +261,7 @@ export default class DebtorController extends BaseController {
}

try {
const result = await DebtorService.handOutFines({ referenceDate, userIds: body.userIds }, req.token.user);
const result = await new DebtorService().handOutFines({ referenceDate, userIds: body.userIds }, req.token.user);
res.json(result);
} catch (error) {
this.logger.error('Could not handout fines:', error);
Expand Down Expand Up @@ -300,7 +300,7 @@ export default class DebtorController extends BaseController {
}

try {
await DebtorService.sendFineWarnings({ referenceDate, userIds: body.userIds });
await new DebtorService().sendFineWarnings({ referenceDate, userIds: body.userIds });
res.status(204).send();
} catch (error) {
this.logger.error('Could not send future fine notification emails:', error);
Expand Down Expand Up @@ -334,7 +334,7 @@ export default class DebtorController extends BaseController {
}

try {
const report = await DebtorService.getFineReport(fromDate, toDate);
const report = await new DebtorService().getFineReport(fromDate, toDate);
res.json(report.toResponse());
} catch (error) {
this.logger.error('Could not get fine report:', error);
Expand Down Expand Up @@ -371,7 +371,7 @@ export default class DebtorController extends BaseController {
}

try {
const report = await DebtorService.getFineReport(fromDate, toDate);
const report = await new DebtorService().getFineReport(fromDate, toDate);

const buffer = fileType === 'PDF' ? await report.createPdf() : await report.createTex();
const from = `${fromDate.getFullYear()}${fromDate.getMonth() + 1}${fromDate.getDate()}`;
Expand Down
20 changes: 20 additions & 0 deletions src/controller/request/debtor-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
* @module debtors
*/

import { DineroObjectRequest } from './dinero-request';

/**
* @typedef {object} HandoutFinesRequest
* @property {Array<integer>} userIds.required - Users to fine. If a user is not eligible for a fine, a fine of 0,00 will be handed out.
Expand All @@ -33,3 +35,21 @@ export interface HandoutFinesRequest {
userIds: number[];
referenceDate: string;
}

/**
* The total request and all its fields are optional for backwards compatibility's sake.
* If this request object is extended, it is probably best to make everything required
* and remove the backwards compatibility, as the frontend will (and should) already use
* this new object. See https://github.com/GEWIS/sudosos-backend/pull/344
*
* @typedef {object} WaiveFinesRequest
* @property {DineroObjectRequest} amount - The amount of fines that have to be
* waived. Cannot be negative or more than the total amount of unpaid fines.
*/
export interface WaiveFinesRequest {
/**
* The amount of fines that have to be waived. Cannot be
* negative or more than the total amount of unpaid fines.
*/
amount?: DineroObjectRequest;
}
28 changes: 22 additions & 6 deletions src/controller/user-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,13 @@ import NfcAuthenticator from '../entity/authenticator/nfc-authenticator';
import KeyAuthenticator from '../entity/authenticator/key-authenticator';
import UpdateKeyResponse from './response/update-key-response';
import { randomBytes } from 'crypto';
import DebtorService from '../service/debtor-service';
import DebtorService, { WaiveFinesParams } from '../service/debtor-service';
import ReportService, { BuyerReportService, SalesReportService } from '../service/report-service';
import { ReturnFileType, UserReportParametersType } from 'pdf-generator-client';
import { reportPDFhelper } from '../helpers/express-pdf';
import { PdfError } from '../errors';
import { WaiveFinesRequest } from './request/debtor-request';
import Dinero from 'dinero.js';

export default class UserController extends BaseController {
private logger: Logger = log4js.getLogger('UserController');
Expand Down Expand Up @@ -330,6 +332,7 @@ export default class UserController extends BaseController {
POST: {
policy: async (req) => this.roleManager.can(req.token.roles, 'delete', 'all', 'Fine', ['*']),
handler: this.waiveUserFines.bind(this),
body: { modelName: 'WaiveFinesRequest', allowBlankTarget: true },
},
},
};
Expand Down Expand Up @@ -1602,6 +1605,8 @@ export default class UserController extends BaseController {
* @summary Waive all given user's fines
* @tags users - Operations of user controller
* @param {integer} id.path.required - The id of the user
* @param {WaiveFinesRequest} request.body
* Optional body, see https://github.com/GEWIS/sudosos-backend/pull/344
* @operationId waiveUserFines
* @security JWT
* @return 204 - Success
Expand All @@ -1610,14 +1615,13 @@ export default class UserController extends BaseController {
*/
public async waiveUserFines(req: RequestWithToken, res: Response): Promise<void> {
const { id: rawId } = req.params;
this.logger.trace('Waive fines of user', rawId, 'by', req.token.user);

const body = req.body as WaiveFinesRequest;
this.logger.trace('Waive fines', body, 'of user', rawId, 'by', req.token.user);

try {

const id = parseInt(rawId, 10);

const user = await User.findOne({ where: { id }, relations: ['currentFines'] });
const user = await User.findOne({ where: { id }, relations: { currentFines: { fines: true } } });
if (user == null) {
res.status(404).json('Unknown user ID.');
return;
Expand All @@ -1627,7 +1631,19 @@ export default class UserController extends BaseController {
return;
}

await DebtorService.waiveFines(id);
const totalAmountOfFines = user.currentFines!.fines.reduce((total, f) => total.add(f.amount), Dinero());
// Backwards compatibility with old version, where you could only waive all user's fines
const amountToWaive = body?.amount ?? totalAmountOfFines.toObject();
if (amountToWaive.amount <= 0) {
res.status(400).json('Amount to waive cannot be zero or negative.');
return;
}
if (amountToWaive.amount > totalAmountOfFines.getAmount()) {
res.status(400).json('Amount to waive cannot be more than the total amount of fines.');
return;
}

await new DebtorService().waiveFines(id, { amount: amountToWaive } as WaiveFinesParams);
res.status(204).send();
} catch (e) {
res.status(500).send();
Expand Down
Loading

0 comments on commit 4d9a271

Please sign in to comment.