From 4a920176f42624e59443fde32142b6451ca65d7a Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Wed, 17 Jan 2024 18:49:38 +0200 Subject: [PATCH] feat(server): sales by items table --- .../FinancialStatements/SalesByItems.ts | 37 ++++++++--- .../SalesByItems/SalesByItemsApplication.ts | 3 + .../SalesByItems/SalesByItemsTable.ts | 62 +++++++++++++------ 3 files changed, 75 insertions(+), 27 deletions(-) diff --git a/packages/server/src/api/controllers/FinancialStatements/SalesByItems.ts b/packages/server/src/api/controllers/FinancialStatements/SalesByItems.ts index 0952610f2..ffc0cb959 100644 --- a/packages/server/src/api/controllers/FinancialStatements/SalesByItems.ts +++ b/packages/server/src/api/controllers/FinancialStatements/SalesByItems.ts @@ -1,18 +1,17 @@ import { Router, Request, Response, NextFunction } from 'express'; -import { query, ValidationChain } from 'express-validator'; -import moment from 'moment'; +import { query, ValidationChain, ValidationSchema } from 'express-validator'; import { Inject, Service } from 'typedi'; import asyncMiddleware from '@/api/middleware/asyncMiddleware'; import BaseFinancialReportController from './BaseFinancialReportController'; -import SalesByItemsReportService from '@/services/FinancialStatements/SalesByItems/SalesByItemsService'; import { AbilitySubject, ReportsAction } from '@/interfaces'; import CheckPolicies from '@/api/middleware/CheckPolicies'; import { ACCEPT_TYPE } from '@/interfaces/Http'; +import { SalesByItemsApplication } from '@/services/FinancialStatements/SalesByItems/SalesByItemsApplication'; @Service() export default class SalesByItemsReportController extends BaseFinancialReportController { @Inject() - salesByItemsService: SalesByItemsReportService; + salesByItemsApp: SalesByItemsApplication; /** * Router constructor. @@ -32,6 +31,7 @@ export default class SalesByItemsReportController extends BaseFinancialReportCon /** * Validation schema. + * @returns {ValidationChain[]} */ private get validationSchema(): ValidationChain[] { return [ @@ -68,7 +68,6 @@ export default class SalesByItemsReportController extends BaseFinancialReportCon ) { const { tenantId } = req; const filter = this.matchedQueryData(req); - const accept = this.accepts(req); const acceptType = accept.types([ @@ -77,13 +76,33 @@ export default class SalesByItemsReportController extends BaseFinancialReportCon ACCEPT_TYPE.APPLICATION_CSV, ACCEPT_TYPE.APPLICATION_XLSX, ]); - + // Retrieves the csv format. if (ACCEPT_TYPE.APPLICATION_CSV === acceptType) { + const buffer = await this.salesByItemsApp.csv(tenantId, filter); + + res.setHeader('Content-Disposition', 'attachment; filename=output.csv'); + res.setHeader('Content-Type', 'text/csv'); + + return res.send(buffer); + // Retrieves the json table format. + } else if (ACCEPT_TYPE.APPLICATION_JSON_TABLE === acceptType) { + const table = await this.salesByItemsApp.table(tenantId, filter); + + return res.status(200).send(table); + // Retrieves the xlsx format. } else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) { - } else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) { + const buffer = this.salesByItemsApp.xlsx(tenantId, filter); + + res.setHeader('Content-Disposition', 'attachment; filename=output.xlsx'); + res.setHeader( + 'Content-Type', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + ); + return res.send(buffer); + // Retrieves the json format. } else { - await this.salesByItemsService.salesByItems(tenantId, filter); - return res.status(200).send({ meta, data, query }); + const sheet = await this.salesByItemsApp.sheet(tenantId, filter); + return res.status(200).send(sheet); } } } diff --git a/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsApplication.ts b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsApplication.ts index 6fbd97459..d70cd703a 100644 --- a/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsApplication.ts +++ b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsApplication.ts @@ -24,6 +24,7 @@ export class SalesByItemsApplication { public sheet(tenantId: number, filter: ISalesByItemsReportQuery) { return this.salesByItemsSheet.salesByItems(tenantId, filter); } + /** * Retrieves the sales by items report in table format. * @param {number} tenantId @@ -33,6 +34,7 @@ export class SalesByItemsApplication { public table(tenantId: number, filter: ISalesByItemsReportQuery) { return this.salesByItemsTable.table(tenantId, filter); } + /** * Retrieves the sales by items report in csv format. * @param {number} tenantId @@ -42,6 +44,7 @@ export class SalesByItemsApplication { public csv(tenantId: number, filter: ISalesByItemsReportQuery) { return this.salesByItemsExport.csv(tenantId, filter); } + /** * Retrieves the sales by items report in xlsx format. * @param {number} tenantId diff --git a/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsTable.ts b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsTable.ts index dad7126dc..33c39ccd6 100644 --- a/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsTable.ts +++ b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsTable.ts @@ -1,47 +1,72 @@ import * as R from 'ramda'; -import { ISalesByItemsSheetStatement, ITableColumn, ITableData, ITableRow } from '@/interfaces'; +import { + ISalesByItemsItem, + ISalesByItemsSheetStatement, + ISalesByItemsTotal, + ITableColumn, + ITableRow, +} from '@/interfaces'; import { tableRowMapper } from '@/utils'; export class SalesByItemsTable { private readonly data: ISalesByItemsSheetStatement; + /** + * Constructor method. + * @param {ISalesByItemsSheetStatement} data + */ constructor(data: ISalesByItemsSheetStatement) { this.data = data; } + /** + * Retrieves the common table accessors. + * @returns {ITableColumn[]} + */ private commonTableAccessors() { return [ { key: 'item_name', accessor: 'name' }, - { key: 'quantity', accessor: 'quantitySoldFormatted' }, - { key: 'sold', accessor: 'soldCostFormatted' }, + { key: 'sold_quantity', accessor: 'quantitySoldFormatted' }, + { key: 'sold_amount', accessor: 'soldCostFormatted' }, + { key: 'average_price', accessor: 'averageSellPriceFormatted' }, ]; } - private itemMap(item: any) { + /** + * Maps the given item node to table row. + * @param {ISalesByItemsItem} item + * @returns {ITableRow} + */ + private itemMap = (item: ISalesByItemsItem): ITableRow => { const columns = this.commonTableAccessors(); const meta = {}; return tableRowMapper(item, columns, meta); - } - - private itemsMap(items: any[]) { - return R.map(this.itemMap, items); - } + }; /** - * - * @param total - * @returns + * Maps the given items nodes to table rows. + * @param {ISalesByItemsItem[]} items + * @returns {ITableRow[]} */ - private totalMap(total: any) { + private itemsMap = (items: ISalesByItemsItem[]): ITableRow[] => { + return R.map(this.itemMap, items); + }; + + /** + * Maps the given total node to table row. + * @param {ISalesByItemsTotal} total + * @returns {ITableRow[]} + */ + private totalMap = (total: ISalesByItemsTotal) => { const columns = this.commonTableAccessors(); const meta = {}; return tableRowMapper(total, columns, meta); - } + }; /** - * + * Retrieves the table rows. * @returns {ITableRow[]} */ public tableData(): ITableRow[] { @@ -57,9 +82,10 @@ export class SalesByItemsTable { */ public tableColumns(): ITableColumn[] { return [ - { key: 'item_name', label: 'Item Name' }, - { key: 'quantity', label: 'Quantity' }, - { key: 'sold_cost', label: 'Sold Cost' }, + { key: 'item_name', label: 'Item name' }, + { key: 'sold_quantity', label: 'Sold quantity' }, + { key: 'sold_amount', label: 'Sold amount' }, + { key: 'average_price', label: 'Average price' }, ]; } }