mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
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:
@@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum ROW_TYPE {
|
||||
ITEM = 'ITEM',
|
||||
TOTAL = 'TOTAL',
|
||||
}
|
||||
Reference in New Issue
Block a user