fix(InventoryValuation): inventory valuation report.

This commit is contained in:
a.bouhuolia
2021-03-29 15:19:35 +02:00
parent 5184365d2a
commit 5f573c095e
4 changed files with 58 additions and 99 deletions

View File

@@ -43,13 +43,6 @@ export default class ExchangeRatesController extends BaseController {
asyncMiddleware(this.editExchangeRate.bind(this)), asyncMiddleware(this.editExchangeRate.bind(this)),
this.handleServiceError this.handleServiceError
); );
router.delete(
'/bulk',
[...this.exchangeRatesIdsSchema],
this.validationResult,
asyncMiddleware(this.bulkDeleteExchangeRates.bind(this)),
this.handleServiceError
);
router.delete( router.delete(
'/:id', '/:id',
[...this.exchangeRateIdSchema], [...this.exchangeRateIdSchema],
@@ -192,31 +185,6 @@ export default class ExchangeRatesController extends BaseController {
} }
} }
/**
* Deletes the given exchange rates in bulk.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async bulkDeleteExchangeRates(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const { ids: exchangeRateIds } = req.query;
try {
await this.exchangeRatesService.deleteBulkExchangeRates(
tenantId,
exchangeRateIds
);
return res.status(200).send();
} catch (error) {
next(error);
}
}
/** /**
* Handle service errors. * Handle service errors.
* @param {Error} error * @param {Error} error

View File

@@ -135,30 +135,6 @@ export default class ExchangeRatesService implements IExchangeRatesService {
return exchangeRates; return exchangeRates;
} }
/**
* Deletes exchange rates in bulk.
* @param {number} tenantId
* @param {number[]} exchangeRatesIds
*/
public async deleteBulkExchangeRates(
tenantId: number,
exchangeRatesIds: number[]
): Promise<void> {
const { ExchangeRate } = this.tenancy.models(tenantId);
this.logger.info('[exchange_rates] trying delete in bulk.', {
tenantId,
exchangeRatesIds,
});
await this.validateExchangeRatesIdsExistance(tenantId, exchangeRatesIds);
await ExchangeRate.query().whereIn('id', exchangeRatesIds).delete();
this.logger.info('[exchange_rates] deleted successfully.', {
tenantId,
exchangeRatesIds,
});
}
/** /**
* Validates period of the exchange rate existance. * Validates period of the exchange rate existance.
* @param {number} tenantId - Tenant id. * @param {number} tenantId - Tenant id.
@@ -214,33 +190,4 @@ export default class ExchangeRatesService implements IExchangeRatesService {
throw new ServiceError(ERRORS.EXCHANGE_RATE_NOT_FOUND); throw new ServiceError(ERRORS.EXCHANGE_RATE_NOT_FOUND);
} }
} }
/**
* Validates exchange rates ids existance.
* @param {number} tenantId - Tenant id.
* @param {number[]} exchangeRatesIds - Exchange rates ids.
* @returns {Promise<void>}
*/
private async validateExchangeRatesIdsExistance(
tenantId: number,
exchangeRatesIds: number[]
): Promise<void> {
const { ExchangeRate } = this.tenancy.models(tenantId);
const storedExchangeRates = await ExchangeRate.query().whereIn(
'id',
exchangeRatesIds
);
const storedExchangeRatesIds = storedExchangeRates.map(
(category) => category.id
);
const notFoundExRates = difference(
exchangeRatesIds,
storedExchangeRatesIds
);
if (notFoundExRates.length > 0) {
throw new ServiceError(ERRORS.NOT_FOUND_EXCHANGE_RATES);
}
}
} }

View File

