@shushed/helpers 0.0.202 → 0.0.203-bug-erp-770-20251208155429
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/cjs/contracts/order/payment.schema.json +5 -5
- package/dist/cjs/dist-dereferenced/messages/order/delivered.js +1 -1
- package/dist/cjs/dist-dereferenced/messages/order/new.js +1 -1
- package/dist/cjs/dist-dereferenced/messages/order/processed.js +1 -1
- package/dist/cjs/dist-dereferenced/messages/order/return-initiated.js +1 -1
- package/dist/cjs/dist-dereferenced/messages/order/returned.js +1 -1
- package/dist/cjs/dist-dereferenced/messages/order/shipped.js +1 -1
- package/dist/cjs/dist-dereferenced/order/orderMain.js +1 -1
- package/dist/cjs/dist-dereferenced/order/payment.js +1 -1
- package/dist/cjs/src-public/bcOrder.js +403 -71
- package/dist/cjs/src-public/dato.js +244 -10
- package/dist/types/dist-dereferenced/messages/order/delivered.d.ts +4 -4
- package/dist/types/dist-dereferenced/messages/order/new.d.ts +4 -4
- package/dist/types/dist-dereferenced/messages/order/processed.d.ts +4 -4
- package/dist/types/dist-dereferenced/messages/order/return-initiated.d.ts +4 -4
- package/dist/types/dist-dereferenced/messages/order/returned.d.ts +4 -4
- package/dist/types/dist-dereferenced/messages/order/shipped.d.ts +4 -4
- package/dist/types/dist-dereferenced/order/index.d.ts +4 -4
- package/dist/types/dist-dereferenced/order/orderMain.d.ts +4 -4
- package/dist/types/dist-dereferenced/order/payment.d.ts +4 -4
- package/dist/types/dist-types/messages/order/delivered.d.ts +2 -2
- package/dist/types/dist-types/messages/order/new.d.ts +2 -2
- package/dist/types/dist-types/messages/order/processed.d.ts +2 -2
- package/dist/types/dist-types/messages/order/return-initiated.d.ts +2 -2
- package/dist/types/dist-types/messages/order/returned.d.ts +2 -2
- package/dist/types/dist-types/messages/order/shipped.d.ts +2 -2
- package/dist/types/dist-types/order/orderMain.d.ts +2 -2
- package/dist/types/dist-types/order/payment.d.ts +2 -2
- package/dist/types/src-public/dato.d.ts +27 -1
- package/package.json +1 -1
|
@@ -14,6 +14,9 @@ const GBP = "GBP";
|
|
|
14
14
|
function toMinorUnits(amount) {
|
|
15
15
|
if (typeof amount !== "number" || !isFinite(amount))
|
|
16
16
|
return 0;
|
|
17
|
+
if (Math.abs(amount) > Number.MAX_SAFE_INTEGER / 100) {
|
|
18
|
+
return Math.round(Number((amount * 100).toPrecision(15)));
|
|
19
|
+
}
|
|
17
20
|
return Math.round(amount * 100);
|
|
18
21
|
}
|
|
19
22
|
function money(value) {
|
|
@@ -99,28 +102,45 @@ function buildLineGrouping(payload) {
|
|
|
99
102
|
}
|
|
100
103
|
function transformMasterOrder(payload) {
|
|
101
104
|
const erp_order_id = payload.no || payload.system_id || "";
|
|
102
|
-
const sale0 = (payload.sales
|
|
103
|
-
const invoice0 = (payload.invoices
|
|
105
|
+
const sale0 = Array.isArray(payload.sales) && payload.sales.length > 0 ? payload.sales[0] : undefined;
|
|
106
|
+
const invoice0 = Array.isArray(payload.invoices) && payload.invoices.length > 0 ? payload.invoices[0] : undefined;
|
|
107
|
+
const normalizeDateForSort = (dateStr) => {
|
|
108
|
+
if (!dateStr || typeof dateStr !== 'string')
|
|
109
|
+
return '';
|
|
110
|
+
if (dateStr.includes('T'))
|
|
111
|
+
return dateStr;
|
|
112
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(dateStr))
|
|
113
|
+
return `${dateStr}T00:00:00Z`;
|
|
114
|
+
try {
|
|
115
|
+
const date = new Date(dateStr);
|
|
116
|
+
if (!isNaN(date.getTime()))
|
|
117
|
+
return date.toISOString();
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
}
|
|
121
|
+
return dateStr;
|
|
122
|
+
};
|
|
104
123
|
const ordered_at_candidates = [];
|
|
105
124
|
if (sale0?.system_created_at)
|
|
106
|
-
ordered_at_candidates.push(sale0.system_created_at);
|
|
125
|
+
ordered_at_candidates.push(normalizeDateForSort(String(sale0.system_created_at)));
|
|
107
126
|
if (Array.isArray(payload.invoices) && payload.invoices.length > 0) {
|
|
108
|
-
const invDates = payload.invoices
|
|
127
|
+
const invDates = payload.invoices
|
|
128
|
+
.map((i) => i.system_created_at)
|
|
129
|
+
.filter(Boolean)
|
|
130
|
+
.map((d) => normalizeDateForSort(String(d)));
|
|
109
131
|
ordered_at_candidates.push(...invDates);
|
|
110
132
|
}
|
|
111
133
|
if (payload.ordered_at)
|
|
112
|
-
ordered_at_candidates.push(String(payload.ordered_at));
|
|
134
|
+
ordered_at_candidates.push(normalizeDateForSort(String(payload.ordered_at)));
|
|
113
135
|
if (payload.created_at)
|
|
114
|
-
ordered_at_candidates.push(String(payload.created_at));
|
|
115
|
-
const
|
|
116
|
-
|
|
136
|
+
ordered_at_candidates.push(normalizeDateForSort(String(payload.created_at)));
|
|
137
|
+
const validDates = ordered_at_candidates.filter(d => d.length > 0);
|
|
138
|
+
const orderedAtRaw = validDates.length > 0
|
|
139
|
+
? validDates.sort()[0]
|
|
117
140
|
: new Date().toISOString();
|
|
118
141
|
let ordered_at;
|
|
119
142
|
try {
|
|
120
|
-
const
|
|
121
|
-
? orderedAtRaw
|
|
122
|
-
: `${orderedAtRaw}T00:00:00Z`;
|
|
123
|
-
const date = new Date(dateStr);
|
|
143
|
+
const date = new Date(orderedAtRaw);
|
|
124
144
|
if (isNaN(date.getTime())) {
|
|
125
145
|
ordered_at = new Date().toISOString();
|
|
126
146
|
}
|
|
@@ -133,7 +153,7 @@ function transformMasterOrder(payload) {
|
|
|
133
153
|
}
|
|
134
154
|
const created_by_raw = sale0?.created_by ?? invoice0?.created_by ?? "";
|
|
135
155
|
const system_created_by = created_by_raw === "SITOO" ? "POS" : created_by_raw === "STOREFRONT" ? "ECOMMERCE" : "SYSTEM";
|
|
136
|
-
const is_gift = Boolean((sale0?.gift_message && String(sale0
|
|
156
|
+
const is_gift = Boolean((sale0?.gift_message && String(sale0?.gift_message).trim().length > 0) ||
|
|
137
157
|
(payload.invoices?.some((i) => (i.gift_message || "").trim().length > 0)));
|
|
138
158
|
const invoiceLinesForReturn = payload.invoices?.flatMap((i) => i.invoice_lines || []) || [];
|
|
139
159
|
const salesLinesFallbackForReturn = (sale0?.sale_lines || []);
|
|
@@ -376,16 +396,31 @@ function transformMasterOrder(payload) {
|
|
|
376
396
|
itemsMap[it.sku].push(it);
|
|
377
397
|
});
|
|
378
398
|
const invoicedLineNos = new Set((payload.invoices || []).flatMap((inv) => (inv?.invoice_lines || []).map((l) => Number(l?.order_line_no || -1))));
|
|
379
|
-
|
|
380
|
-
|
|
399
|
+
const isDeliveryItem = (line) => {
|
|
400
|
+
const desc = String(line?.description || '').trim().toLowerCase();
|
|
401
|
+
return desc === 'delivery';
|
|
402
|
+
};
|
|
403
|
+
let invoicesNetMinor = 0;
|
|
404
|
+
let invoicesGrossMinor = 0;
|
|
405
|
+
(payload.invoices || []).forEach((inv) => {
|
|
406
|
+
(inv.invoice_lines || []).forEach((line) => {
|
|
407
|
+
if (!isDeliveryItem(line)) {
|
|
408
|
+
invoicesNetMinor += toMinorUnits(Number(line?.amount || line?.amount_excl_vat || 0));
|
|
409
|
+
invoicesGrossMinor += toMinorUnits(Number(line?.amount_including_vat || line?.amount_incl_vat || 0));
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
if (invoicesGrossMinor === 0 && (payload.invoices || []).length > 0) {
|
|
414
|
+
invoicesNetMinor = toMinorUnits((payload.invoices || []).reduce((acc, inv) => acc + (inv.amount_excl_vat || 0), 0));
|
|
415
|
+
invoicesGrossMinor = toMinorUnits((payload.invoices || []).reduce((acc, inv) => acc + (inv.amount_incl_vat || 0), 0));
|
|
416
|
+
}
|
|
381
417
|
if (invoicesGrossMinor === 0 && invoicedLineNos.size > 0 && sale0?.sale_lines) {
|
|
382
|
-
const invoicedSaleLines = (sale0
|
|
383
|
-
|
|
384
|
-
const itemNo = String(l?.item_no || l?.no || '').toUpperCase();
|
|
385
|
-
if (itemNo === 'X DELCHR')
|
|
418
|
+
const invoicedSaleLines = (sale0?.sale_lines || []).filter((l) => {
|
|
419
|
+
if (isDeliveryItem(l))
|
|
386
420
|
return false;
|
|
387
421
|
if (String(l?.type || '').toLowerCase() !== 'item')
|
|
388
422
|
return false;
|
|
423
|
+
const lineNo = Number(l?.line_no || -1);
|
|
389
424
|
return invoicedLineNos.has(lineNo);
|
|
390
425
|
});
|
|
391
426
|
invoicesNetMinor = toMinorUnits(invoicedSaleLines.reduce((acc, l) => acc + (Number(l?.amount || 0)), 0));
|
|
@@ -393,7 +428,26 @@ function transformMasterOrder(payload) {
|
|
|
393
428
|
}
|
|
394
429
|
let nonInvSalesNetMinor = 0;
|
|
395
430
|
let nonInvSalesGrossMinor = 0;
|
|
396
|
-
if (sale0 &&
|
|
431
|
+
if (sale0?.sale_lines && sale0.sale_lines.length > 0) {
|
|
432
|
+
const allSaleLines = (sale0.sale_lines || []).filter((l) => {
|
|
433
|
+
if (isDeliveryItem(l))
|
|
434
|
+
return false;
|
|
435
|
+
if (String(l?.type || '').toLowerCase() !== 'item')
|
|
436
|
+
return false;
|
|
437
|
+
return true;
|
|
438
|
+
});
|
|
439
|
+
const totalSaleNetMinor = toMinorUnits(allSaleLines.reduce((acc, l) => acc + (Number(l?.amount || 0)), 0));
|
|
440
|
+
const totalSaleGrossMinor = toMinorUnits(allSaleLines.reduce((acc, l) => acc + (Number(l?.amount_including_vat || 0)), 0));
|
|
441
|
+
if (invoicesGrossMinor > 0) {
|
|
442
|
+
nonInvSalesNetMinor = Math.max(0, totalSaleNetMinor - invoicesNetMinor);
|
|
443
|
+
nonInvSalesGrossMinor = Math.max(0, totalSaleGrossMinor - invoicesGrossMinor);
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
nonInvSalesNetMinor = totalSaleNetMinor;
|
|
447
|
+
nonInvSalesGrossMinor = totalSaleGrossMinor;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
else if (sale0 && (sale0.amount_excl_vat || sale0.amount_incl_vat)) {
|
|
397
451
|
const saleNetMinor = toMinorUnits(sale0.amount_excl_vat || 0);
|
|
398
452
|
const saleGrossMinor = toMinorUnits(sale0.amount_incl_vat || 0);
|
|
399
453
|
if (invoicesGrossMinor > 0) {
|
|
@@ -407,12 +461,11 @@ function transformMasterOrder(payload) {
|
|
|
407
461
|
}
|
|
408
462
|
else {
|
|
409
463
|
const nonInvoicedSales = (sale0?.sale_lines || []).filter((l) => {
|
|
410
|
-
|
|
411
|
-
const itemNo = String(l?.item_no || l?.no || '').toUpperCase();
|
|
412
|
-
if (itemNo === 'X DELCHR')
|
|
464
|
+
if (isDeliveryItem(l))
|
|
413
465
|
return false;
|
|
414
466
|
if (String(l?.type || '').toLowerCase() !== 'item')
|
|
415
467
|
return false;
|
|
468
|
+
const lineNo = Number(l?.line_no || -1);
|
|
416
469
|
return !invoicedLineNos.has(lineNo);
|
|
417
470
|
});
|
|
418
471
|
nonInvSalesNetMinor = toMinorUnits(nonInvoicedSales.reduce((acc, l) => acc + (Number(l?.amount || 0)), 0));
|
|
@@ -557,21 +610,27 @@ function transformMasterOrder(payload) {
|
|
|
557
610
|
const typeStr = String(l?.type || "").toLowerCase();
|
|
558
611
|
const isItemish = typeStr.includes('item') || looksProduct;
|
|
559
612
|
const isRefund = itemNo.toUpperCase() === 'REFUND';
|
|
560
|
-
|
|
561
|
-
if ((l?.quantity || 0) > 0 && isItemish && !isRefund && !isDelivery) {
|
|
613
|
+
if ((l?.quantity || 0) > 0 && isItemish && !isRefund && !isDeliveryItem(l)) {
|
|
562
614
|
hasQualifying = true;
|
|
563
615
|
return s + (l?.amount_including_vat || 0);
|
|
564
616
|
}
|
|
565
617
|
return s;
|
|
566
618
|
}, 0);
|
|
567
|
-
|
|
568
|
-
|
|
619
|
+
if (!hasQualifying)
|
|
620
|
+
return acc;
|
|
621
|
+
if (cm?.amount_incl_vat != null && cm.amount_incl_vat > 0) {
|
|
622
|
+
return acc + cm.amount_incl_vat;
|
|
623
|
+
}
|
|
624
|
+
return acc + sum;
|
|
569
625
|
}, 0);
|
|
570
626
|
const merchReturnGrossFromReturnSales = (payload.sales || []).reduce((acc, sale) => {
|
|
571
627
|
const docTypeRaw = String(sale?.document_type || sale?.documentType || "");
|
|
572
628
|
const isReturnOrder = docTypeRaw.toLowerCase().includes('return');
|
|
573
629
|
if (!isReturnOrder)
|
|
574
630
|
return acc;
|
|
631
|
+
if (sale?.amount_incl_vat != null && sale.amount_incl_vat > 0) {
|
|
632
|
+
return acc + sale.amount_incl_vat;
|
|
633
|
+
}
|
|
575
634
|
const lines = sale?.sale_lines || [];
|
|
576
635
|
let hasQualifying = false;
|
|
577
636
|
const sum = lines.reduce((s, l) => {
|
|
@@ -581,15 +640,13 @@ function transformMasterOrder(payload) {
|
|
|
581
640
|
const typeStr = String(l?.type || "").toLowerCase();
|
|
582
641
|
const isItemish = typeStr.includes('item') || looksProduct;
|
|
583
642
|
const isRefund = itemNo.toUpperCase() === 'REFUND';
|
|
584
|
-
|
|
585
|
-
if ((l?.quantity || 0) > 0 && isItemish && !isRefund && !isDelivery) {
|
|
643
|
+
if ((l?.quantity || 0) > 0 && isItemish && !isRefund && !isDeliveryItem(l)) {
|
|
586
644
|
hasQualifying = true;
|
|
587
645
|
return s + (l?.amount_including_vat || 0);
|
|
588
646
|
}
|
|
589
647
|
return s;
|
|
590
648
|
}, 0);
|
|
591
|
-
|
|
592
|
-
return acc + add;
|
|
649
|
+
return acc + (sum > 0 ? sum : (hasQualifying ? (sale?.amount_incl_vat || sale?.amount_including_vat || 0) : 0));
|
|
593
650
|
}, 0);
|
|
594
651
|
const merchReturnGross = merchReturnGrossFromCreditMemos + merchReturnGrossFromReturnSales;
|
|
595
652
|
const hasMerchReturn = merchReturnGross > 0;
|
|
@@ -647,12 +704,12 @@ function transformMasterOrder(payload) {
|
|
|
647
704
|
return acc + (s.amount_incl_vat || 0);
|
|
648
705
|
}
|
|
649
706
|
const lineAmounts = (s.shipment_lines || []).reduce((lineAcc, l) => {
|
|
650
|
-
|
|
651
|
-
if (itemNo === 'X DELCHR')
|
|
707
|
+
if (isDeliveryItem(l))
|
|
652
708
|
return lineAcc;
|
|
653
709
|
const orderLineNo = Number(l?.order_line_no || -1);
|
|
654
710
|
let sourceLine = orderLineNo > 0 ? sourceLineMap.get(orderLineNo) : null;
|
|
655
711
|
if (!sourceLine) {
|
|
712
|
+
const itemNo = String(l?.item_no || l?.no || "").toUpperCase();
|
|
656
713
|
const variant = String(l?.variant_code || "");
|
|
657
714
|
const sku = variant ? `${itemNo}-${variant}` : itemNo;
|
|
658
715
|
sourceLine = sourceLineBySku.get(sku);
|
|
@@ -686,85 +743,358 @@ function transformMasterOrder(payload) {
|
|
|
686
743
|
const shipping_from = invoice0 || sale0 || null;
|
|
687
744
|
const mapPaymentMethod = (code, card) => {
|
|
688
745
|
const c = String(code || '').toUpperCase();
|
|
689
|
-
|
|
746
|
+
const processorId = String(card?.processor_id || '').toUpperCase();
|
|
747
|
+
const giftCertId = String(card?.gift_certificate_id || '').trim();
|
|
748
|
+
const cardType = String(card?.type || '').trim().toLowerCase();
|
|
749
|
+
if (giftCertId.length > 0)
|
|
690
750
|
return 'GIFT-CARD';
|
|
691
|
-
if (
|
|
692
|
-
return '
|
|
693
|
-
if (
|
|
751
|
+
if (cardType === 'gift certificate')
|
|
752
|
+
return 'GIFT-CARD';
|
|
753
|
+
if (processorId.includes('GIFT_CERTIFICATE') || processorId.includes('GIFT-CERTIFICATE'))
|
|
754
|
+
return 'GIFT-CARD';
|
|
755
|
+
if (/(GVOUCHER|VOUCHER|GIFT|EGIFT)/.test(c))
|
|
756
|
+
return 'GIFT-CARD';
|
|
757
|
+
if (c.includes('APPLE'))
|
|
694
758
|
return 'APPLE-PAY';
|
|
695
|
-
if (
|
|
759
|
+
if ((c.includes('PAYPAL') || processorId === 'PAYPAL') && giftCertId.length === 0) {
|
|
760
|
+
return 'PAYPAL';
|
|
761
|
+
}
|
|
762
|
+
if (c.includes('MOTO'))
|
|
763
|
+
return 'MOTO';
|
|
764
|
+
if (c.includes('ADYEN') && cardType === 'credit card' && giftCertId.length === 0) {
|
|
765
|
+
return 'CREDIT-CARD';
|
|
766
|
+
}
|
|
767
|
+
const serviceType = String(card?.service_type || '').toUpperCase();
|
|
768
|
+
if (serviceType.includes('VOUCHER') && giftCertId.length === 0 && processorId !== 'PAYPAL') {
|
|
769
|
+
return 'GIFT-CARD';
|
|
770
|
+
}
|
|
771
|
+
if (card?.card_number)
|
|
696
772
|
return 'CREDIT-CARD';
|
|
697
773
|
return 'OTHER';
|
|
698
774
|
};
|
|
699
|
-
const journeyFromCreatedBy = (createdBy) => {
|
|
700
|
-
const v = String(createdBy || '').toUpperCase();
|
|
701
|
-
if (v === 'SITOO')
|
|
702
|
-
return 'POS';
|
|
703
|
-
if (v === 'STOREFRONT')
|
|
704
|
-
return 'CHECKOUT';
|
|
705
|
-
if (v === 'CENTRA')
|
|
706
|
-
return 'CHECKOUT';
|
|
707
|
-
return 'SYSTEM';
|
|
708
|
-
};
|
|
709
775
|
const cardPays = (payload.card_payments || []).map((c) => ({
|
|
710
776
|
created_at: c.created_at,
|
|
711
|
-
amount: c.amount,
|
|
712
|
-
type: 'Payment',
|
|
777
|
+
amount: c.mount || c.amount,
|
|
778
|
+
type: c.type || 'Payment',
|
|
713
779
|
payment_method_code: String(c.service_type || ''),
|
|
714
780
|
transaction_id: c.transaction_id,
|
|
715
781
|
card_number: c.card_number,
|
|
716
782
|
psp_reference: c.psp_reference,
|
|
783
|
+
gift_certificate_id: c.gift_certificate_id,
|
|
784
|
+
service_type: c.service_type,
|
|
785
|
+
processor_id: c.processor_id,
|
|
717
786
|
created_by: invoice0?.created_by || sale0?.created_by,
|
|
787
|
+
source: 'card_payment',
|
|
788
|
+
order_no: c.order_no,
|
|
789
|
+
captured: c.captured,
|
|
718
790
|
}));
|
|
719
791
|
const normalPays = (payload.payments || []).map((p) => ({
|
|
720
792
|
created_at: p.created_at,
|
|
721
793
|
amount: p.amount,
|
|
722
794
|
type: p.type,
|
|
723
795
|
payment_method_code: p.payment_method_code,
|
|
724
|
-
transaction_id: p.
|
|
796
|
+
transaction_id: p.transaction_id,
|
|
725
797
|
card_number: undefined,
|
|
726
798
|
psp_reference: p.psp_reference,
|
|
799
|
+
payment_gateway_token: p.payment_gateway_token,
|
|
800
|
+
payment_number: p.payment_number,
|
|
801
|
+
payment_refund_number: p.payment_refund_number,
|
|
802
|
+
original_payment_refund_id: p.original_payment_refund_id,
|
|
727
803
|
created_by: p.created_by || invoice0?.created_by || sale0?.created_by,
|
|
804
|
+
source: 'payment',
|
|
805
|
+
order_no: p.master_order_no,
|
|
806
|
+
ledger_entry_no: p.ledger_entry_no,
|
|
728
807
|
}));
|
|
729
808
|
const flat = [...cardPays, ...normalPays];
|
|
730
809
|
const groupsMap = new Map();
|
|
731
|
-
flat.forEach((p
|
|
810
|
+
flat.forEach((p) => {
|
|
811
|
+
try {
|
|
812
|
+
const isRefund = String(p.type || '').toLowerCase() === 'refund';
|
|
813
|
+
const logicalType = isRefund ? 'REFUND' : 'CHARGE';
|
|
814
|
+
let keyBase;
|
|
815
|
+
const unifiedId = p.transaction_id || p.payment_gateway_token;
|
|
816
|
+
if (unifiedId) {
|
|
817
|
+
keyBase = unifiedId;
|
|
818
|
+
}
|
|
819
|
+
else if (p.payment_number) {
|
|
820
|
+
keyBase = `PN:${p.payment_number}`;
|
|
821
|
+
}
|
|
822
|
+
else if (!isRefund) {
|
|
823
|
+
const paymentType = String(p.type || '').toUpperCase();
|
|
824
|
+
const amount = Number(p.amount || 0);
|
|
825
|
+
const paymentMethod = String(p.payment_method_code || '').toUpperCase().trim();
|
|
826
|
+
const orderNo = p.order_no || erp_order_id;
|
|
827
|
+
if (paymentType === 'CAPTURE' || paymentType === 'AUTHORISATION') {
|
|
828
|
+
keyBase = `CHARGE-${orderNo}-${amount}-${paymentMethod}`;
|
|
829
|
+
}
|
|
830
|
+
else if (p.gift_certificate_id) {
|
|
831
|
+
keyBase = `GIFT:${p.gift_certificate_id}`;
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
const orderNo = p.order_no || erp_order_id;
|
|
835
|
+
const paymentMethod = String(p.payment_method_code || '').toUpperCase().trim();
|
|
836
|
+
keyBase = `${orderNo}-${amount}-${paymentMethod}`;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
if (p.payment_refund_number) {
|
|
841
|
+
keyBase = `PRN:${p.payment_refund_number}`;
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
const orderNo = p.order_no || erp_order_id;
|
|
845
|
+
const amount = Number(p.amount || 0);
|
|
846
|
+
const paymentMethod = String(p.payment_method_code || '').toUpperCase().trim();
|
|
847
|
+
keyBase = `${orderNo}-${amount}-${paymentMethod}`;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
const key = `${logicalType}:${keyBase}`;
|
|
851
|
+
const g = groupsMap.get(key) || { entries: [], type: logicalType, key };
|
|
852
|
+
g.entries.push(p);
|
|
853
|
+
groupsMap.set(key, g);
|
|
854
|
+
}
|
|
855
|
+
catch (error) {
|
|
856
|
+
console.error(`Error processing payment entry:`, {
|
|
857
|
+
error: error instanceof Error ? error.message : String(error),
|
|
858
|
+
payment_type: p?.type,
|
|
859
|
+
amount: p?.amount,
|
|
860
|
+
order_no: p?.order_no || erp_order_id,
|
|
861
|
+
transaction_id: p?.transaction_id,
|
|
862
|
+
payment_gateway_token: p?.payment_gateway_token,
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
const allPaymentEntriesByOrder = new Map();
|
|
867
|
+
flat.forEach(p => {
|
|
868
|
+
const orderNo = p.order_no || erp_order_id;
|
|
869
|
+
const amount = Number(p.amount || 0);
|
|
870
|
+
const key = `${orderNo}-${amount}`;
|
|
871
|
+
if (!allPaymentEntriesByOrder.has(key)) {
|
|
872
|
+
allPaymentEntriesByOrder.set(key, []);
|
|
873
|
+
}
|
|
874
|
+
allPaymentEntriesByOrder.get(key).push(p);
|
|
875
|
+
});
|
|
876
|
+
const cardPaymentsByToken = new Map();
|
|
877
|
+
cardPays.forEach(cp => {
|
|
878
|
+
if (cp.transaction_id) {
|
|
879
|
+
cardPaymentsByToken.set(cp.transaction_id, cp);
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
const paymentsByOriginalRefundId = new Map();
|
|
883
|
+
const paymentsByPaymentNumber = new Map();
|
|
884
|
+
const paymentsByPaymentRefundNumber = new Map();
|
|
885
|
+
normalPays.forEach(p => {
|
|
732
886
|
const isRefund = String(p.type || '').toLowerCase() === 'refund';
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
887
|
+
if (!isRefund) {
|
|
888
|
+
if (p.original_payment_refund_id) {
|
|
889
|
+
paymentsByOriginalRefundId.set(p.original_payment_refund_id, p);
|
|
890
|
+
}
|
|
891
|
+
if (p.payment_number) {
|
|
892
|
+
paymentsByPaymentNumber.set(p.payment_number, p);
|
|
893
|
+
}
|
|
894
|
+
if (p.payment_refund_number) {
|
|
895
|
+
paymentsByPaymentRefundNumber.set(p.payment_refund_number, p);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
739
898
|
});
|
|
740
|
-
const payments = Array.from(groupsMap.values()).map((g
|
|
899
|
+
const payments = Array.from(groupsMap.values()).map((g) => {
|
|
900
|
+
const sortedEntries = [...g.entries].sort((a, b) => {
|
|
901
|
+
if (a.source === 'card_payment' && b.source !== 'card_payment')
|
|
902
|
+
return -1;
|
|
903
|
+
if (b.source === 'card_payment' && a.source !== 'card_payment')
|
|
904
|
+
return 1;
|
|
905
|
+
if (a.source === 'payment' && b.source === 'payment') {
|
|
906
|
+
const aType = String(a.type || '').toUpperCase();
|
|
907
|
+
const bType = String(b.type || '').toUpperCase();
|
|
908
|
+
if (aType === 'AUTHORISATION' && bType !== 'AUTHORISATION')
|
|
909
|
+
return -1;
|
|
910
|
+
if (bType === 'AUTHORISATION' && aType !== 'AUTHORISATION')
|
|
911
|
+
return 1;
|
|
912
|
+
if (aType === 'CAPTURE' && bType !== 'CAPTURE' && bType !== 'AUTHORISATION')
|
|
913
|
+
return -1;
|
|
914
|
+
if (bType === 'CAPTURE' && aType !== 'CAPTURE' && aType !== 'AUTHORISATION')
|
|
915
|
+
return 1;
|
|
916
|
+
}
|
|
917
|
+
return 0;
|
|
918
|
+
});
|
|
741
919
|
const created_at = g.entries
|
|
742
920
|
.map(e => e.created_at)
|
|
743
921
|
.filter(Boolean)
|
|
744
922
|
.map(d => new Date(d).toISOString())
|
|
745
923
|
.sort()[0] || ordered_at;
|
|
746
|
-
const
|
|
747
|
-
const
|
|
748
|
-
const
|
|
749
|
-
const
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
const
|
|
924
|
+
const amountInclVat = Math.max(...g.entries.map(e => Number(e.amount || 0)), 0);
|
|
925
|
+
const amount = money(amountInclVat);
|
|
926
|
+
const primary = sortedEntries[0] || {};
|
|
927
|
+
const paymentEntryForMethod = sortedEntries.find(e => e.source === 'payment' && e.payment_method_code);
|
|
928
|
+
const paymentMethodCode = paymentEntryForMethod?.payment_method_code || primary.payment_method_code;
|
|
929
|
+
let cardPaymentEntryForMethod = sortedEntries.find(e => e.source === 'card_payment');
|
|
930
|
+
const groupCardData = {
|
|
931
|
+
card_number: cardPaymentEntryForMethod?.card_number || sortedEntries.find(e => e.card_number)?.card_number,
|
|
932
|
+
transaction_id: cardPaymentEntryForMethod?.transaction_id || sortedEntries.find(e => e.transaction_id)?.transaction_id,
|
|
933
|
+
service_type: cardPaymentEntryForMethod?.service_type || sortedEntries.find(e => e.service_type)?.service_type,
|
|
934
|
+
processor_id: cardPaymentEntryForMethod?.processor_id || sortedEntries.find(e => e.processor_id)?.processor_id,
|
|
935
|
+
gift_certificate_id: cardPaymentEntryForMethod?.gift_certificate_id || sortedEntries.find(e => e.gift_certificate_id)?.gift_certificate_id,
|
|
936
|
+
type: cardPaymentEntryForMethod?.type || sortedEntries.find(e => e.type && e.source === 'card_payment')?.type,
|
|
937
|
+
};
|
|
938
|
+
let pm;
|
|
939
|
+
if (cardPaymentEntryForMethod?.type && String(cardPaymentEntryForMethod.type).trim().toLowerCase() === 'apple pay') {
|
|
940
|
+
pm = 'APPLE-PAY';
|
|
941
|
+
}
|
|
942
|
+
else {
|
|
943
|
+
pm = mapPaymentMethod(paymentMethodCode, groupCardData);
|
|
944
|
+
}
|
|
945
|
+
let cardPaymentEntryForTransaction = sortedEntries.find(e => e.source === 'card_payment' && e.transaction_id);
|
|
946
|
+
if (!cardPaymentEntryForTransaction && primary.payment_gateway_token) {
|
|
947
|
+
cardPaymentEntryForTransaction = cardPaymentsByToken.get(primary.payment_gateway_token);
|
|
948
|
+
}
|
|
949
|
+
if (!cardPaymentEntryForTransaction && g.type === 'REFUND') {
|
|
950
|
+
let originalPayment;
|
|
951
|
+
const refundEntryWithPaymentNumber = sortedEntries.find(e => e.payment_number && String(e.payment_number).trim().length > 0);
|
|
952
|
+
if (refundEntryWithPaymentNumber?.payment_number) {
|
|
953
|
+
originalPayment = paymentsByPaymentNumber.get(refundEntryWithPaymentNumber.payment_number);
|
|
954
|
+
}
|
|
955
|
+
if (!originalPayment) {
|
|
956
|
+
const refundEntryWithOriginalId = sortedEntries.find(e => e.original_payment_refund_id);
|
|
957
|
+
if (refundEntryWithOriginalId?.original_payment_refund_id) {
|
|
958
|
+
originalPayment = Array.from(normalPays).find(p => p.ledger_entry_no === refundEntryWithOriginalId.original_payment_refund_id &&
|
|
959
|
+
String(p.type || '').toLowerCase() !== 'refund');
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
if (!originalPayment) {
|
|
963
|
+
const refundEntryWithRefundNumber = sortedEntries.find(e => e.payment_refund_number && String(e.payment_refund_number).trim().length > 0);
|
|
964
|
+
if (refundEntryWithRefundNumber?.payment_refund_number) {
|
|
965
|
+
originalPayment = paymentsByPaymentRefundNumber.get(refundEntryWithRefundNumber.payment_refund_number);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
if (originalPayment?.payment_gateway_token) {
|
|
969
|
+
cardPaymentEntryForTransaction = cardPaymentsByToken.get(originalPayment.payment_gateway_token);
|
|
970
|
+
if (!cardPaymentEntryForMethod && cardPaymentEntryForTransaction) {
|
|
971
|
+
cardPaymentEntryForMethod = cardPaymentEntryForTransaction;
|
|
972
|
+
if (cardPaymentEntryForMethod?.type && String(cardPaymentEntryForMethod.type).trim().toLowerCase() === 'apple pay') {
|
|
973
|
+
pm = 'APPLE-PAY';
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
const refundCardData = {
|
|
977
|
+
card_number: cardPaymentEntryForMethod?.card_number,
|
|
978
|
+
transaction_id: cardPaymentEntryForMethod?.transaction_id,
|
|
979
|
+
service_type: cardPaymentEntryForMethod?.service_type,
|
|
980
|
+
processor_id: cardPaymentEntryForMethod?.processor_id,
|
|
981
|
+
gift_certificate_id: cardPaymentEntryForMethod?.gift_certificate_id,
|
|
982
|
+
type: cardPaymentEntryForMethod?.type,
|
|
983
|
+
};
|
|
984
|
+
pm = mapPaymentMethod(paymentMethodCode, refundCardData);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
let transaction_id = null;
|
|
990
|
+
if (g.type === 'REFUND' && !cardPaymentEntryForTransaction && !primary.transaction_id && !primary.payment_gateway_token) {
|
|
991
|
+
let originalPaymentForTransaction;
|
|
992
|
+
const refundEntryWithPaymentNumber = sortedEntries.find(e => e.payment_number && String(e.payment_number).trim().length > 0);
|
|
993
|
+
if (refundEntryWithPaymentNumber?.payment_number) {
|
|
994
|
+
originalPaymentForTransaction = paymentsByPaymentNumber.get(refundEntryWithPaymentNumber.payment_number);
|
|
995
|
+
}
|
|
996
|
+
if (!originalPaymentForTransaction && primary.original_payment_refund_id) {
|
|
997
|
+
originalPaymentForTransaction = Array.from(normalPays).find(p => p.ledger_entry_no === primary.original_payment_refund_id &&
|
|
998
|
+
String(p.type || '').toLowerCase() !== 'refund');
|
|
999
|
+
}
|
|
1000
|
+
if (!originalPaymentForTransaction && primary.payment_refund_number) {
|
|
1001
|
+
const refundEntryWithRefundNumber = sortedEntries.find(e => e.payment_refund_number && String(e.payment_refund_number).trim().length > 0);
|
|
1002
|
+
if (refundEntryWithRefundNumber?.payment_refund_number) {
|
|
1003
|
+
originalPaymentForTransaction = paymentsByPaymentRefundNumber.get(refundEntryWithRefundNumber.payment_refund_number);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
if (originalPaymentForTransaction?.payment_gateway_token) {
|
|
1007
|
+
const originalCardPayment = cardPaymentsByToken.get(originalPaymentForTransaction.payment_gateway_token);
|
|
1008
|
+
transaction_id = originalCardPayment?.transaction_id || null;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
transaction_id = cardPaymentEntryForTransaction?.transaction_id || primary.transaction_id || null;
|
|
1013
|
+
}
|
|
1014
|
+
const psp_reference = transaction_id;
|
|
1015
|
+
const paymentCreatedByRaw = invoice0?.created_by || primary.created_by || sale0?.created_by || "";
|
|
1016
|
+
const paymentCreatedByUpper = String(paymentCreatedByRaw).toUpperCase();
|
|
1017
|
+
const isEmail = typeof paymentCreatedByRaw === 'string' && paymentCreatedByRaw.includes('@');
|
|
1018
|
+
const validEnumValues = ['CUSTOMER', 'SYSTEM', 'CENTRA', 'SITOO', 'STOREFRONT', 'MOTO'];
|
|
1019
|
+
const isValidEnum = validEnumValues.includes(paymentCreatedByUpper);
|
|
1020
|
+
const created_by = isEmail
|
|
1021
|
+
? paymentCreatedByRaw
|
|
1022
|
+
: isValidEnum
|
|
1023
|
+
? paymentCreatedByUpper
|
|
1024
|
+
: 'MOTO';
|
|
1025
|
+
const journey_label = null;
|
|
1026
|
+
let card_number = sortedEntries.find(e => e.source === 'card_payment' && e.card_number)?.card_number
|
|
1027
|
+
|| primary.card_number
|
|
1028
|
+
|| null;
|
|
1029
|
+
if (!card_number && g.type === 'REFUND' && cardPaymentEntryForTransaction) {
|
|
1030
|
+
card_number = cardPaymentEntryForTransaction.card_number || null;
|
|
1031
|
+
}
|
|
1032
|
+
let cardPaymentForBrand = sortedEntries.find(e => e.source === 'card_payment' && e.service_type);
|
|
1033
|
+
if (!cardPaymentForBrand && g.type === 'REFUND' && cardPaymentEntryForTransaction) {
|
|
1034
|
+
cardPaymentForBrand = cardPaymentEntryForTransaction;
|
|
1035
|
+
}
|
|
1036
|
+
const card_brand = cardPaymentForBrand?.service_type
|
|
1037
|
+
? String(cardPaymentForBrand.service_type).trim() || null
|
|
1038
|
+
: null;
|
|
1039
|
+
let status = 'CAPTURED';
|
|
1040
|
+
const isVoucher = pm === 'GIFT-CARD' ||
|
|
1041
|
+
sortedEntries.some(e => e.gift_certificate_id) ||
|
|
1042
|
+
sortedEntries.some(e => /(GVOUCHER|VOUCHER|GIFT|EGIFT)/.test(String(e.payment_method_code || '').toUpperCase())) ||
|
|
1043
|
+
sortedEntries.some(e => String(e.service_type || '').toUpperCase().includes('VOUCHER'));
|
|
1044
|
+
const cardPaymentEntry = sortedEntries.find(e => e.source === 'card_payment');
|
|
1045
|
+
if (isVoucher) {
|
|
1046
|
+
let paymentEntry = g.entries.find(e => e.source === 'payment' &&
|
|
1047
|
+
(String(e.type || '').toUpperCase() === 'PAYMENT' || String(e.type || '').toUpperCase() === 'CAPTURE'));
|
|
1048
|
+
let paymentType = paymentEntry ? String(paymentEntry.type || '').toUpperCase() : '';
|
|
1049
|
+
if (!paymentEntry && g.entries.length > 0) {
|
|
1050
|
+
const firstEntry = g.entries[0];
|
|
1051
|
+
const orderNo = firstEntry.order_no || erp_order_id;
|
|
1052
|
+
const amount = Number(firstEntry.amount || 0);
|
|
1053
|
+
const lookupKey = `${orderNo}-${amount}`;
|
|
1054
|
+
const relatedEntries = allPaymentEntriesByOrder.get(lookupKey) || [];
|
|
1055
|
+
paymentEntry = relatedEntries.find(e => e.source === 'payment' &&
|
|
1056
|
+
(String(e.type || '').toUpperCase() === 'PAYMENT' || String(e.type || '').toUpperCase() === 'CAPTURE'));
|
|
1057
|
+
paymentType = paymentEntry ? String(paymentEntry.type || '').toUpperCase() : '';
|
|
1058
|
+
}
|
|
1059
|
+
if (paymentType === 'PAYMENT' || paymentType === 'CAPTURE') {
|
|
1060
|
+
status = 'CAPTURED';
|
|
1061
|
+
}
|
|
1062
|
+
else {
|
|
1063
|
+
status = 'FAILED';
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
else {
|
|
1067
|
+
if (cardPaymentEntry?.captured === true) {
|
|
1068
|
+
status = 'CAPTURED';
|
|
1069
|
+
}
|
|
1070
|
+
else if (cardPaymentEntry?.captured === false) {
|
|
1071
|
+
status = 'FAILED';
|
|
1072
|
+
}
|
|
1073
|
+
else {
|
|
1074
|
+
const paymentType = String(primary.type || '').toUpperCase();
|
|
1075
|
+
if (paymentType === 'CAPTURE' || paymentType === 'PAYMENT') {
|
|
1076
|
+
status = 'CAPTURED';
|
|
1077
|
+
}
|
|
1078
|
+
else {
|
|
1079
|
+
status = 'CAPTURED';
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
753
1083
|
return {
|
|
754
1084
|
type: g.type,
|
|
755
1085
|
created_at,
|
|
756
1086
|
payment_method: pm,
|
|
757
1087
|
transaction_id,
|
|
758
|
-
card_number
|
|
1088
|
+
card_number,
|
|
759
1089
|
card_holder: null,
|
|
760
1090
|
card_token: null,
|
|
761
1091
|
expiration_month: null,
|
|
762
1092
|
expiration_year: null,
|
|
763
1093
|
journey_label,
|
|
764
1094
|
psp_reference,
|
|
765
|
-
card_brand
|
|
766
|
-
status
|
|
767
|
-
amount
|
|
1095
|
+
card_brand,
|
|
1096
|
+
status,
|
|
1097
|
+
amount,
|
|
768
1098
|
created_by,
|
|
769
1099
|
};
|
|
770
1100
|
});
|
|
@@ -917,8 +1247,10 @@ function transformMasterOrder(payload) {
|
|
|
917
1247
|
return_total,
|
|
918
1248
|
net_total,
|
|
919
1249
|
shipping_method,
|
|
920
|
-
payments,
|
|
921
1250
|
};
|
|
1251
|
+
if (payments.length > 0) {
|
|
1252
|
+
result.payments = payments;
|
|
1253
|
+
}
|
|
922
1254
|
if (shipmentsWithItems.length > 0) {
|
|
923
1255
|
result.shipments = shipmentsWithItems;
|
|
924
1256
|
}
|