diff --git a/packages/server/src/api/controllers/FinancialStatements/InventoryValuationSheet.ts b/packages/server/src/api/controllers/FinancialStatements/InventoryValuationSheet.ts index a98c8c997..3a2d3c196 100644 --- a/packages/server/src/api/controllers/FinancialStatements/InventoryValuationSheet.ts +++ b/packages/server/src/api/controllers/FinancialStatements/InventoryValuationSheet.ts @@ -3,14 +3,15 @@ import { query, ValidationChain } from 'express-validator'; import { Inject, Service } from 'typedi'; import asyncMiddleware from '@/api/middleware/asyncMiddleware'; import BaseFinancialReportController from './BaseFinancialReportController'; -import InventoryValuationService from '@/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetService'; import { AbilitySubject, ReportsAction } from '@/interfaces'; import CheckPolicies from '@/api/middleware/CheckPolicies'; +import { InventoryValuationSheetApplication } from '@/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetApplication'; +import { ACCEPT_TYPE } from '@/interfaces/Http'; @Service() export default class InventoryValuationReportController extends BaseFinancialReportController { @Inject() - inventoryValuationService: InventoryValuationService; + private inventoryValuationApp: InventoryValuationSheetApplication; /** * Router constructor. @@ -71,19 +72,45 @@ export default class InventoryValuationReportController extends BaseFinancialRep const { tenantId } = req; const filter = this.matchedQueryData(req); - try { - const { data, query, meta } = - await this.inventoryValuationService.inventoryValuationSheet( - 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_XLSX, + ACCEPT_TYPE.APPLICATION_CSV, + ]); + + // Retrieves the json table format. + if (ACCEPT_TYPE.APPLICATION_JSON_TABLE === acceptType) { + const table = await this.inventoryValuationApp.table(tenantId, filter); + + return res.status(200).send(table); + // Retrieves the csv format. + } else if (ACCEPT_TYPE.APPLICATION_CSV == acceptType) { + const buffer = await this.inventoryValuationApp.csv(tenantId, filter); + + res.setHeader('Content-Disposition', 'attachment; filename=output.csv'); + res.setHeader('Content-Type', 'text/csv'); + + return res.send(buffer); + // Retrieves the xslx buffer format. + } else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) { + const buffer = await this.inventoryValuationApp.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 { + const { data, query, meta } = await this.inventoryValuationApp.sheet( + tenantId, + filter + ); + return res.status(200).send({ meta, data, query }); } } } diff --git a/packages/server/src/interfaces/IInventoryValuationSheet.ts b/packages/server/src/interfaces/IInventoryValuationSheet.ts index dedb6c483..ab006d7b3 100644 --- a/packages/server/src/interfaces/IInventoryValuationSheet.ts +++ b/packages/server/src/interfaces/IInventoryValuationSheet.ts @@ -1,4 +1,5 @@ import { INumberFormatQuery } from './FinancialStatements'; +import { IFinancialTable } from './Table'; export interface IInventoryValuationReportQuery { asDate: Date | string; @@ -39,9 +40,19 @@ export interface IInventoryValuationTotal { quantityFormatted: string; } -export type IInventoryValuationStatement = - | { - items: IInventoryValuationItem[]; - total: IInventoryValuationTotal; - } - | {}; +export type IInventoryValuationStatement = { + items: IInventoryValuationItem[]; + total: IInventoryValuationTotal; +}; +export type IInventoryValuationSheetData = IInventoryValuationStatement; + +export interface IInventoryValuationSheet { + data: IInventoryValuationStatement; + meta: IInventoryValuationSheetMeta; + query: IInventoryValuationReportQuery; +} + +export interface IInventoryValuationTable extends IFinancialTable { + meta: IInventoryValuationSheetMeta; + query: IInventoryValuationReportQuery; +} diff --git a/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheet.ts b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheet.ts index 693ee2692..ab6e410e6 100644 --- a/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheet.ts +++ b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheet.ts @@ -11,7 +11,7 @@ import { } from '@/interfaces'; import { allPassedConditionsPass, transformToMap } from 'utils'; -export default class InventoryValuationSheet extends FinancialSheet { +export class InventoryValuationSheet extends FinancialSheet { readonly query: IInventoryValuationReportQuery; readonly items: IItem[]; readonly INInventoryCostLots: Map; diff --git a/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetApplication.ts b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetApplication.ts new file mode 100644 index 000000000..43406b0d8 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetApplication.ts @@ -0,0 +1,76 @@ +import { + IInventoryValuationReportQuery, + IInventoryValuationSheet, + IInventoryValuationTable, +} from '@/interfaces'; +import { Inject, Service } from 'typedi'; +import { InventoryValuationSheetService } from './InventoryValuationSheetService'; +import { InventoryValuationSheetTableInjectable } from './InventoryValuationSheetTableInjectable'; +import { InventoryValuationSheetExportable } from './InventoryValuationSheetExportable'; + +@Service() +export class InventoryValuationSheetApplication { + @Inject() + private inventoryValuationSheet: InventoryValuationSheetService; + + @Inject() + private inventoryValuationTable: InventoryValuationSheetTableInjectable; + + @Inject() + private inventoryValuationExport: InventoryValuationSheetExportable; + + /** + * Retrieves the inventory valuation json format. + * @param {number} tenantId + * @param {IInventoryValuationReportQuery} query + * @returns + */ + public sheet( + tenantId: number, + query: IInventoryValuationReportQuery + ): Promise { + return this.inventoryValuationSheet.inventoryValuationSheet( + tenantId, + query + ); + } + + /** + * Retrieves the inventory valuation json table format. + * @param {number} tenantId + * @param {IInventoryValuationReportQuery} query + * @returns {Promise} + */ + public table( + tenantId: number, + query: IInventoryValuationReportQuery + ): Promise { + return this.inventoryValuationTable.table(tenantId, query); + } + + /** + * Retrieves the inventory valuation xlsx format. + * @param {number} tenantId + * @param {IInventoryValuationReportQuery} query + * @returns + */ + public xlsx( + tenantId: number, + query: IInventoryValuationReportQuery + ): Promise { + return this.inventoryValuationExport.xlsx(tenantId, query); + } + + /** + * Retrieves the inventory valuation csv format. + * @param {number} tenantId + * @param {IInventoryValuationReportQuery} query + * @returns + */ + public csv( + tenantId: number, + query: IInventoryValuationReportQuery + ): Promise { + return this.inventoryValuationExport.csv(tenantId, query); + } +} diff --git a/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetExportable.ts b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetExportable.ts new file mode 100644 index 000000000..2403ed16b --- /dev/null +++ b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetExportable.ts @@ -0,0 +1,46 @@ +import { Inject, Service } from 'typedi'; +import { IInventoryValuationReportQuery } from '@/interfaces'; +import { InventoryValuationSheetTableInjectable } from './InventoryValuationSheetTableInjectable'; +import { TableSheet } from '@/lib/Xlsx/TableSheet'; + +@Service() +export class InventoryValuationSheetExportable { + @Inject() + private inventoryValuationTable: InventoryValuationSheetTableInjectable; + + /** + * Retrieves the trial balance sheet in XLSX format. + * @param {number} tenantId + * @param {IInventoryValuationReportQuery} query + * @returns {Promise} + */ + public async xlsx( + tenantId: number, + query: IInventoryValuationReportQuery + ): Promise { + const table = await this.inventoryValuationTable.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 {IInventoryValuationReportQuery} query + * @returns {Promise} + */ + public async csv( + tenantId: number, + query: IInventoryValuationReportQuery + ): Promise { + const table = await this.inventoryValuationTable.table(tenantId, query); + + const tableSheet = new TableSheet(table.table); + const tableCsv = tableSheet.convertToCSV(); + + return tableCsv; + } +} diff --git a/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetService.ts b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetService.ts index 75c871ce8..7351522e9 100644 --- a/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetService.ts +++ b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetService.ts @@ -3,15 +3,16 @@ import moment from 'moment'; import { isEmpty } from 'lodash'; import { IInventoryValuationReportQuery, + IInventoryValuationSheet, IInventoryValuationSheetMeta, } from '@/interfaces'; import TenancyService from '@/services/Tenancy/TenancyService'; -import InventoryValuationSheet from './InventoryValuationSheet'; +import { InventoryValuationSheet } from './InventoryValuationSheet'; import InventoryService from '@/services/Inventory/Inventory'; import { Tenant } from '@/system/models'; @Service() -export default class InventoryValuationSheetService { +export class InventoryValuationSheetService { @Inject() tenancy: TenancyService; @@ -80,7 +81,7 @@ export default class InventoryValuationSheetService { public async inventoryValuationSheet( tenantId: number, query: IInventoryValuationReportQuery - ) { + ): Promise { const { Item, InventoryCostLotTracker } = this.tenancy.models(tenantId); const tenant = await Tenant.query() diff --git a/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetTable.ts b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetTable.ts new file mode 100644 index 000000000..dd675d3e3 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetTable.ts @@ -0,0 +1,103 @@ +import * as R from 'ramda'; +import { + IInventoryValuationItem, + IInventoryValuationSheetData, + IInventoryValuationTotal, + ITableColumn, + ITableColumnAccessor, + ITableRow, +} from '@/interfaces'; +import { tableRowMapper } from '@/utils'; +import FinancialSheet from '../FinancialSheet'; +import { FinancialSheetStructure } from '../FinancialSheetStructure'; +import { FinancialTable } from '../FinancialTable'; +import { ROW_TYPE } from './_constants'; + +export class InventoryValuationSheetTable extends R.compose( + FinancialTable, + FinancialSheetStructure +)(FinancialSheet) { + private readonly data: IInventoryValuationSheetData; + + /** + * Constructor method. + * @param {IInventoryValuationSheetData} data + */ + constructor(data: IInventoryValuationSheetData) { + super(); + this.data = data; + } + + /** + * Retrieves the common columns accessors. + * @returns {ITableColumnAccessor} + */ + private commonColumnsAccessors(): ITableColumnAccessor[] { + return [ + { key: 'item_name', accessor: 'name' }, + { key: 'quantity', accessor: 'quantityFormatted' }, + { key: 'valuation', accessor: 'valuationFormatted' }, + { key: 'average', accessor: 'averageFormatted' }, + ]; + } + + /** + * Maps the given total node to table row. + * @param {IInventoryValuationTotal} total + * @returns {ITableRow} + */ + private totalRowMapper = (total: IInventoryValuationTotal): ITableRow => { + const accessors = this.commonColumnsAccessors(); + const meta = { + rowTypes: [ROW_TYPE.TOTAL], + }; + return tableRowMapper(total, accessors, meta); + }; + + /** + * Maps the given item node to table row. + * @param {IInventoryValuationItem} item + * @returns {ITableRow} + */ + private itemRowMapper = (item: IInventoryValuationItem): ITableRow => { + const accessors = this.commonColumnsAccessors(); + const meta = { + rowTypes: [ROW_TYPE.ITEM], + }; + return tableRowMapper(item, accessors, meta); + }; + + /** + * Maps the given items nodes to table rowes. + * @param {IInventoryValuationItem[]} items + * @returns {ITableRow[]} + */ + private itemsRowsMapper = (items: IInventoryValuationItem[]): ITableRow[] => { + return R.map(this.itemRowMapper)(items); + }; + + /** + * Retrieves the table rows. + * @returns {ITableRow[]} + */ + public tableRows(): ITableRow[] { + const itemsRows = this.itemsRowsMapper(this.data.items); + const totalRow = this.totalRowMapper(this.data.total); + + return [...itemsRows, totalRow]; + } + + /** + * Retrieves the table columns. + * @returns {ITableColumn[]} + */ + public tableColumns(): ITableColumn[] { + const columns = [ + { key: 'item_name', label: 'Item Name' }, + { key: 'quantity', label: 'Quantity' }, + { key: 'valuation', label: 'Valuation' }, + { key: 'average', label: 'Average' }, + ]; + return R.compose(this.tableColumnsCellIndexing)(columns); + } +} diff --git a/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetTableInjectable.ts b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetTableInjectable.ts new file mode 100644 index 000000000..cad8126b4 --- /dev/null +++ b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/InventoryValuationSheetTableInjectable.ts @@ -0,0 +1,39 @@ +import { Inject, Service } from 'typedi'; +import { InventoryValuationSheetService } from './InventoryValuationSheetService'; +import { + IInventoryValuationReportQuery, + IInventoryValuationTable, +} from '@/interfaces'; +import { InventoryValuationSheetTable } from './InventoryValuationSheetTable'; + +@Service() +export class InventoryValuationSheetTableInjectable { + @Inject() + private sheet: InventoryValuationSheetService; + + /** + * Retrieves the inventory valuation json table format. + * @param {number} tenantId - + * @param {IInventoryValuationReportQuery} filter - + * @returns {Promise} + */ + public async table( + tenantId: number, + filter: IInventoryValuationReportQuery + ): Promise { + const { data, query, meta } = await this.sheet.inventoryValuationSheet( + tenantId, + filter + ); + const table = new InventoryValuationSheetTable(data); + + return { + table: { + columns: table.tableColumns(), + rows: table.tableRows(), + }, + query, + meta, + }; + } +} diff --git a/packages/server/src/services/FinancialStatements/InventoryValuationSheet/_constants.ts b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/_constants.ts new file mode 100644 index 000000000..a4154f92f --- /dev/null +++ b/packages/server/src/services/FinancialStatements/InventoryValuationSheet/_constants.ts @@ -0,0 +1,4 @@ +export enum ROW_TYPE { + ITEM = 'ITEM', + TOTAL = 'TOTAL', +} diff --git a/packages/webapp/src/containers/FinancialStatements/InventoryValuation/InventoryValuationActionsBar.tsx b/packages/webapp/src/containers/FinancialStatements/InventoryValuation/InventoryValuationActionsBar.tsx index eb5b8fdad..9e1c20ab1 100644 --- a/packages/webapp/src/containers/FinancialStatements/InventoryValuation/InventoryValuationActionsBar.tsx +++ b/packages/webapp/src/containers/FinancialStatements/InventoryValuation/InventoryValuationActionsBar.tsx @@ -19,6 +19,7 @@ import withInventoryValuationActions from './withInventoryValuationActions'; import { useInventoryValuationContext } from './InventoryValuationProvider'; import { compose, saveInvoke } from '@/utils'; +import { InventoryValuationExportMenu } from './components'; function InventoryValuationActionsBar({ // #withInventoryValuation @@ -109,11 +110,18 @@ function InventoryValuationActionsBar({ icon={} text={} /> -