@softwear/latestcollectioncore 1.0.138 → 1.0.139
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.js +17 -2
- package/dist/transaction.js +5 -2
- package/package.json +1 -1
- package/src/articleStatus.ts +17 -2
- package/src/transaction.ts +5 -2
- package/test/articleStatus.spec.js +119 -0
- package/test/transaction.spec.js +3 -1
package/dist/articleStatus.js
CHANGED
|
@@ -75,7 +75,16 @@ function addTransactionToAggregations(beginTimestamp, endTimestamp, aggregateKey
|
|
|
75
75
|
}
|
|
76
76
|
if (transaction.time > endTimestamp)
|
|
77
77
|
return;
|
|
78
|
+
// Period transactions: add everything from BEGIN_STOCK_VECTOR_END onwards
|
|
78
79
|
addVectors(aggregateVector, v, BEGIN_STOCK_VECTOR_END, v.length);
|
|
80
|
+
// Also update QTY_SHELF_STOCK and AMOUNT_SHELF_STOCK from period transactions
|
|
81
|
+
// (they're in the begin stock range but need to be updated by period transactions too)
|
|
82
|
+
if (v[QTY_SHELF_STOCK] !== undefined) {
|
|
83
|
+
aggregateVector[QTY_SHELF_STOCK] += v[QTY_SHELF_STOCK] || 0;
|
|
84
|
+
}
|
|
85
|
+
if (v[AMOUNT_SHELF_STOCK] !== undefined) {
|
|
86
|
+
aggregateVector[AMOUNT_SHELF_STOCK] += v[AMOUNT_SHELF_STOCK] || 0;
|
|
87
|
+
}
|
|
79
88
|
// Calculate derived fields
|
|
80
89
|
aggregateVector[QTY_RETURN] += v[QTY_SOLD] < 0 ? v[QTY_SOLD] : 0;
|
|
81
90
|
aggregateVector[AMOUNT_RETURN] += v[AMOUNT_SOLD_EXCL] < 0 ? v[AMOUNT_SOLD_EXCL] : 0;
|
|
@@ -254,8 +263,14 @@ function postAgg(v, timeFrame) {
|
|
|
254
263
|
const totalCostSold = v[COSTPRICE_SOLD] + v[COSTPRICE_B2B_SOLD] + v[COSTPRICE_B2B_RESOLD] - v[COSTPRICE_B2B_RETURN];
|
|
255
264
|
v[QTY_END_STOCK] = v[QTY_STOCK] + v[QTY_RECIEVED] + v[QTY_CHANGE] + v[QTY_TRANSIT] - totalQtySold;
|
|
256
265
|
v[AMOUNT_END_STOCK] = v[AMOUNT_STOCK] + v[AMOUNT_RECIEVED] + v[AMOUNT_CHANGE] + v[AMOUNT_TRANSIT] + v[AMOUNT_REVALUATE] - totalCostSold;
|
|
257
|
-
|
|
258
|
-
|
|
266
|
+
// QTY_SHELF_STOCK includes:
|
|
267
|
+
// - beginStock from type 0 (same as QTY_STOCK)
|
|
268
|
+
// - beginShelfStock adjustment from type 10 (negative picktickets from previous shards)
|
|
269
|
+
// - shelfStock-affecting transactions during the period (type 1, type 2, etc.)
|
|
270
|
+
// So QTY_SHELF_STOCK already represents the shelfStock value including all transactions
|
|
271
|
+
// endShelfStock = QTY_SHELF_STOCK - consignment - picklist
|
|
272
|
+
v[QTY_END_SHELF_STOCK] = v[QTY_SHELF_STOCK] - v[QTY_CONSIGNMENT] - v[QTY_PICKLIST];
|
|
273
|
+
v[AMOUNT_END_SHELF_STOCK] = v[AMOUNT_SHELF_STOCK] - v[COSTPRICE_CONSIGNMENT] - v[AMOUNT_PICKLIST];
|
|
259
274
|
v[SELLOUT_PERCENTAGE] = (totalQtySold / (totalQtySold + v[QTY_END_STOCK])) * 100;
|
|
260
275
|
v[ROI] = totalAmountSold - v[AMOUNT_RECIEVED] + v[AMOUNT_TRANSIT];
|
|
261
276
|
v[QTY_AVG_STOCK] = (v[QTY_STOCK] * timeFrame + v[STOCK_TIME_PRODUCT]) / timeFrame;
|
package/dist/transaction.js
CHANGED
|
@@ -138,6 +138,8 @@ const buildVector = function (transaction) {
|
|
|
138
138
|
vector[tv.QTY_TRANSACTION] = transaction.qty;
|
|
139
139
|
vector[tv.QTY_STOCK] = transaction.qty;
|
|
140
140
|
vector[tv.AMOUNT_STOCK] = (0, index_1.round2)(transaction.buyprice * transaction.qty);
|
|
141
|
+
// Type 0 (START_STOCK) sets QTY_SHELF_STOCK to same value as QTY_STOCK
|
|
142
|
+
// Type 10 (START_SHELF_STOCK) will subtract outstanding picktickets from this
|
|
141
143
|
vector[tv.QTY_SHELF_STOCK] = transaction.qty;
|
|
142
144
|
vector[tv.AMOUNT_SHELF_STOCK] = (0, index_1.round2)(transaction.buyprice * transaction.qty);
|
|
143
145
|
vector[tv.QTY_CHANGE] = transaction.qty;
|
|
@@ -197,10 +199,11 @@ const buildVector = function (transaction) {
|
|
|
197
199
|
return vector;
|
|
198
200
|
}
|
|
199
201
|
if (transaction.type == types_1.transactionTypeE.PICK_LIST) {
|
|
202
|
+
// Type 94 (PICK_LIST) should NOT set QTY_SHELF_STOCK
|
|
203
|
+
// Picklists reduce shelfStock, which is handled by subtracting QTY_PICKLIST in postAgg
|
|
200
204
|
const vector = new Float64Array(tv.COSTPRICE_PICKLIST + 1);
|
|
201
205
|
vector[tv.QTY_TRANSACTION] = transaction.qty;
|
|
202
|
-
|
|
203
|
-
vector[tv.AMOUNT_SHELF_STOCK] = (0, index_1.round2)(transaction.buyprice * transaction.qty);
|
|
206
|
+
// QTY_SHELF_STOCK is NOT set - picklists are subtracted separately via QTY_PICKLIST
|
|
204
207
|
vector[tv.QTY_PICKLIST] = transaction.qty;
|
|
205
208
|
vector[tv.AMOUNT_PICKLIST] = (0, index_1.round2)(transaction.sellprice * transaction.qty);
|
|
206
209
|
vector[tv.COSTPRICE_PICKLIST] = (0, index_1.round2)(transaction.buyprice * transaction.qty);
|
package/package.json
CHANGED
package/src/articleStatus.ts
CHANGED
|
@@ -137,7 +137,16 @@ function addTransactionToAggregations(beginTimestamp: number, endTimestamp: numb
|
|
|
137
137
|
return
|
|
138
138
|
}
|
|
139
139
|
if (transaction.time > endTimestamp) return
|
|
140
|
+
// Period transactions: add everything from BEGIN_STOCK_VECTOR_END onwards
|
|
140
141
|
addVectors(aggregateVector, v, BEGIN_STOCK_VECTOR_END, v.length)
|
|
142
|
+
// Also update QTY_SHELF_STOCK and AMOUNT_SHELF_STOCK from period transactions
|
|
143
|
+
// (they're in the begin stock range but need to be updated by period transactions too)
|
|
144
|
+
if (v[QTY_SHELF_STOCK] !== undefined) {
|
|
145
|
+
aggregateVector[QTY_SHELF_STOCK] += v[QTY_SHELF_STOCK] || 0
|
|
146
|
+
}
|
|
147
|
+
if (v[AMOUNT_SHELF_STOCK] !== undefined) {
|
|
148
|
+
aggregateVector[AMOUNT_SHELF_STOCK] += v[AMOUNT_SHELF_STOCK] || 0
|
|
149
|
+
}
|
|
141
150
|
|
|
142
151
|
// Calculate derived fields
|
|
143
152
|
|
|
@@ -336,8 +345,14 @@ function postAgg(v: any, timeFrame: number): void {
|
|
|
336
345
|
|
|
337
346
|
v[QTY_END_STOCK] = v[QTY_STOCK] + v[QTY_RECIEVED] + v[QTY_CHANGE] + v[QTY_TRANSIT] - totalQtySold
|
|
338
347
|
v[AMOUNT_END_STOCK] = v[AMOUNT_STOCK] + v[AMOUNT_RECIEVED] + v[AMOUNT_CHANGE] + v[AMOUNT_TRANSIT] + v[AMOUNT_REVALUATE] - totalCostSold
|
|
339
|
-
|
|
340
|
-
|
|
348
|
+
// QTY_SHELF_STOCK includes:
|
|
349
|
+
// - beginStock from type 0 (same as QTY_STOCK)
|
|
350
|
+
// - beginShelfStock adjustment from type 10 (negative picktickets from previous shards)
|
|
351
|
+
// - shelfStock-affecting transactions during the period (type 1, type 2, etc.)
|
|
352
|
+
// So QTY_SHELF_STOCK already represents the shelfStock value including all transactions
|
|
353
|
+
// endShelfStock = QTY_SHELF_STOCK - consignment - picklist
|
|
354
|
+
v[QTY_END_SHELF_STOCK] = v[QTY_SHELF_STOCK] - v[QTY_CONSIGNMENT] - v[QTY_PICKLIST]
|
|
355
|
+
v[AMOUNT_END_SHELF_STOCK] = v[AMOUNT_SHELF_STOCK] - v[COSTPRICE_CONSIGNMENT] - v[AMOUNT_PICKLIST]
|
|
341
356
|
v[SELLOUT_PERCENTAGE] = (totalQtySold / (totalQtySold + v[QTY_END_STOCK])) * 100
|
|
342
357
|
v[ROI] = totalAmountSold - v[AMOUNT_RECIEVED] + v[AMOUNT_TRANSIT]
|
|
343
358
|
v[QTY_AVG_STOCK] = (v[QTY_STOCK] * timeFrame + v[STOCK_TIME_PRODUCT]) / timeFrame
|
package/src/transaction.ts
CHANGED
|
@@ -146,6 +146,8 @@ const buildVector = function (transaction: TransactionI): Float64Array {
|
|
|
146
146
|
vector[tv.QTY_TRANSACTION] = transaction.qty
|
|
147
147
|
vector[tv.QTY_STOCK] = transaction.qty
|
|
148
148
|
vector[tv.AMOUNT_STOCK] = round2(transaction.buyprice * transaction.qty)
|
|
149
|
+
// Type 0 (START_STOCK) sets QTY_SHELF_STOCK to same value as QTY_STOCK
|
|
150
|
+
// Type 10 (START_SHELF_STOCK) will subtract outstanding picktickets from this
|
|
149
151
|
vector[tv.QTY_SHELF_STOCK] = transaction.qty
|
|
150
152
|
vector[tv.AMOUNT_SHELF_STOCK] = round2(transaction.buyprice * transaction.qty)
|
|
151
153
|
vector[tv.QTY_CHANGE] = transaction.qty
|
|
@@ -213,10 +215,11 @@ const buildVector = function (transaction: TransactionI): Float64Array {
|
|
|
213
215
|
}
|
|
214
216
|
|
|
215
217
|
if (transaction.type == transactionTypeE.PICK_LIST) {
|
|
218
|
+
// Type 94 (PICK_LIST) should NOT set QTY_SHELF_STOCK
|
|
219
|
+
// Picklists reduce shelfStock, which is handled by subtracting QTY_PICKLIST in postAgg
|
|
216
220
|
const vector = new Float64Array(tv.COSTPRICE_PICKLIST + 1)
|
|
217
221
|
vector[tv.QTY_TRANSACTION] = transaction.qty
|
|
218
|
-
|
|
219
|
-
vector[tv.AMOUNT_SHELF_STOCK] = round2(transaction.buyprice * transaction.qty)
|
|
222
|
+
// QTY_SHELF_STOCK is NOT set - picklists are subtracted separately via QTY_PICKLIST
|
|
220
223
|
vector[tv.QTY_PICKLIST] = transaction.qty
|
|
221
224
|
vector[tv.AMOUNT_PICKLIST] = round2(transaction.sellprice * transaction.qty)
|
|
222
225
|
vector[tv.COSTPRICE_PICKLIST] = round2(transaction.buyprice * transaction.qty)
|
|
@@ -267,6 +267,125 @@ describe('articleStatus', () => {
|
|
|
267
267
|
expect(result[2].vector[articleStatus.VALUE_AVG_STOCK]).toBe(90.00000031709791)
|
|
268
268
|
expect(result[2].vector[articleStatus.MARGIN]).toBe(50)
|
|
269
269
|
})
|
|
270
|
+
|
|
271
|
+
it('should calculate shelfStock correctly with type 10 (beginShelfStock) and type 94 (picklist)', () => {
|
|
272
|
+
// Test scenario:
|
|
273
|
+
// - beginStock (type 0): 100 units
|
|
274
|
+
// - beginShelfStock (type 10): -5 units (outstanding picktickets from previous shard)
|
|
275
|
+
// - receiving (type 1): +20 units
|
|
276
|
+
// - picklist (type 94): +3 units (new pickticket created)
|
|
277
|
+
// - sale (type 2): -2 units
|
|
278
|
+
// Expected endStock: 100 + 20 - 2 = 118
|
|
279
|
+
// Expected endShelfStock: 118 + (-5) - 0 - 3 = 110
|
|
280
|
+
// (endStock + beginShelfStock - consignment - picklist)
|
|
281
|
+
|
|
282
|
+
const shelfStockTransactions = [
|
|
283
|
+
{
|
|
284
|
+
time: 1546297200000, // Before beginTimestamp - beginStock
|
|
285
|
+
type: '0',
|
|
286
|
+
ean: 'TEST001',
|
|
287
|
+
wh: '50',
|
|
288
|
+
qty: 100,
|
|
289
|
+
buyprice: 10.0,
|
|
290
|
+
docnr: 'BEGINSTOCK',
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
time: 1546297200000, // Before beginTimestamp - beginShelfStock (negative picktickets)
|
|
294
|
+
type: '10',
|
|
295
|
+
ean: 'TEST001',
|
|
296
|
+
wh: '50',
|
|
297
|
+
qty: -5, // Negative: outstanding picktickets from previous shard
|
|
298
|
+
buyprice: 10.0,
|
|
299
|
+
docnr: 'PT-001',
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
time: 1569316408000, // Within timeframe - receiving
|
|
303
|
+
type: '1',
|
|
304
|
+
ean: 'TEST001',
|
|
305
|
+
wh: '50',
|
|
306
|
+
qty: 20,
|
|
307
|
+
buyprice: 10.0,
|
|
308
|
+
docnr: 'REC-001',
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
time: 1569316408500, // Within timeframe - picklist
|
|
312
|
+
type: '94',
|
|
313
|
+
ean: 'TEST001',
|
|
314
|
+
wh: '50',
|
|
315
|
+
qty: 3,
|
|
316
|
+
buyprice: 10.0,
|
|
317
|
+
sellprice: 15.0,
|
|
318
|
+
docnr: 'PT-002',
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
time: 1569316409000, // Within timeframe - sale
|
|
322
|
+
type: '2',
|
|
323
|
+
ean: 'TEST001',
|
|
324
|
+
wh: '50',
|
|
325
|
+
qty: 2,
|
|
326
|
+
buyprice: 10.0,
|
|
327
|
+
sellprice: 20.0,
|
|
328
|
+
vat: 21,
|
|
329
|
+
docnr: 'SALE-001',
|
|
330
|
+
},
|
|
331
|
+
]
|
|
332
|
+
|
|
333
|
+
const rawAggregations = {}
|
|
334
|
+
articleStatus.runQuery(
|
|
335
|
+
{ beginTimeStamp: 1569316407000, endTimeStamp: 1569316409000 + msYear },
|
|
336
|
+
rawAggregations,
|
|
337
|
+
true, // beginStock = true, so type 0 and type 10 are included
|
|
338
|
+
shelfStockTransactions,
|
|
339
|
+
['transaction.ean'],
|
|
340
|
+
{},
|
|
341
|
+
{},
|
|
342
|
+
{},
|
|
343
|
+
false,
|
|
344
|
+
'',
|
|
345
|
+
false
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
const result = []
|
|
349
|
+
Object.entries(rawAggregations).map((oneAggregatedRow) => {
|
|
350
|
+
const vector = [].slice.call(oneAggregatedRow[1])
|
|
351
|
+
articleStatus.postAgg(vector, msYear)
|
|
352
|
+
result.push({ key: oneAggregatedRow[0], vector })
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
// Find the TEST001 result
|
|
356
|
+
const testResult = result.find((r) => r.key === 'TEST001')
|
|
357
|
+
expect(testResult).toBeDefined()
|
|
358
|
+
|
|
359
|
+
// Verify calculations
|
|
360
|
+
// beginStock: 100
|
|
361
|
+
expect(testResult.vector[articleStatus.QTY_STOCK]).toBe(100)
|
|
362
|
+
expect(testResult.vector[articleStatus.AMOUNT_STOCK]).toBe(1000)
|
|
363
|
+
|
|
364
|
+
// QTY_SHELF_STOCK includes beginShelfStock (100 - 5 = 95) + period transactions (20 - 2 = 18) = 113
|
|
365
|
+
// beginShelfStock from beginTimestamp: 100 (type 0) - 5 (type 10) = 95
|
|
366
|
+
// Period transactions: +20 (receiving) - 2 (sale) = +18
|
|
367
|
+
// Total: 95 + 18 = 113
|
|
368
|
+
expect(testResult.vector[articleStatus.QTY_SHELF_STOCK]).toBe(113)
|
|
369
|
+
expect(testResult.vector[articleStatus.AMOUNT_SHELF_STOCK]).toBe(1130)
|
|
370
|
+
|
|
371
|
+
// Transactions during period: +20 (receiving) - 2 (sale) = +18
|
|
372
|
+
// endStock: 100 + 20 - 2 = 118
|
|
373
|
+
expect(testResult.vector[articleStatus.QTY_END_STOCK]).toBe(118)
|
|
374
|
+
expect(testResult.vector[articleStatus.AMOUNT_END_STOCK]).toBe(1180)
|
|
375
|
+
|
|
376
|
+
// picklist: 3
|
|
377
|
+
expect(testResult.vector[articleStatus.QTY_PICKLIST]).toBe(3)
|
|
378
|
+
expect(testResult.vector[articleStatus.AMOUNT_PICKLIST]).toBe(45)
|
|
379
|
+
|
|
380
|
+
// consignment: 0 (not in test data)
|
|
381
|
+
expect(testResult.vector[articleStatus.QTY_CONSIGNMENT]).toBe(0)
|
|
382
|
+
|
|
383
|
+
// endShelfStock = QTY_SHELF_STOCK - consignment - picklist
|
|
384
|
+
// = 113 - 0 - 3 = 110
|
|
385
|
+
expect(testResult.vector[articleStatus.QTY_END_SHELF_STOCK]).toBe(110)
|
|
386
|
+
expect(testResult.vector[articleStatus.AMOUNT_END_SHELF_STOCK]).toBe(1085)
|
|
387
|
+
})
|
|
388
|
+
|
|
270
389
|
it('#getDateRangeFromPicker', () => {
|
|
271
390
|
const dateRange = articleStatus.getDateRangeFromPicker(['2024-01-01', '2024-12-31'])
|
|
272
391
|
expect(dateRange.beginTimeStamp).toBe(1704063600000)
|
package/test/transaction.spec.js
CHANGED
|
@@ -356,7 +356,9 @@ describe('#Transactions', () => {
|
|
|
356
356
|
wh: '51',
|
|
357
357
|
time: 314159265,
|
|
358
358
|
docnr: '51-123',
|
|
359
|
-
|
|
359
|
+
// Type 94 (PICK_LIST) does NOT set QTY_SHELF_STOCK or AMOUNT_SHELF_STOCK
|
|
360
|
+
// Picklists reduce shelfStock, which is handled by subtracting QTY_PICKLIST in postAgg
|
|
361
|
+
vector: [123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 123 * 19.95, 123 * 9.95],
|
|
360
362
|
})
|
|
361
363
|
})
|
|
362
364
|
|