mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
fix(InventoryValuation): inventory valuation report.
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user