@darkpos/pricing 1.0.78 → 1.0.80

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.
@@ -3699,4 +3699,168 @@ describe('Order actions', () => {
3699
3699
  expect(splittedOrders[1].items[0].name).toEqual(item1.name);
3700
3700
  expect(splittedOrders[1].items[0].quantity).toEqual(6);
3701
3701
  });
3702
+
3703
+ test('CU-86dve295v Should add a modifier just once at order level even if properties.limits.maxOrderUse=5', () => {
3704
+ const order = {
3705
+ id: 'ord-123',
3706
+ items: [],
3707
+ modifiers: [],
3708
+ };
3709
+ const modifier = {
3710
+ _id: 1,
3711
+ compute: {
3712
+ amount: 10,
3713
+ action: 'subtract',
3714
+ type: 'fixed',
3715
+ },
3716
+ modifierId: 'abc123',
3717
+ properties: {
3718
+ limits: { maxOrderUse: 5 },
3719
+ },
3720
+ };
3721
+ order.items.push({
3722
+ quantity: 2,
3723
+ itemId: '123',
3724
+ price: 100,
3725
+ modifiers: [],
3726
+ });
3727
+
3728
+ let currentOrder = { ...order };
3729
+
3730
+ for (let i = 0; i < 5; i += 1) {
3731
+ currentOrder = pricingService.order.addModifier({
3732
+ order: currentOrder,
3733
+ modifier,
3734
+ });
3735
+ }
3736
+
3737
+ expect(currentOrder.modifiers.length).toBe(1);
3738
+ });
3739
+
3740
+ test('CU-86dve295v Should add the modifier just once at order level even if properties.limits.maxOrderUse=-1', () => {
3741
+ const order = {
3742
+ id: 'ord-123',
3743
+ items: [],
3744
+ modifiers: [],
3745
+ };
3746
+ const modifier = {
3747
+ _id: 1,
3748
+ compute: {
3749
+ amount: 10,
3750
+ action: 'subtract',
3751
+ type: 'fixed',
3752
+ },
3753
+ modifierId: 'abc123',
3754
+ properties: {
3755
+ limits: { maxOrderUse: -1 },
3756
+ },
3757
+ };
3758
+ order.items.push({
3759
+ quantity: 2,
3760
+ itemId: '123',
3761
+ price: 100,
3762
+ modifiers: [],
3763
+ });
3764
+
3765
+ let currentOrder = { ...order };
3766
+
3767
+ for (let i = 0; i < 5; i += 1) {
3768
+ currentOrder = pricingService.order.addModifier({
3769
+ order: currentOrder,
3770
+ modifier,
3771
+ });
3772
+ }
3773
+
3774
+ expect(currentOrder.modifiers.length).toBe(1);
3775
+ });
3776
+
3777
+ test('CU-86dve295v Should not add a modifier more than once if properties.limits.maxOrderUse is not defined', () => {
3778
+ const order = {
3779
+ id: 'ord-123',
3780
+ items: [],
3781
+ modifiers: [],
3782
+ };
3783
+ const modifier = {
3784
+ _id: 1,
3785
+ compute: {
3786
+ amount: 10,
3787
+ action: 'subtract',
3788
+ type: 'fixed',
3789
+ },
3790
+ modifierId: 'abc123',
3791
+ properties: {
3792
+ limits: { maxOrderUse: 0 },
3793
+ },
3794
+ };
3795
+ order.items.push({
3796
+ quantity: 2,
3797
+ itemId: '123',
3798
+ price: 100,
3799
+ modifiers: [],
3800
+ });
3801
+
3802
+ let currentOrder = { ...order };
3803
+
3804
+ for (let i = 0; i < 5; i += 1) {
3805
+ currentOrder = pricingService.order.addModifier({
3806
+ order: currentOrder,
3807
+ modifier,
3808
+ });
3809
+ }
3810
+
3811
+ expect(currentOrder.modifiers.length).toBe(1);
3812
+ });
3813
+
3814
+ test('CU-86dve295v Should add to modifiers at order level', () => {
3815
+ const order = {
3816
+ id: 'ord-123',
3817
+ items: [],
3818
+ modifiers: [],
3819
+ };
3820
+ const modifier = {
3821
+ _id: 1,
3822
+ compute: {
3823
+ amount: 10,
3824
+ action: 'subtract',
3825
+ type: 'fixed',
3826
+ },
3827
+ modifierId: 'abc123',
3828
+ properties: {
3829
+ limits: { maxOrderUse: 0 },
3830
+ },
3831
+ };
3832
+
3833
+ const modifier2 = {
3834
+ _id: 2,
3835
+ compute: {
3836
+ amount: 10,
3837
+ action: 'subtract',
3838
+ type: 'fixed',
3839
+ },
3840
+ modifierId: 'abc123',
3841
+ };
3842
+
3843
+ order.items.push({
3844
+ quantity: 2,
3845
+ itemId: '123',
3846
+ price: 100,
3847
+ modifiers: [],
3848
+ });
3849
+
3850
+ let currentOrder = { ...order };
3851
+
3852
+ for (let i = 0; i < 5; i += 1) {
3853
+ currentOrder = pricingService.order.addModifier({
3854
+ order: currentOrder,
3855
+ modifier,
3856
+ });
3857
+ }
3858
+
3859
+ currentOrder = pricingService.order.addModifier({
3860
+ order: currentOrder,
3861
+ modifier: modifier2,
3862
+ });
3863
+
3864
+ expect(currentOrder.modifiers.length).toBe(2);
3865
+ });
3702
3866
  });
