-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(summaries): add endpoint to fetch container's summary (#415)
* feat: add minimal transaction summary service * feat: add endpoint to fetch container summary * fix: failing test case * chore: add reference to PR * chore: add documentation
- Loading branch information
Showing
6 changed files
with
548 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/** | ||
* SudoSOS back-end API service. | ||
* Copyright (C) 2024 Study association GEWIS | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published | ||
* by the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
* | ||
* @license | ||
*/ | ||
|
||
/** | ||
* This is the module page of the transaction summaries. | ||
* Not that this module has been created in very strict time constraints, | ||
* so its implementation is very minimal. | ||
* https://github.com/GEWIS/sudosos-backend/pull/415 | ||
* | ||
* @module transaction-summaries | ||
*/ | ||
|
||
import { BaseUserResponse } from './user-response'; | ||
import { DineroObjectResponse } from './dinero-response'; | ||
|
||
/** | ||
* @typedef {object} ContainerSummaryResponse | ||
* @property {allOf|BaseUserResponse} user.required | ||
* @property {allOf|DineroObjectResponse} totalInclVat.required | ||
* @property {integer} amountOfProducts.required | ||
* @property {integer} containerId.required | ||
*/ | ||
export interface ContainerSummaryResponse { | ||
user: BaseUserResponse; | ||
totalInclVat: DineroObjectResponse; | ||
amountOfProducts: number; | ||
containerId: number; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/** | ||
* SudoSOS back-end API service. | ||
* Copyright (C) 2024 Study association GEWIS | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published | ||
* by the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
* | ||
* @license | ||
*/ | ||
|
||
/** | ||
* This is the module page of the transaction summaries. | ||
* Not that this module has been created in very strict time constraints, | ||
* so its implementation is very minimal. | ||
* https://github.com/GEWIS/sudosos-backend/pull/415 | ||
* | ||
* @module transaction-summaries | ||
*/ | ||
|
||
import { Response } from 'express'; | ||
import log4js, { Logger } from 'log4js'; | ||
import BaseController, { BaseControllerOptions } from './base-controller'; | ||
import Policy from './policy'; | ||
import { RequestWithToken } from '../middleware/token-middleware'; | ||
import TransactionSummaryService from '../service/transaction-summary-service'; | ||
|
||
export default class TransactionSummaryController extends BaseController { | ||
private logger: Logger = log4js.getLogger('TransactionSummaryController'); | ||
|
||
public constructor(options: BaseControllerOptions) { | ||
super(options); | ||
this.logger.level = process.env.LOG_LEVEL; | ||
} | ||
|
||
public getPolicy(): Policy { | ||
return { | ||
'/container/:id(\\d+)': { | ||
GET: { | ||
policy: async (req) => this.roleManager.can(req.token.roles, 'get', 'all', 'Transaction', ['*']), | ||
handler: this.getSingleContainerSummary.bind(this), | ||
}, | ||
}, | ||
}; | ||
} | ||
|
||
/** | ||
* GET /transactions/summary/container/{id} | ||
* @summary Returns a summary of all purchases within a container | ||
* @operationId getSingleContainerSummary | ||
* @tags transactionSummaries - Operations of the transaction summary controller | ||
* @security JWT | ||
* @deprecated - Hotfix for Feestcafé "De BAC" - 70s Disco Edition. Do not use for anything else. https://github.com/GEWIS/sudosos-backend/pull/415 | ||
* @param {integer} id.path.required - The ID of the container | ||
* @return {Array<ContainerSummaryResponse>} 200 - The requested summary | ||
* @return {string} 404 - Not found error | ||
* @return {string} 500 - Internal server error | ||
*/ | ||
public async getSingleContainerSummary(req: RequestWithToken, res: Response): Promise<void> { | ||
const { id: rawId } = req.params; | ||
this.logger.trace('Get single container summary of container', rawId, ', by user', req.token.user); | ||
|
||
try { | ||
const id = Number(rawId); | ||
const summaries = await new TransactionSummaryService().getContainerSummary({ containerId: id }); | ||
if (summaries.length === 0) { | ||
// This also causes a 404 if the container exists, but no transactions have been made. | ||
// However, this is a won't fix for now (because time) | ||
// https://github.com/GEWIS/sudosos-backend/pull/415 | ||
res.status(404).json('Container not found.'); | ||
return; | ||
} | ||
|
||
res.status(200).json(summaries.map((s) => TransactionSummaryService.toContainerSummaryResponse(s))); | ||
} catch (e) { | ||
res.status(500).send('Internal server error.'); | ||
this.logger.error(e); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/** | ||
* SudoSOS back-end API service. | ||
* Copyright (C) 2024 Study association GEWIS | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published | ||
* by the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
* | ||
* @license | ||
*/ | ||
|
||
/** | ||
* This is the module page of the transaction summaries. | ||
* Not that this module has been created in very strict time constraints, | ||
* so its implementation is very minimal. | ||
* https://github.com/GEWIS/sudosos-backend/pull/415 | ||
* | ||
* @module transaction-summaries | ||
*/ | ||
|
||
import { Dinero } from 'dinero.js'; | ||
import User from '../entity/user/user'; | ||
import WithManager from '../database/with-manager'; | ||
import SubTransactionRow from '../entity/transactions/sub-transaction-row'; | ||
import Transaction from '../entity/transactions/transaction'; | ||
import SubTransaction from '../entity/transactions/sub-transaction'; | ||
import ProductRevision from '../entity/product/product-revision'; | ||
import { SelectQueryBuilder } from 'typeorm'; | ||
import DineroTransformer from '../entity/transformer/dinero-transformer'; | ||
import { ContainerSummaryResponse } from '../controller/response/transaction-summary-response'; | ||
|
||
interface BaseSummary { | ||
user: User; | ||
totalInclVat: Dinero; | ||
amountOfProducts: number; | ||
} | ||
|
||
interface ProductSummary extends BaseSummary { | ||
productId: number; | ||
} | ||
|
||
interface ContainerSummary extends BaseSummary { | ||
containerId: number; | ||
} | ||
|
||
interface PointOfSaleSummary extends BaseSummary { | ||
pointOfSaleId: number; | ||
} | ||
|
||
interface UserSummary extends BaseSummary { | ||
user: User; | ||
products: ProductSummary[]; | ||
containers: ContainerSummary[]; | ||
pointsOfSale: PointOfSaleSummary[]; | ||
} | ||
|
||
interface SummaryFilters { | ||
containerId?: number; | ||
} | ||
|
||
/** | ||
* Minimal implementation of the summary service. | ||
* https://github.com/GEWIS/sudosos-backend/pull/415 | ||
*/ | ||
export default class TransactionSummaryService extends WithManager { | ||
public static toContainerSummaryResponse(containerSummary: ContainerSummary): ContainerSummaryResponse { | ||
return { | ||
user: { | ||
id: containerSummary.user.id, | ||
firstName: containerSummary.user.firstName, | ||
nickname: containerSummary.user.nickname, | ||
lastName: containerSummary.user.lastName, | ||
}, | ||
totalInclVat: containerSummary.totalInclVat.toObject(), | ||
amountOfProducts: containerSummary.amountOfProducts, | ||
containerId: containerSummary.containerId, | ||
}; | ||
} | ||
|
||
private getBaseQueryBuilder(): SelectQueryBuilder<User> { | ||
return this.manager.createQueryBuilder(User, 'user') | ||
.innerJoinAndSelect(Transaction, 'transaction', 'transaction.fromId = user.id') | ||
.innerJoinAndSelect(SubTransaction, 'subTransaction', 'subTransaction.transactionId = transaction.id') | ||
// .innerJoinAndSelect(ContainerRevision, 'containerRevision', 'containerRevision.containerId = subTransaction.containerContainerId AND containerRevision.revision = subTransaction.containerRevision') | ||
.innerJoinAndSelect(SubTransactionRow, 'subTransactionRow', 'subTransactionRow.subTransactionId = subTransaction.id') | ||
.innerJoin(ProductRevision, 'productRevision', 'productRevision.productId = subTransactionRow.productProductId AND productRevision.revision = subTransactionRow.productRevision') | ||
.addSelect('sum(subTransactionRow.amount * productRevision.priceInclVat) as totalValueInclVat') | ||
.addSelect('sum(subTransactionRow.amount) as totalAmount'); | ||
} | ||
|
||
private addFilters<T>(query: SelectQueryBuilder<T>, filters?: SummaryFilters): SelectQueryBuilder<T> { | ||
if (!filters) return query; | ||
if (filters.containerId) query.where('subTransaction.containerContainerId = :containerId', { containerId: filters.containerId }); | ||
return query; | ||
} | ||
|
||
public async getContainerSummary(filters?: SummaryFilters): Promise<ContainerSummary[]> { | ||
const query = this.getBaseQueryBuilder() | ||
.groupBy('user.id, subTransaction.containerContainerId'); | ||
|
||
const data = await this.addFilters(query, filters) | ||
.getRawAndEntities(); | ||
|
||
return data.raw.map((r): ContainerSummary => { | ||
const user = data.entities.find((u) => u.id === r.user_id); | ||
return { | ||
user, | ||
totalInclVat: DineroTransformer.Instance.from(r.totalValueInclVat), | ||
amountOfProducts: Number(r.totalAmount), | ||
containerId: r.subTransaction_containerContainerId, | ||
}; | ||
}); | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
public async getSummary(filters?: SummaryFilters): Promise<UserSummary> { | ||
throw new Error('Not yet implemented'); | ||
} | ||
} |
Oops, something went wrong.