@darkpos/pricing 1.0.150 → 1.0.152

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.
@@ -104,263 +104,3 @@ describe('computeRefundables', () => {
104
104
  expect(refundables).toEqual([0, 0]);
105
105
  });
106
106
  });
107
-
108
- describe('applyRefundToInvoices', () => {
109
- const invoicesForOnePayment = [
110
- {
111
- _id: 'inv-1',
112
- amount: 25,
113
- paymentOrderItems: [overpaidInvoiceItem, partiallyPaidInvoiceItem],
114
- },
115
- ];
116
-
117
- test('the refund invoice has the correct total amount and both item refunds', () => {
118
- const refundInvoices = pricing.invoice.applyRefundToInvoices(
119
- invoicesForOnePayment,
120
- ordersById,
121
- 4
122
- );
123
- expect(refundInvoices).toHaveLength(1);
124
- expect(refundInvoices[0].amount).toBe(-4);
125
- expect(refundInvoices[0].paymentOrderItems).toHaveLength(2);
126
- expect(refundInvoices[0].paymentOrderItems[0].amount).toBe(-4.2);
127
- expect(refundInvoices[0].paymentOrderItems[1].amount).toBe(0.2);
128
- });
129
-
130
- test('the overpaid item is cleared to its exact total and marked as paid', () => {
131
- const refundInvoices = pricing.invoice.applyRefundToInvoices(
132
- invoicesForOnePayment,
133
- ordersById,
134
- 4
135
- );
136
- expect(refundInvoices[0].paymentOrderItems[0].status.paid.value).toBe(true);
137
- });
138
-
139
- test('the underpaid item receives a credit that covers its remaining balance and is marked as paid', () => {
140
- const refundInvoices = pricing.invoice.applyRefundToInvoices(
141
- invoicesForOnePayment,
142
- ordersById,
143
- 4
144
- );
145
- expect(refundInvoices[0].paymentOrderItems[1].status.paid.value).toBe(true);
146
- });
147
-
148
- test('returns an empty array when the refund amount is 0', () => {
149
- const refundInvoices = pricing.invoice.applyRefundToInvoices(
150
- invoicesForOnePayment,
151
- ordersById,
152
- 0
153
- );
154
- expect(refundInvoices).toEqual([]);
155
- });
156
-
157
- test('skips invoices that are already refunded or have no positive balance', () => {
158
- const invoicesIncludingAlreadyRefunded = [
159
- {
160
- _id: 'inv-already-refunded',
161
- amount: -5,
162
- paymentOrderItems: [overpaidInvoiceItem],
163
- },
164
- ...invoicesForOnePayment,
165
- ];
166
- const refundInvoices = pricing.invoice.applyRefundToInvoices(
167
- invoicesIncludingAlreadyRefunded,
168
- ordersById,
169
- 4
170
- );
171
- expect(refundInvoices).toHaveLength(1);
172
- expect(refundInvoices[0]._id).toBe('inv-1');
173
- });
174
-
175
- test('distributes the refund across multiple invoices when the first is exhausted', () => {
176
- const splitPaymentInvoices = [
177
- {
178
- _id: 'first-payment',
179
- amount: 14.7,
180
- paymentOrderItems: [overpaidInvoiceItem],
181
- },
182
- {
183
- _id: 'second-payment',
184
- amount: 10.3,
185
- paymentOrderItems: [partiallyPaidInvoiceItem],
186
- },
187
- ];
188
- // Refund 5: overpaid item covers 4.2, the remaining 0.8 comes from the second invoice
189
- const refundInvoices = pricing.invoice.applyRefundToInvoices(
190
- splitPaymentInvoices,
191
- ordersById,
192
- 5
193
- );
194
- expect(refundInvoices).toHaveLength(2);
195
- expect(refundInvoices[0].amount).toBe(-4.2);
196
- expect(refundInvoices[1].amount).toBe(-0.8);
197
- });
198
- });
199
-
200
- describe('applyRefundToInvoices — 5-item order with 3 items priced to 0', () => {
201
- const ORDER_5_ID = 'order-5items';
202
-
203
- // Items as they appear in the payment invoice (stale totals from payment time)
204
- const shortsInvoiceItem = {
205
- orderId: ORDER_5_ID,
206
- orderItemId: 'shorts',
207
- total: 6.3,
208
- totalPaid: 6.3,
209
- amount: 6.3,
210
- status: { paid: { value: true } },
211
- };
212
- const jacket1InvoiceItem = {
213
- orderId: ORDER_5_ID,
214
- orderItemId: 'jacket-1',
215
- total: 8.93,
216
- totalPaid: 8.93,
217
- amount: 8.93,
218
- status: { paid: { value: true } },
219
- };
220
- const longJacketInvoiceItem = {
221
- orderId: ORDER_5_ID,
222
- orderItemId: 'long-jacket',
223
- total: 12.6,
224
- totalPaid: 12.6,
225
- amount: 12.6,
226
- status: { paid: { value: true } },
227
- };
228
- const jacket2InvoiceItem = {
229
- orderId: ORDER_5_ID,
230
- orderItemId: 'jacket-2',
231
- total: 8.93,
232
- totalPaid: 8.93,
233
- amount: 8.93,
234
- status: { paid: { value: true } },
235
- };
236
- const coatInvoiceItem = {
237
- orderId: ORDER_5_ID,
238
- orderItemId: 'coat',
239
- total: 13.65,
240
- totalPaid: 3.24,
241
- amount: 3.24,
242
- status: { paid: { value: false } },
243
- };
244
-
245
- // Live order state after Jacket×2 and Long Jacket prices changed to 0
246
- const liveOrder5 = {
247
- _id: ORDER_5_ID,
248
- items: [
249
- {
250
- _id: 'shorts',
251
- total: 6.3,
252
- totalPaid: 6.3,
253
- status: { paid: { value: true } },
254
- },
255
- {
256
- _id: 'jacket-1',
257
- total: 0,
258
- totalPaid: 8.93,
259
- status: { paid: { value: true } },
260
- },
261
- {
262
- _id: 'long-jacket',
263
- total: 0,
264
- totalPaid: 12.6,
265
- status: { paid: { value: true } },
266
- },
267
- {
268
- _id: 'jacket-2',
269
- total: 0,
270
- totalPaid: 8.93,
271
- status: { paid: { value: true } },
272
- },
273
- {
274
- _id: 'coat',
275
- total: 13.65,
276
- totalPaid: 3.24,
277
- status: { paid: { value: false } },
278
- },
279
- ],
280
- };
281
-
282
- const ordersById5 = { [ORDER_5_ID]: liveOrder5 };
283
-
284
- const paymentInvoices5 = [
285
- {
286
- invoiceId: 'inv-5items',
287
- amount: 40,
288
- paymentOrderItems: [
289
- shortsInvoiceItem,
290
- jacket1InvoiceItem,
291
- longJacketInvoiceItem,
292
- jacket2InvoiceItem,
293
- coatInvoiceItem,
294
- ],
295
- },
296
- ];
297
-
298
- test('correctly refunds 20.05 — clears the 3 zero-priced items and credits the remaining balance to coat', () => {
299
- const refundInvoices = pricing.invoice.applyRefundToInvoices(
300
- paymentInvoices5,
301
- ordersById5,
302
- 20.05
303
- );
304
-
305
- expect(refundInvoices).toHaveLength(1);
306
- expect(refundInvoices[0].amount).toBe(-20.05);
307
- expect(refundInvoices[0].paymentOrderItems).toHaveLength(4); // shorts has 0 excess, skipped
308
-
309
- const byItemId = Object.fromEntries(
310
- refundInvoices[0].paymentOrderItems.map(item => [item.orderItemId, item])
311
- );
312
-
313
- expect(byItemId['jacket-1'].amount).toBe(-8.93);
314
- expect(byItemId['jacket-1'].status.paid.value).toBe(true);
315
-
316
- expect(byItemId['long-jacket'].amount).toBe(-12.6);
317
- expect(byItemId['long-jacket'].status.paid.value).toBe(true);
318
-
319
- expect(byItemId['jacket-2'].amount).toBe(-8.93);
320
- expect(byItemId['jacket-2'].status.paid.value).toBe(true);
321
-
322
- expect(byItemId.coat.amount).toBe(10.41);
323
- expect(byItemId.coat.status.paid.value).toBe(true);
324
- });
325
- });
326
-
327
- describe('buildItemRefund', () => {
328
- const overpaidItemState = pricing.invoice.resolveItemState(
329
- overpaidInvoiceItem,
330
- ordersById
331
- );
332
- const underpaidItemState = pricing.invoice.resolveItemState(
333
- partiallyPaidInvoiceItem,
334
- ordersById
335
- );
336
-
337
- test('overpaid item: refunding its full excess results in a negative amount and paid = true', () => {
338
- const itemRefund = pricing.invoice.buildItemRefund(overpaidItemState, 4.2);
339
- expect(itemRefund.amount).toBe(-4.2);
340
- expect(itemRefund.totalPaid).toBe(14.7); // pre-refund value preserved
341
- expect(itemRefund.total).toBe(10.5);
342
- expect(itemRefund.status.paid.value).toBe(true); // 14.7 - 4.2 = 10.5 >= 10.5
343
- });
344
-
345
- test('underpaid item: applying a credit results in a positive amount and paid = true', () => {
346
- const itemRefund = pricing.invoice.buildItemRefund(
347
- underpaidItemState,
348
- -0.2
349
- );
350
- expect(itemRefund.amount).toBe(0.2); // positive = credit
351
- expect(itemRefund.totalPaid).toBe(10.3);
352
- expect(itemRefund.total).toBe(10.5);
353
- expect(itemRefund.status.paid.value).toBe(true); // 10.3 - (-0.2) = 10.5 >= 10.5
354
- });
355
-
356
- test('paid stays false when the refund does not fully cover the remaining balance', () => {
357
- const itemRefund = pricing.invoice.buildItemRefund(underpaidItemState, 2); // totalPaid drops to 8.3
358
- expect(itemRefund.status.paid.value).toBe(false); // 8.3 < 10.5
359
- });
360
-
361
- test('preserves all other fields from the original invoice item', () => {
362
- const itemRefund = pricing.invoice.buildItemRefund(overpaidItemState, 4.2);
363
- expect(itemRefund.orderId).toBe(ORDER_ID);
364
- expect(itemRefund.orderItemId).toBe(OVERPAID_ITEM_ID);
365
- });
366
- });
@@ -574,9 +574,11 @@ describe('Order actions', () => {
574
574
  lockPaymentModifiers: true,
575
575
  });
