rerobe-js-orm 4.9.13 → 4.9.15
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/lib/constants/order-constants.d.ts +1 -0
- package/lib/constants/order-constants.js +5 -1
- package/lib/helpers/OrderHelpers.d.ts +3 -0
- package/lib/helpers/OrderHelpers.js +31 -4
- package/lib/helpers/ReceiptXmlHelpers.d.ts +1 -0
- package/lib/helpers/ReceiptXmlHelpers.js +13 -2
- package/package.json +1 -1
|
@@ -31,6 +31,7 @@ export namespace SALES_CHANNELS {
|
|
|
31
31
|
let ribbnSellThroughChannelPartner: string;
|
|
32
32
|
let ribbnSellOnChannelPartner: string;
|
|
33
33
|
}
|
|
34
|
+
export const POS_SALES_CHANNELS: string[];
|
|
34
35
|
export namespace FULFILLMENT_TYPES {
|
|
35
36
|
let fulfilled: string;
|
|
36
37
|
let partiallyFulfilled: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SALES_CHANNEL_TO_RETURN_POLICY_MAPPING = exports.RETURN_POLICY_TYPES = exports.FINANCIAL_TYPES = exports.FULFILLMENT_TYPES = exports.SALES_CHANNELS = exports.ORDER_STATES = exports.PAYMENT_TYPES = exports.SHIPPING_TYPES = void 0;
|
|
3
|
+
exports.SALES_CHANNEL_TO_RETURN_POLICY_MAPPING = exports.RETURN_POLICY_TYPES = exports.FINANCIAL_TYPES = exports.FULFILLMENT_TYPES = exports.POS_SALES_CHANNELS = exports.SALES_CHANNELS = exports.ORDER_STATES = exports.PAYMENT_TYPES = exports.SHIPPING_TYPES = void 0;
|
|
4
4
|
exports.SHIPPING_TYPES = {
|
|
5
5
|
localPickUp: 'LOCAL_PICK_UP',
|
|
6
6
|
localDelivery: 'LOCAL_DELIVERY',
|
|
@@ -34,6 +34,10 @@ exports.SALES_CHANNELS = {
|
|
|
34
34
|
ribbnSellThroughChannelPartner: 'RIBBN_SELL_THROUGH_CHANNEL_PARTNER',
|
|
35
35
|
ribbnSellOnChannelPartner: 'RIBBN_SELL_ON_CHANNEL_PARTNER',
|
|
36
36
|
};
|
|
37
|
+
// Point-of-sale / in-person channels: the customer is handed the goods at the
|
|
38
|
+
// moment of sale, so every item is fulfilled at sale time (there is no separate
|
|
39
|
+
// fulfillment step, and no per-item fulfillment record is created).
|
|
40
|
+
exports.POS_SALES_CHANNELS = [exports.SALES_CHANNELS.ribbnWebPOS, exports.SALES_CHANNELS.ribbnMobilePOS];
|
|
37
41
|
exports.FULFILLMENT_TYPES = {
|
|
38
42
|
fulfilled: 'FULFILLED',
|
|
39
43
|
partiallyFulfilled: 'PARTIALLY_FULFILLED',
|
|
@@ -15,6 +15,9 @@ export default class OrderHelpers {
|
|
|
15
15
|
currencyCode: string;
|
|
16
16
|
};
|
|
17
17
|
getAmountSumByField(array: OrderRefund[] | ReRobeOrderLineItem[], fieldKey: string): number;
|
|
18
|
+
isPosOrder(order: {
|
|
19
|
+
salesChannel?: string | null;
|
|
20
|
+
} | null | undefined): boolean;
|
|
18
21
|
getOrderInfoWithRefundsAndFulfillments(order: CompleteOrder): OrderWithRefundsAndFulfillmentsInfoObj;
|
|
19
22
|
getUpdatedOrderObjWithRefunds(orderObj: CompleteOrder, refundObj: OrderRefund): CompleteOrder;
|
|
20
23
|
getUpdatedOrderObjWithFulfillment(orderObj: CompleteOrder, fulfillmentObj: FulfillmentAdmin, totalUnfulfilledItems: number): CompleteOrder;
|
|
@@ -160,11 +160,21 @@ class OrderHelpers {
|
|
|
160
160
|
getAmountSumByField(array, fieldKey) {
|
|
161
161
|
return array.map((item) => { var _a; return Number(((_a = item[fieldKey]) === null || _a === void 0 ? void 0 : _a.amount) || 0); }).reduce((a, b) => a + b, 0);
|
|
162
162
|
}
|
|
163
|
+
// Point-of-sale / in-person sale (Ribbn POS or the Ribbn Mobile App): goods are
|
|
164
|
+
// handed over at the moment of sale, so all items are fulfilled at sale time.
|
|
165
|
+
isPosOrder(order) {
|
|
166
|
+
return order_constants_1.POS_SALES_CHANNELS.includes((order && order.salesChannel) || '');
|
|
167
|
+
}
|
|
163
168
|
getOrderInfoWithRefundsAndFulfillments(order) {
|
|
164
169
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
165
170
|
const orderRefunds = order.refunds ? [...order.refunds] : [];
|
|
166
171
|
const orderFulfillment = order.fulfillments ? [...order.fulfillments] : [];
|
|
167
172
|
const orderLineItems = order.lineItems && Array.isArray(order.lineItems) ? [...order.lineItems] : [];
|
|
173
|
+
// POS / in-person orders fulfill every item at sale and never create per-item
|
|
174
|
+
// fulfillment records, so without this they'd all bucket as "unfulfilled"
|
|
175
|
+
// (and a refund would strand the non-returned items there). When there are no
|
|
176
|
+
// real fulfillment records, baseline every item as fulfilled instead.
|
|
177
|
+
const posFulfilledBaseline = this.isPosOrder(order) && !orderFulfillment.length;
|
|
168
178
|
const subtotalPrice = Object.assign(Object.assign({}, order.subtotalPrice), { amount: this.getSubtotalPrice(orderLineItems) });
|
|
169
179
|
const subtotalPricePresentment = Object.assign(Object.assign({}, order.subtotalPricePresentment), { amount: this.getAmountSumByField(orderLineItems, 'presentmentTotalPrice') });
|
|
170
180
|
const preparedOrder = {
|
|
@@ -226,6 +236,10 @@ class OrderHelpers {
|
|
|
226
236
|
});
|
|
227
237
|
preparedOrder.fulfilledItems = preparedOrder.activeLineItems.filter(({ fulfilledQuantity }) => fulfilledQuantity > 0);
|
|
228
238
|
}
|
|
239
|
+
else if (posFulfilledBaseline) {
|
|
240
|
+
preparedOrder.activeLineItems = preparedOrder.activeLineItems.map((item) => (Object.assign(Object.assign({}, item), { fulfilledQuantity: Number(item.quantity), unfulfilledQuantity: 0 })));
|
|
241
|
+
preparedOrder.fulfilledItems = [...preparedOrder.activeLineItems];
|
|
242
|
+
}
|
|
229
243
|
preparedOrder.unfulfilledItems = preparedOrder.activeLineItems.filter(({ unfulfilledQuantity }) => unfulfilledQuantity > 0);
|
|
230
244
|
if (!orderRefunds.length) {
|
|
231
245
|
return preparedOrder;
|
|
@@ -277,6 +291,16 @@ class OrderHelpers {
|
|
|
277
291
|
}, { refundedQuantity: 0, canceledQuantity: 0 });
|
|
278
292
|
// 3. fulfilledQuantity = (sum of order.fulfillment) - refundedQuantity
|
|
279
293
|
// 4. quantity = unfulfilled quantity (prev quantity - sum of order.fulfillment) - cancelled
|
|
294
|
+
//
|
|
295
|
+
// POS / in-person: every item started fulfilled, so a return (recorded as a
|
|
296
|
+
// refund OR a cancel, depending on the refund flow) comes out of the
|
|
297
|
+
// fulfilled quantity — never out of unfulfilled (which is 0 here). Otherwise
|
|
298
|
+
// a returned POS item would stay in the fulfilled bucket AND show as removed.
|
|
299
|
+
if (posFulfilledBaseline) {
|
|
300
|
+
return Object.assign(Object.assign(Object.assign({}, item), { fulfilledQuantity: Number(item.fulfilledQuantity) -
|
|
301
|
+
refundedAndCanceledQuantity.refundedQuantity -
|
|
302
|
+
refundedAndCanceledQuantity.canceledQuantity, unfulfilledQuantity: 0 }), refundedAndCanceledQuantity);
|
|
303
|
+
}
|
|
280
304
|
return Object.assign(Object.assign(Object.assign({}, item), { fulfilledQuantity: Number(item.fulfilledQuantity) - refundedAndCanceledQuantity.refundedQuantity, unfulfilledQuantity: Number(item.unfulfilledQuantity) - refundedAndCanceledQuantity.canceledQuantity }), refundedAndCanceledQuantity);
|
|
281
305
|
});
|
|
282
306
|
// Active lineItems - not refunded and not removed(not canceled) positions
|
|
@@ -679,12 +703,15 @@ class OrderHelpers {
|
|
|
679
703
|
})
|
|
680
704
|
.reduce((acc, cur) => {
|
|
681
705
|
if (cur.taxRate) {
|
|
682
|
-
|
|
683
|
-
|
|
706
|
+
// Key by whole percent (25), matching the backend ReceiptBuilder and the
|
|
707
|
+
// canonical "Moms 25%" display format, so every receipt path is consistent.
|
|
708
|
+
const ratePercent = Math.round(Number(cur.taxRate) * 100);
|
|
709
|
+
if (!acc[ratePercent]) {
|
|
710
|
+
acc[ratePercent] = 0;
|
|
684
711
|
}
|
|
685
712
|
const amount = Number(cur.taxAmount);
|
|
686
|
-
acc[
|
|
687
|
-
acc[
|
|
713
|
+
acc[ratePercent] += isRefund ? -Math.abs(amount) : amount;
|
|
714
|
+
acc[ratePercent] = parseFloat(acc[ratePercent].toFixed(2));
|
|
688
715
|
}
|
|
689
716
|
return acc;
|
|
690
717
|
}, {});
|
|
@@ -7,6 +7,7 @@ export type ReceiptPreviewLine = {
|
|
|
7
7
|
};
|
|
8
8
|
export default class ReceiptXmlHelpers {
|
|
9
9
|
static escapeXML(text: any): string;
|
|
10
|
+
static vatRatePercent(rate: any): number;
|
|
10
11
|
private static variantLabelForReceipt;
|
|
11
12
|
static formatLineItemXML(lineItem: any): string;
|
|
12
13
|
static buildReceiptContentXML(receiptData: any): string;
|
|
@@ -18,6 +18,17 @@ class ReceiptXmlHelpers {
|
|
|
18
18
|
.replace(/"/g, '"')
|
|
19
19
|
.replace(/'/g, ''');
|
|
20
20
|
}
|
|
21
|
+
// Canonical VAT-rate-for-display normalizer — the single source of truth for how
|
|
22
|
+
// a VAT rate is shown on EVERY receipt/report ("Moms 25%"). Accepts a fraction
|
|
23
|
+
// (0.25), a whole percent (25), or their string forms, and always returns the
|
|
24
|
+
// integer percent (25). A value in (0,1) is treated as a fraction; >=1 is already
|
|
25
|
+
// a percent. Covers every Swedish rate (0, 6, 12, 25) from either representation.
|
|
26
|
+
static vatRatePercent(rate) {
|
|
27
|
+
const n = Math.abs(Number(rate));
|
|
28
|
+
if (!n || Number.isNaN(n))
|
|
29
|
+
return 0;
|
|
30
|
+
return Math.round(n < 1 ? n * 100 : n);
|
|
31
|
+
}
|
|
21
32
|
// Variant label for a receipt line (e.g. "M · R"), delegated to the shared
|
|
22
33
|
// helper so cart, order detail, and receipt render the same rule.
|
|
23
34
|
static variantLabelForReceipt(lineItem) {
|
|
@@ -131,7 +142,7 @@ class ReceiptXmlHelpers {
|
|
|
131
142
|
if (receiptData.taxRates && typeof receiptData.taxRates === 'object') {
|
|
132
143
|
receipt += '<text font="font_a" width="1" height="1">';
|
|
133
144
|
Object.entries(receiptData.taxRates).forEach(([taxRate, value]) => {
|
|
134
|
-
const rate =
|
|
145
|
+
const rate = this.vatRatePercent(taxRate);
|
|
135
146
|
receipt += `Moms ${this.escapeXML(String(rate))}%: ${this.escapeXML(value)}`;
|
|
136
147
|
receipt += '</text><feed/><text font="font_a" width="1" height="1">';
|
|
137
148
|
});
|
|
@@ -340,7 +351,7 @@ class ReceiptXmlHelpers {
|
|
|
340
351
|
// --- VAT ---
|
|
341
352
|
receipt += dividerLine;
|
|
342
353
|
vatLines.forEach((v) => {
|
|
343
|
-
receipt += row(`Moms ${this.escapeXML(String(v.rate))}%:`, String(v.amount));
|
|
354
|
+
receipt += row(`Moms ${this.escapeXML(String(this.vatRatePercent(v.rate)))}%:`, String(v.amount));
|
|
344
355
|
});
|
|
345
356
|
if (netExVat)
|
|
346
357
|
receipt += row('Netto (exkl. moms):', netExVat, true);
|