@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.
Files changed (30) hide show
  1. package/dist/cjs/contracts/order/payment.schema.json +5 -5
  2. package/dist/cjs/dist-dereferenced/messages/order/delivered.js +1 -1
  3. package/dist/cjs/dist-dereferenced/messages/order/new.js +1 -1
  4. package/dist/cjs/dist-dereferenced/messages/order/processed.js +1 -1
  5. package/dist/cjs/dist-dereferenced/messages/order/return-initiated.js +1 -1
  6. package/dist/cjs/dist-dereferenced/messages/order/returned.js +1 -1
  7. package/dist/cjs/dist-dereferenced/messages/order/shipped.js +1 -1
  8. package/dist/cjs/dist-dereferenced/order/orderMain.js +1 -1
  9. package/dist/cjs/dist-dereferenced/order/payment.js +1 -1
  10. package/dist/cjs/src-public/bcOrder.js +403 -71
  11. package/dist/cjs/src-public/dato.js +244 -10
  12. package/dist/types/dist-dereferenced/messages/order/delivered.d.ts +4 -4
  13. package/dist/types/dist-dereferenced/messages/order/new.d.ts +4 -4
  14. package/dist/types/dist-dereferenced/messages/order/processed.d.ts +4 -4
  15. package/dist/types/dist-dereferenced/messages/order/return-initiated.d.ts +4 -4
  16. package/dist/types/dist-dereferenced/messages/order/returned.d.ts +4 -4
  17. package/dist/types/dist-dereferenced/messages/order/shipped.d.ts +4 -4
  18. package/dist/types/dist-dereferenced/order/index.d.ts +4 -4
  19. package/dist/types/dist-dereferenced/order/orderMain.d.ts +4 -4
  20. package/dist/types/dist-dereferenced/order/payment.d.ts +4 -4
  21. package/dist/types/dist-types/messages/order/delivered.d.ts +2 -2
  22. package/dist/types/dist-types/messages/order/new.d.ts +2 -2
  23. package/dist/types/dist-types/messages/order/processed.d.ts +2 -2
  24. package/dist/types/dist-types/messages/order/return-initiated.d.ts +2 -2
  25. package/dist/types/dist-types/messages/order/returned.d.ts +2 -2
  26. package/dist/types/dist-types/messages/order/shipped.d.ts +2 -2
  27. package/dist/types/dist-types/order/orderMain.d.ts +2 -2
  28. package/dist/types/dist-types/order/payment.d.ts +2 -2
  29. package/dist/types/src-public/dato.d.ts +27 -1
  30. 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 || [])[0];
103
- const invoice0 = (payload.invoices || [])[0];
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.map((i) => i.system_created_at).filter(Boolean);
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 orderedAtRaw = ordered_at_candidates.length > 0
116
- ? ordered_at_candidates.sort()[0]
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 dateStr = typeof orderedAtRaw === 'string' && orderedAtRaw.includes('T')
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.gift_message).trim().length > 0) ||
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
- let invoicesNetMinor = toMinorUnits((payload.invoices || []).reduce((acc, inv) => acc + (inv.amount_excl_vat || 0), 0));
380
- let invoicesGrossMinor = toMinorUnits((payload.invoices || []).reduce((acc, inv) => acc + (inv.amount_incl_vat || 0), 0));
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.sale_lines || []).filter((l) => {
383
- const lineNo = Number(l?.line_no || -1);
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 && (sale0.amount_excl_vat || sale0.amount_incl_vat)) {
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
- const lineNo = Number(l?.line_no || -1);
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
- const isDelivery = itemNo.toUpperCase() === 'X DELCHR';
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
- const add = sum > 0 ? sum : (hasQualifying ? (cm?.amount_incl_vat || 0) : 0);
568
- return acc + add;
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
- const isDelivery = itemNo.toUpperCase() === 'X DELCHR';
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
- const add = sum > 0 ? sum : (hasQualifying ? (sale?.amount_incl_vat || sale?.amount_including_vat || 0) : 0);
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
- const itemNo = String(l?.item_no || l?.no || "").toUpperCase();
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
- if (/(GVOUCHER|VOUCHER|GIFT|EGIFT)/.test(c) || String(card?.service_type || '').toUpperCase().includes('VOUCHER'))
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 (c.includes('ADYEN') && !card?.card_number)
692
- return 'APPLE-PAY';
693
- if (c.includes('APPLE') || String(card?.service_type || '').toUpperCase().includes('APPLE'))
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 (card?.transaction_id || card?.card_number)
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.ledger_entry_no ? String(p.ledger_entry_no) : undefined,
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, idx) => {
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
- const logicalType = isRefund ? 'REFUND' : 'CHARGE';
734
- const keyBase = p.transaction_id || `${erp_order_id}-${logicalType}-${idx + 1}`;
735
- const key = `${logicalType}:${keyBase}`;
736
- const g = groupsMap.get(key) || { entries: [], type: logicalType, key };
737
- g.entries.push(p);
738
- groupsMap.set(key, g);
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, i) => {
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 amount = Math.max(...g.entries.map(e => Number(e.amount || 0)), 0);
747
- const any = g.entries[0] || {};
748
- const pm = mapPaymentMethod(any.payment_method_code, any);
749
- const transaction_id = (any.transaction_id) || `${erp_order_id}-PAY-${i + 1}`;
750
- const psp_reference = any.psp_reference || transaction_id;
751
- const created_by = 'CUSTOMER';
752
- const journey_label = journeyFromCreatedBy(invoice0?.created_by || sale0?.created_by);
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: any.card_number || null,
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: null,
766
- status: 'CAPTURED',
767
- amount: money(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
  }