add server to monorepo.

This commit is contained in:
a.bouhuolia
2023-02-03 11:57:50 +02:00
parent 28e309981b
commit 80b97b5fdc
1303 changed files with 137049 additions and 0 deletions

View File

@@ -0,0 +1,264 @@
import { sumBy, get, isEmpty } from 'lodash';
import * as R from 'ramda';
import FinancialSheet from '../FinancialSheet';
import {
IItem,
IInventoryValuationReportQuery,
IInventoryValuationItem,
InventoryCostLotTracker,
IInventoryValuationStatement,
IInventoryValuationTotal,
} from '@/interfaces';
import { allPassedConditionsPass, transformToMap } from 'utils';
export default class InventoryValuationSheet extends FinancialSheet {
readonly query: IInventoryValuationReportQuery;
readonly items: IItem[];
readonly INInventoryCostLots: Map<number, InventoryCostLotTracker>;
readonly OUTInventoryCostLots: Map<number, InventoryCostLotTracker>;
readonly baseCurrency: string;
/**
* Constructor method.
* @param {IInventoryValuationReportQuery} query
* @param items
* @param INInventoryCostLots
* @param OUTInventoryCostLots
* @param baseCurrency
*/
constructor(
query: IInventoryValuationReportQuery,
items: IItem[],
INInventoryCostLots: Map<number, InventoryCostLotTracker[]>,
OUTInventoryCostLots: Map<number, InventoryCostLotTracker[]>,
baseCurrency: string
) {
super();
this.query = query;
this.items = items;
this.INInventoryCostLots = transformToMap(INInventoryCostLots, 'itemId');
this.OUTInventoryCostLots = transformToMap(OUTInventoryCostLots, 'itemId');
this.baseCurrency = baseCurrency;
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
*/
private getItemTransaction(
transactionsMap: Map<number, InventoryCostLotTracker[]>,
itemId: number
): { cost: number; quantity: number } {
const meta = transactionsMap.get(itemId);
const cost = get(meta, 'cost', 0);
const quantity = get(meta, 'quantity', 0);
return { cost, quantity };
}
/**
* Retrieve the cost and quantity of the givne item from `IN` transactions.
* @param {number} itemId -
*/
private getItemINTransaction(itemId: number): {
cost: number;
quantity: number;
} {
return this.getItemTransaction(this.INInventoryCostLots, itemId);
}
/**
* Retrieve the cost and quantity of the given item from `OUT` transactions.
* @param {number} itemId -
*/
private getItemOUTTransaction(itemId: number): {
cost: number;
quantity: number;
} {
return this.getItemTransaction(this.OUTInventoryCostLots, itemId);
}
/**
* Retrieve the item closing valuation.
* @param {number} itemId - Item id.
*/
private getItemValuation(itemId: number): number {
const { cost: INValuation } = this.getItemINTransaction(itemId);
const { cost: OUTValuation } = this.getItemOUTTransaction(itemId);
return Math.max(INValuation - OUTValuation, 0);
}
/**
* Retrieve the item closing quantity.
* @param {number} itemId - Item id.
*/
private getItemQuantity(itemId: number): number {
const { quantity: INQuantity } = this.getItemINTransaction(itemId);
const { quantity: OUTQuantity } = this.getItemOUTTransaction(itemId);
return INQuantity - OUTQuantity;
}
/**
* Calculates the item weighted average cost from the given valuation and quantity.
* @param {number} valuation
* @param {number} quantity
* @returns {number}
*/
private calcAverage(valuation: number, quantity: number): number {
return quantity ? valuation / quantity : 0;
}
/**
* Mapping the item model object to inventory valuation item
* @param {IItem} item
* @returns {IInventoryValuationItem}
*/
private itemMapper(item: IItem): IInventoryValuationItem {
const valuation = this.getItemValuation(item.id);
const quantity = this.getItemQuantity(item.id);
const average = this.calcAverage(valuation, quantity);
return {
id: item.id,
name: item.name,
code: item.code,
valuation,
quantity,
average,
valuationFormatted: this.formatNumber(valuation),
quantityFormatted: this.formatNumber(quantity, { money: false }),
averageFormatted: this.formatNumber(average, { money: false }),
currencyCode: this.baseCurrency,
};
}
/**
* Filter none transactions items.
* @param {IInventoryValuationItem} valuationItem -
* @return {boolean}
*/
private filterNoneTransactions = (
valuationItem: IInventoryValuationItem
): boolean => {
const transactionIN = this.INInventoryCostLots.get(valuationItem.id);
const transactionOUT = this.OUTInventoryCostLots.get(valuationItem.id);
return transactionOUT || transactionIN;
};
/**
* Filter active only items.
* @param {IInventoryValuationItem} valuationItem -
* @returns {boolean}
*/
private filterActiveOnly = (
valuationItem: IInventoryValuationItem
): boolean => {
return (
valuationItem.average !== 0 ||
valuationItem.quantity !== 0 ||
valuationItem.valuation !== 0
);
};
/**
* Filter none-zero total valuation items.
* @param {IInventoryValuationItem} valuationItem
* @returns {boolean}
*/
private filterNoneZero = (valuationItem: IInventoryValuationItem) => {
return valuationItem.valuation !== 0;
};
/**
* Filters the inventory valuation items based on query.
* @param {IInventoryValuationItem} valuationItem
* @returns {boolean}
*/
private itemFilter = (valuationItem: IInventoryValuationItem): boolean => {
const { noneTransactions, noneZero, onlyActive } = this.query;
// Conditions pair filter detarminer.
const condsPairFilters = [
[noneTransactions, this.filterNoneTransactions],
[noneZero, this.filterNoneZero],
[onlyActive, this.filterActiveOnly],
];
return allPassedConditionsPass(condsPairFilters)(valuationItem);
};
/**
* Mappes the items to inventory valuation items nodes.
* @param {IItem[]} items
* @returns {IInventoryValuationItem[]}
*/
private itemsMapper = (items: IItem[]): IInventoryValuationItem[] => {
return this.items.map(this.itemMapper.bind(this));
};
/**
* Filters the inventory valuation items nodes.
* @param {IInventoryValuationItem[]} nodes -
* @returns {IInventoryValuationItem[]}
*/
private itemsFilter = (
nodes: IInventoryValuationItem[]
): IInventoryValuationItem[] => {
return nodes.filter(this.itemFilter);
};
/**
* Detarmines whether the items post filter is active.
*/
private isItemsPostFilter = (): boolean => {
return isEmpty(this.query.itemsIds);
};
/**
* Retrieve the inventory valuation items.
* @returns {IInventoryValuationItem[]}
*/
private itemsSection(): IInventoryValuationItem[] {
return R.compose(
R.when(this.isItemsPostFilter, this.itemsFilter),
this.itemsMapper
)(this.items);
}
/**
* Retrieve the inventory valuation total.
* @param {IInventoryValuationItem[]} items
* @returns {IInventoryValuationTotal}
*/
private totalSection(
items: IInventoryValuationItem[]
): IInventoryValuationTotal {
const valuation = sumBy(items, (item) => item.valuation);
const quantity = sumBy(items, (item) => item.quantity);
return {
valuation,
quantity,
valuationFormatted: this.formatTotalNumber(valuation),
quantityFormatted: this.formatTotalNumber(quantity, { money: false }),
};
}
/**
* Retrieve the inventory valuation report data.
* @returns {IInventoryValuationStatement}
*/
public reportData(): IInventoryValuationStatement {
const items = this.itemsSection();
const total = this.totalSection(items);
return items.length > 0 ? { items, total } : {};
}
}

View File

@@ -0,0 +1,144 @@
import { Service, Inject } from 'typedi';
import moment from 'moment';
import { isEmpty } from 'lodash';
import {
IInventoryValuationReportQuery,
IInventoryValuationSheetMeta,
} from '@/interfaces';
import TenancyService from '@/services/Tenancy/TenancyService';
import InventoryValuationSheet from './InventoryValuationSheet';
import InventoryService from '@/services/Inventory/Inventory';
import { Tenant } from '@/system/models';
@Service()
export default class InventoryValuationSheetService {
@Inject()
tenancy: TenancyService;
@Inject('logger')
logger: any;
@Inject()
inventoryService: InventoryService;
/**
* Defaults balance sheet filter query.
* @return {IBalanceSheetQuery}
*/
get defaultQuery(): IInventoryValuationReportQuery {
return {
asDate: moment().endOf('year').format('YYYY-MM-DD'),
itemsIds: [],
numberFormat: {
precision: 2,
divideOn1000: false,
showZero: false,
formatMoney: 'always',
negativeFormat: 'mines',
},
noneTransactions: true,
noneZero: false,
onlyActive: false,
warehousesIds: [],
branchesIds: [],
};
}
/**
* Retrieve the balance sheet meta.
* @param {number} tenantId -
* @returns {IBalanceSheetMeta}
*/
reportMetadata(tenantId: number): IInventoryValuationSheetMeta {
const settings = this.tenancy.settings(tenantId);
const isCostComputeRunning =
this.inventoryService.isItemsCostComputeRunning(tenantId);
const organizationName = settings.get({
group: 'organization',
key: 'name',
});
const baseCurrency = settings.get({
group: 'organization',
key: 'base_currency',
});
return {
organizationName,
baseCurrency,
isCostComputeRunning,
};
}
/**
* Inventory valuation sheet.
* @param {number} tenantId - Tenant id.
* @param {IInventoryValuationReportQuery} query - Valuation query.
*/
public async inventoryValuationSheet(
tenantId: number,
query: IInventoryValuationReportQuery
) {
const { Item, InventoryCostLotTracker } = this.tenancy.models(tenantId);
const tenant = await Tenant.query()
.findById(tenantId)
.withGraphFetched('metadata');
const filter = {
...this.defaultQuery,
...query,
};
const inventoryItems = await Item.query().onBuild((q) => {
q.where('type', 'inventory');
if (filter.itemsIds.length > 0) {
q.whereIn('id', filter.itemsIds);
}
});
const inventoryItemsIds = inventoryItems.map((item) => item.id);
const commonQuery = (builder) => {
builder.whereIn('item_id', inventoryItemsIds);
builder.sum('rate as rate');
builder.sum('quantity as quantity');
builder.sum('cost as cost');
builder.select('itemId');
builder.groupBy('itemId');
if (!isEmpty(query.branchesIds)) {
builder.modify('filterByBranches', query.branchesIds);
}
if (!isEmpty(query.warehousesIds)) {
builder.modify('filterByWarehouses', query.warehousesIds);
}
};
// Retrieve the inventory cost `IN` transactions.
const INTransactions = await InventoryCostLotTracker.query()
.onBuild(commonQuery)
.where('direction', 'IN');
// Retrieve the inventory cost `OUT` transactions.
const OUTTransactions = await InventoryCostLotTracker.query()
.onBuild(commonQuery)
.where('direction', 'OUT');
const inventoryValuationInstance = new InventoryValuationSheet(
filter,
inventoryItems,
INTransactions,
OUTTransactions,
tenant.metadata.baseCurrency
);
// Retrieve the inventory valuation report data.
const inventoryValuationData = inventoryValuationInstance.reportData();
return {
data: inventoryValuationData,
query: filter,
meta: this.reportMetadata(tenantId),
};
}
}