@@ -6,4 +6,5 @@ module.exports = {
6
6
  LIST: 'list',
7
7
  DEPARTMENT: 'department',
8
8
  OVERRIDE: 'override',
9
+ LIMITS: 'limits',
9
10
  };
@@ -11,6 +11,8 @@ module.exports = ({ _, utils, actions, modifierActions }) => {
11
11
  const startRequestDate = opts.startRequestDate || null;
12
12
  const endRequestDate = opts.endRequestDate || null;
13
13
  const lockPaymentModifiers = !!opts.lockPaymentModifiers;
14
+ const allItems = Array.isArray(opts.items) ? opts.items : [];
15
+
14
16
  const { paymentId } = opts;
15
17
 
16
18
  if (!item) return item;
@@ -43,6 +45,7 @@ module.exports = ({ _, utils, actions, modifierActions }) => {
43
45
  item,
44
46
  startRequestDate,
45
47
  endRequestDate,
48
+ allItems,
46
49
  })
47
50
  );
48
51
 
@@ -195,10 +198,10 @@ module.exports = ({ _, utils, actions, modifierActions }) => {
195
198
 
196
199
  //
197
200
  const calculateMany = (items = [], opts = null) =>
198
- items.map(item => calculateOne(item, opts));
201
+ items.map(item => calculateOne(item, { ...opts, items }));
199
202
 
200
203
  return function calculate(item, opts) {
201
204
  if (Array.isArray(item)) return calculateMany(item, opts);
202
- return calculateOne(item, opts);
205
+ return calculateOne(item, { ...opts });
203
206
  };
204
207
  };
@@ -11,6 +11,7 @@ module.exports = ({ modifierActions, settings }) =>
11
11
  modifierActions.isValid(each) &&
12
12
  (!modifierActions.isGroup(each) || modifierActions.isDepartment(each))
13
13
  )
