feat: inventory valuation csv and xlsx export (#308)

* feat: inventory valuation csv and xlsx export

* feat(server): inventory valuation sheet exporting

* feat(webapp): inventory valuation sheet dyanmic columns

* feat: inventory valuation dynamic columns

* feat: inventory valuation TS types
This commit is contained in:
Ahmed Bouhuolia
2024-01-18 20:16:29 +02:00
committed by GitHub
parent 8495990ec2
commit 3672abe7a5
15 changed files with 622 additions and 65 deletions

View File

@@ -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 });
}
}
}

View File

@@ -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;
}

View File

@@ -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<number, InventoryCostLotTracker>;

View File

@@ -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<IInventoryValuationSheet> {
return this.inventoryValuationSheet.inventoryValuationSheet(
tenantId,
query
);
}
/**
* Retrieves the inventory valuation json table format.
* @param {number} tenantId
* @param {IInventoryValuationReportQuery} query
* @returns {Promise<IInventoryValuationTable>}
*/
public table(
tenantId: number,
query: IInventoryValuationReportQuery
): Promise<IInventoryValuationTable> {
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<Buffer> {
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<string> {
return this.inventoryValuationExport.csv(tenantId, query);
}
}

View File

@@ -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<Buffer>}
*/
public async xlsx(
tenantId: number,
query: IInventoryValuationReportQuery
): Promise<Buffer> {
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<Buffer>}
*/
public async csv(
tenantId: number,
query: IInventoryValuationReportQuery
): Promise<string> {
const table = await this.inventoryValuationTable.table(tenantId, query);
const tableSheet = new TableSheet(table.table);
const tableCsv = tableSheet.convertToCSV();
return tableCsv;
}
}

View File

@@ -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<IInventoryValuationSheet> {
const { Item, InventoryCostLotTracker } = this.tenancy.models(tenantId);
const tenant = await Tenant.query()

View File

@@ -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);
}
}

View File

@@ -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<IInventoryValuationTable>}
*/
public async table(
tenantId: number,
filter: IInventoryValuationReportQuery
): Promise<IInventoryValuationTable> {
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,
};
}
}

View File

@@ -0,0 +1,4 @@
export enum ROW_TYPE {
ITEM = 'ITEM',
TOTAL = 'TOTAL',
}