diff --git a/packages/server/src/api/controllers/FinancialStatements/SalesByItems.ts b/packages/server/src/api/controllers/FinancialStatements/SalesByItems.ts index d31954398..0952610f2 100644 --- a/packages/server/src/api/controllers/FinancialStatements/SalesByItems.ts +++ b/packages/server/src/api/controllers/FinancialStatements/SalesByItems.ts @@ -7,6 +7,7 @@ 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'; @Service() export default class SalesByItemsReportController extends BaseFinancialReportController { @@ -68,18 +69,21 @@ export default class SalesByItemsReportController extends BaseFinancialReportCon const { tenantId } = req; const filter = this.matchedQueryData(req); - try { - const { data, query, meta } = await this.salesByItemsService.salesByItems( - tenantId, - filter - ); - return res.status(200).send({ - meta: this.transfromToResponse(meta), - data: this.transfromToResponse(data), - query: this.transfromToResponse(query), - }); - } catch (error) { - next(error); + const accept = this.accepts(req); + + const acceptType = accept.types([ + ACCEPT_TYPE.APPLICATION_JSON, + ACCEPT_TYPE.APPLICATION_JSON_TABLE, + ACCEPT_TYPE.APPLICATION_CSV, + ACCEPT_TYPE.APPLICATION_XLSX, + ]); + + if (ACCEPT_TYPE.APPLICATION_CSV === acceptType) { + } else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) { + } else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) { + } else { + await this.salesByItemsService.salesByItems(tenantId, filter); + return res.status(200).send({ meta, data, query }); } } } diff --git a/packages/server/src/interfaces/SalesByItemsSheet.ts b/packages/server/src/interfaces/SalesByItemsSheet.ts index 0e0a41f9e..2e253c49d 100644 --- a/packages/server/src/interfaces/SalesByItemsSheet.ts +++ b/packages/server/src/interfaces/SalesByItemsSheet.ts @@ -41,5 +41,5 @@ export interface ISalesByItemsTotal { export type ISalesByItemsSheetStatement = { items: ISalesByItemsItem[], total: ISalesByItemsTotal -} | {}; +}; diff --git a/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsApplication.ts b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsApplication.ts new file mode 100644 index 000000000..6fbd97459 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsApplication.ts @@ -0,0 +1,54 @@ +import { ISalesByItemsReportQuery } from '@/interfaces'; +import { SalesByItemsReportService } from './SalesByItemsService'; +import { SalesByItemsTableInjectable } from './SalesByItemsTableInjectable'; +import { SalesByItemsExport } from './SalesByItemsExport'; +import { Inject, Service } from 'typedi'; + +@Service() +export class SalesByItemsApplication { + @Inject() + private salesByItemsSheet: SalesByItemsReportService; + + @Inject() + private salesByItemsTable: SalesByItemsTableInjectable; + + @Inject() + private salesByItemsExport: SalesByItemsExport; + + /** + * Retrieves the sales by items report in json format. + * @param {number} tenantId + * @param {ISalesByItemsReportQuery} filter + * @returns {Promise} + */ + public sheet(tenantId: number, filter: ISalesByItemsReportQuery) { + return this.salesByItemsSheet.salesByItems(tenantId, filter); + } + /** + * Retrieves the sales by items report in table format. + * @param {number} tenantId + * @param {ISalesByItemsReportQuery} filter + * @returns {Promise} + */ + public table(tenantId: number, filter: ISalesByItemsReportQuery) { + return this.salesByItemsTable.table(tenantId, filter); + } + /** + * Retrieves the sales by items report in csv format. + * @param {number} tenantId + * @param {ISalesByItemsReportQuery} filter + * @returns {Promise} + */ + public csv(tenantId: number, filter: ISalesByItemsReportQuery) { + return this.salesByItemsExport.csv(tenantId, filter); + } + /** + * Retrieves the sales by items report in xlsx format. + * @param {number} tenantId + * @param {ISalesByItemsReportQuery} filter + * @returns {Promise} + */ + public xlsx(tenantId: number, filter: ISalesByItemsReportQuery) { + return this.salesByItemsExport.xlsx(tenantId, filter); + } +} diff --git a/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsExport.ts b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsExport.ts new file mode 100644 index 000000000..067aab546 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsExport.ts @@ -0,0 +1,43 @@ +import { Inject, Service } from 'typedi'; +import { TableSheet } from '@/lib/Xlsx/TableSheet'; +import { ISalesByItemsReportQuery } from '@/interfaces'; +import { SalesByItemsTableInjectable } from './SalesByItemsTableInjectable'; + +@Service() +export class SalesByItemsExport { + @Inject() + private salesByItemsTable: SalesByItemsTableInjectable; + + /** + * Retrieves the trial balance sheet in XLSX format. + * @param {number} tenantId + * @param {ISalesByItemsReportQuery} query + * @returns {Promise} + */ + public async xlsx(tenantId: number, query: ISalesByItemsReportQuery) { + const table = await this.salesByItemsTable.table(tenantId, query); + + const tableSheet = new TableSheet(table.table); + const tableCsv = tableSheet.convertToXLSX(); + + return tableSheet.convertToBuffer(tableCsv, 'xlsx'); + } + + /** + * Retrieves the trial balance sheet in CSV format. + * @param {number} tenantId + * @param {ISalesByItemsReportQuery} query + * @returns {Promise} + */ + public async csv( + tenantId: number, + query: ISalesByItemsReportQuery + ): Promise { + const table = await this.salesByItemsTable.table(tenantId, query); + + const tableSheet = new TableSheet(table.table); + const tableCsv = tableSheet.convertToCSV(); + + return tableCsv; + } +} diff --git a/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsService.ts b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsService.ts index 6f81e1489..086284d8e 100644 --- a/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsService.ts +++ b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsService.ts @@ -10,7 +10,7 @@ import SalesByItems from './SalesByItems'; import { Tenant } from '@/system/models'; @Service() -export default class SalesByItemsReportService { +export class SalesByItemsReportService { @Inject() tenancy: TenancyService; diff --git a/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsTable.ts b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsTable.ts new file mode 100644 index 000000000..dad7126dc --- /dev/null +++ b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsTable.ts @@ -0,0 +1,65 @@ +import * as R from 'ramda'; +import { ISalesByItemsSheetStatement, ITableColumn, ITableData, ITableRow } from '@/interfaces'; +import { tableRowMapper } from '@/utils'; + +export class SalesByItemsTable { + private readonly data: ISalesByItemsSheetStatement; + + constructor(data: ISalesByItemsSheetStatement) { + this.data = data; + } + + private commonTableAccessors() { + return [ + { key: 'item_name', accessor: 'name' }, + { key: 'quantity', accessor: 'quantitySoldFormatted' }, + { key: 'sold', accessor: 'soldCostFormatted' }, + ]; + } + + private itemMap(item: any) { + const columns = this.commonTableAccessors(); + const meta = {}; + + return tableRowMapper(item, columns, meta); + } + + private itemsMap(items: any[]) { + return R.map(this.itemMap, items); + } + + /** + * + * @param total + * @returns + */ + private totalMap(total: any) { + const columns = this.commonTableAccessors(); + const meta = {}; + + return tableRowMapper(total, columns, meta); + } + + /** + * + * @returns {ITableRow[]} + */ + public tableData(): ITableRow[] { + const itemsRows = this.itemsMap(this.data.items); + const totalRow = this.totalMap(this.data.total); + + return [...itemsRows, totalRow]; + } + + /** + * Retrieves the table columns. + * @returns {ITableColumn[]} + */ + public tableColumns(): ITableColumn[] { + return [ + { key: 'item_name', label: 'Item Name' }, + { key: 'quantity', label: 'Quantity' }, + { key: 'sold_cost', label: 'Sold Cost' }, + ]; + } +} diff --git a/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsTableInjectable.tsx b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsTableInjectable.tsx new file mode 100644 index 000000000..037ca7073 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/SalesByItems/SalesByItemsTableInjectable.tsx @@ -0,0 +1,33 @@ +import { ISalesByItemsReportQuery } from '@/interfaces'; +import { Inject, Service } from 'typedi'; +import { SalesByItemsReportService } from './SalesByItemsService'; +import { SalesByItemsTable } from './SalesByItemsTable'; + +@Service() +export class SalesByItemsTableInjectable { + @Inject() + salesByItemSheet: SalesByItemsReportService; + + /** + * Retrieves the sales by items report in table format. + * @param {number} tenantId + * @param {ISalesByItemsReportQuery} filter + * @returns {Promise} + */ + public async table(tenantId: number, filter: ISalesByItemsReportQuery) { + const { data, query, meta } = await this.salesByItemSheet.salesByItems( + tenantId, + filter + ); + const table = new SalesByItemsTable(data); + + return { + table: { + columns: table.tableColumns(), + rows: table.tableData(), + }, + meta, + query, + }; + } +}