576
576
 
577
+ paidOrderWithCredit.totalPaid += 1.25;
577
578
  const orderBalanceWithCredit = pricingService.order.getOrdersBalance({
578
579
  orders: [paidOrderWithCredit],
579
580
  });
581
+
580
582
  const itemBalanceWithCredit = pricingService.item.getItemsBalance({
581
583
  items: paidOrderWithCredit.items,
582
584
  });
@@ -3818,7 +3818,7 @@ describe('Order actions', () => {
3818
3818
  const pricing = usePricing({
3819
3819
  store: { _settings: { order: { autoMarkAsPaid: false } } },
3820
3820
  });
3821
- const order = { items: [orderItem], status: {} };
3821
+ const order = { items: [orderItem], status: {}, totalPaid: 30 };
3822
3822
  const newOrder = pricing.order.calculate(order);
3823
3823
 
3824
3824
  expect(newOrder).toHaveProperty('total', 25);
@@ -6,6 +6,17 @@ const session = {
6
6
  };
7
7
 
8
8
  const pricingService = usePricing(session);
9
+ const pricingServiceMethodLabel = usePricing({
10
+ store: {
11
+ _settings: {
12
+ _settings: {
13
+ payment: {
14
+ methods: mockStores[0]._settings.payment.methods,
15
+ },
16
+ },
17
+ },
18
+ },
19
+ });
9
20
 
10
21
  describe('getMaxAmountToRefund tests', () => {
11
22
  test('Returns 0 when payment is undefined', () => {
@@ -49,3 +60,220 @@ describe('getMaxAmountToRefund tests', () => {
49
60
  expect(pricingService.payment.getMaxAmountToRefund({ payment })).toBe(50);
50
61
  });
51
62
  });
63
+
64
+ describe('getRefundableOrderAmount tests', () => {
65
+ test('Returns paid amount not attached to current items', () => {
66
+ const order = {
67
+ _id: 'order-1',
68
+ totalPaid: 5,
69
+ items: [
70
+ {
71
+ _id: 'item-2',
72
+ name: 'Simple Item 2',
73
+ total: 5,
74
+ totalPaid: 0,
75
+ status: { paid: { value: false } },
76
+ },
77
+ ],
78
+ };
79
+
80
+ expect(pricingService.payment.getRefundableOrderAmount({ order })).toBe(5);
81
+ });
82
+
83
+ test('Returns only the excess from items that actually received payment', () => {
84
+ const order = {
85
+ _id: 'order-2',
86
+ totalPaid: 14.7,
87
+ items: [
88
+ {
89
+ _id: 'item-1',
90
+ name: 'Winter Coat',
91
+ total: 1.05,
92
+ totalPaid: 14.7,
93
+ status: { paid: { value: true } },
94
+ },
95
+ {
96
+ _id: 'item-2',
97
+ name: 'Coat',
98
+ total: 1.05,
99
+ totalPaid: 0,
100
+ status: { paid: { value: false } },
101
+ },
102
+ ],
103
+ };
104
+
105
+ expect(pricingService.payment.getRefundableOrderAmount({ order })).toBe(
106
+ 13.65
107
+ );
108
+ });
109
+ });
110
+
111
+ describe('getoverPaidOrders tests', () => {
112
+ test('Collects refundable child orders from a mixed parent list', () => {
113
+ const orders = [
114
+ {
115
+ _id: 'parent-1',
116
+ isParent: true,
117
+ orders: [
118
+ {
119
+ _id: 'child-1',
120
+ totalPaid: 5,
121
+ items: [
122
+ {
123
+ _id: 'item-1',
124
+ total: 5,
125
+ totalPaid: 0,
126
+ status: { paid: { value: false } },
127
+ },
128
+ ],
129
+ },
130
+ {
131
+ _id: 'child-2',
132
+ totalPaid: 5,
133
+ items: [
134
+ {
135
+ _id: 'item-2',
136
+ total: 5,
137
+ totalPaid: 5,
138
+ status: { paid: { value: true } },
139
+ },
140
+ ],
141
+ },
142
+ ],
143
+ },
144
+ ];
145
+
146
+ expect(pricingService.payment.getOverPaidOrders({ orders })).toEqual({
147
+ overPaidAmount: 5,
148
+ overPaidOrders: [
149
+ {
150
+ _id: 'child-1',
151
+ totalPaid: 5,
152
+ items: [
153
+ {
154
+ _id: 'item-1',
155
+ total: 5,
156
+ totalPaid: 0,
157
+ status: { paid: { value: false } },
158
+ },
159
+ ],
160
+ refundableAmount: 5,
161
+ },
162
+ ],
163
+ });
164
+ });
165
+
166
+ test('Ignores empty and non-refundable orders', () => {
167
+ const orders = [
168
+ null,
169
+ {
170
+ _id: 'order-1',
171
+ totalPaid: 5,
172
+ items: [
173
+ {
174
+ _id: 'item-1',
175
+ total: 5,
176
+ totalPaid: 5,
177
+ status: { paid: { value: true } },
178
+ },
179
+ ],
180
+ },
181
+ {
182
+ isParent: true,
183
+ orders: [
184
+ {
185
+ _id: 'child-1',
186
+ totalPaid: 1,
187
+ items: [
188
+ {
189
+ _id: 'item-2',
190
+ total: 1,
191
+ totalPaid: 1,
192
+ status: { paid: { value: true } },
193
+ },
194
+ ],
195
+ },
196
+ ],
197
+ },
198
+ ];
199
+
200
+ expect(pricingService.payment.getOverPaidOrders({ orders })).toEqual({
201
+ overPaidAmount: 0,
202
+ overPaidOrders: [],
203
+ });
204
+ });
205
+ });
206
+
207
+ describe('getMethodLabel tests', () => {
208
+ test('Returns the configured label for the payment provider', () => {
209
+ expect(
210
+ pricingServiceMethodLabel.payment.getMethodLabel({
211
+ paymentProvider: 'cash',
212
+ })
213
+ ).toBe('Cash');
214
+ });
215
+
216
+ test('Returns empty string when the provider is missing', () => {
217
+ expect(
218
+ pricingServiceMethodLabel.payment.getMethodLabel({
219
+ paymentProvider: 'missing-provider',
220
+ })
221
+ ).toBe('');
222
+ });
223
+ });
224
+
225
+ describe('appendIfOverPaid tests', () => {
226
+ test('Appends the order with refundableAmount when the order is overpaid', () => {
227
+ const currentOrder = {
228
+ _id: 'order-1',
229
+ displayId: '101',
230
+ totalPaid: 5,
231
+ items: [
232
+ {
233
+ _id: 'item-2',
234
+ name: 'Simple Item 2',
235
+ total: 5,
236
+ totalPaid: 0,
237
+ status: { paid: { value: false } },
238
+ },
239
+ ],
240
+ };
241
+
242
+ expect(
243
+ pricingService.payment.appendIfOverPaid({
244
+ currentOrder,
245
+ overPaidOrders: [{ _id: 'existing-order', refundableAmount: 1 }],
246
+ })
247
+ ).toEqual([
248
+ { _id: 'existing-order', refundableAmount: 1 },
249
+ {
250
+ ...currentOrder,
251
+ refundableAmount: 5,
252
+ },
253
+ ]);
254
+ });
255
+
256
+ test('Leaves the collection unchanged when the order has no refundable amount', () => {
257
+ const overPaidOrders = [{ _id: 'existing-order', refundableAmount: 1 }];
258
+ const currentOrder = {
259
+ _id: 'order-3',
260
+ totalPaid: 5,
261
+ items: [
262
+ {
263
+ _id: 'item-1',
264
+ name: 'Simple Item 1',
265
+ total: 5,
266
+ totalPaid: 5,
267
+ status: { paid: { value: true } },
268
+ },
269
+ ],
270
+ };
271
+
272
+ expect(
273
+ pricingService.payment.appendIfOverPaid({
274
+ currentOrder,
275
+ overPaidOrders,
276
+ })
277
+ ).toEqual(overPaidOrders);
278
+ });
279
+ });
@@ -2,8 +2,6 @@ const getStatusByItems = require('./getStatusByItems');
2
2
  const getTotalByItems = require('./getTotalByItems');
3
3
  const resolveItemState = require('./resolveItemState');
4
4
  const computeRefundables = require('./computeRefundables');
5
- const buildItemRefund = require('./buildItemRefund');
6
- const applyRefundToInvoices = require('./applyRefundToInvoices');
7
5
 
8
6
  const invoiceActions = (deps = {}) => {
9
7
  const actions = {};
@@ -18,8 +16,6 @@ const invoiceActions = (deps = {}) => {
18
16
  getTotalByItems: getTotalByItems(innerDeps),
19
17
  resolveItemState: resolveItemState(innerDeps),
20
18
  computeRefundables: computeRefundables(innerDeps),
21
- buildItemRefund: buildItemRefund(innerDeps),
22
- applyRefundToInvoices: applyRefundToInvoices(innerDeps),
23
19
  });
24
20
 
25
21
  Object.keys(freezedActions).forEach(actionName => {
package/lib/item/index.js CHANGED
@@ -84,7 +84,6 @@ const getAmountToPayById = require('./getAmountToPayById');
84
84
  const applyPayment = require('./applyPayment');
85
85
  const getBalanceForPaymentModifier = require('./getBalanceForPaymentModifier');
86
86
  const isOverpaid = require('./isOverpaid');
87
- const getOverpaidAmount = require('./getOverpaidAmount');
88
87
 
89
88
  const itemActions = (deps = {}) => {
90
89
  const actions = {};
@@ -182,7 +181,6 @@ const itemActions = (deps = {}) => {
182
181
  applyPayment: applyPayment(innerDeps),
183
182
  getBalanceForPaymentModifier: getBalanceForPaymentModifier(innerDeps),
184
183
  isOverpaid: isOverpaid(innerDeps),
185
- getOverpaidAmount: getOverpaidAmount(innerDeps),
186
184
  });
187
185
 
188
186
  Object.keys(freezedActions).forEach(actionName => {
@@ -203,7 +203,6 @@ module.exports = ({
203
203
  paid,
204
204
  order: picked && paid ? 'closed' : 'open',
205
205
  },
206
- totalPaid,
207
206
  properties: {
208
207
  ...(order.properties || {}),
209
208
  pieceCount,
@@ -1,7 +1,7 @@
1
- module.exports = ({ actions, modifierActions }) => {
1
+ module.exports = ({ actions, modifierActions, _ }) => {
2
2
  const getRemaining = (subscriptions, orderId) => {
3
3
  const remaining = subscriptions.reduce((total, subscription) => {
4
- const { usesLimit = 0, uses = 0, history = {} } = subscription;
4
+ const { usesLimit = 0, uses = 0, history = {} } = subscription.item || {};
5
5
  const orderHistory = history[orderId] || 0;
6
6
  return total + usesLimit + orderHistory - uses;
7
7
  }, 0);
@@ -37,8 +37,18 @@ module.exports = ({ actions, modifierActions }) => {
37
37
 
38
38
  const getSubscriptions = subscriptions =>
39
39
  subscriptions
40
- .map(each => modifierActions.getSubscriptionItem(each))
41
- .filter(Boolean);
40
+ .map(each => _.get(each, 'properties.subscription'))
41
+ .filter(Boolean)
42
+ .filter(each => {
43
+ const renewalType = _.get(each, 'item.renewalType');
44
+ const endDate = _.get(each, 'dateLimit.to');
45
+ const today = new Date();
46
+
47
+ if (renewalType === 'none' && endDate && new Date(endDate) < today)
48
+ return false;
49
+
50
+ return true;
51
+ });
42
52
 
43
53
  return function hasRemainingSubscription({ order, item }) {
44
54
  if (!order || !item || !item.itemId) return [false];
@@ -53,7 +63,9 @@ module.exports = ({ actions, modifierActions }) => {
53
63
  subscriptions = getSubscriptions(subscriptions);
54
64
 
55
65
  const isUnlimited = subscriptions.some(
56
- each => !each.usesLimit || each.renewalType === 'limit_reached'
66
+ each =>
67
+ !_.get(each, 'item.usesLimit') ||
68
+ _.get(each, 'item.renewalType') === 'limit_reached'
57
69
  );
58
70
  if (isUnlimited) return [true];
59
71
 
@@ -100,7 +100,6 @@ const removeEmptyNotes = require('./removeEmptyNotes');
100
100
  const getTaxes = require('./getTaxes');
101
101
  const getPickedStatus = require('./getPickedStatus');
102
102
  const calculateWithPayment = require('./calculateWithPayment');
103
- const getOverpaidAmount = require('./getOverpaidAmount');
104
103
  const hasSerial = require('./hasSerial');
105
104
 
106
105
  const orderActions = (deps = {}) => {
@@ -213,7 +212,6 @@ const orderActions = (deps = {}) => {
213
212
  getTaxes: getTaxes(innerDeps),
214
213
  getPickedStatus: getPickedStatus(innerDeps),
215
214
  calculateWithPayment: calculateWithPayment(innerDeps),
216
- getOverpaidAmount: getOverpaidAmount(innerDeps),
217
215
  hasSerial: hasSerial(innerDeps),
218
216
  });
219
217
 
@@ -0,0 +1,17 @@
1
+ module.exports = ({ actions }) =>
2
+ function appendIfOverPaid({ currentOrder, overPaidOrders = [] }) {
3
+ if (!currentOrder || !currentOrder._id) return overPaidOrders;
4
+
5
+ const refundableAmount = actions.getRefundableOrderAmount({
6
+ order: currentOrder,
7
+ });
8
+ if (refundableAmount <= 0) return overPaidOrders;
9
+
10
+ return [
11
+ ...overPaidOrders,
12
+ {
13
+ ...currentOrder,
14
+ refundableAmount,
15
+ },
16
+ ];
17
+ };
@@ -0,0 +1,7 @@
1
+ module.exports = ({ settings, _ }) =>
2
+ function getMethodLabel({ paymentProvider }) {
3
+ const methods = _.get(settings, '_settings.payment.methods', {});
4
+
5
+ if (!paymentProvider || !methods[paymentProvider]) return '';
6
+ return methods[paymentProvider].label || '';
7
+ };
@@ -0,0 +1,36 @@
1
+ module.exports = ({ actions }) =>
2
+ function getOverPaidOrders({ orders = [] }) {
3
+ let overPaidOrders = [];
4
+ let overPaidAmount = 0;
5
+
6
+ const appendIfOverPaid = currentOrder => {
7
+ overPaidOrders = actions.appendIfOverPaid({
8
+ currentOrder,
9
+ overPaidOrders,
10
+ });
11
+ };
12
+
13
+ (orders || []).forEach(order => {
14
+ if (!order || !order._id) return;
15
+ if (order.isParent) {
16
+ const childOrders = order.orders || [];
17
+
18
+ if (childOrders.length > 0) {
19
+ childOrders.forEach(appendIfOverPaid);
20
+ return;
21
+ }
22
+ }
23
+
24
+ appendIfOverPaid(order);
25
+ });
26
+
27
+ overPaidAmount = overPaidOrders.reduce(
28
+ (total, order) => total + Number(order.refundableAmount || 0),
29
+ 0
30
+ );
31
+
32
+ return {
33
+ overPaidOrders,
34
+ overPaidAmount,
35
+ };
36
+ };
@@ -0,0 +1,34 @@
1
+ module.exports = ({ utils }) => {
2
+ const { math } = utils;
3
+
4
+ return function getRefundableOrderAmount({ order }) {
5
+ if (!order) return 0;
6
+
7
+ const items = Array.isArray(order.items) ? order.items : [];
8
+ const paidItems = items.filter(item => Number(item.totalPaid || 0) > 0);
9
+
10
+ const itemsTotalPaid = items.reduce(
11
+ (acc, item) => math.add(acc, Number(item.totalPaid || 0)),
12
+ 0
13
+ );
14
+ const paidItemsTotalPaid = paidItems.reduce(
15
+ (acc, item) => math.add(acc, Number(item.totalPaid || 0)),
16
+ 0
17
+ );
18
+ const paidItemsTotal = paidItems.reduce(
19
+ (acc, item) => math.add(acc, Number(item.total || 0)),
20
+ 0
21
+ );
22
+
23
+ const orphanedPaidAmount = math.max(
24
+ math.sub(order.totalPaid || 0, itemsTotalPaid),
25
+ 0
26
+ );
27
+ const refundablePaidItemsAmount = math.max(
28
+ math.sub(paidItemsTotalPaid, paidItemsTotal),
29
+ 0
30
+ );
31
+
32
+ return math.add(orphanedPaidAmount, refundablePaidItemsAmount);
33
+ };
34
+ };
@@ -1,5 +1,9 @@
1
1
  //
2
+ const appendIfOverPaid = require('./appendIfOverPaid');
2
3
  const getMaxAmountToRefund = require('./getMaxAmountToRefund');
4
+ const getRefundableOrderAmount = require('./getRefundableOrderAmount');
5
+ const getOverPaidOrders = require('./getOverPaidOrders');
6
+ const getMethodLabel = require('./getMethodLabel');
3
7
 
4
8
  const orderActions = (deps = {}) => {
5
9
  const actions = {};
@@ -10,7 +14,11 @@ const orderActions = (deps = {}) => {
10
14
  };
11
15
 
12
16
  const freezedActions = Object.freeze({
17
+ appendIfOverPaid: appendIfOverPaid(innerDeps),
13
18
  getMaxAmountToRefund: getMaxAmountToRefund(innerDeps),
19
+ getRefundableOrderAmount: getRefundableOrderAmount(innerDeps),
20
+ getOverPaidOrders: getOverPaidOrders(innerDeps),
21
+ getMethodLabel: getMethodLabel(innerDeps),
14
22
  });
15
23
 
16
24
  Object.keys(freezedActions).forEach(actionName => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@darkpos/pricing",
3
- "version": "1.0.150",
3
+ "version": "1.0.152",
4
4
  "description": "Pricing calculator",
5
5
  "author": "Dark POS",
6
6
  "license": "ISC",
@@ -54,5 +54,5 @@
54
54
  "supertest": "^6.2.3",
55
55
  "supervisor": "^0.12.0"
56
56
  },
57
- "gitHead": "5ba2aa85fa455644dfa228072d30f8a8560d161c"
57
+ "gitHead": "1dff1f50b45bf68bd10e8d8ef7fe18c2b37c751e"
58
58
  }
@@ -1,52 +0,0 @@
1
- module.exports = ({ utils, actions }) => {
2
- const { math } = utils;
3
-
4
- /**
5
- * Distributes a refund amount across a set of payment invoices.
6
- *
7
- * For each invoice, it resolves the live state of each item, computes
8
- * the refundable amounts (order-independent), and builds the refund entries.
9
- *
10
- * Returns an array of refund invoices ready to be persisted — each with a
11
- * negative amount (the refund) and updated paymentOrderItems.
12
- */
13
- return function applyRefundToInvoices(
14
- paymentInvoices,
15
- ordersById,
16
- totalToRefund
17
- ) {
18
- let remaining = totalToRefund;
19
-
20
- return paymentInvoices
21
- .filter(({ amount }) => amount > 0)
22
- .map(({ amount, paymentOrderItems, ...rest }) => {
23
- if (remaining <= 0) return null;
24
-
25
- const itemStates = (paymentOrderItems || []).map(item =>
26
- actions.resolveItemState(item, ordersById)
27
- );
28
- const refundables = actions.computeRefundables(itemStates, remaining);
29
-
30
- let invoiceRefundAmount = 0;
31
- const itemRefunds = itemStates
32
- .map((state, i) => {
33
- const refundable = refundables[i];
34
- if (refundable === 0) return null;
35
- invoiceRefundAmount = math.add(invoiceRefundAmount, refundable);
36
- return actions.buildItemRefund(state, refundable);
37
- })
38
- .filter(Boolean);
39
-
40
- remaining = math.sub(remaining, invoiceRefundAmount);
41
-
42
- if (invoiceRefundAmount <= 0) return null;
43
-
44
- return {
45
- ...rest,
46
- amount: -invoiceRefundAmount,
47
- paymentOrderItems: itemRefunds,
48
- };
49
- })
50
- .filter(Boolean);
51
- };
52
- };
@@ -1,23 +0,0 @@
1
- module.exports = ({ utils }) => {
2
- const { math } = utils;
3
-
4
- return function buildItemRefund(
5
- { item, itemTotal, itemTotalPaid, currentStatus },
6
- refundable
7
- ) {
8
- const nextTotalPaid = math.sub(itemTotalPaid, refundable);
9
- return {
10
- ...item,
11
- total: itemTotal,
12
- totalPaid: itemTotalPaid,
13
- amount: -refundable,
14
- status: {
15
- ...currentStatus,
16
- paid: {
17
- ...((currentStatus && currentStatus.paid) || {}),
18
- value: nextTotalPaid >= itemTotal,
19
- },
20
- },
21
- };
22
- };
23
- };
@@ -1,7 +0,0 @@
1
- module.exports = ({ utils }) =>
2
- function getOverpaidAmount({ item }) {
3
- const total = Number(item.total || 0);
4
- const totalPaid = Number(item.totalPaid || 0);
5
-
6
- return utils.math.sub(totalPaid, total);
7
- };
@@ -1,13 +0,0 @@
1
- module.exports = ({ utils }) =>
2
- function getOverPaidAmount({ order }) {
3
- if (!order) return 0;
4
-
5
- return (order.items || []).reduce((net, item) => {
6
- const itemTotalPaid = Number(item.totalPaid || 0);
7
- if (itemTotalPaid <= 0) return net;
8
- return utils.math.add(
9
- net,
10
- utils.math.sub(itemTotalPaid, Number(item.total || 0))
11
- );
12
- }, 0);
13
- };