14
+
14
15
  .map(modifier => {
15
16
  const { name, _id } = modifier;
16
17
 
@@ -1,4 +1,4 @@
1
- module.exports = ({ modifierActions }) =>
1
+ module.exports = ({ modifierActions, utils }) =>
2
2
  function removeModifiersByQuantity(item) {
3
3
  if (!item || !Array.isArray(item.modifiers)) {
4
4
  return item;
@@ -14,13 +14,18 @@ module.exports = ({ modifierActions }) =>
14
14
  modifierActions.isCalculatedPaymentModifier(modifier) ||
15
15
  modifierActions.isGroupOfModifiers(modifier) ||
16
16
  !modifier.modifierId ||
17
- !modifierActions.isDirect(modifier)
17
+ !modifierActions.isDirect(modifier) ||
18
+ modifierActions.isUnlimitedItemUse(modifier)
18
19
  )
19
20
  return true;
20
21
 
21
22
  const count = modifierCounts[modifier.modifierId] || 0;
22
23
 
23
- if (count < item.quantity) {
24
+ const maxApplies = modifierActions.getMaxItemUse(modifier) || 0;
25
+ const maxQuantity =
26
+ utils.math.mul(item.quantity || 0, maxApplies) || item.quantity;
27
+
28
+ if (count < maxQuantity) {
24
29
  modifierCounts[modifier.modifierId] = count + 1;
25
30
  return true;
26
31
  }
@@ -1,11 +1,68 @@
1
- module.exports = ({ actions }) => {
1
+ module.exports = ({ actions, utils }) => {
2
2
  const modifierConditionPass = (
3
3
  modifier,
4
- { item, startRequestDate, endRequestDate }
4
+ { item, startRequestDate, endRequestDate, allItems }
5
5
  ) =>
6
6
  modifier.conditions && Array.isArray(modifier.conditions.rules)
7
7
  ? modifier.conditions.rules.every(condition => {
8
8
  switch (condition.key) {
9
+ case 'itemSet': {
10
+ if (!Array.isArray(allItems)) return true;
11
+ const conditionValue = Number(condition.value.itemSet || 0);
12
+ const conditionRule = condition.value.itemRule || 'lowestPrice';
13
+
14
+ const matchingItems = allItems
15
+ .filter(eachItem =>
16
+ eachItem.modifiers.some(
17
+ mod => mod.modifierId === modifier.modifierId
18
+ )
19
+ )
20
+ .sort((a, b) => {
21
+ switch (conditionRule) {
22
+ case 'lowestPrice':
23
+ return a.price - b.price;
24
+ case 'highestPrice':
25
+ return b.price - a.price;
26
+ case 'firstSelected':
27
+ return a._id.localeCompare(b._id);
28
+ case 'lastSelected':
29
+ return b._id.localeCompare(a._id);
30
+ default:
31
+ return 0;
32
+ }
33
+ });
34
+
35
+ let totalItemsLength = 0;
36
+
37
+ matchingItems.forEach(matchItem => {
38
+ totalItemsLength += matchItem.quantity || 0;
39
+ });
40
+
41
+ const itemIndex = matchingItems.findIndex(
42
+ entry => entry._id === item._id
43
+ );
44
+
45
+ const itemSetIndex = itemIndex >= 0 ? itemIndex + 1 : null;
46
+
47
+ if (
48
+ condition.operand === '$eq' &&
49
+ conditionValue &&
50
+ totalItemsLength >= conditionValue
51
+ ) {
52
+ const result = Math.floor(
53
+ utils.math.div(totalItemsLength, conditionValue)
54
+ );
55
+
56
+ if (itemSetIndex <= result) {
57
+ return true;
58
+ }
59
+ }
60
+ return actions.validateItemNumber(
61
+ itemSetIndex,
62
+ conditionValue,
63
+ condition.operand
64
+ );
65
+ }
9
66
  case 'itemPieces':
10
67
  return actions.validateNumberCondition(
11
68
  item.pieces,
@@ -46,14 +103,15 @@ module.exports = ({ actions }) => {
46
103
  item = null,
47
104
  endRequestDate = null,
48
105
  startRequestDate = null,
106
+ allItems = null,
49
107
  } = opts;
50
- // const recommendedEndDate = storeActions.getRecommendedEndDate();
51
108
  return (
52
109
  modifier &&
53
110
  modifierConditionPass(modifier, {
54
111
  item,
55
112
  startRequestDate,
56
113
  endRequestDate,
114
+ allItems,
57
115
  })
58
116
  );
59
117
  }
@@ -0,0 +1,5 @@
1
+ module.exports = ({ actions }) =>
2
+ function getMaxItemUse(modifier) {
3
+ if (!actions.isLimits(modifier)) return 0;
4
+ return modifier.properties.limits.maxItemUse;
5
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = ({ actions }) =>
2
+ function getMaxOrderUse(modifier) {
3
+ if (!actions.isLimits(modifier)) return 0;
4
+ return modifier.properties.limits.maxOrderUse;
5
+ };
@@ -150,6 +150,14 @@ const isGroupPath = require('./isGroupPath');
150
150
  const isSpreadFrom = require('./isSpreadFrom');
151
151
  const getRelatedModifiers = require('./getRelatedModifiers');
152
152
  const isRelatedModifier = require('./isRelatedModifier');
153
+ const getMaxItemUse = require('./getMaxItemUse');
154
+ const getMaxOrderUse = require('./getMaxOrderUse');
155
+ const isUnlimitedItemUse = require('./isUnlimitedItemUse');
156
+ const isUnlimitedOrderUse = require('./isUnlimitedOrderUse');
157
+ const validateItemNumber = require('./validateItemNumber');
158
+ const isLimits = require('./isLimits');
159
+ const isItemSet = require('./isItemSet');
160
+ const isOrderUseValid = require('./isOrderUseValid');
153
161
 
154
162
  const modifierActions = (deps = {}) => {
155
163
  const actions = {};
@@ -281,6 +289,7 @@ const modifierActions = (deps = {}) => {
281
289
  getGroupedModifierLabels: getGroupedModifierLabels(innerDeps),
282
290
  validate: validate(innerDeps),
283
291
  validateNumberCondition: validateNumberCondition(innerDeps),
292
+ validateItemNumber: validateItemNumber(innerDeps),
284
293
  validateRequiredModifiers: validateRequiredModifiers(innerDeps),
285
294
  validateDateHoursDiff: validateDateHoursDiff(innerDeps),
286
295
  validateInArr: validateInArr(innerDeps),
@@ -313,6 +322,13 @@ const modifierActions = (deps = {}) => {
313
322
  isSpreadFrom: isSpreadFrom(innerDeps),
314
323
  getRelatedModifiers: getRelatedModifiers(innerDeps),
315
324
  isRelatedModifier: isRelatedModifier(innerDeps),
325
+ getMaxItemUse: getMaxItemUse(innerDeps),
326
+ getMaxOrderUse: getMaxOrderUse(innerDeps),
327
+ isUnlimitedItemUse: isUnlimitedItemUse(innerDeps),
328
+ isUnlimitedOrderUse: isUnlimitedOrderUse(innerDeps),
329
+ isLimits: isLimits(innerDeps),
330
+ isItemSet: isItemSet(innerDeps),
331
+ isOrderUseValid: isOrderUseValid(innerDeps),
316
332
  });
317
333
 
318
334
  Object.keys(freezedActions).forEach(actionName => {
@@ -0,0 +1,11 @@
1
+ module.exports = () =>
2
+ function isItemSet(modifier) {
3
+ if (
4
+ !modifier ||
5
+ !modifier.conditions ||
6
+ !Array.isArray(modifier.conditions.rules)
7
+ )
8
+ return false;
9
+
10
+ return modifier.conditions.rules.some(rule => rule.key === 'itemSet');
11
+ };
@@ -0,0 +1,9 @@
1
+ module.exports = ({ actions, constants }) => {
2
+ const { Modifier } = constants;
3
+ return function isLimits(modifier) {
4
+ return (
5
+ actions.hasAttribute(modifier, Modifier.Attributes.LIMITS) ||
6
+ !!actions.getProperty(modifier, Modifier.Attributes.LIMITS)
7
+ );
8
+ };
9
+ };
@@ -0,0 +1,25 @@
1
+ module.exports = ({ actions }) =>
2
+ function isOrderUseValid({ order, modifier, item: itemParam }) {
3
+ if (
4
+ !actions.isLimits(modifier) ||
5
+ actions.isUnlimitedOrderUse(modifier) ||
6
+ !actions.getMaxOrderUse(modifier)
7
+ )
8
+ return true;
9
+
10
+ const limit = actions.getMaxOrderUse(modifier);
11
+
12
+ const usedIn = new Set();
13
+
14
+ // eslint-disable-next-line no-restricted-syntax
15
+ for (const item of order.items) {
16
+ if (item.modifiers.some(mod => mod.modifierId === modifier._id)) {
17
+ usedIn.add(item.itemId);
18
+ }
19
+ }
20
+
21
+ if (itemParam && itemParam.itemId && usedIn.has(itemParam.itemId))
22
+ return true;
23
+
24
+ return usedIn.size < limit;
25
+ };
@@ -0,0 +1,4 @@
1
+ module.exports = ({ actions }) =>
2
+ function isUnlimitedItemUse(modifier) {
3
+ return actions.getMaxItemUse(modifier) === -1;
4
+ };
@@ -0,0 +1,4 @@
1
+ module.exports = ({ actions }) =>
2
+ function isUnlimitedOrderUse(modifier) {
3
+ return actions.getMaxOrderUse(modifier) === -1;
4
+ };
@@ -0,0 +1,18 @@
1
+ module.exports = () =>
2
+ function validateItemNumber(itemSet, conditionNumber, operand) {
3
+ if (!conditionNumber && !operand) return true;
4
+ switch (operand) {
5
+ case '$ne':
6
+ return Number(conditionNumber) !== itemSet;
7
+ case '$gt':
8
+ return Number(conditionNumber) < itemSet;
9
+ case '$gte':
10
+ return Number(conditionNumber) <= itemSet;
11
+ case '$lt':
12
+ return Number(conditionNumber) > itemSet;
13
+ case '$lte':
14
+ return Number(conditionNumber) >= itemSet;
15
+ default:
16
+ return false;
17
+ }
18
+ };
@@ -86,12 +86,26 @@ module.exports = ({ actions, itemActions, modifierActions, utils, _ }) => {
86
86
  };
87
87
 
88
88
  const addModifier = ({
89
+ order,
89
90
  modifier: _modifier,
90
91
  item: itemProp,
91
92
  customer,
92
93
  originalItem,
93
94
  onConditionsNotMet,
95
+ onError,
94
96
  }) => {
97
+ if (
98
+ !modifierActions.isOrderUseValid({
99
+ order,
100
+ modifier: _modifier,
101
+ item: itemProp,
102
+ })
103
+ ) {
104
+ onError('modifier.has.reached.the.maximum.amount.of.applies');
105
+
106
+ return itemProp;
107
+ }
108
+
95
109
  const modifier = modifierActions.removeGroupRelations(_modifier); // to avoid no param reassign lint rule
96
110
  let item = { ...itemProp };
97
111
  const compute = getComputeModField(modifier);
@@ -190,11 +204,24 @@ module.exports = ({ actions, itemActions, modifierActions, utils, _ }) => {
190
204
  return order;
191
205
  }
192
206
  // Remove if it has it already only if it is not group of value or override modifier
207
+
208
+ const maxApplies = modifierActions.getMaxItemUse(modifier) || 0;
209
+ const isUnlimitedItemUse = modifierActions.isUnlimitedItemUse(modifier);
210
+
211
+ const maxQuantity =
212
+ math.mul(item.quantity || 0, maxApplies) || item.quantity;
213
+
193
214
  if (
215
+ !isUnlimitedItemUse &&
194
216
  contains &&
195
- usingCount >= item.quantity &&
217
+ usingCount >= maxQuantity &&
196
218
  !modifierActions.isOverride(modifier)
197
219
  ) {
220
+ if (maxApplies && onError) {
221
+ onError('modifier.has.reached.the.maximum.amount.of.applies');
222
+ return order;
223
+ }
224
+
198
225
  order = actions.removeItemModifier({
199
226
  order,
200
227
  modifier,
@@ -239,11 +266,13 @@ module.exports = ({ actions, itemActions, modifierActions, utils, _ }) => {
239
266
  }
240
267
 
241
268
  item = addModifier({
269
+ order,
242
270
  item,
243
271
  modifier,
244
272
  originalItem,
245
273
  customer,
246
274
  onConditionsNotMet,
275
+ onError,
247
276
  });
248
277
 
249
278
  // Recursive Rules:
@@ -255,10 +284,12 @@ module.exports = ({ actions, itemActions, modifierActions, utils, _ }) => {
255
284
  item = relatedModifiers.reduce(
256
285
  (acc, each) =>
257
286
  addModifier({
287
+ order,
258
288
  item: acc,
259
289
  modifier: each,
260
290
  originalItem,
261
291
  customer,
292
+ onError,
262
293
  }),
263
294
  item
264
295
  );
@@ -1,23 +1,15 @@
1
- module.exports = ({ actions, modifierActions }) => {
2
- const { getComputeModField } = require('../modifier/utils');
3
- return function addModifier({ order, modifier }) {
1
+ module.exports = ({ modifierActions }) =>
2
+ function addModifier({ order, modifier }) {
4
3
  if (!modifier) return order;
5
- const { modifiers = [] } = order;
6
- if (modifierActions.isFixedDiscount(modifier)) {
7
- const compute = getComputeModField(modifier);
8
- const orderDue = actions.calculateDue(order);
9
- if (compute.amount > orderDue) {
10
- const err = new Error();
11
- err.valid = false;
12
- err.orderDue = orderDue;
13
- err.message = 'discount.can.not.be.applied.order.due.is.this.amount';
14
- throw err;
15
- }
16
- }
4
+
5
+ const orderModifiers = Array.isArray(order.modifiers)
6
+ ? order.modifiers
7
+ : [];
8
+
9
+ if (modifierActions.contains(orderModifiers, modifier)) return order;
17
10
 
18
11
  return {
19
12
  ...order,
20
- modifiers: [...modifiers, { ...modifier }],
13
+ modifiers: [...orderModifiers, { ...modifier }],
21
14
  };
22
15
  };
23
- };
@@ -3,7 +3,7 @@ module.exports = ({ _ }) =>
3
3
  if (!orders || !Array.isArray(orders)) return 0;
4
4
  const sum = orders.reduce((acc, order) => {
5
5
  const items = _.get(order, 'items', []);
6
- return acc + items.lenght;
6
+ return acc + items.length;
7
7
  }, 0);
8
8
 
9
9
  return sum;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@darkpos/pricing",
3
- "version": "1.0.78",
3
+ "version": "1.0.80",
4
4
  "description": "Pricing calculator",
5
5
  "author": "Dark POS",
6
6
  "license": "ISC",
@@ -51,5 +51,5 @@
51
51
  "supertest": "^6.2.3",
52
52
  "supervisor": "^0.12.0"
53
53
  },
54
- "gitHead": "4e966b805abc74cc31cb0b3a0754e71f9bb913e5"
54
+ "gitHead": "8c3967eacf0122f2024d9b29b66acc0e3a23d51d"
55
55
  }