@@ -4,7 +4,8 @@ import {
IItem, IItem,
IInventoryValuationReportQuery, IInventoryValuationReportQuery,
IInventoryValuationItem, IInventoryValuationItem,
IAccountTransaction, InventoryCostLotTracker,
IInventoryValuationStatement,
IInventoryValuationTotal IInventoryValuationTotal
} from 'interfaces'; } from 'interfaces';
import { transformToMap } from 'utils' import { transformToMap } from 'utils'
@@ -12,8 +13,8 @@ import { transformToMap } from 'utils'
export default class InventoryValuationSheet extends FinancialSheet { export default class InventoryValuationSheet extends FinancialSheet {
readonly query: IInventoryValuationReportQuery; readonly query: IInventoryValuationReportQuery;
readonly items: IItem[]; readonly items: IItem[];
readonly INInventoryCostLots: Map<number, IAccountTransaction>; readonly INInventoryCostLots: Map<number, InventoryCostLotTracker>;
readonly OUTInventoryCostLots: Map<number, IAccountTransaction>; readonly OUTInventoryCostLots: Map<number, InventoryCostLotTracker>;
readonly baseCurrency: string; readonly baseCurrency: string;
/** /**
@@ -27,8 +28,8 @@ export default class InventoryValuationSheet extends FinancialSheet {
constructor( constructor(
query: IInventoryValuationReportQuery, query: IInventoryValuationReportQuery,
items: IItem[], items: IItem[],
INInventoryCostLots: IAccountTransaction[], INInventoryCostLots: Map<number, InventoryCostLotTracker[]>,
OUTInventoryCostLots: IAccountTransaction[], OUTInventoryCostLots: Map<number, InventoryCostLotTracker[]>,
baseCurrency: string baseCurrency: string
) { ) {
super(); super();
@@ -41,37 +42,59 @@ export default class InventoryValuationSheet extends FinancialSheet {
this.numberFormat = this.query.numberFormat; this.numberFormat = this.query.numberFormat;
} }
/**
* Retrieve the item cost and quantity from the given transaction map.
* @param {Map<number, InventoryCostLotTracker[]>} transactionsMap
* @param {number} itemId
* @returns
*/
getItemTransaction( getItemTransaction(
transactionsMap, transactionsMap: Map<number, InventoryCostLotTracker[]>,
itemId: number, itemId: number,
): { cost: number, quantity: number } { ): { cost: number, quantity: number } {
const meta = transactionsMap.get(itemId); const meta = transactionsMap.get(itemId);
const cost = get(meta, 'cost', 0); const cost = get(meta, 'cost', 0);
const quantity = get(meta, 'cost', 0); const quantity = get(meta, 'quantity', 0);
return { cost, quantity }; return { cost, quantity };
} }
/**
* Retrieve the cost and quantity of the givne item from `IN` transactions.
* @param {number} itemId -
*/
getItemINTransaction( getItemINTransaction(
itemId: number, itemId: number,
): { cost: number, quantity: number } { ): { cost: number, quantity: number } {
return this.getItemTransaction(this.INInventoryCostLots, itemId); return this.getItemTransaction(this.INInventoryCostLots, itemId);
} }
/**
* Retrieve the cost and quantity of the given item from `OUT` transactions.
* @param {number} itemId -
*/
getItemOUTTransaction( getItemOUTTransaction(
itemId: number, itemId: number,
): { cost: number, quantity: number } { ): { cost: number, quantity: number } {
return this.getItemTransaction(this.OUTInventoryCostLots, itemId); return this.getItemTransaction(this.OUTInventoryCostLots, itemId);
} }
/**
* Retrieve the item closing valuation.
* @param {number} itemId - Item id.
*/
getItemValuation(itemId: number): number { getItemValuation(itemId: number): number {
const { cost: INValuation } = this.getItemINTransaction(itemId); const { cost: INValuation } = this.getItemINTransaction(itemId);
const { cost: OUTValuation } = this.getItemOUTTransaction(itemId); const { cost: OUTValuation } = this.getItemOUTTransaction(itemId);
return INValuation - OUTValuation; return Math.max(INValuation - OUTValuation, 0);
} }
/**
* Retrieve the item closing quantity.
* @param {number} itemId - Item id.
*/
getItemQuantity(itemId: number): number { getItemQuantity(itemId: number): number {
const { quantity: INQuantity } = this.getItemINTransaction(itemId); const { quantity: INQuantity } = this.getItemINTransaction(itemId);
const { quantity: OUTQuantity } = this.getItemOUTTransaction(itemId); const { quantity: OUTQuantity } = this.getItemOUTTransaction(itemId);
@@ -79,10 +102,21 @@ export default class InventoryValuationSheet extends FinancialSheet {
return INQuantity - OUTQuantity; return INQuantity - OUTQuantity;
} }
/**
* Calculates the item weighted average cost from the given valuation and quantity.
* @param {number} valuation
* @param {number} quantity
* @returns {number}
*/
calcAverage(valuation: number, quantity: number): number { calcAverage(valuation: number, quantity: number): number {
return quantity ? valuation / quantity : 0; return quantity ? valuation / quantity : 0;
} }
/**
* Mapping the item model object to inventory valuation item
* @param {IItem} item
* @returns {IInventoryValuationItem}
*/
itemMapper(item: IItem): IInventoryValuationItem { itemMapper(item: IItem): IInventoryValuationItem {
const valuation = this.getItemValuation(item.id); const valuation = this.getItemValuation(item.id);
const quantity = this.getItemQuantity(item.id); const quantity = this.getItemQuantity(item.id);
@@ -103,13 +137,18 @@ export default class InventoryValuationSheet extends FinancialSheet {
} }
/** /**
* * Retrieve the inventory valuation items.
* @returns * @returns {IInventoryValuationItem[]}
*/ */
itemsSection() { itemsSection(): IInventoryValuationItem[] {
return this.items.map(this.itemMapper.bind(this)); return this.items.map(this.itemMapper.bind(this));
} }
/**
* Retrieve the inventory valuation total.
* @param {IInventoryValuationItem[]} items
* @returns {IInventoryValuationTotal}
*/
totalSection(items: IInventoryValuationItem[]): IInventoryValuationTotal { totalSection(items: IInventoryValuationItem[]): IInventoryValuationTotal {
const valuation = sumBy(items, item => item.valuation); const valuation = sumBy(items, item => item.valuation);
const quantity = sumBy(items, item => item.quantity); const quantity = sumBy(items, item => item.quantity);
@@ -122,7 +161,11 @@ export default class InventoryValuationSheet extends FinancialSheet {
}; };
} }
reportData() { /**
* Retrieve the inventory valuation report data.
* @returns {IInventoryValuationStatement}
*/
reportData(): IInventoryValuationStatement {
const items = this.itemsSection(); const items = this.itemsSection();
const total = this.totalSection(items); const total = this.totalSection(items);

View File

@@ -89,11 +89,12 @@ export default class InventoryValuationSheetService {
builder.select('itemId'); builder.select('itemId');
builder.groupBy('itemId'); builder.groupBy('itemId');
}; };
// Retrieve the inventory cost `IN` transactions.
const INTransactions = await InventoryCostLotTracker.query() const INTransactions = await InventoryCostLotTracker.query()
.onBuild(commonQuery) .onBuild(commonQuery)
.where('direction', 'IN'); .where('direction', 'IN');
// Retrieve the inventory cost `OUT` transactions.
const OUTTransactions = await InventoryCostLotTracker.query() const OUTTransactions = await InventoryCostLotTracker.query()
.onBuild(commonQuery) .onBuild(commonQuery)
.where('direction', 'OUT'); .where('direction', 'OUT');
@@ -105,7 +106,7 @@ export default class InventoryValuationSheetService {
OUTTransactions, OUTTransactions,
baseCurrency, baseCurrency,
); );
// Retrieve the inventory valuation report data.
const inventoryValuationData = inventoryValuationInstance.reportData(); const inventoryValuationData = inventoryValuationInstance.reportData();
return { return {