@softwear/latestcollectioncore 1.0.76 → 1.0.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/articleStatus.d.ts +79 -0
- package/dist/articleStatus.js +414 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/types.d.ts +52 -1
- package/dist/types.js +10 -1
- package/package.json +5 -1
- package/src/articleStatus.ts +464 -0
- package/src/index.ts +1 -0
- package/src/types.ts +61 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { TransactionI, RaGI, HrTimeframeI, TimeGranularityE, SkuI } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* The propFunction for FiltreX needs some data that can't be passed as parameters. That's why the 'globalsRelations' variable is needed.
|
|
4
|
+
* TODO: Functional: Experiment with curried functions
|
|
5
|
+
*/
|
|
6
|
+
declare function propFunction(propertyName: string, getPropertyName: Function, obj: Record<string, any>): any;
|
|
7
|
+
declare function whichShardsToProcess(dateRange: any, granularity: 'year' | 'month'): any[];
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @param {*} v: a vector
|
|
11
|
+
* @param {*} timeFrame: nr miliseconds in timeframe
|
|
12
|
+
*
|
|
13
|
+
* Calculate all derived columns on a given vector.
|
|
14
|
+
*/
|
|
15
|
+
declare function postAgg(v: any, timeFrame: number): void;
|
|
16
|
+
declare function getTimeGrouper(groupers: string[]): TimeGranularityE | undefined;
|
|
17
|
+
declare function runQuery(timeframe: HrTimeframeI, rawAggregations: RaGI, beginStock: boolean, transactions: TransactionI[], selectedGroupsValues: string[], skus: SkuI[], warehouses: any[], subtotalsForOuterGrouper: boolean, computedFilter: string, parentGroupTotals: boolean): RaGI;
|
|
18
|
+
declare const _default: {
|
|
19
|
+
getDateRangeFromPicker: (inputDates: string[]) => HrTimeframeI;
|
|
20
|
+
getTimeGrouper: typeof getTimeGrouper;
|
|
21
|
+
filtrexOptions: {
|
|
22
|
+
extraFunctions: {
|
|
23
|
+
lower: (s: string) => string;
|
|
24
|
+
upper: (s: string) => string;
|
|
25
|
+
};
|
|
26
|
+
customProp: typeof propFunction;
|
|
27
|
+
};
|
|
28
|
+
postAgg: typeof postAgg;
|
|
29
|
+
runQuery: typeof runQuery;
|
|
30
|
+
whichShardsToProcess: typeof whichShardsToProcess;
|
|
31
|
+
AMOUNT_CHANGE: number;
|
|
32
|
+
AMOUNT_CONSIGNMENT: number;
|
|
33
|
+
AMOUNT_END_SHELF_STOCK: number;
|
|
34
|
+
AMOUNT_END_STOCK: number;
|
|
35
|
+
AMOUNT_MINIMUM_STOCK: number;
|
|
36
|
+
AMOUNT_PO: number;
|
|
37
|
+
AMOUNT_PO_COMPLETE: number;
|
|
38
|
+
AMOUNT_RECIEVED: number;
|
|
39
|
+
AMOUNT_RETURN: number;
|
|
40
|
+
AMOUNT_REVALUATE: number;
|
|
41
|
+
AMOUNT_SHELF_STOCK: number;
|
|
42
|
+
AMOUNT_SOLD: number;
|
|
43
|
+
AMOUNT_SOLD_DISCOUNT: number;
|
|
44
|
+
AMOUNT_SOLD_EXCL: number;
|
|
45
|
+
AMOUNT_SOLD_MAX: number;
|
|
46
|
+
AMOUNT_STOCK: number;
|
|
47
|
+
AMOUNT_TRANSIT: number;
|
|
48
|
+
AMOUNT_TURNOVER_VELOCITY: number;
|
|
49
|
+
COSTPRICE_CONSIGNMENT: number;
|
|
50
|
+
COSTPRICE_SOLD: number;
|
|
51
|
+
MARGIN: number;
|
|
52
|
+
MAX_RECIEVE_TIMESTAMP: number;
|
|
53
|
+
PROFIT: number;
|
|
54
|
+
PROFITABILITY: number;
|
|
55
|
+
QTY_AVG_STOCK: number;
|
|
56
|
+
QTY_CHANGE: number;
|
|
57
|
+
QTY_CONSIGNMENT: number;
|
|
58
|
+
QTY_END_SHELF_STOCK: number;
|
|
59
|
+
QTY_END_STOCK: number;
|
|
60
|
+
QTY_MINIMUM_STOCK: number;
|
|
61
|
+
QTY_PO: number;
|
|
62
|
+
QTY_PO_COMPLETE: number;
|
|
63
|
+
QTY_RECIEVED: number;
|
|
64
|
+
QTY_RETURN: number;
|
|
65
|
+
QTY_SHELF_STOCK: number;
|
|
66
|
+
QTY_SOLD: number;
|
|
67
|
+
QTY_SOLD_BEFORE_RETURNS: number;
|
|
68
|
+
QTY_STOCK: number;
|
|
69
|
+
QTY_TRANSACTION: number;
|
|
70
|
+
QTY_TRANSIT: number;
|
|
71
|
+
QTY_TURNOVER_VELOCITY: number;
|
|
72
|
+
RETURN_PERCENTAGE: number;
|
|
73
|
+
ROI: number;
|
|
74
|
+
SELLOUT_PERCENTAGE: number;
|
|
75
|
+
STOCKAMOUNT_TIME_PRODUCT: number;
|
|
76
|
+
STOCK_TIME_PRODUCT: number;
|
|
77
|
+
VALUE_AVG_STOCK: number;
|
|
78
|
+
};
|
|
79
|
+
export default _default;
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const filtrex_1 = require("filtrex");
|
|
7
|
+
const types_1 = require("./types");
|
|
8
|
+
const transaction_1 = __importDefault(require("./transaction"));
|
|
9
|
+
const date_fns_1 = require("date-fns");
|
|
10
|
+
// fields in Vector
|
|
11
|
+
const { QTY_TRANSACTION, QTY_STOCK, AMOUNT_STOCK, QTY_SHELF_STOCK, AMOUNT_SHELF_STOCK, AMOUNT_REVALUATE, QTY_RECIEVED, AMOUNT_RECIEVED, QTY_SOLD, AMOUNT_SOLD, AMOUNT_SOLD_EXCL, COSTPRICE_SOLD, QTY_CHANGE, AMOUNT_CHANGE, QTY_TRANSIT, AMOUNT_TRANSIT, QTY_PO, AMOUNT_PO, QTY_PO_COMPLETE, AMOUNT_PO_COMPLETE, QTY_MINIMUM_STOCK, AMOUNT_MINIMUM_STOCK, QTY_CONSIGNMENT, AMOUNT_CONSIGNMENT, COSTPRICE_CONSIGNMENT, } = transaction_1.default.transactionVector;
|
|
12
|
+
// DERIVED FIELDS
|
|
13
|
+
const AMOUNT_SOLD_MAX = 25;
|
|
14
|
+
const AMOUNT_SOLD_DISCOUNT = 26;
|
|
15
|
+
const MAX_RECIEVE_TIMESTAMP = 27;
|
|
16
|
+
const STOCK_TIME_PRODUCT = 28;
|
|
17
|
+
const STOCKAMOUNT_TIME_PRODUCT = 29;
|
|
18
|
+
const QTY_RETURN = 30;
|
|
19
|
+
const AMOUNT_RETURN = 31;
|
|
20
|
+
// Vector length to allocate for aggregation
|
|
21
|
+
const VECTOR_LENGTH = 32;
|
|
22
|
+
// POST AGGREGATION COLUMNS
|
|
23
|
+
const SELLOUT_PERCENTAGE = 32;
|
|
24
|
+
const ROI = 33;
|
|
25
|
+
const QTY_AVG_STOCK = 34;
|
|
26
|
+
const VALUE_AVG_STOCK = 35;
|
|
27
|
+
const QTY_TURNOVER_VELOCITY = 36;
|
|
28
|
+
const AMOUNT_TURNOVER_VELOCITY = 37;
|
|
29
|
+
const PROFITABILITY = 38;
|
|
30
|
+
const MARGIN = 39;
|
|
31
|
+
const PROFIT = 40;
|
|
32
|
+
const QTY_END_SHELF_STOCK = 41;
|
|
33
|
+
const AMOUNT_END_SHELF_STOCK = 42;
|
|
34
|
+
const QTY_END_STOCK = 43;
|
|
35
|
+
const AMOUNT_END_STOCK = 44;
|
|
36
|
+
const RETURN_PERCENTAGE = 45;
|
|
37
|
+
const QTY_SOLD_BEFORE_RETURNS = 46;
|
|
38
|
+
const BEGIN_STOCK_VECTOR_START = QTY_STOCK;
|
|
39
|
+
const BEGIN_STOCK_VECTOR_END = AMOUNT_SHELF_STOCK + 1;
|
|
40
|
+
const msYear = 24 * 3600 * 365 * 1000;
|
|
41
|
+
const globalJoinableTables = { sku: 'ean', wh: 'wh', transaction: 'row' }; // defines the keys we can join on
|
|
42
|
+
/**
|
|
43
|
+
* Global variables
|
|
44
|
+
*/
|
|
45
|
+
let globalRelations = {}; // holds the joined tables
|
|
46
|
+
let globalNrAggegationExpressions = 0; // tracks the # aggregations rows so we can quit computing if it grows out of bounds
|
|
47
|
+
/**
|
|
48
|
+
* The propFunction for FiltreX needs some data that can't be passed as parameters. That's why the 'globalsRelations' variable is needed.
|
|
49
|
+
* TODO: Functional: Experiment with curried functions
|
|
50
|
+
*/
|
|
51
|
+
function propFunction(propertyName, getPropertyName, obj) {
|
|
52
|
+
var _a, _b;
|
|
53
|
+
const positionOfDot = propertyName.indexOf('.');
|
|
54
|
+
if (positionOfDot < 0)
|
|
55
|
+
return getPropertyName(propertyName);
|
|
56
|
+
const table = propertyName.substring(0, positionOfDot);
|
|
57
|
+
const field = propertyName.substring(positionOfDot + 1);
|
|
58
|
+
if (table == 'transaction')
|
|
59
|
+
return getPropertyName(field);
|
|
60
|
+
return ((_b = (_a = globalRelations[table]) === null || _a === void 0 ? void 0 : _a[obj[globalJoinableTables[table]]]) === null || _b === void 0 ? void 0 : _b[field]) || '~ ?';
|
|
61
|
+
}
|
|
62
|
+
const lower = (s) => s.toLowerCase();
|
|
63
|
+
const upper = (s) => s.toUpperCase();
|
|
64
|
+
const filtrexOptions = {
|
|
65
|
+
extraFunctions: { lower, upper },
|
|
66
|
+
customProp: propFunction,
|
|
67
|
+
};
|
|
68
|
+
// This is how the actual joining of 2 vectors happens
|
|
69
|
+
function addVectors(targetVector, otherVector, begin, end) {
|
|
70
|
+
for (let i = begin; i < end; i++)
|
|
71
|
+
targetVector[i] += otherVector[i] || 0;
|
|
72
|
+
}
|
|
73
|
+
// This function takes a transaction and adds it to the aggregations
|
|
74
|
+
function addTransactionToAggregations(beginTimestamp, endTimestamp, aggregateKey, rawAggregations, transaction, maxRows) {
|
|
75
|
+
let aggregateVector = rawAggregations[aggregateKey];
|
|
76
|
+
if (globalNrAggegationExpressions >= maxRows)
|
|
77
|
+
return;
|
|
78
|
+
if (!aggregateVector) {
|
|
79
|
+
globalNrAggegationExpressions++;
|
|
80
|
+
aggregateVector = new Float64Array(VECTOR_LENGTH);
|
|
81
|
+
rawAggregations[aggregateKey] = aggregateVector;
|
|
82
|
+
}
|
|
83
|
+
const vector = transaction.vector;
|
|
84
|
+
if (transaction.type == '1' && transaction.time > aggregateVector[MAX_RECIEVE_TIMESTAMP])
|
|
85
|
+
aggregateVector[MAX_RECIEVE_TIMESTAMP] = transaction.time;
|
|
86
|
+
if (transaction.time <= beginTimestamp)
|
|
87
|
+
return addVectors(aggregateVector, vector, BEGIN_STOCK_VECTOR_START, BEGIN_STOCK_VECTOR_END);
|
|
88
|
+
if (transaction.time > endTimestamp)
|
|
89
|
+
return;
|
|
90
|
+
addVectors(aggregateVector, vector, BEGIN_STOCK_VECTOR_END, vector.length);
|
|
91
|
+
// Calculate derived fields
|
|
92
|
+
aggregateVector[QTY_RETURN] += vector[QTY_SOLD] < 0 ? vector[QTY_SOLD] : 0;
|
|
93
|
+
aggregateVector[AMOUNT_RETURN] += vector[AMOUNT_SOLD_EXCL] < 0 ? vector[AMOUNT_SOLD_EXCL] : 0;
|
|
94
|
+
aggregateVector[STOCK_TIME_PRODUCT] +=
|
|
95
|
+
(vector[QTY_RECIEVED] || 0 + vector[QTY_TRANSIT] || 0 + vector[QTY_CHANGE] || 0 - vector[QTY_SOLD] || 0) * (endTimestamp - transaction.time);
|
|
96
|
+
aggregateVector[STOCKAMOUNT_TIME_PRODUCT] +=
|
|
97
|
+
(vector[AMOUNT_RECIEVED] || 0 + vector[AMOUNT_TRANSIT] || 0 + vector[AMOUNT_CHANGE] || 0 - vector[COSTPRICE_SOLD] || 0) * (endTimestamp - transaction.time);
|
|
98
|
+
}
|
|
99
|
+
function buildAggregationEvaluator(aggregationExpression) {
|
|
100
|
+
return (0, filtrex_1.compileExpression)(aggregationExpression, filtrexOptions);
|
|
101
|
+
}
|
|
102
|
+
function prepareSubTotalAggKey(aggKey) {
|
|
103
|
+
return aggKey.split('\t').slice(0, -1).join('\t').concat(' Σ');
|
|
104
|
+
}
|
|
105
|
+
/*
|
|
106
|
+
* A factory function that returns a function that can be used to aggregate transactions
|
|
107
|
+
* The factory function is called once for each aggregationExpression
|
|
108
|
+
* @param {Object} options
|
|
109
|
+
*
|
|
110
|
+
* @param {number} options.beginTimestamp - The begin of the time frame UNIX timestamp
|
|
111
|
+
* @param {number} options.endTimestamp - The end of the time frame UNIX timestamp
|
|
112
|
+
*
|
|
113
|
+
* @param {string} options.filterExpression - The FiltreX preprocessed filter expression for: brand, collection, warehouse and articleGroup
|
|
114
|
+
* Example: sku.brand in ("Gabor") and sku.collection not in ("21summer")
|
|
115
|
+
*
|
|
116
|
+
* @param {string} options.aggregationExpression - The aggregation expression that contains the aggregateKey
|
|
117
|
+
* Example: sku.brand+" "+sku.collection+" "+sku.articleGroup+" "+sku.articleCodeSupplier
|
|
118
|
+
*
|
|
119
|
+
* @param {Object} options.rawAggregations - The raw aggregations object where keys are the aggregateKeys and values are the Float64Array vectors
|
|
120
|
+
*
|
|
121
|
+
* @param {number} options.maxRows - The maximum number of rows to aggregate
|
|
122
|
+
* @param {boolean} options.totals - Whether to aggregate totals
|
|
123
|
+
* @param {boolean} options.subtotalsForOuterGrouper - Whether to aggregate single subtotals
|
|
124
|
+
*
|
|
125
|
+
* @returns {Function} - The aggregator function
|
|
126
|
+
*/
|
|
127
|
+
function aggregator({ beginTimestamp, endTimestamp, filterExpression, aggregationExpression, rawAggregations, maxRows, totals, subtotalsForOuterGrouper, parentGroupTotals, }) {
|
|
128
|
+
const filterExpressionCache = {};
|
|
129
|
+
let filter;
|
|
130
|
+
let filterDependencyEvaluator;
|
|
131
|
+
if (filterExpression) {
|
|
132
|
+
filter = (0, filtrex_1.compileExpression)(filterExpression, filtrexOptions);
|
|
133
|
+
// Maintain a cache of evaluated filterExpressions
|
|
134
|
+
// The granularity of the cache is determined by the joined tables in the filterExpression
|
|
135
|
+
const filterGranularity = Object.entries(globalJoinableTables)
|
|
136
|
+
.filter((join) => filterExpression.includes(join[0] + '.'))
|
|
137
|
+
.map((join) => join[1]);
|
|
138
|
+
filterDependencyEvaluator = (0, filtrex_1.compileExpression)(filterGranularity.join('+'));
|
|
139
|
+
}
|
|
140
|
+
const aggregationEvaluator = buildAggregationEvaluator(aggregationExpression);
|
|
141
|
+
// Maintain a cache of evaluated aggregationExpressions
|
|
142
|
+
// The granularity of the cache is determined by the joined tables in the aggregationExpression
|
|
143
|
+
const aggregationExpressionCache = {};
|
|
144
|
+
const aggregationGranularity = Object.entries(globalJoinableTables)
|
|
145
|
+
.filter((join) => aggregationExpression.includes(join[0] + '.'))
|
|
146
|
+
.map((join) => join[1]);
|
|
147
|
+
let aggregationDependencyExpression = aggregationGranularity.join('+');
|
|
148
|
+
if (!aggregationDependencyExpression)
|
|
149
|
+
aggregationDependencyExpression = '"N.A."';
|
|
150
|
+
const aggregationDependencyEvaluator = (0, filtrex_1.compileExpression)(aggregationDependencyExpression);
|
|
151
|
+
return function (transaction) {
|
|
152
|
+
if (filter) {
|
|
153
|
+
const filterDependency = filterDependencyEvaluator(transaction);
|
|
154
|
+
const filterExpression = filterExpressionCache[filterDependency];
|
|
155
|
+
if (filterExpression == -1)
|
|
156
|
+
return;
|
|
157
|
+
if (!filterExpression) {
|
|
158
|
+
filterExpressionCache[filterDependency] = filter(transaction) ? 1 : -1;
|
|
159
|
+
if (filterExpressionCache[filterDependency] == -1)
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const aggregationDependency = aggregationDependencyEvaluator(transaction);
|
|
164
|
+
let aggregateKey = aggregationExpressionCache[aggregationDependency];
|
|
165
|
+
if (!aggregateKey) {
|
|
166
|
+
aggregateKey = aggregationEvaluator(transaction);
|
|
167
|
+
aggregationExpressionCache[aggregationDependency] = aggregateKey;
|
|
168
|
+
}
|
|
169
|
+
// Prepare joined values for derived fields
|
|
170
|
+
if (transaction.type == '2') {
|
|
171
|
+
const sku = globalRelations['sku'][transaction.ean];
|
|
172
|
+
const vector = transaction.vector;
|
|
173
|
+
vector[AMOUNT_SOLD_MAX] = ((sku === null || sku === void 0 ? void 0 : sku.price) || 0) * vector[QTY_SOLD];
|
|
174
|
+
vector[AMOUNT_SOLD_DISCOUNT] = vector[AMOUNT_SOLD_MAX] - vector[AMOUNT_SOLD];
|
|
175
|
+
}
|
|
176
|
+
if (totals)
|
|
177
|
+
addTransactionToAggregations(beginTimestamp, endTimestamp, '** TOTAL **', rawAggregations, transaction, maxRows);
|
|
178
|
+
// Single subtotals are subtotals for first grouper level
|
|
179
|
+
// Example: for 4 groupers we have rows like
|
|
180
|
+
// g0 g1 g2 g3 <- normal row with no subtotal
|
|
181
|
+
// g0 <- subtotal for g0
|
|
182
|
+
if (subtotalsForOuterGrouper == true) {
|
|
183
|
+
addTransactionToAggregations(beginTimestamp, endTimestamp, `${aggregateKey.substr(0, aggregateKey.indexOf('\t'))} Σ`, rawAggregations, transaction, maxRows);
|
|
184
|
+
}
|
|
185
|
+
if (parentGroupTotals) {
|
|
186
|
+
const KPIKey = prepareSubTotalAggKey(aggregateKey);
|
|
187
|
+
addTransactionToAggregations(beginTimestamp, endTimestamp, KPIKey, rawAggregations, transaction, maxRows);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
addTransactionToAggregations(beginTimestamp, endTimestamp, aggregateKey, rawAggregations, transaction, maxRows);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function aggregate({ beginTimestamp, endTimestamp, filterExpression, aggregationExpression, transactions, beginStock, relations, rawAggregations, maxRows, totals, subtotalsForOuterGrouper, parentGroupTotals, }) {
|
|
195
|
+
globalNrAggegationExpressions = 0;
|
|
196
|
+
globalRelations = relations;
|
|
197
|
+
const compiledAggregator = aggregator({
|
|
198
|
+
beginTimestamp,
|
|
199
|
+
endTimestamp,
|
|
200
|
+
filterExpression,
|
|
201
|
+
aggregationExpression,
|
|
202
|
+
rawAggregations,
|
|
203
|
+
maxRows,
|
|
204
|
+
totals,
|
|
205
|
+
subtotalsForOuterGrouper,
|
|
206
|
+
parentGroupTotals,
|
|
207
|
+
});
|
|
208
|
+
if (!beginStock)
|
|
209
|
+
transactions.filter((t) => t.type != '0').forEach(compiledAggregator);
|
|
210
|
+
else
|
|
211
|
+
transactions.forEach(compiledAggregator);
|
|
212
|
+
return rawAggregations;
|
|
213
|
+
}
|
|
214
|
+
function whichShardsToProcess(dateRange, granularity) {
|
|
215
|
+
const padl = function (s) {
|
|
216
|
+
if (s > 9)
|
|
217
|
+
return '' + s;
|
|
218
|
+
return '0' + s;
|
|
219
|
+
};
|
|
220
|
+
const result = [];
|
|
221
|
+
if (granularity == 'year')
|
|
222
|
+
for (let year = dateRange.fromYear; year <= dateRange.toYear; year++)
|
|
223
|
+
result.push(year);
|
|
224
|
+
else
|
|
225
|
+
for (let year = dateRange.fromYear; year <= dateRange.toYear; year++) {
|
|
226
|
+
if (year == dateRange.fromYear && year < dateRange.toYear)
|
|
227
|
+
for (let month = dateRange.fromMonth; month <= 12; month++)
|
|
228
|
+
result.push('' + year + '' + padl(month));
|
|
229
|
+
if (year == dateRange.fromYear && year == dateRange.toYear)
|
|
230
|
+
for (let month = dateRange.fromMonth; month <= dateRange.toMonth; month++)
|
|
231
|
+
result.push('' + year + '' + padl(month));
|
|
232
|
+
if (year != dateRange.fromYear && year != dateRange.toYear)
|
|
233
|
+
for (let month = 1; month <= 12; month++)
|
|
234
|
+
result.push('' + year + '' + padl(month));
|
|
235
|
+
if (year != dateRange.fromYear && year == dateRange.toYear)
|
|
236
|
+
for (let month = 1; month <= dateRange.toMonth; month++)
|
|
237
|
+
result.push('' + year + '' + padl(month));
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
*
|
|
243
|
+
* @param {*} v: a vector
|
|
244
|
+
* @param {*} timeFrame: nr miliseconds in timeframe
|
|
245
|
+
*
|
|
246
|
+
* Calculate all derived columns on a given vector.
|
|
247
|
+
*/
|
|
248
|
+
function postAgg(v, timeFrame) {
|
|
249
|
+
v[QTY_END_STOCK] = v[QTY_STOCK] + v[QTY_RECIEVED] - v[QTY_SOLD] + v[QTY_CHANGE] + v[QTY_TRANSIT];
|
|
250
|
+
v[AMOUNT_END_STOCK] = v[AMOUNT_STOCK] + v[AMOUNT_RECIEVED] - v[COSTPRICE_SOLD] + v[AMOUNT_CHANGE] + v[AMOUNT_TRANSIT] + v[AMOUNT_REVALUATE];
|
|
251
|
+
v[QTY_END_SHELF_STOCK] = v[QTY_SHELF_STOCK] + v[QTY_RECIEVED] - v[QTY_SOLD] - v[QTY_CONSIGNMENT] + v[QTY_CHANGE] + v[QTY_TRANSIT];
|
|
252
|
+
v[AMOUNT_END_SHELF_STOCK] = v[AMOUNT_SHELF_STOCK] + v[AMOUNT_RECIEVED] - v[COSTPRICE_SOLD] + v[COSTPRICE_CONSIGNMENT] + v[AMOUNT_CHANGE] + v[AMOUNT_TRANSIT];
|
|
253
|
+
v[SELLOUT_PERCENTAGE] = (v[QTY_SOLD] / (v[QTY_SOLD] + v[QTY_END_STOCK])) * 100;
|
|
254
|
+
v[ROI] = v[AMOUNT_SOLD_EXCL] - v[AMOUNT_RECIEVED] + v[AMOUNT_TRANSIT];
|
|
255
|
+
v[QTY_AVG_STOCK] = (v[QTY_STOCK] * timeFrame + v[STOCK_TIME_PRODUCT]) / timeFrame;
|
|
256
|
+
v[VALUE_AVG_STOCK] = (v[AMOUNT_STOCK] * timeFrame + v[STOCKAMOUNT_TIME_PRODUCT]) / timeFrame;
|
|
257
|
+
v[QTY_TURNOVER_VELOCITY] = ((v[QTY_SOLD] / v[QTY_AVG_STOCK]) * msYear) / timeFrame;
|
|
258
|
+
v[AMOUNT_TURNOVER_VELOCITY] = ((v[COSTPRICE_SOLD] / v[VALUE_AVG_STOCK]) * msYear) / timeFrame;
|
|
259
|
+
v[PROFIT] = v[AMOUNT_SOLD_EXCL] - v[COSTPRICE_SOLD];
|
|
260
|
+
v[PROFITABILITY] = (v[PROFIT] / v[COSTPRICE_SOLD]) * v[QTY_TURNOVER_VELOCITY] * 100;
|
|
261
|
+
v[MARGIN] = (v[PROFIT] / v[AMOUNT_SOLD_EXCL]) * 100;
|
|
262
|
+
v[RETURN_PERCENTAGE] = (v[QTY_RETURN] / v[QTY_RECIEVED]) * 100;
|
|
263
|
+
v[QTY_SOLD_BEFORE_RETURNS] = v[QTY_SOLD] + Math.abs(v[QTY_RETURN]);
|
|
264
|
+
}
|
|
265
|
+
function getTimeGrouper(groupers) {
|
|
266
|
+
if (groupers.includes('transaction.year'))
|
|
267
|
+
return types_1.TimeGranularityE.YEAR;
|
|
268
|
+
if (groupers.includes('transaction.month'))
|
|
269
|
+
return types_1.TimeGranularityE.MONTH;
|
|
270
|
+
if (groupers.includes('transaction.quarter'))
|
|
271
|
+
return types_1.TimeGranularityE.QUARTER;
|
|
272
|
+
if (groupers.includes('transaction.week'))
|
|
273
|
+
return types_1.TimeGranularityE.WEEK;
|
|
274
|
+
if (groupers.includes('transaction.day'))
|
|
275
|
+
return types_1.TimeGranularityE.DAY;
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
function runQuery(timeframe, rawAggregations, beginStock, transactions, selectedGroupsValues, skus, warehouses, subtotalsForOuterGrouper, computedFilter, parentGroupTotals) {
|
|
279
|
+
const timeGrouper = getTimeGrouper(selectedGroupsValues);
|
|
280
|
+
const aggregationExpression = selectedGroupsValues
|
|
281
|
+
.map((g) => {
|
|
282
|
+
if (g == 'transaction.year')
|
|
283
|
+
return '"' + timeframe.hrYear + '"';
|
|
284
|
+
if (g == 'transaction.quarter')
|
|
285
|
+
return '"' + timeframe.hrQuarter + '"';
|
|
286
|
+
if (g == 'transaction.month')
|
|
287
|
+
return '"' + timeframe.hrMonth + '"';
|
|
288
|
+
if (g == 'transaction.week')
|
|
289
|
+
return '"' + timeframe.hrWeek + '"';
|
|
290
|
+
if (g == 'transaction.day')
|
|
291
|
+
return '"' + timeframe.hrDay + '"';
|
|
292
|
+
return g;
|
|
293
|
+
})
|
|
294
|
+
.join('+"\t"+');
|
|
295
|
+
try {
|
|
296
|
+
aggregate({
|
|
297
|
+
beginTimestamp: timeframe.beginTimeStamp,
|
|
298
|
+
endTimestamp: timeframe.endTimeStamp,
|
|
299
|
+
filterExpression: computedFilter,
|
|
300
|
+
aggregationExpression: aggregationExpression,
|
|
301
|
+
transactions: transactions,
|
|
302
|
+
beginStock: beginStock,
|
|
303
|
+
relations: {
|
|
304
|
+
sku: skus,
|
|
305
|
+
wh: warehouses,
|
|
306
|
+
},
|
|
307
|
+
rawAggregations: rawAggregations,
|
|
308
|
+
maxRows: 100000,
|
|
309
|
+
totals: timeGrouper === undefined,
|
|
310
|
+
subtotalsForOuterGrouper,
|
|
311
|
+
parentGroupTotals,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
catch (e) {
|
|
315
|
+
return rawAggregations; // most probably an end-user typo in the manualFilter
|
|
316
|
+
}
|
|
317
|
+
return rawAggregations;
|
|
318
|
+
}
|
|
319
|
+
const getDateRangeFromPicker = function (inputDates) {
|
|
320
|
+
if (!Array.isArray(inputDates))
|
|
321
|
+
return {
|
|
322
|
+
beginTimeStamp: 0,
|
|
323
|
+
endTimeStamp: 0,
|
|
324
|
+
timeFrame: 0,
|
|
325
|
+
fromYear: 0,
|
|
326
|
+
toYear: 0,
|
|
327
|
+
fromMonth: 0,
|
|
328
|
+
toMonth: 0,
|
|
329
|
+
hrYear: '',
|
|
330
|
+
hrQuarter: '',
|
|
331
|
+
hrMonth: '',
|
|
332
|
+
hrWeek: '',
|
|
333
|
+
hrDay: '',
|
|
334
|
+
};
|
|
335
|
+
const dates = inputDates.slice();
|
|
336
|
+
dates.sort((a, b) => {
|
|
337
|
+
return ('' + a).localeCompare(b);
|
|
338
|
+
});
|
|
339
|
+
const fromDate = (0, date_fns_1.parse)(dates[0], 'yyyy-MM-dd', new Date());
|
|
340
|
+
fromDate.setSeconds(fromDate.getSeconds());
|
|
341
|
+
const toDate = (0, date_fns_1.parse)(dates[1], 'yyyy-MM-dd', new Date());
|
|
342
|
+
const beginTimeStamp = fromDate.getTime();
|
|
343
|
+
const endTimeStamp = toDate.getTime() + 24 * 3600 * 1000;
|
|
344
|
+
const timeFrame = endTimeStamp - beginTimeStamp;
|
|
345
|
+
return {
|
|
346
|
+
beginTimeStamp,
|
|
347
|
+
endTimeStamp,
|
|
348
|
+
timeFrame,
|
|
349
|
+
fromYear: parseInt((0, date_fns_1.format)(fromDate, 'yyyy')),
|
|
350
|
+
toYear: parseInt((0, date_fns_1.format)(toDate, 'yyyy')),
|
|
351
|
+
fromMonth: parseInt((0, date_fns_1.format)(fromDate, 'MM')),
|
|
352
|
+
toMonth: parseInt((0, date_fns_1.format)(toDate, 'MM')),
|
|
353
|
+
hrYear: (0, date_fns_1.format)(fromDate, 'yyyy'),
|
|
354
|
+
hrQuarter: (0, date_fns_1.format)(fromDate, 'yyyy-QQQ'),
|
|
355
|
+
hrMonth: (0, date_fns_1.format)(fromDate, 'yyyy-MM'),
|
|
356
|
+
hrWeek: (0, date_fns_1.format)(fromDate, 'RRRR-II'),
|
|
357
|
+
hrDay: (0, date_fns_1.format)(fromDate, 'yyyy-MM-dd'),
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
exports.default = {
|
|
361
|
+
getDateRangeFromPicker,
|
|
362
|
+
getTimeGrouper,
|
|
363
|
+
filtrexOptions,
|
|
364
|
+
postAgg,
|
|
365
|
+
runQuery,
|
|
366
|
+
whichShardsToProcess,
|
|
367
|
+
AMOUNT_CHANGE,
|
|
368
|
+
AMOUNT_CONSIGNMENT,
|
|
369
|
+
AMOUNT_END_SHELF_STOCK,
|
|
370
|
+
AMOUNT_END_STOCK,
|
|
371
|
+
AMOUNT_MINIMUM_STOCK,
|
|
372
|
+
AMOUNT_PO,
|
|
373
|
+
AMOUNT_PO_COMPLETE,
|
|
374
|
+
AMOUNT_RECIEVED,
|
|
375
|
+
AMOUNT_RETURN,
|
|
376
|
+
AMOUNT_REVALUATE,
|
|
377
|
+
AMOUNT_SHELF_STOCK,
|
|
378
|
+
AMOUNT_SOLD,
|
|
379
|
+
AMOUNT_SOLD_DISCOUNT,
|
|
380
|
+
AMOUNT_SOLD_EXCL,
|
|
381
|
+
AMOUNT_SOLD_MAX,
|
|
382
|
+
AMOUNT_STOCK,
|
|
383
|
+
AMOUNT_TRANSIT,
|
|
384
|
+
AMOUNT_TURNOVER_VELOCITY,
|
|
385
|
+
COSTPRICE_CONSIGNMENT,
|
|
386
|
+
COSTPRICE_SOLD,
|
|
387
|
+
MARGIN,
|
|
388
|
+
MAX_RECIEVE_TIMESTAMP,
|
|
389
|
+
PROFIT,
|
|
390
|
+
PROFITABILITY,
|
|
391
|
+
QTY_AVG_STOCK,
|
|
392
|
+
QTY_CHANGE,
|
|
393
|
+
QTY_CONSIGNMENT,
|
|
394
|
+
QTY_END_SHELF_STOCK,
|
|
395
|
+
QTY_END_STOCK,
|
|
396
|
+
QTY_MINIMUM_STOCK,
|
|
397
|
+
QTY_PO,
|
|
398
|
+
QTY_PO_COMPLETE,
|
|
399
|
+
QTY_RECIEVED,
|
|
400
|
+
QTY_RETURN,
|
|
401
|
+
QTY_SHELF_STOCK,
|
|
402
|
+
QTY_SOLD,
|
|
403
|
+
QTY_SOLD_BEFORE_RETURNS,
|
|
404
|
+
QTY_STOCK,
|
|
405
|
+
QTY_TRANSACTION,
|
|
406
|
+
QTY_TRANSIT,
|
|
407
|
+
QTY_TURNOVER_VELOCITY,
|
|
408
|
+
RETURN_PERCENTAGE,
|
|
409
|
+
ROI,
|
|
410
|
+
SELLOUT_PERCENTAGE,
|
|
411
|
+
STOCKAMOUNT_TIME_PRODUCT,
|
|
412
|
+
STOCK_TIME_PRODUCT,
|
|
413
|
+
VALUE_AVG_STOCK,
|
|
414
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -9,5 +9,6 @@ export { default as sizeToMap } from './sizeToMap';
|
|
|
9
9
|
export { default as findSkuByBarcode } from './findSkuByBarcode';
|
|
10
10
|
export { default as round2 } from './round2';
|
|
11
11
|
export { default as transaction } from './transaction';
|
|
12
|
+
export { default as articleStatus } from './articleStatus';
|
|
12
13
|
export * from './types';
|
|
13
14
|
export * from './consts';
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.transaction = exports.round2 = exports.findSkuByBarcode = exports.sizeToMap = exports.hasOnlyDigits = exports.hashBrand = exports.getPreferedPropertyMappings = exports.isean13 = exports.ean13 = exports.deepCopy = exports.buildPropertyMappingFn = void 0;
|
|
20
|
+
exports.articleStatus = exports.transaction = exports.round2 = exports.findSkuByBarcode = exports.sizeToMap = exports.hasOnlyDigits = exports.hashBrand = exports.getPreferedPropertyMappings = exports.isean13 = exports.ean13 = exports.deepCopy = exports.buildPropertyMappingFn = void 0;
|
|
21
21
|
var buildPropertyMappingFn_1 = require("./buildPropertyMappingFn");
|
|
22
22
|
Object.defineProperty(exports, "buildPropertyMappingFn", { enumerable: true, get: function () { return __importDefault(buildPropertyMappingFn_1).default; } });
|
|
23
23
|
var deepCopy_1 = require("./deepCopy");
|
|
@@ -40,5 +40,7 @@ var round2_1 = require("./round2");
|
|
|
40
40
|
Object.defineProperty(exports, "round2", { enumerable: true, get: function () { return __importDefault(round2_1).default; } });
|
|
41
41
|
var transaction_1 = require("./transaction");
|
|
42
42
|
Object.defineProperty(exports, "transaction", { enumerable: true, get: function () { return __importDefault(transaction_1).default; } });
|
|
43
|
+
var articleStatus_1 = require("./articleStatus");
|
|
44
|
+
Object.defineProperty(exports, "articleStatus", { enumerable: true, get: function () { return __importDefault(articleStatus_1).default; } });
|
|
43
45
|
__exportStar(require("./types"), exports);
|
|
44
46
|
__exportStar(require("./consts"), exports);
|
package/dist/types.d.ts
CHANGED
|
@@ -256,6 +256,8 @@ interface dbTransactionI {
|
|
|
256
256
|
docnr: string;
|
|
257
257
|
time: number;
|
|
258
258
|
vector: number[];
|
|
259
|
+
row?: number;
|
|
260
|
+
unixtime?: number;
|
|
259
261
|
}
|
|
260
262
|
interface TransactionI {
|
|
261
263
|
type: transactionTypeE;
|
|
@@ -269,5 +271,54 @@ interface TransactionI {
|
|
|
269
271
|
sellprice: number;
|
|
270
272
|
vat?: number;
|
|
271
273
|
buyprice: number;
|
|
274
|
+
vector: Float64Array;
|
|
272
275
|
}
|
|
273
|
-
|
|
276
|
+
type RaGI = Record<string, Float64Array>;
|
|
277
|
+
interface AggregateFnI {
|
|
278
|
+
beginTimestamp: number;
|
|
279
|
+
endTimestamp: number;
|
|
280
|
+
filterExpression: string;
|
|
281
|
+
aggregationExpression: string;
|
|
282
|
+
transactions: TransactionI[];
|
|
283
|
+
beginStock: boolean;
|
|
284
|
+
relations: any;
|
|
285
|
+
rawAggregations: RaGI;
|
|
286
|
+
maxRows: number;
|
|
287
|
+
totals: boolean;
|
|
288
|
+
subtotalsForOuterGrouper?: boolean;
|
|
289
|
+
parentGroupTotals?: boolean;
|
|
290
|
+
}
|
|
291
|
+
interface AggregatorFnI {
|
|
292
|
+
beginTimestamp: number;
|
|
293
|
+
endTimestamp: number;
|
|
294
|
+
filterExpression: string;
|
|
295
|
+
aggregationExpression: string;
|
|
296
|
+
rawAggregations: RaGI;
|
|
297
|
+
maxRows: number;
|
|
298
|
+
totals: boolean;
|
|
299
|
+
subtotalsForOuterGrouper?: boolean;
|
|
300
|
+
reportViz?: string;
|
|
301
|
+
parentGroupTotals?: boolean;
|
|
302
|
+
}
|
|
303
|
+
interface HrTimeframeI {
|
|
304
|
+
beginTimeStamp: number;
|
|
305
|
+
endTimeStamp: number;
|
|
306
|
+
timeFrame: number;
|
|
307
|
+
fromYear: number;
|
|
308
|
+
toYear: number;
|
|
309
|
+
fromMonth: number;
|
|
310
|
+
toMonth: number;
|
|
311
|
+
hrYear: string;
|
|
312
|
+
hrQuarter: string;
|
|
313
|
+
hrMonth: string;
|
|
314
|
+
hrWeek: string;
|
|
315
|
+
hrDay: string;
|
|
316
|
+
}
|
|
317
|
+
declare enum TimeGranularityE {
|
|
318
|
+
DAY = "day",
|
|
319
|
+
WEEK = "week",
|
|
320
|
+
MONTH = "month",
|
|
321
|
+
QUARTER = "quarter",
|
|
322
|
+
YEAR = "year"
|
|
323
|
+
}
|
|
324
|
+
export { RowI, StockTransferSelectionI, MarkedSkuI, vatCategoryE, tagTypeE, SkuI, GroupI, ProductI, ImageI, AttributeI, ColorI, MatrixI, StockTransferDataI, StockTransferMatrixI, BrandSettingI, mappingStrategyE, subscriptionE, dbTransactionI, TransactionI, transactionTypeE, HrTimeframeI, TimeGranularityE, AggregatorFnI, AggregateFnI, RaGI, };
|
package/dist/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.transactionTypeE = exports.subscriptionE = exports.mappingStrategyE = exports.tagTypeE = exports.vatCategoryE = void 0;
|
|
3
|
+
exports.TimeGranularityE = exports.transactionTypeE = exports.subscriptionE = exports.mappingStrategyE = exports.tagTypeE = exports.vatCategoryE = void 0;
|
|
4
4
|
var subscriptionE;
|
|
5
5
|
(function (subscriptionE) {
|
|
6
6
|
subscriptionE["DATA_ONLY"] = "data_only";
|
|
@@ -54,3 +54,12 @@ var transactionTypeE;
|
|
|
54
54
|
transactionTypeE["OFFER"] = "98";
|
|
55
55
|
})(transactionTypeE || (transactionTypeE = {}));
|
|
56
56
|
exports.transactionTypeE = transactionTypeE;
|
|
57
|
+
var TimeGranularityE;
|
|
58
|
+
(function (TimeGranularityE) {
|
|
59
|
+
TimeGranularityE["DAY"] = "day";
|
|
60
|
+
TimeGranularityE["WEEK"] = "week";
|
|
61
|
+
TimeGranularityE["MONTH"] = "month";
|
|
62
|
+
TimeGranularityE["QUARTER"] = "quarter";
|
|
63
|
+
TimeGranularityE["YEAR"] = "year";
|
|
64
|
+
})(TimeGranularityE || (TimeGranularityE = {}));
|
|
65
|
+
exports.TimeGranularityE = TimeGranularityE;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softwear/latestcollectioncore",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.78",
|
|
4
4
|
"description": "Core functions for LatestCollections applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -26,5 +26,9 @@
|
|
|
26
26
|
"mocha": "^10.2.0",
|
|
27
27
|
"ts-node": "^10.9.1",
|
|
28
28
|
"typescript": "^4.9.4"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"date-fns": "^3.0.0",
|
|
32
|
+
"filtrex": "^3.0.0"
|
|
29
33
|
}
|
|
30
34
|
}
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { compileExpression } from 'filtrex'
|
|
2
|
+
import { TransactionI, RaGI, AggregateFnI, AggregatorFnI, HrTimeframeI, TimeGranularityE, SkuI } from './types'
|
|
3
|
+
import transaction from './transaction'
|
|
4
|
+
import { format, parse } from 'date-fns'
|
|
5
|
+
|
|
6
|
+
// fields in Vector
|
|
7
|
+
const {
|
|
8
|
+
QTY_TRANSACTION,
|
|
9
|
+
QTY_STOCK,
|
|
10
|
+
AMOUNT_STOCK,
|
|
11
|
+
QTY_SHELF_STOCK,
|
|
12
|
+
AMOUNT_SHELF_STOCK,
|
|
13
|
+
AMOUNT_REVALUATE,
|
|
14
|
+
QTY_RECIEVED,
|
|
15
|
+
AMOUNT_RECIEVED,
|
|
16
|
+
QTY_SOLD,
|
|
17
|
+
AMOUNT_SOLD,
|
|
18
|
+
AMOUNT_SOLD_EXCL,
|
|
19
|
+
COSTPRICE_SOLD,
|
|
20
|
+
QTY_CHANGE,
|
|
21
|
+
AMOUNT_CHANGE,
|
|
22
|
+
QTY_TRANSIT,
|
|
23
|
+
AMOUNT_TRANSIT,
|
|
24
|
+
QTY_PO,
|
|
25
|
+
AMOUNT_PO,
|
|
26
|
+
QTY_PO_COMPLETE,
|
|
27
|
+
AMOUNT_PO_COMPLETE,
|
|
28
|
+
QTY_MINIMUM_STOCK,
|
|
29
|
+
AMOUNT_MINIMUM_STOCK,
|
|
30
|
+
QTY_CONSIGNMENT,
|
|
31
|
+
AMOUNT_CONSIGNMENT,
|
|
32
|
+
COSTPRICE_CONSIGNMENT,
|
|
33
|
+
} = transaction.transactionVector
|
|
34
|
+
|
|
35
|
+
// DERIVED FIELDS
|
|
36
|
+
const AMOUNT_SOLD_MAX = 25
|
|
37
|
+
const AMOUNT_SOLD_DISCOUNT = 26
|
|
38
|
+
const MAX_RECIEVE_TIMESTAMP = 27
|
|
39
|
+
const STOCK_TIME_PRODUCT = 28
|
|
40
|
+
const STOCKAMOUNT_TIME_PRODUCT = 29
|
|
41
|
+
const QTY_RETURN = 30
|
|
42
|
+
const AMOUNT_RETURN = 31
|
|
43
|
+
|
|
44
|
+
// Vector length to allocate for aggregation
|
|
45
|
+
const VECTOR_LENGTH = 32
|
|
46
|
+
|
|
47
|
+
// POST AGGREGATION COLUMNS
|
|
48
|
+
const SELLOUT_PERCENTAGE = 32
|
|
49
|
+
const ROI = 33
|
|
50
|
+
const QTY_AVG_STOCK = 34
|
|
51
|
+
const VALUE_AVG_STOCK = 35
|
|
52
|
+
const QTY_TURNOVER_VELOCITY = 36
|
|
53
|
+
const AMOUNT_TURNOVER_VELOCITY = 37
|
|
54
|
+
const PROFITABILITY = 38
|
|
55
|
+
const MARGIN = 39
|
|
56
|
+
const PROFIT = 40
|
|
57
|
+
const QTY_END_SHELF_STOCK = 41
|
|
58
|
+
const AMOUNT_END_SHELF_STOCK = 42
|
|
59
|
+
const QTY_END_STOCK = 43
|
|
60
|
+
const AMOUNT_END_STOCK = 44
|
|
61
|
+
const RETURN_PERCENTAGE = 45
|
|
62
|
+
const QTY_SOLD_BEFORE_RETURNS = 46
|
|
63
|
+
|
|
64
|
+
const BEGIN_STOCK_VECTOR_START = QTY_STOCK
|
|
65
|
+
const BEGIN_STOCK_VECTOR_END = AMOUNT_SHELF_STOCK + 1
|
|
66
|
+
|
|
67
|
+
const msYear = 24 * 3600 * 365 * 1000
|
|
68
|
+
const globalJoinableTables = { sku: 'ean', wh: 'wh', transaction: 'row' } // defines the keys we can join on
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Global variables
|
|
72
|
+
*/
|
|
73
|
+
let globalRelations = {} // holds the joined tables
|
|
74
|
+
let globalNrAggegationExpressions = 0 // tracks the # aggregations rows so we can quit computing if it grows out of bounds
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The propFunction for FiltreX needs some data that can't be passed as parameters. That's why the 'globalsRelations' variable is needed.
|
|
78
|
+
* TODO: Functional: Experiment with curried functions
|
|
79
|
+
*/
|
|
80
|
+
function propFunction(propertyName: string, getPropertyName: Function, obj: Record<string, any>): any {
|
|
81
|
+
const positionOfDot = propertyName.indexOf('.')
|
|
82
|
+
if (positionOfDot < 0) return getPropertyName(propertyName)
|
|
83
|
+
const table = propertyName.substring(0, positionOfDot)
|
|
84
|
+
const field = propertyName.substring(positionOfDot + 1)
|
|
85
|
+
if (table == 'transaction') return getPropertyName(field)
|
|
86
|
+
return globalRelations[table]?.[obj[globalJoinableTables[table]]]?.[field] || '~ ?'
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const lower = (s: string) => s.toLowerCase()
|
|
90
|
+
const upper = (s: string) => s.toUpperCase()
|
|
91
|
+
|
|
92
|
+
const filtrexOptions = {
|
|
93
|
+
extraFunctions: { lower, upper },
|
|
94
|
+
customProp: propFunction,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// This is how the actual joining of 2 vectors happens
|
|
98
|
+
function addVectors(targetVector: Float64Array, otherVector: Float64Array, begin: number, end: number): void {
|
|
99
|
+
for (let i = begin; i < end; i++) targetVector[i] += otherVector[i] || 0
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// This function takes a transaction and adds it to the aggregations
|
|
103
|
+
function addTransactionToAggregations(beginTimestamp: number, endTimestamp: number, aggregateKey: string, rawAggregations: RaGI, transaction: TransactionI, maxRows: number): void {
|
|
104
|
+
let aggregateVector = rawAggregations[aggregateKey]
|
|
105
|
+
if (globalNrAggegationExpressions >= maxRows) return
|
|
106
|
+
if (!aggregateVector) {
|
|
107
|
+
globalNrAggegationExpressions++
|
|
108
|
+
aggregateVector = new Float64Array(VECTOR_LENGTH)
|
|
109
|
+
rawAggregations[aggregateKey] = aggregateVector
|
|
110
|
+
}
|
|
111
|
+
const vector = transaction.vector
|
|
112
|
+
if (transaction.type == '1' && transaction.time > aggregateVector[MAX_RECIEVE_TIMESTAMP]) aggregateVector[MAX_RECIEVE_TIMESTAMP] = transaction.time
|
|
113
|
+
if (transaction.time <= beginTimestamp) return addVectors(aggregateVector, vector, BEGIN_STOCK_VECTOR_START, BEGIN_STOCK_VECTOR_END)
|
|
114
|
+
if (transaction.time > endTimestamp) return
|
|
115
|
+
addVectors(aggregateVector, vector, BEGIN_STOCK_VECTOR_END, vector.length)
|
|
116
|
+
|
|
117
|
+
// Calculate derived fields
|
|
118
|
+
|
|
119
|
+
aggregateVector[QTY_RETURN] += vector[QTY_SOLD] < 0 ? vector[QTY_SOLD] : 0
|
|
120
|
+
aggregateVector[AMOUNT_RETURN] += vector[AMOUNT_SOLD_EXCL] < 0 ? vector[AMOUNT_SOLD_EXCL] : 0
|
|
121
|
+
|
|
122
|
+
aggregateVector[STOCK_TIME_PRODUCT] +=
|
|
123
|
+
(vector[QTY_RECIEVED] || 0 + vector[QTY_TRANSIT] || 0 + vector[QTY_CHANGE] || 0 - vector[QTY_SOLD] || 0) * (endTimestamp - transaction.time)
|
|
124
|
+
aggregateVector[STOCKAMOUNT_TIME_PRODUCT] +=
|
|
125
|
+
(vector[AMOUNT_RECIEVED] || 0 + vector[AMOUNT_TRANSIT] || 0 + vector[AMOUNT_CHANGE] || 0 - vector[COSTPRICE_SOLD] || 0) * (endTimestamp - transaction.time)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function buildAggregationEvaluator(aggregationExpression: string): Function {
|
|
129
|
+
return compileExpression(aggregationExpression, filtrexOptions)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function prepareSubTotalAggKey(aggKey) {
|
|
133
|
+
return aggKey.split('\t').slice(0, -1).join('\t').concat(' Σ')
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/*
|
|
137
|
+
* A factory function that returns a function that can be used to aggregate transactions
|
|
138
|
+
* The factory function is called once for each aggregationExpression
|
|
139
|
+
* @param {Object} options
|
|
140
|
+
*
|
|
141
|
+
* @param {number} options.beginTimestamp - The begin of the time frame UNIX timestamp
|
|
142
|
+
* @param {number} options.endTimestamp - The end of the time frame UNIX timestamp
|
|
143
|
+
*
|
|
144
|
+
* @param {string} options.filterExpression - The FiltreX preprocessed filter expression for: brand, collection, warehouse and articleGroup
|
|
145
|
+
* Example: sku.brand in ("Gabor") and sku.collection not in ("21summer")
|
|
146
|
+
*
|
|
147
|
+
* @param {string} options.aggregationExpression - The aggregation expression that contains the aggregateKey
|
|
148
|
+
* Example: sku.brand+" "+sku.collection+" "+sku.articleGroup+" "+sku.articleCodeSupplier
|
|
149
|
+
*
|
|
150
|
+
* @param {Object} options.rawAggregations - The raw aggregations object where keys are the aggregateKeys and values are the Float64Array vectors
|
|
151
|
+
*
|
|
152
|
+
* @param {number} options.maxRows - The maximum number of rows to aggregate
|
|
153
|
+
* @param {boolean} options.totals - Whether to aggregate totals
|
|
154
|
+
* @param {boolean} options.subtotalsForOuterGrouper - Whether to aggregate single subtotals
|
|
155
|
+
*
|
|
156
|
+
* @returns {Function} - The aggregator function
|
|
157
|
+
*/
|
|
158
|
+
function aggregator({
|
|
159
|
+
beginTimestamp,
|
|
160
|
+
endTimestamp,
|
|
161
|
+
filterExpression,
|
|
162
|
+
aggregationExpression,
|
|
163
|
+
rawAggregations,
|
|
164
|
+
maxRows,
|
|
165
|
+
totals,
|
|
166
|
+
subtotalsForOuterGrouper,
|
|
167
|
+
parentGroupTotals,
|
|
168
|
+
}: AggregatorFnI) {
|
|
169
|
+
const filterExpressionCache = {}
|
|
170
|
+
|
|
171
|
+
let filter
|
|
172
|
+
let filterDependencyEvaluator
|
|
173
|
+
if (filterExpression) {
|
|
174
|
+
filter = compileExpression(filterExpression, filtrexOptions)
|
|
175
|
+
// Maintain a cache of evaluated filterExpressions
|
|
176
|
+
// The granularity of the cache is determined by the joined tables in the filterExpression
|
|
177
|
+
const filterGranularity = Object.entries(globalJoinableTables)
|
|
178
|
+
.filter((join) => filterExpression.includes(join[0] + '.'))
|
|
179
|
+
.map((join) => join[1])
|
|
180
|
+
filterDependencyEvaluator = compileExpression(filterGranularity.join('+'))
|
|
181
|
+
}
|
|
182
|
+
const aggregationEvaluator = buildAggregationEvaluator(aggregationExpression)
|
|
183
|
+
// Maintain a cache of evaluated aggregationExpressions
|
|
184
|
+
// The granularity of the cache is determined by the joined tables in the aggregationExpression
|
|
185
|
+
const aggregationExpressionCache = {}
|
|
186
|
+
const aggregationGranularity = Object.entries(globalJoinableTables)
|
|
187
|
+
.filter((join) => aggregationExpression.includes(join[0] + '.'))
|
|
188
|
+
.map((join) => join[1])
|
|
189
|
+
|
|
190
|
+
let aggregationDependencyExpression = aggregationGranularity.join('+')
|
|
191
|
+
if (!aggregationDependencyExpression) aggregationDependencyExpression = '"N.A."'
|
|
192
|
+
const aggregationDependencyEvaluator = compileExpression(aggregationDependencyExpression)
|
|
193
|
+
return function (transaction: TransactionI): void {
|
|
194
|
+
if (filter) {
|
|
195
|
+
const filterDependency = filterDependencyEvaluator(transaction)
|
|
196
|
+
const filterExpression = filterExpressionCache[filterDependency]
|
|
197
|
+
if (filterExpression == -1) return
|
|
198
|
+
if (!filterExpression) {
|
|
199
|
+
filterExpressionCache[filterDependency] = filter(transaction) ? 1 : -1
|
|
200
|
+
if (filterExpressionCache[filterDependency] == -1) return
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const aggregationDependency = aggregationDependencyEvaluator(transaction)
|
|
204
|
+
let aggregateKey = aggregationExpressionCache[aggregationDependency]
|
|
205
|
+
if (!aggregateKey) {
|
|
206
|
+
aggregateKey = aggregationEvaluator(transaction)
|
|
207
|
+
aggregationExpressionCache[aggregationDependency] = aggregateKey
|
|
208
|
+
}
|
|
209
|
+
// Prepare joined values for derived fields
|
|
210
|
+
if (transaction.type == '2') {
|
|
211
|
+
const sku = globalRelations['sku'][transaction.ean]
|
|
212
|
+
const vector = transaction.vector
|
|
213
|
+
vector[AMOUNT_SOLD_MAX] = (sku?.price || 0) * vector[QTY_SOLD]
|
|
214
|
+
vector[AMOUNT_SOLD_DISCOUNT] = vector[AMOUNT_SOLD_MAX] - vector[AMOUNT_SOLD]
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (totals) addTransactionToAggregations(beginTimestamp, endTimestamp, '** TOTAL **', rawAggregations, transaction, maxRows)
|
|
218
|
+
|
|
219
|
+
// Single subtotals are subtotals for first grouper level
|
|
220
|
+
// Example: for 4 groupers we have rows like
|
|
221
|
+
// g0 g1 g2 g3 <- normal row with no subtotal
|
|
222
|
+
// g0 <- subtotal for g0
|
|
223
|
+
if (subtotalsForOuterGrouper == true) {
|
|
224
|
+
addTransactionToAggregations(beginTimestamp, endTimestamp, `${aggregateKey.substr(0, aggregateKey.indexOf('\t'))} Σ`, rawAggregations, transaction, maxRows)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (parentGroupTotals) {
|
|
228
|
+
const KPIKey = prepareSubTotalAggKey(aggregateKey)
|
|
229
|
+
addTransactionToAggregations(beginTimestamp, endTimestamp, KPIKey, rawAggregations, transaction, maxRows)
|
|
230
|
+
} else {
|
|
231
|
+
addTransactionToAggregations(beginTimestamp, endTimestamp, aggregateKey, rawAggregations, transaction, maxRows)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function aggregate({
|
|
237
|
+
beginTimestamp,
|
|
238
|
+
endTimestamp,
|
|
239
|
+
filterExpression,
|
|
240
|
+
aggregationExpression,
|
|
241
|
+
transactions,
|
|
242
|
+
beginStock,
|
|
243
|
+
relations,
|
|
244
|
+
rawAggregations,
|
|
245
|
+
maxRows,
|
|
246
|
+
totals,
|
|
247
|
+
subtotalsForOuterGrouper,
|
|
248
|
+
parentGroupTotals,
|
|
249
|
+
}: AggregateFnI) {
|
|
250
|
+
globalNrAggegationExpressions = 0
|
|
251
|
+
globalRelations = relations
|
|
252
|
+
|
|
253
|
+
const compiledAggregator = aggregator({
|
|
254
|
+
beginTimestamp,
|
|
255
|
+
endTimestamp,
|
|
256
|
+
filterExpression,
|
|
257
|
+
aggregationExpression,
|
|
258
|
+
rawAggregations,
|
|
259
|
+
maxRows,
|
|
260
|
+
totals,
|
|
261
|
+
subtotalsForOuterGrouper,
|
|
262
|
+
parentGroupTotals,
|
|
263
|
+
})
|
|
264
|
+
if (!beginStock) transactions.filter((t: TransactionI) => t.type != '0').forEach(compiledAggregator)
|
|
265
|
+
else transactions.forEach(compiledAggregator)
|
|
266
|
+
|
|
267
|
+
return rawAggregations
|
|
268
|
+
}
|
|
269
|
+
function whichShardsToProcess(dateRange: any, granularity: 'year' | 'month'): any[] {
|
|
270
|
+
const padl = function (s: number) {
|
|
271
|
+
if (s > 9) return '' + s
|
|
272
|
+
return '0' + s
|
|
273
|
+
}
|
|
274
|
+
const result = [] as any[]
|
|
275
|
+
if (granularity == 'year') for (let year = dateRange.fromYear; year <= dateRange.toYear; year++) result.push(year)
|
|
276
|
+
else
|
|
277
|
+
for (let year = dateRange.fromYear; year <= dateRange.toYear; year++) {
|
|
278
|
+
if (year == dateRange.fromYear && year < dateRange.toYear) for (let month = dateRange.fromMonth; month <= 12; month++) result.push('' + year + '' + padl(month))
|
|
279
|
+
if (year == dateRange.fromYear && year == dateRange.toYear)
|
|
280
|
+
for (let month = dateRange.fromMonth; month <= dateRange.toMonth; month++) result.push('' + year + '' + padl(month))
|
|
281
|
+
if (year != dateRange.fromYear && year != dateRange.toYear) for (let month = 1; month <= 12; month++) result.push('' + year + '' + padl(month))
|
|
282
|
+
if (year != dateRange.fromYear && year == dateRange.toYear) for (let month = 1; month <= dateRange.toMonth; month++) result.push('' + year + '' + padl(month))
|
|
283
|
+
}
|
|
284
|
+
return result
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
*
|
|
288
|
+
* @param {*} v: a vector
|
|
289
|
+
* @param {*} timeFrame: nr miliseconds in timeframe
|
|
290
|
+
*
|
|
291
|
+
* Calculate all derived columns on a given vector.
|
|
292
|
+
*/
|
|
293
|
+
function postAgg(v: any, timeFrame: number): void {
|
|
294
|
+
v[QTY_END_STOCK] = v[QTY_STOCK] + v[QTY_RECIEVED] - v[QTY_SOLD] + v[QTY_CHANGE] + v[QTY_TRANSIT]
|
|
295
|
+
v[AMOUNT_END_STOCK] = v[AMOUNT_STOCK] + v[AMOUNT_RECIEVED] - v[COSTPRICE_SOLD] + v[AMOUNT_CHANGE] + v[AMOUNT_TRANSIT] + v[AMOUNT_REVALUATE]
|
|
296
|
+
v[QTY_END_SHELF_STOCK] = v[QTY_SHELF_STOCK] + v[QTY_RECIEVED] - v[QTY_SOLD] - v[QTY_CONSIGNMENT] + v[QTY_CHANGE] + v[QTY_TRANSIT]
|
|
297
|
+
v[AMOUNT_END_SHELF_STOCK] = v[AMOUNT_SHELF_STOCK] + v[AMOUNT_RECIEVED] - v[COSTPRICE_SOLD] + v[COSTPRICE_CONSIGNMENT] + v[AMOUNT_CHANGE] + v[AMOUNT_TRANSIT]
|
|
298
|
+
v[SELLOUT_PERCENTAGE] = (v[QTY_SOLD] / (v[QTY_SOLD] + v[QTY_END_STOCK])) * 100
|
|
299
|
+
v[ROI] = v[AMOUNT_SOLD_EXCL] - v[AMOUNT_RECIEVED] + v[AMOUNT_TRANSIT]
|
|
300
|
+
v[QTY_AVG_STOCK] = (v[QTY_STOCK] * timeFrame + v[STOCK_TIME_PRODUCT]) / timeFrame
|
|
301
|
+
v[VALUE_AVG_STOCK] = (v[AMOUNT_STOCK] * timeFrame + v[STOCKAMOUNT_TIME_PRODUCT]) / timeFrame
|
|
302
|
+
v[QTY_TURNOVER_VELOCITY] = ((v[QTY_SOLD] / v[QTY_AVG_STOCK]) * msYear) / timeFrame
|
|
303
|
+
v[AMOUNT_TURNOVER_VELOCITY] = ((v[COSTPRICE_SOLD] / v[VALUE_AVG_STOCK]) * msYear) / timeFrame
|
|
304
|
+
v[PROFIT] = v[AMOUNT_SOLD_EXCL] - v[COSTPRICE_SOLD]
|
|
305
|
+
v[PROFITABILITY] = (v[PROFIT] / v[COSTPRICE_SOLD]) * v[QTY_TURNOVER_VELOCITY] * 100
|
|
306
|
+
v[MARGIN] = (v[PROFIT] / v[AMOUNT_SOLD_EXCL]) * 100
|
|
307
|
+
v[RETURN_PERCENTAGE] = (v[QTY_RETURN] / v[QTY_RECIEVED]) * 100
|
|
308
|
+
v[QTY_SOLD_BEFORE_RETURNS] = v[QTY_SOLD] + Math.abs(v[QTY_RETURN])
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function getTimeGrouper(groupers: string[]): TimeGranularityE | undefined {
|
|
312
|
+
if (groupers.includes('transaction.year')) return TimeGranularityE.YEAR
|
|
313
|
+
if (groupers.includes('transaction.month')) return TimeGranularityE.MONTH
|
|
314
|
+
if (groupers.includes('transaction.quarter')) return TimeGranularityE.QUARTER
|
|
315
|
+
if (groupers.includes('transaction.week')) return TimeGranularityE.WEEK
|
|
316
|
+
if (groupers.includes('transaction.day')) return TimeGranularityE.DAY
|
|
317
|
+
return
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function runQuery(
|
|
321
|
+
timeframe: HrTimeframeI,
|
|
322
|
+
rawAggregations: RaGI,
|
|
323
|
+
beginStock: boolean,
|
|
324
|
+
transactions: TransactionI[],
|
|
325
|
+
selectedGroupsValues: string[],
|
|
326
|
+
skus: SkuI[],
|
|
327
|
+
warehouses: any[],
|
|
328
|
+
subtotalsForOuterGrouper: boolean,
|
|
329
|
+
computedFilter: string,
|
|
330
|
+
parentGroupTotals: boolean
|
|
331
|
+
) {
|
|
332
|
+
const timeGrouper = getTimeGrouper(selectedGroupsValues)
|
|
333
|
+
const aggregationExpression = selectedGroupsValues
|
|
334
|
+
.map((g) => {
|
|
335
|
+
if (g == 'transaction.year') return '"' + timeframe.hrYear + '"'
|
|
336
|
+
if (g == 'transaction.quarter') return '"' + timeframe.hrQuarter + '"'
|
|
337
|
+
if (g == 'transaction.month') return '"' + timeframe.hrMonth + '"'
|
|
338
|
+
if (g == 'transaction.week') return '"' + timeframe.hrWeek + '"'
|
|
339
|
+
if (g == 'transaction.day') return '"' + timeframe.hrDay + '"'
|
|
340
|
+
return g
|
|
341
|
+
})
|
|
342
|
+
.join('+"\t"+')
|
|
343
|
+
try {
|
|
344
|
+
aggregate({
|
|
345
|
+
beginTimestamp: timeframe.beginTimeStamp,
|
|
346
|
+
endTimestamp: timeframe.endTimeStamp,
|
|
347
|
+
filterExpression: computedFilter,
|
|
348
|
+
aggregationExpression: aggregationExpression,
|
|
349
|
+
transactions: transactions,
|
|
350
|
+
beginStock: beginStock,
|
|
351
|
+
relations: {
|
|
352
|
+
sku: skus,
|
|
353
|
+
wh: warehouses,
|
|
354
|
+
},
|
|
355
|
+
rawAggregations: rawAggregations,
|
|
356
|
+
maxRows: 100000,
|
|
357
|
+
totals: timeGrouper === undefined,
|
|
358
|
+
subtotalsForOuterGrouper,
|
|
359
|
+
parentGroupTotals,
|
|
360
|
+
})
|
|
361
|
+
} catch (e) {
|
|
362
|
+
return rawAggregations // most probably an end-user typo in the manualFilter
|
|
363
|
+
}
|
|
364
|
+
return rawAggregations
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const getDateRangeFromPicker = function (inputDates: string[]): HrTimeframeI {
|
|
368
|
+
if (!Array.isArray(inputDates))
|
|
369
|
+
return {
|
|
370
|
+
beginTimeStamp: 0,
|
|
371
|
+
endTimeStamp: 0,
|
|
372
|
+
timeFrame: 0,
|
|
373
|
+
fromYear: 0,
|
|
374
|
+
toYear: 0,
|
|
375
|
+
fromMonth: 0,
|
|
376
|
+
toMonth: 0,
|
|
377
|
+
hrYear: '',
|
|
378
|
+
hrQuarter: '',
|
|
379
|
+
hrMonth: '',
|
|
380
|
+
hrWeek: '',
|
|
381
|
+
hrDay: '',
|
|
382
|
+
}
|
|
383
|
+
const dates = inputDates.slice()
|
|
384
|
+
dates.sort((a, b) => {
|
|
385
|
+
return ('' + a).localeCompare(b)
|
|
386
|
+
})
|
|
387
|
+
const fromDate = parse(dates[0], 'yyyy-MM-dd', new Date())
|
|
388
|
+
fromDate.setSeconds(fromDate.getSeconds())
|
|
389
|
+
|
|
390
|
+
const toDate = parse(dates[1], 'yyyy-MM-dd', new Date())
|
|
391
|
+
const beginTimeStamp = fromDate.getTime()
|
|
392
|
+
const endTimeStamp = toDate.getTime() + 24 * 3600 * 1000
|
|
393
|
+
const timeFrame = endTimeStamp - beginTimeStamp
|
|
394
|
+
return {
|
|
395
|
+
beginTimeStamp,
|
|
396
|
+
endTimeStamp,
|
|
397
|
+
timeFrame,
|
|
398
|
+
fromYear: parseInt(format(fromDate, 'yyyy')),
|
|
399
|
+
toYear: parseInt(format(toDate, 'yyyy')),
|
|
400
|
+
fromMonth: parseInt(format(fromDate, 'MM')),
|
|
401
|
+
toMonth: parseInt(format(toDate, 'MM')),
|
|
402
|
+
hrYear: format(fromDate, 'yyyy'),
|
|
403
|
+
hrQuarter: format(fromDate, 'yyyy-QQQ'),
|
|
404
|
+
hrMonth: format(fromDate, 'yyyy-MM'),
|
|
405
|
+
hrWeek: format(fromDate, 'RRRR-II'),
|
|
406
|
+
hrDay: format(fromDate, 'yyyy-MM-dd'),
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export default {
|
|
411
|
+
getDateRangeFromPicker,
|
|
412
|
+
getTimeGrouper,
|
|
413
|
+
filtrexOptions,
|
|
414
|
+
postAgg,
|
|
415
|
+
runQuery,
|
|
416
|
+
whichShardsToProcess,
|
|
417
|
+
AMOUNT_CHANGE,
|
|
418
|
+
AMOUNT_CONSIGNMENT,
|
|
419
|
+
AMOUNT_END_SHELF_STOCK,
|
|
420
|
+
AMOUNT_END_STOCK,
|
|
421
|
+
AMOUNT_MINIMUM_STOCK,
|
|
422
|
+
AMOUNT_PO,
|
|
423
|
+
AMOUNT_PO_COMPLETE,
|
|
424
|
+
AMOUNT_RECIEVED,
|
|
425
|
+
AMOUNT_RETURN,
|
|
426
|
+
AMOUNT_REVALUATE,
|
|
427
|
+
AMOUNT_SHELF_STOCK,
|
|
428
|
+
AMOUNT_SOLD,
|
|
429
|
+
AMOUNT_SOLD_DISCOUNT,
|
|
430
|
+
AMOUNT_SOLD_EXCL,
|
|
431
|
+
AMOUNT_SOLD_MAX,
|
|
432
|
+
AMOUNT_STOCK,
|
|
433
|
+
AMOUNT_TRANSIT,
|
|
434
|
+
AMOUNT_TURNOVER_VELOCITY,
|
|
435
|
+
COSTPRICE_CONSIGNMENT,
|
|
436
|
+
COSTPRICE_SOLD,
|
|
437
|
+
MARGIN,
|
|
438
|
+
MAX_RECIEVE_TIMESTAMP,
|
|
439
|
+
PROFIT,
|
|
440
|
+
PROFITABILITY,
|
|
441
|
+
QTY_AVG_STOCK,
|
|
442
|
+
QTY_CHANGE,
|
|
443
|
+
QTY_CONSIGNMENT,
|
|
444
|
+
QTY_END_SHELF_STOCK,
|
|
445
|
+
QTY_END_STOCK,
|
|
446
|
+
QTY_MINIMUM_STOCK,
|
|
447
|
+
QTY_PO,
|
|
448
|
+
QTY_PO_COMPLETE,
|
|
449
|
+
QTY_RECIEVED,
|
|
450
|
+
QTY_RETURN,
|
|
451
|
+
QTY_SHELF_STOCK,
|
|
452
|
+
QTY_SOLD,
|
|
453
|
+
QTY_SOLD_BEFORE_RETURNS,
|
|
454
|
+
QTY_STOCK,
|
|
455
|
+
QTY_TRANSACTION,
|
|
456
|
+
QTY_TRANSIT,
|
|
457
|
+
QTY_TURNOVER_VELOCITY,
|
|
458
|
+
RETURN_PERCENTAGE,
|
|
459
|
+
ROI,
|
|
460
|
+
SELLOUT_PERCENTAGE,
|
|
461
|
+
STOCKAMOUNT_TIME_PRODUCT,
|
|
462
|
+
STOCK_TIME_PRODUCT,
|
|
463
|
+
VALUE_AVG_STOCK,
|
|
464
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -9,5 +9,6 @@ export { default as sizeToMap } from './sizeToMap'
|
|
|
9
9
|
export { default as findSkuByBarcode } from './findSkuByBarcode'
|
|
10
10
|
export { default as round2 } from './round2'
|
|
11
11
|
export { default as transaction } from './transaction'
|
|
12
|
+
export { default as articleStatus } from './articleStatus'
|
|
12
13
|
export * from './types'
|
|
13
14
|
export * from './consts'
|
package/src/types.ts
CHANGED
|
@@ -272,6 +272,8 @@ interface dbTransactionI {
|
|
|
272
272
|
docnr: string
|
|
273
273
|
time: number
|
|
274
274
|
vector: number[]
|
|
275
|
+
row?: number
|
|
276
|
+
unixtime?: number
|
|
275
277
|
}
|
|
276
278
|
interface TransactionI {
|
|
277
279
|
type: transactionTypeE
|
|
@@ -285,7 +287,61 @@ interface TransactionI {
|
|
|
285
287
|
sellprice: number
|
|
286
288
|
vat?: number
|
|
287
289
|
buyprice: number
|
|
290
|
+
vector: Float64Array
|
|
288
291
|
}
|
|
292
|
+
|
|
293
|
+
type RaGI = Record<string, Float64Array>
|
|
294
|
+
|
|
295
|
+
interface AggregateFnI {
|
|
296
|
+
beginTimestamp: number
|
|
297
|
+
endTimestamp: number
|
|
298
|
+
filterExpression: string
|
|
299
|
+
aggregationExpression: string
|
|
300
|
+
transactions: TransactionI[]
|
|
301
|
+
beginStock: boolean
|
|
302
|
+
relations: any
|
|
303
|
+
rawAggregations: RaGI
|
|
304
|
+
maxRows: number
|
|
305
|
+
totals: boolean
|
|
306
|
+
subtotalsForOuterGrouper?: boolean
|
|
307
|
+
parentGroupTotals?: boolean
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
interface AggregatorFnI {
|
|
311
|
+
beginTimestamp: number
|
|
312
|
+
endTimestamp: number
|
|
313
|
+
filterExpression: string
|
|
314
|
+
aggregationExpression: string
|
|
315
|
+
rawAggregations: RaGI
|
|
316
|
+
maxRows: number
|
|
317
|
+
totals: boolean
|
|
318
|
+
subtotalsForOuterGrouper?: boolean
|
|
319
|
+
reportViz?: string
|
|
320
|
+
parentGroupTotals?: boolean
|
|
321
|
+
}
|
|
322
|
+
interface HrTimeframeI {
|
|
323
|
+
beginTimeStamp: number
|
|
324
|
+
endTimeStamp: number
|
|
325
|
+
timeFrame: number
|
|
326
|
+
fromYear: number
|
|
327
|
+
toYear: number
|
|
328
|
+
fromMonth: number
|
|
329
|
+
toMonth: number
|
|
330
|
+
hrYear: string
|
|
331
|
+
hrQuarter: string
|
|
332
|
+
hrMonth: string
|
|
333
|
+
hrWeek: string
|
|
334
|
+
hrDay: string
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
enum TimeGranularityE {
|
|
338
|
+
DAY = 'day',
|
|
339
|
+
WEEK = 'week',
|
|
340
|
+
MONTH = 'month',
|
|
341
|
+
QUARTER = 'quarter',
|
|
342
|
+
YEAR = 'year',
|
|
343
|
+
}
|
|
344
|
+
|
|
289
345
|
export {
|
|
290
346
|
RowI,
|
|
291
347
|
StockTransferSelectionI,
|
|
@@ -307,4 +363,9 @@ export {
|
|
|
307
363
|
dbTransactionI,
|
|
308
364
|
TransactionI,
|
|
309
365
|
transactionTypeE,
|
|
366
|
+
HrTimeframeI,
|
|
367
|
+
TimeGranularityE,
|
|
368
|
+
AggregatorFnI,
|
|
369
|
+
AggregateFnI,
|
|
370
|
+
RaGI,
|
|
310
371
|
}
|