feat: Inventory item details report.

feat: Cash flow statement report.
This commit is contained in:
a.bouhuolia
2021-05-31 13:17:02 +02:00
parent 256d915f06
commit d47633b8ea
80 changed files with 5474 additions and 376 deletions

View File

@@ -10,7 +10,7 @@ export default class BaseController {
* Converts plain object keys to cameCase style.
* @param {Object} data
*/
private dataToCamelCase(data) {
protected dataToCamelCase(data) {
return mapKeysDeep(data, (v, k) => camelCase(k));
}
@@ -19,7 +19,7 @@ export default class BaseController {
* @param {Request} req
* @param options
*/
matchedBodyData(req: Request, options: any = {}) {
protected matchedBodyData(req: Request, options: any = {}) {
const data = matchedData(req, {
locations: ['body'],
includeOptionals: true,
@@ -32,7 +32,7 @@ export default class BaseController {
* Matches the query data from validation schema.
* @param {Request} req
*/
matchedQueryData(req: Request) {
protected matchedQueryData(req: Request) {
const data = matchedData(req, {
locations: ['query'],
});
@@ -45,7 +45,7 @@ export default class BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
validationResult(req: Request, res: Response, next: NextFunction) {
protected validationResult(req: Request, res: Response, next: NextFunction) {
const validationErrors = validationResult(req);
if (!validationErrors.isEmpty()) {
@@ -61,7 +61,7 @@ export default class BaseController {
* Transform the given data to response.
* @param {any} data
*/
transfromToResponse(
protected transfromToResponse(
data: any,
translatable?: string | string[],
req?: Request
@@ -85,16 +85,16 @@ export default class BaseController {
* Async middleware.
* @param {function} callback
*/
asyncMiddleware(callback) {
protected asyncMiddleware(callback) {
return asyncMiddleware(callback);
}
/**
*
* @param {Request} req
* @returns
*
* @param {Request} req
* @returns
*/
accepts(req) {
protected accepts(req) {
return accepts(req);
}
}

View File

@@ -15,6 +15,8 @@ import CustomerBalanceSummaryController from './FinancialStatements/CustomerBala
import VendorBalanceSummaryController from './FinancialStatements/VendorBalanceSummary';
import TransactionsByCustomers from './FinancialStatements/TransactionsByCustomers';
import TransactionsByVendors from './FinancialStatements/TransactionsByVendors';
import CashFlowStatementController from './FinancialStatements/CashFlow/CashFlow';
import InventoryDetailsController from './FinancialStatements/InventoryDetails';
@Service()
export default class FinancialStatementsService {
@@ -77,6 +79,14 @@ export default class FinancialStatementsService {
'/transactions-by-vendors',
Container.get(TransactionsByVendors).router(),
);
router.use(
'/cash-flow',
Container.get(CashFlowStatementController).router(),
);
router.use(
'/inventory-item-details',
Container.get(InventoryDetailsController).router(),
);
return router;
}
}

View File

@@ -0,0 +1,113 @@
import { Inject, Service } from 'typedi';
import { query } from 'express-validator';
import {
NextFunction,
Router,
Request,
Response,
ValidationChain,
} from 'express';
import BaseFinancialReportController from '../BaseFinancialReportController';
import CashFlowStatementService from 'services/FinancialStatements/CashFlow/CashFlowService';
import { ICashFlowStatement } from 'interfaces';
import CashFlowTable from 'services/FinancialStatements/CashFlow/CashFlowTable';
@Service()
export default class CashFlowController extends BaseFinancialReportController {
@Inject()
cashFlowService: CashFlowStatementService;
/**
* Router constructor.
*/
router() {
const router = Router();
router.get(
'/',
this.cashflowValidationSchema,
this.validationResult,
this.asyncMiddleware(this.cashFlow.bind(this))
);
return router;
}
/**
* Balance sheet validation schecma.
* @returns {ValidationChain[]}
*/
get cashflowValidationSchema(): ValidationChain[] {
return [
...this.sheetNumberFormatValidationSchema,
query('from_date').optional(),
query('to_date').optional(),
query('display_columns_type').optional().isIn(['date_periods', 'total']),
query('display_columns_by')
.optional({ nullable: true, checkFalsy: true })
.isIn(['year', 'month', 'week', 'day', 'quarter']),
query('none_zero').optional().isBoolean().toBoolean(),
query('none_transactions').optional().isBoolean().toBoolean(),
];
}
/**
* Retrieve the cashflow statment to json response.
* @param {ICashFlowStatement} cashFlow -
*/
private transformJsonResponse(cashFlow: ICashFlowStatement) {
const { data, query } = cashFlow;
return {
data: this.transfromToResponse(data),
meta: this.transfromToResponse(query),
};
}
/**
* Transformes the report statement to table rows.
* @param {ITransactionsByVendorsStatement} statement -
*
*/
private transformToTableRows(cashFlow: ICashFlowStatement) {
const cashFlowTable = new CashFlowTable(cashFlow);
return {
table: {
data: cashFlowTable.tableRows(),
columns: cashFlowTable.tableColumns(),
},
meta: this.transfromToResponse(cashFlow.query),
};
}
/**
* Retrieve the cash flow statment.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Response}
*/
async cashFlow(req: Request, res: Response, next: NextFunction) {
const { tenantId, settings } = req;
const filter = {
...this.matchedQueryData(req),
};
try {
const cashFlow = await this.cashFlowService.cashFlow(tenantId, filter);
const accept = this.accepts(req);
const acceptType = accept.types(['json', 'application/json+table']);
switch (acceptType) {
case 'application/json+table':
return res.status(200).send(this.transformToTableRows(cashFlow));
case 'json':
default:
return res.status(200).send(this.transformJsonResponse(cashFlow));
}
} catch (error) {
next(error);
}
}
}

View File

@@ -0,0 +1,120 @@
import { Inject, Service } from 'typedi';
import { query } from 'express-validator';
import {
NextFunction,
Router,
Request,
Response,
ValidationChain,
} from 'express';
import BaseController from 'api/controllers/BaseController';
import InventoryDetailsService from 'services/FinancialStatements/InventoryDetails/InventoryDetailsService';
import InventoryDetailsTable from 'services/FinancialStatements/InventoryDetails/InventoryDetailsTable';
@Service()
export default class InventoryDetailsController extends BaseController {
@Inject()
inventoryDetailsService: InventoryDetailsService;
/**
* Router constructor.
*/
router() {
const router = Router();
router.get(
'/',
this.validationSchema,
this.validationResult,
this.asyncMiddleware(this.inventoryDetails.bind(this))
);
return router;
}
/**
* Balance sheet validation schecma.
* @returns {ValidationChain[]}
*/
get validationSchema(): ValidationChain[] {
return [
query('number_format.precision')
.optional()
.isInt({ min: 0, max: 5 })
.toInt(),
query('number_format.divide_on_1000').optional().isBoolean().toBoolean(),
query('number_format.negative_format')
.optional()
.isIn(['parentheses', 'mines'])
.trim()
.escape(),
query('from_date').optional(),
query('to_date').optional(),
query('none_zero').optional().isBoolean().toBoolean(),
query('none_transactions').optional().isBoolean().toBoolean(),
];
}
/**
* Retrieve the cashflow statment to json response.
* @param {ICashFlowStatement} cashFlow -
*/
private transformJsonResponse(inventoryDetails) {
const { data, query } = inventoryDetails;
return {
data: this.transfromToResponse(data),
meta: this.transfromToResponse(query),
};
}
/**
* Transformes the report statement to table rows.
*/
private transformToTableRows(inventoryDetails) {
const inventoryDetailsTable = new InventoryDetailsTable(inventoryDetails);
return {
table: {
data: inventoryDetailsTable.tableData(),
columns: inventoryDetailsTable.tableColumns(),
},
meta: this.transfromToResponse(inventoryDetails.query),
};
}
/**
* Retrieve the cash flow statment.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Response}
*/
async inventoryDetails(req: Request, res: Response, next: NextFunction) {
const { tenantId, settings } = req;
const filter = {
...this.matchedQueryData(req),
};
try {
const inventoryDetails =
await this.inventoryDetailsService.inventoryDetails(tenantId, filter);
const accept = this.accepts(req);
const acceptType = accept.types(['json', 'application/json+table']);
switch (acceptType) {
case 'application/json+table':
return res
.status(200)
.send(this.transformToTableRows(inventoryDetails));
case 'json':
default:
return res
.status(200)
.send(this.transformJsonResponse(inventoryDetails));
}
} catch (error) {
next(error);
}
}
}