shareneus 1.7.4 → 1.7.6

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 (212) hide show
  1. package/README.md +1 -0
  2. package/dist/accounting/invoice/invoice-pdf/invoice-pdf.service.js +18 -45
  3. package/dist/accounting/invoice/reports/excel/analysis-excel.service.js +17 -7
  4. package/dist/accounting/invoice/reports/excel/category-wise-item-excel.service.js +17 -7
  5. package/dist/accounting/invoice/reports/excel/cust-wise-sales-details.js +17 -7
  6. package/dist/accounting/invoice/reports/excel/cust-wise-sales-summary-excel.service.js +17 -7
  7. package/dist/accounting/invoice/reports/excel/invoice-wise-excel.service.js +17 -7
  8. package/dist/accounting/invoice/reports/excel/item-wise-doctor-sale-excel.service.js +17 -7
  9. package/dist/accounting/invoice/reports/excel/items-wise-sales-excel.service.js +17 -7
  10. package/dist/accounting/invoice/reports/excel/manf-wise-sales-excel.service.js +17 -7
  11. package/dist/accounting/invoice/reports/excel/operator-wise-details.js +17 -7
  12. package/dist/accounting/invoice/reports/excel/operator-wise-summary.js +17 -7
  13. package/dist/accounting/invoice/reports/excel/sa-wise-labor-sales-excel.service.js +17 -7
  14. package/dist/accounting/invoice/reports/excel/sa-wise-part-sales-excel.service.js +17 -7
  15. package/dist/accounting/invoice/reports/excel/sale-summary-excel.service.js +17 -7
  16. package/dist/accounting/invoice/reports/excel/sales-by-service-summary-excel.service.js +17 -7
  17. package/dist/accounting/invoice/reports/excel/scheduled-drug-summary-excel.service.js +17 -7
  18. package/dist/accounting/invoice/reports/excel/scheduled-drugs-excel.service.js +17 -7
  19. package/dist/accounting/invoice/unified-invoice-pdf.service.d.ts +1 -1
  20. package/dist/accounting/payment-receive/reports/excel/cust-balance-excel.service.js +17 -7
  21. package/dist/accounting/payment-receive/reports/excel/payment-receive-excel.service.js +17 -7
  22. package/dist/common/reports/excel/product-without-owner-excel.service.js +17 -7
  23. package/dist/gst/excel/GSTR-RO-excel.service.js +17 -7
  24. package/dist/gst/excel/GSTR1-excel.service.js +17 -7
  25. package/dist/gst/excel/GSTR2-excel.service.js +17 -7
  26. package/dist/gst/excel/hsn-summary.js +17 -7
  27. package/dist/gst/excel/tally-sales-import.service.js +17 -7
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js +6 -6
  30. package/dist/inventory/items/reports/excel/expiring-drugs-excel.service.js +17 -7
  31. package/dist/inventory/items/reports/excel/item-wise-mout-details.js +17 -7
  32. package/dist/inventory/items/reports/excel/item-wise-mout-summary.js +17 -7
  33. package/dist/inventory/items/reports/excel/spares-issue-excel.service.js +17 -7
  34. package/dist/inventory/items/reports/excel/stock-excel.service.js +17 -7
  35. package/dist/purchases/payment-made/reports/excel/payments-made-excel.service.js +17 -7
  36. package/dist/purchases/payment-made/reports/excel/ven-balance-excel.service.js +17 -7
  37. package/dist/services/reports/excel/insurance-expire-excel.service.js +17 -7
  38. package/dist/services/reports/excel/next-service-date-excel.service.js +17 -7
  39. package/dist/services/reports/excel/repair-orders-excel.service.js +17 -7
  40. package/dist/shared/table-section/pdf-table.header.d.ts +1 -1
  41. package/dist/shared/table-section/pdf-table.header.js +14 -2
  42. package/dist/shared/table-section/pdf-table.section.js +6 -13
  43. package/dist/{accounting/invoice/invoice-pdf.service.js → shared/transactions-pdf.service.js} +15 -15
  44. package/package.json +1 -1
  45. package/src/accounting/counter-sales/auto-sales-receipt-pdf.service.ts +569 -0
  46. package/src/accounting/counter-sales/pos-receipt-pdf.ts +577 -0
  47. package/src/accounting/counter-sales/sales-receipt-pdf.service.ts +628 -0
  48. package/src/accounting/counter-sales/sales-receipt-print.service.ts +506 -0
  49. package/src/accounting/credit-note/cn-print.service.ts +264 -0
  50. package/src/accounting/credit-note/credit-note-pdf.service.ts +602 -0
  51. package/src/accounting/credit-note/credit-note-totals.service.ts +424 -0
  52. package/src/accounting/debit-note/debit-note-pdf.service.ts +681 -0
  53. package/src/accounting/debit-note/debit-note-print.service.ts +276 -0
  54. package/src/accounting/debit-note/debit-note-totals.service.ts +361 -0
  55. package/src/accounting/invoice/hc-inv-pdf.service.ts +880 -0
  56. package/src/accounting/invoice/inv-pdf.service.ts +812 -0
  57. package/src/accounting/invoice/inv-print.service.ts +532 -0
  58. package/src/accounting/invoice/invoice-landscape-pdf.service.ts +947 -0
  59. package/src/accounting/invoice/invoice-letterhead-pdf.service.ts +813 -0
  60. package/src/accounting/invoice/invoice-pdf/invoice-pdf.service.ts +359 -0
  61. package/src/accounting/invoice/invoice-portrait-pdf.ts +972 -0
  62. package/src/accounting/invoice/invoice-print.service.ts +2906 -0
  63. package/src/accounting/invoice/invoice-total.service.ts +834 -0
  64. package/src/accounting/invoice/reports/excel/analysis-excel.service.ts +291 -0
  65. package/src/accounting/invoice/reports/excel/category-wise-item-excel.service.ts +267 -0
  66. package/src/accounting/invoice/reports/excel/cust-wise-sales-details.ts +321 -0
  67. package/src/accounting/invoice/reports/excel/cust-wise-sales-summary-excel.service.ts +300 -0
  68. package/src/accounting/invoice/reports/excel/invoice-wise-excel.service.ts +859 -0
  69. package/src/accounting/invoice/reports/excel/item-wise-doctor-sale-excel.service.ts +255 -0
  70. package/src/accounting/invoice/reports/excel/items-wise-sales-excel.service.ts +312 -0
  71. package/src/accounting/invoice/reports/excel/manf-wise-sales-excel.service.ts +273 -0
  72. package/src/accounting/invoice/reports/excel/operator-wise-details.ts +258 -0
  73. package/src/accounting/invoice/reports/excel/operator-wise-summary.ts +259 -0
  74. package/src/accounting/invoice/reports/excel/sa-wise-labor-sales-excel.service.ts +230 -0
  75. package/src/accounting/invoice/reports/excel/sa-wise-part-sales-excel.service.ts +231 -0
  76. package/src/accounting/invoice/reports/excel/sale-summary-excel.service.ts +326 -0
  77. package/src/accounting/invoice/reports/excel/sales-by-service-details-excel.service.ts +0 -0
  78. package/src/accounting/invoice/reports/excel/sales-by-service-summary-excel.service.ts +432 -0
  79. package/src/accounting/invoice/reports/excel/scheduled-drug-summary-excel.service.ts +373 -0
  80. package/src/accounting/invoice/reports/excel/scheduled-drugs-excel.service.ts +372 -0
  81. package/src/accounting/invoice/reports/pdf/analysis-pdf.service.ts +113 -0
  82. package/src/accounting/invoice/reports/pdf/category-wise-item-pdf.service.ts +107 -0
  83. package/src/accounting/invoice/reports/pdf/cust-wise-sales-details-pdf.service.ts +125 -0
  84. package/src/accounting/invoice/reports/pdf/cust-wise-sales-summary-pdf.service.ts +119 -0
  85. package/src/accounting/invoice/reports/pdf/item-wise-doctor-sale-pdf.service.ts +180 -0
  86. package/src/accounting/invoice/reports/pdf/item-wise-sales-pdf.service.ts +193 -0
  87. package/src/accounting/invoice/reports/pdf/manf-wise-sales-pdf.service.ts +188 -0
  88. package/src/accounting/invoice/reports/pdf/operator-wise-details-pdf.service.ts +118 -0
  89. package/src/accounting/invoice/reports/pdf/operator-wise-summary-pdf.ts +116 -0
  90. package/src/accounting/invoice/reports/pdf/sales-by-service-pdf.service.ts +132 -0
  91. package/src/accounting/invoice/reports/pdf/scheduled-drug-pdf.service.ts +191 -0
  92. package/src/accounting/invoice/reports/pdf/scheduled-drug-summary-pdf.service.ts +202 -0
  93. package/src/accounting/invoice/shared-inv-pdf.service.ts +787 -0
  94. package/src/accounting/invoice/unified-invoice-pdf.service.ts +937 -0
  95. package/src/accounting/payment-receive/payment-pdf.service.ts +410 -0
  96. package/src/accounting/payment-receive/payment-receipt-pdf/receipt-pdf.service.ts +470 -0
  97. package/src/accounting/payment-receive/receipt-print.service.ts +71 -0
  98. package/src/accounting/payment-receive/reports/excel/cust-balance-excel.service.ts +298 -0
  99. package/src/accounting/payment-receive/reports/excel/payment-receive-excel.service.ts +221 -0
  100. package/src/accounting/payment-receive/reports/pdf/customer-balances-pdf.service.ts +182 -0
  101. package/src/accounting/payment-receive/reports/pdf/payment-report-pdf.service.ts +116 -0
  102. package/src/aggregation/aggregation.ts +58 -0
  103. package/src/appointments/appointments/appointment-total.service.ts +298 -0
  104. package/src/appointments/consultations/consultation-fee-receipt.service.ts +407 -0
  105. package/src/appointments/consultations/consultation-full-pdf.service.ts +238 -0
  106. package/src/appointments/consultations/consultation-letterhead-pdf.service.ts +430 -0
  107. package/src/appointments/consultations/consultation-pdf.service.ts +417 -0
  108. package/src/common/reports/excel/product-without-owner-excel.service.ts +308 -0
  109. package/src/common/reports/pdf/product-without-owner-pdf.service.ts +146 -0
  110. package/src/enums/cache-enums.ts +33 -0
  111. package/src/enums/code-enums.ts +291 -0
  112. package/src/enums/country-enums.ts +9 -0
  113. package/src/enums/enums.ts +364 -0
  114. package/src/enums/industry-enums.ts +26 -0
  115. package/src/enums/treatment-enums.ts +9 -0
  116. package/src/gst/excel/GSTR-RO-excel.service.ts +926 -0
  117. package/src/gst/excel/GSTR1-excel.service.ts +313 -0
  118. package/src/gst/excel/GSTR2-excel.service.ts +314 -0
  119. package/src/gst/excel/hsn-summary.ts +314 -0
  120. package/src/gst/excel/tally-sales-import.service.ts +767 -0
  121. package/src/gst/pdf/hsn-summary-pdf.ts +176 -0
  122. package/src/index.ts +194 -0
  123. package/src/inventory/items/adjustment-pdf.service.ts +177 -0
  124. package/src/inventory/items/issue-parts-pdf.service.ts +795 -0
  125. package/src/inventory/items/item-bar-code-label-pdf.ts +194 -0
  126. package/src/inventory/items/item-detais-pdf.ts +141 -0
  127. package/src/inventory/items/item-price-for-pricelist.ts +368 -0
  128. package/src/inventory/items/reports/excel/expiring-drugs-excel.service.ts +290 -0
  129. package/src/inventory/items/reports/excel/item-wise-mout-details.ts +284 -0
  130. package/src/inventory/items/reports/excel/item-wise-mout-summary.ts +279 -0
  131. package/src/inventory/items/reports/excel/spares-issue-excel.service.ts +494 -0
  132. package/src/inventory/items/reports/excel/stock-excel.service.ts +319 -0
  133. package/src/inventory/items/reports/pdf/expiring-drugs-pdf.service.ts +172 -0
  134. package/src/inventory/items/reports/pdf/item-wise-mout-details-pdf.ts +122 -0
  135. package/src/inventory/items/reports/pdf/item-wise-mout-summary-pdf.ts +115 -0
  136. package/src/inventory/items/reports/pdf/reorder-point-pdf.service.ts +163 -0
  137. package/src/inventory/material-out/mout-pdf.service.ts +545 -0
  138. package/src/inventory/transfer-order/transfer-order-pdf.service.ts +154 -0
  139. package/src/purchases/bills/bill-pdf/bill-pdf.service.ts +211 -0
  140. package/src/purchases/bills/bill-pdf.service.ts +21 -0
  141. package/src/purchases/payment-made/reports/excel/payments-made-excel.service.ts +313 -0
  142. package/src/purchases/payment-made/reports/excel/ven-balance-excel.service.ts +307 -0
  143. package/src/purchases/payment-made/reports/pdf/vendor-balances-pdf.service.ts +114 -0
  144. package/src/purchases/purchase-order/po-totals.service.ts +343 -0
  145. package/src/purchases/purchase-order/purchase-order-pdf.service.ts +1016 -0
  146. package/src/purchases/purchase-order/purchase-order-print.service.ts +279 -0
  147. package/src/purchases/purchase-order/purchase-order-totals.service.ts +637 -0
  148. package/src/purchases/vendor-credit-note/vendor-credit-note-pdf.service.ts +1055 -0
  149. package/src/purchases/vendor-credit-note/vendor-credit-note-print.service.ts +145 -0
  150. package/src/purchases/vendor-credit-note/vendor-credit-note-totals.service.ts +399 -0
  151. package/src/purchases/vendor-debit-note/vendor-debit-note-pdf.service.ts +582 -0
  152. package/src/purchases/vendor-debit-note/vendor-debit-note-print.service.ts +295 -0
  153. package/src/purchases/vendor-debit-note/vendor-debit-note-totals.service.ts +377 -0
  154. package/src/sales/delivery-challan/dc-landscape-pdf.service.ts +922 -0
  155. package/src/sales/delivery-challan/dc-landscape-without-price-pdf.service.ts +869 -0
  156. package/src/sales/delivery-challan/dc-without-price-pdf.service.ts +505 -0
  157. package/src/sales/delivery-challan/delivery-challan-pdf.service.ts +461 -0
  158. package/src/sales/delivery-challan/delivery-challan-print.service.ts +229 -0
  159. package/src/sales/delivery-challan/delivery-challan-totals.ts +466 -0
  160. package/src/sales/sales/equipment-design-pdf.service.ts +153 -0
  161. package/src/sales/sales/pack-ship-pdf.service.ts +128 -0
  162. package/src/sales/sales/pack-ship-print.service.ts +198 -0
  163. package/src/sales/sales/sales-pdf.service.ts +658 -0
  164. package/src/sales/sales/sales-print.service.ts +376 -0
  165. package/src/sales/sales/sales-totals.service.ts +500 -0
  166. package/src/sales-receive/sales-receive-pdf.service.ts +602 -0
  167. package/src/sales-receive/sales-receive-print.service.ts +242 -0
  168. package/src/sales-receive/sales-receive-totals.service.ts +651 -0
  169. package/src/services/checklist-pdf.ts +151 -0
  170. package/src/services/checklists-pdf.ts +133 -0
  171. package/src/services/est.print-service.ts +1155 -0
  172. package/src/services/reports/excel/insurance-expire-excel.service.ts +292 -0
  173. package/src/services/reports/excel/next-service-date-excel.service.ts +317 -0
  174. package/src/services/reports/excel/repair-orders-excel.service.ts +249 -0
  175. package/src/services/reports/pdf/insurance-expire-pdf.service.ts +115 -0
  176. package/src/services/reports/pdf/next-service-date-pdf.service.ts +198 -0
  177. package/src/services/reports/pdf/repair-orders-pdf.service.ts +184 -0
  178. package/src/services/ro-pdf.service.ts +1917 -0
  179. package/src/services/ro-print-service.ts +881 -0
  180. package/src/services/ro-totals.service.ts +1314 -0
  181. package/src/services/separate-wo-print.service.ts +396 -0
  182. package/src/services/service-history-pdf.service.ts +145 -0
  183. package/src/services/service-price-for-pricelist.ts +649 -0
  184. package/src/services/technician-pdf.service.ts +234 -0
  185. package/src/services/technician-print.service.ts +95 -0
  186. package/src/shared/header-footer-section/pdf-header-footer.section.ts +519 -0
  187. package/src/shared/header-footer-section/pdf-shared.utils.ts +46 -0
  188. package/src/shared/math-operations.ts +208 -0
  189. package/src/shared/party-details-section/pdf-party-details.section.ts +602 -0
  190. package/src/shared/shared-pdf.service.ts +3042 -0
  191. package/src/shared/shared-print.service.ts +879 -0
  192. package/src/shared/table-section/pdf-table.config.ts +8 -0
  193. package/src/shared/table-section/pdf-table.header.ts +396 -0
  194. package/src/shared/table-section/pdf-table.row.ts +248 -0
  195. package/src/shared/table-section/pdf-table.section.ts +447 -0
  196. package/src/shared/totals-section/pdf-totals.section.ts +921 -0
  197. package/src/shared/transactions-pdf.service.ts +191 -0
  198. package/src/shared/util.ts +101 -0
  199. package/src/tasks/meetings/meeting-pdf.ts +410 -0
  200. package/src/tasks/tasks/task-pdf.service.ts +238 -0
  201. package/src/tasks/tasks/task-reports-pdf.service.ts +313 -0
  202. package/src/tax/index.ts +86 -0
  203. package/src/tax/tax-calculator.ts +1025 -0
  204. package/src/tax/tax.types.ts +535 -0
  205. package/src/transaction-calculations/discounts-distribution.ts +343 -0
  206. package/src/transaction-calculations/index.ts +3 -0
  207. package/src/transaction-calculations/total-calculation.ts +443 -0
  208. package/src/transaction-calculations/transaction-calculation-engine.ts +903 -0
  209. package/src/utils/my-date.ts +111 -0
  210. package/src/utils/tr-utils.ts +104 -0
  211. package/tsconfig.json +2 -2
  212. /package/dist/{accounting/invoice/invoice-pdf.service.d.ts → shared/transactions-pdf.service.d.ts} +0 -0
@@ -0,0 +1,649 @@
1
+ import { CalcDiscPrice } from "../shared/math-operations";
2
+ import { IsNotNull, IsNull } from "../shared/util";
3
+
4
+ type QuantityTier = {
5
+ MinQty?: number | null;
6
+ Price?: number | null;
7
+ Disc?: number | null;
8
+ };
9
+
10
+ type PriceListDoc = {
11
+ _id?: any;
12
+ TType: "SALE" | "PUR";
13
+ PrCalc: "FIXED" | "DISC" | "FORMULA";
14
+ BasedOn?: "SP" | "PC" | "PCBD" | "PCAD";
15
+ Fixed?: { Price?: number | null };
16
+ CalcDisc?: { Mark?: boolean; Perc?: number | null };
17
+ Round?: string | null;
18
+ Deci?: number | null;
19
+ QtyTiers?: QuantityTier[];
20
+ Currency?: string | null;
21
+ };
22
+
23
+ const ROUND_VALUES = new Set(["-1", "0", "0.99", "0.50", "0.49", "Dec"]);
24
+
25
+ export function ApplyServicePriceListToProcedure(
26
+ procedure: any,
27
+ priceList: PriceListDoc | null,
28
+ quantity: number = 1
29
+ ): any {
30
+ if (!procedure || !priceList) {
31
+ return procedure;
32
+ }
33
+
34
+ const resolvedQty = Math.max(1, Number(quantity) || 1);
35
+ const warnings: string[] = [];
36
+
37
+ if (priceList.TType === "SALE" && procedure.Sale) {
38
+ const basePrice = IsNotNull(procedure.Sale.Price) ? procedure.Sale.Price : null;
39
+ const { value, tierWarnings, appliedTier, appliedCalc } = evaluatePriceBlock(
40
+ basePrice,
41
+ procedure.Pur,
42
+ priceList,
43
+ resolvedQty
44
+ );
45
+
46
+ procedure.Sale.PriceBeforePriceList = basePrice;
47
+ if (value != null) {
48
+ procedure.Sale.Price = value;
49
+ procedure.Sale.AppliedPriceListId = priceList._id ?? null;
50
+ procedure.Sale.AppliedPriceTier = appliedTier ?? undefined;
51
+ procedure.Sale.AppliedPriceCalc = appliedCalc ?? undefined;
52
+ }
53
+ if (tierWarnings.length > 0) {
54
+ warnings.push(...tierWarnings);
55
+ }
56
+ }
57
+
58
+ if (priceList.TType === "PUR" && procedure.Pur) {
59
+ const baseCost = IsNotNull(procedure.Pur.Cost) ? procedure.Pur.Cost : null;
60
+ const { value, tierWarnings, appliedTier, appliedCalc } = evaluatePriceBlock(
61
+ baseCost,
62
+ procedure.Pur,
63
+ priceList,
64
+ resolvedQty
65
+ );
66
+
67
+ procedure.Pur.CostBeforePriceList = baseCost;
68
+ if (value != null) {
69
+ procedure.Pur.Cost = value;
70
+ procedure.Pur.AppliedPriceListId = priceList._id ?? null;
71
+ procedure.Pur.AppliedPriceTier = appliedTier ?? undefined;
72
+ procedure.Pur.AppliedPriceCalc = appliedCalc ?? undefined;
73
+ }
74
+ if (tierWarnings.length > 0) {
75
+ warnings.push(...tierWarnings);
76
+ }
77
+ }
78
+
79
+ if (warnings.length > 0) {
80
+ procedure.PriceListWarnings = warnings;
81
+ }
82
+
83
+ return procedure;
84
+ }
85
+
86
+ export function ApplyServicePriceListToOpcode(
87
+ opcode: any,
88
+ priceList: PriceListDoc | null,
89
+ quantity: number = 1
90
+ ): any {
91
+ if (!opcode || !priceList) {
92
+ return opcode;
93
+ }
94
+
95
+ const resolvedQty = Math.max(1, Number(quantity) || 1);
96
+ const warnings: string[] = [];
97
+
98
+ if (Array.isArray(opcode.Prices) && opcode.Prices.length > 0) {
99
+ opcode.Prices = opcode.Prices.map((pricing: any) => {
100
+ const cloned: any = { ...pricing };
101
+ if (cloned.Sale) {
102
+ cloned.Sale = { ...cloned.Sale };
103
+ }
104
+ if (cloned.Pur) {
105
+ cloned.Pur = { ...cloned.Pur };
106
+ }
107
+
108
+ if (
109
+ priceList.TType === "SALE" &&
110
+ cloned.Sale &&
111
+ (!cloned.PS || cloned.PS === "S" || cloned.PS === "SP")
112
+ ) {
113
+ const basePrice = IsNotNull(cloned.Sale.Price) ? cloned.Sale.Price : null;
114
+ const { value, tierWarnings, appliedTier, appliedCalc } = evaluatePriceBlock(
115
+ basePrice,
116
+ cloned.Pur,
117
+ priceList,
118
+ resolvedQty
119
+ );
120
+ cloned.Sale.PriceBeforePriceList = basePrice;
121
+ if (value != null) {
122
+ cloned.Sale.Price = value;
123
+ cloned.Sale.AppliedPriceListId = priceList._id ?? null;
124
+ cloned.Sale.AppliedPriceTier = appliedTier ?? undefined;
125
+ cloned.Sale.AppliedPriceCalc = appliedCalc ?? undefined;
126
+ }
127
+ if (tierWarnings.length > 0) {
128
+ warnings.push(...tierWarnings);
129
+ }
130
+ }
131
+
132
+ if (
133
+ priceList.TType === "PUR" &&
134
+ cloned.Pur &&
135
+ (!cloned.PS || cloned.PS === "P" || cloned.PS === "SP")
136
+ ) {
137
+ const baseCost = IsNotNull(cloned.Pur.Cost) ? cloned.Pur.Cost : null;
138
+ const { value, tierWarnings, appliedTier, appliedCalc } = evaluatePriceBlock(
139
+ baseCost,
140
+ cloned.Pur,
141
+ priceList,
142
+ resolvedQty
143
+ );
144
+ cloned.Pur.CostBeforePriceList = baseCost;
145
+ if (value != null) {
146
+ cloned.Pur.Cost = value;
147
+ cloned.Pur.AppliedPriceListId = priceList._id ?? null;
148
+ cloned.Pur.AppliedPriceTier = appliedTier ?? undefined;
149
+ cloned.Pur.AppliedPriceCalc = appliedCalc ?? undefined;
150
+ }
151
+ if (tierWarnings.length > 0) {
152
+ warnings.push(...tierWarnings);
153
+ }
154
+ }
155
+
156
+ return cloned;
157
+ });
158
+ }
159
+
160
+ if (warnings.length > 0) {
161
+ opcode.PriceListWarnings = warnings;
162
+ }
163
+
164
+ return opcode;
165
+ }
166
+
167
+ function evaluatePriceBlock(
168
+ baseValue: number | null,
169
+ purchaseBlock: any,
170
+ priceList: PriceListDoc,
171
+ quantity: number
172
+ ): {
173
+ value: number | null;
174
+ tierWarnings: string[];
175
+ appliedTier?: any;
176
+ appliedCalc?: string;
177
+ } {
178
+ const warnings: string[] = [];
179
+
180
+ const tier = resolveTier(quantity, priceList.QtyTiers);
181
+ if (tier) {
182
+ const tierValue = applyTierAdjustment(baseValue, purchaseBlock, tier, priceList);
183
+ const rounded = applyRounding(priceList, tierValue);
184
+ return {
185
+ value: rounded,
186
+ tierWarnings: warnings,
187
+ appliedTier: buildAppliedTierMeta(tier),
188
+ appliedCalc: "TIER",
189
+ };
190
+ }
191
+
192
+ switch (priceList.PrCalc) {
193
+ case "FIXED": {
194
+ if (IsNull(priceList.Fixed?.Price)) {
195
+ warnings.push("Fixed price list selected but Fixed.Price is not configured.");
196
+ return { value: null, tierWarnings: warnings };
197
+ }
198
+ const rounded = applyRounding(priceList, priceList.Fixed?.Price ?? null);
199
+ return { value: rounded, tierWarnings: warnings, appliedCalc: "FIXED" };
200
+ }
201
+ case "DISC": {
202
+ const base = resolveDiscountBase(priceList, baseValue, purchaseBlock);
203
+ if (base == null) {
204
+ warnings.push("Discount price list requires a base value but none was available.");
205
+ return { value: null, tierWarnings: warnings };
206
+ }
207
+ const perc = priceList.CalcDisc?.Perc ?? 0;
208
+ const mark = priceList.CalcDisc?.Mark ?? false;
209
+ const price = CalcDiscPrice(base, perc, mark);
210
+ return {
211
+ value: applyRounding(priceList, price),
212
+ tierWarnings: warnings,
213
+ appliedCalc: "DISCOUNT",
214
+ };
215
+ }
216
+ case "FORMULA": {
217
+ warnings.push("Formula-based service price lists are not implemented.");
218
+ return { value: null, tierWarnings: warnings };
219
+ }
220
+ default:
221
+ return { value: null, tierWarnings: warnings };
222
+ }
223
+ }
224
+
225
+ function applyTierAdjustment(
226
+ baseValue: number | null,
227
+ purchaseBlock: any,
228
+ tier: QuantityTier,
229
+ priceList: PriceListDoc
230
+ ): number | null {
231
+ if (IsNotNull(tier.Price)) {
232
+ return tier.Price ?? null;
233
+ }
234
+
235
+ const targetBase = resolveDiscountBase(priceList, baseValue, purchaseBlock);
236
+ if (targetBase == null || IsNull(tier.Disc)) {
237
+ return baseValue;
238
+ }
239
+
240
+ return CalcDiscPrice(targetBase, tier.Disc ?? 0, false);
241
+ }
242
+
243
+ function resolveDiscountBase(
244
+ priceList: PriceListDoc,
245
+ baseValue: number | null,
246
+ purchaseBlock: any
247
+ ): number | null {
248
+ const basedOn = priceList.BasedOn ?? "SP";
249
+ switch (basedOn) {
250
+ case "SP":
251
+ return baseValue;
252
+ case "PC":
253
+ case "PCBD":
254
+ case "PCAD":
255
+ if (purchaseBlock && IsNotNull(purchaseBlock.Cost)) {
256
+ return purchaseBlock.Cost;
257
+ }
258
+ return null;
259
+ default:
260
+ return baseValue;
261
+ }
262
+ }
263
+
264
+ function applyRounding(priceList: PriceListDoc, value: number | null): number | null {
265
+ if (value == null) {
266
+ return null;
267
+ }
268
+ if (!priceList.Round || !ROUND_VALUES.has(priceList.Round)) {
269
+ return value;
270
+ }
271
+
272
+ switch (priceList.Round) {
273
+ case "-1":
274
+ return value;
275
+ case "0":
276
+ return Math.round(value);
277
+ case "0.99":
278
+ return Math.floor(value) + 0.99;
279
+ case "0.50":
280
+ return Math.round(value * 2) / 2;
281
+ case "0.49":
282
+ return Math.floor(value) + 0.49;
283
+ case "Dec":
284
+ if (IsNotNull(priceList.Deci)) {
285
+ const factor = Math.pow(10, priceList.Deci ?? 0);
286
+ return Math.round(value * factor) / factor;
287
+ }
288
+ return value;
289
+ default:
290
+ return value;
291
+ }
292
+ }
293
+
294
+ function resolveTier(quantity: number, tiers?: QuantityTier[] | null): QuantityTier | null {
295
+ if (!Array.isArray(tiers) || tiers.length === 0) {
296
+ return null;
297
+ }
298
+
299
+ const sorted = [...tiers]
300
+ .filter((tier) => IsNotNull(tier?.MinQty))
301
+ .sort((a, b) => (a?.MinQty ?? 0) - (b?.MinQty ?? 0));
302
+
303
+ let selected: QuantityTier | null = null;
304
+ for (const tier of sorted) {
305
+ if (quantity >= (tier?.MinQty ?? 0)) {
306
+ selected = tier ?? null;
307
+ } else {
308
+ break;
309
+ }
310
+ }
311
+ return selected;
312
+ }
313
+
314
+ function buildAppliedTierMeta(tier: QuantityTier | null) {
315
+ if (!tier) {
316
+ return undefined;
317
+ }
318
+ return {
319
+ MinQty: IsNotNull(tier.MinQty) ? tier.MinQty : null,
320
+ Price: IsNotNull(tier.Price) ? tier.Price : null,
321
+ Disc: IsNotNull(tier.Disc) ? tier.Disc : null,
322
+ };
323
+ }
324
+
325
+ /**
326
+ * Applies price list to Service with support for equipment-based pricing (FitPriceCosts)
327
+ * Handles both simple services (healthcare) and equipment-based services (automotive, HVAC, medical, industrial, etc.)
328
+ */
329
+ export function GetServicePriceForPriceList(
330
+ service: any,
331
+ priceList: PriceListDoc | null,
332
+ equipment: any = null,
333
+ quantity: number = 1
334
+ ): any {
335
+ if (!service || !priceList) {
336
+ return service;
337
+ }
338
+
339
+ const resolvedQty = Math.max(1, Number(quantity) || 1);
340
+ const warnings: string[] = [];
341
+ const priceListId = priceList?._id?.toString?.();
342
+
343
+ // Step 1: Determine base pricing (equipment-specific or simple)
344
+ let basePrice: any = null;
345
+ let basePriceSource: string = "BASE";
346
+ let matchedFitPriceCost: any = null;
347
+
348
+ if (priceList.TType === "SALE" && service.Sale) {
349
+ // Check if equipment-based service (FitPriceCosts) or simple service (Price)
350
+ if (service.FitPriceCosts && service.FitPriceCosts.length > 0) {
351
+ // Equipment-based service - need equipment information
352
+ if (!equipment) {
353
+ warnings.push("Equipment information required for equipment-based service pricing.");
354
+ service.PriceListWarnings = warnings;
355
+ return service;
356
+ }
357
+
358
+ // Find matching FitPriceCost for equipment
359
+ matchedFitPriceCost = findMatchingFitPriceCost(service.FitPriceCosts, equipment);
360
+ if (!matchedFitPriceCost || !matchedFitPriceCost.Sale) {
361
+ warnings.push(`No pricing found for ${equipment.Make || 'Unknown'} ${equipment.Model || ''}`);
362
+ service.PriceListWarnings = warnings;
363
+ return service;
364
+ }
365
+
366
+ basePrice = matchedFitPriceCost.Sale;
367
+ basePriceSource = "FIT";
368
+ } else if (IsNotNull(service.Sale.Price)) {
369
+ // Simple service - single base price
370
+ basePrice = {
371
+ Price: service.Sale.Price,
372
+ FSale: service.Sale.FSale,
373
+ BHours: service.Sale.BHours,
374
+ Dur: service.Sale.Dur,
375
+ };
376
+ basePriceSource = "BASE";
377
+ } else {
378
+ warnings.push("No base pricing configured for service.");
379
+ service.PriceListWarnings = warnings;
380
+ return service;
381
+ }
382
+
383
+ // Step 2: Check for item-specific price list overrides (SalePrices)
384
+ // For equipment services: Look inside matched FitPriceCost
385
+ // For simple services: Look at root level
386
+ let salePriceOverride: any = null;
387
+ if (priceListId) {
388
+ if (matchedFitPriceCost && Array.isArray(matchedFitPriceCost.Sale?.SalePrices)) {
389
+ // Equipment service - check nested SalePrices inside matched FitPriceCost
390
+ salePriceOverride = matchedFitPriceCost.Sale.SalePrices.find(
391
+ (sp: any) => sp?.PrListId && sp.PrListId.toString() === priceListId
392
+ );
393
+ } else if (Array.isArray(service.SalePrices)) {
394
+ // Simple service - check root-level SalePrices
395
+ salePriceOverride = service.SalePrices.find(
396
+ (sp: any) => sp?.PrListId && sp.PrListId.toString() === priceListId
397
+ );
398
+ }
399
+ }
400
+
401
+ if (salePriceOverride) {
402
+ // Apply item-specific override
403
+ const originalPrice = basePrice.Price;
404
+ service.Sale.PriceBeforePriceList = originalPrice;
405
+
406
+ // Check for quantity tiers in override
407
+ const tierList = salePriceOverride.QtyTiers ?? priceList.QtyTiers;
408
+ const tier = resolveTier(resolvedQty, tierList);
409
+
410
+ if (tier) {
411
+ // Apply tier pricing
412
+ const tierValue = applyTierAdjustmentForSale(basePrice, tier);
413
+ service.Sale.Price = applyRounding(priceList, tierValue);
414
+ service.Sale.AppliedPriceListId = priceList._id ?? null;
415
+ service.Sale.AppliedPriceTier = buildAppliedTierMeta(tier);
416
+ service.Sale.AppliedPriceCalc = "TIER_OVERRIDE";
417
+ service.Sale.PriceSource = basePriceSource;
418
+ } else if (IsNotNull(salePriceOverride.Price)) {
419
+ // Fixed price override
420
+ service.Sale.Price = applyRounding(priceList, salePriceOverride.Price);
421
+ service.Sale.AppliedPriceListId = priceList._id ?? null;
422
+ service.Sale.AppliedPriceCalc = "FIXED_OVERRIDE";
423
+ service.Sale.PriceSource = basePriceSource;
424
+ } else if (IsNotNull(salePriceOverride.Disc)) {
425
+ // Percentage discount override
426
+ const discountedPrice = CalcDiscPrice(originalPrice, salePriceOverride.Disc, false);
427
+ service.Sale.Price = applyRounding(priceList, discountedPrice);
428
+ service.Sale.AppliedPriceListId = priceList._id ?? null;
429
+ service.Sale.AppliedPriceCalc = "DISCOUNT_OVERRIDE";
430
+ service.Sale.PriceSource = basePriceSource;
431
+ }
432
+
433
+ // Apply other override fields
434
+ if (IsNotNull(salePriceOverride.BHours)) {
435
+ service.Sale.BHours = salePriceOverride.BHours;
436
+ }
437
+ if (IsNotNull(salePriceOverride.Dur)) {
438
+ service.Sale.Dur = salePriceOverride.Dur;
439
+ }
440
+ if (IsNotNull(salePriceOverride.FSale)) {
441
+ service.Sale.FSale = salePriceOverride.FSale;
442
+ }
443
+ } else {
444
+ // Step 3: Apply global price list calculation
445
+ const originalPrice = basePrice.Price;
446
+ service.Sale.PriceBeforePriceList = originalPrice;
447
+
448
+ const tier = resolveTier(resolvedQty, priceList.QtyTiers);
449
+ if (tier) {
450
+ const tierValue = applyTierAdjustmentForSale(basePrice, tier);
451
+ service.Sale.Price = applyRounding(priceList, tierValue);
452
+ service.Sale.AppliedPriceListId = priceList._id ?? null;
453
+ service.Sale.AppliedPriceTier = buildAppliedTierMeta(tier);
454
+ service.Sale.AppliedPriceCalc = "TIER";
455
+ service.Sale.PriceSource = basePriceSource;
456
+ } else {
457
+ // Apply global calculation
458
+ const { value, tierWarnings, appliedCalc } = evaluatePriceBlock(
459
+ originalPrice,
460
+ service.Pur,
461
+ priceList,
462
+ resolvedQty
463
+ );
464
+
465
+ if (value != null) {
466
+ service.Sale.Price = value;
467
+ service.Sale.AppliedPriceListId = priceList._id ?? null;
468
+ service.Sale.AppliedPriceCalc = appliedCalc ?? "GLOBAL";
469
+ service.Sale.PriceSource = basePriceSource;
470
+ }
471
+
472
+ if (tierWarnings.length > 0) {
473
+ warnings.push(...tierWarnings);
474
+ }
475
+ }
476
+ }
477
+ }
478
+
479
+ // Step 4: Handle Purchase pricing (similar logic for Pur side)
480
+ if (priceList.TType === "PUR" && service.Pur) {
481
+ let baseCost: any = null;
482
+ let baseCostSource: string = "BASE";
483
+
484
+ if (service.FitPriceCosts && service.FitPriceCosts.length > 0) {
485
+ // Equipment-based service - need equipment information
486
+ if (!equipment) {
487
+ warnings.push("Equipment information required for equipment-based service costing.");
488
+ service.PriceListWarnings = warnings;
489
+ return service;
490
+ }
491
+
492
+ // Reuse the matchedFitPriceCost from Sale section if available
493
+ const fitPriceCostEntry = matchedFitPriceCost || findMatchingFitPriceCost(service.FitPriceCosts, equipment);
494
+ if (!fitPriceCostEntry || !fitPriceCostEntry.Pur) {
495
+ warnings.push(`No costing found for ${equipment.Make || 'Unknown'} ${equipment.Model || ''}`);
496
+ service.PriceListWarnings = warnings;
497
+ return service;
498
+ }
499
+
500
+ baseCost = fitPriceCostEntry.Pur;
501
+ baseCostSource = "FIT";
502
+ matchedFitPriceCost = fitPriceCostEntry; // Store for later use
503
+ } else if (IsNotNull(service.Pur.Cost)) {
504
+ baseCost = {
505
+ Cost: service.Pur.Cost,
506
+ FCost: service.Pur.FCost,
507
+ };
508
+ baseCostSource = "BASE";
509
+ }
510
+
511
+ if (baseCost) {
512
+ // Check for purchase cost overrides
513
+ // For equipment services: Look inside matched FitPriceCost
514
+ // For simple services: Look at root level
515
+ let purchaseCostOverride: any = null;
516
+ if (priceListId) {
517
+ if (matchedFitPriceCost && Array.isArray(matchedFitPriceCost.Pur?.PurchaseCosts)) {
518
+ // Equipment service - check nested PurchaseCosts inside matched FitPriceCost
519
+ purchaseCostOverride = matchedFitPriceCost.Pur.PurchaseCosts.find(
520
+ (pc: any) => pc?.PrListId && pc.PrListId.toString() === priceListId
521
+ );
522
+ } else if (Array.isArray(service.PurchaseCosts)) {
523
+ // Simple service - check root-level PurchaseCosts
524
+ purchaseCostOverride = service.PurchaseCosts.find(
525
+ (pc: any) => pc?.PrListId && pc.PrListId.toString() === priceListId
526
+ );
527
+ }
528
+ }
529
+
530
+ const originalCost = baseCost.Cost;
531
+ service.Pur.CostBeforePriceList = originalCost;
532
+
533
+ if (purchaseCostOverride) {
534
+ if (IsNotNull(purchaseCostOverride.Cost)) {
535
+ service.Pur.Cost = applyRounding(priceList, purchaseCostOverride.Cost);
536
+ service.Pur.AppliedPriceListId = priceList._id ?? null;
537
+ service.Pur.CostSource = baseCostSource;
538
+ } else if (IsNotNull(purchaseCostOverride.Disc)) {
539
+ const discountedCost = CalcDiscPrice(originalCost, purchaseCostOverride.Disc, false);
540
+ service.Pur.Cost = applyRounding(priceList, discountedCost);
541
+ service.Pur.AppliedPriceListId = priceList._id ?? null;
542
+ service.Pur.CostSource = baseCostSource;
543
+ }
544
+ } else {
545
+ const { value, tierWarnings } = evaluatePriceBlock(
546
+ originalCost,
547
+ service.Pur,
548
+ priceList,
549
+ resolvedQty
550
+ );
551
+
552
+ if (value != null) {
553
+ service.Pur.Cost = value;
554
+ service.Pur.AppliedPriceListId = priceList._id ?? null;
555
+ service.Pur.CostSource = baseCostSource;
556
+ }
557
+
558
+ if (tierWarnings.length > 0) {
559
+ warnings.push(...tierWarnings);
560
+ }
561
+ }
562
+ }
563
+ }
564
+
565
+ if (warnings.length > 0) {
566
+ service.PriceListWarnings = warnings;
567
+ }
568
+
569
+ return service;
570
+ }
571
+
572
+ /**
573
+ * Finds the matching FitPriceCost entry for a given equipment (automotive, HVAC, medical, industrial, etc.)
574
+ * Tries most specific match first (Make + Model + Variant), then progressively less specific
575
+ */
576
+ function findMatchingFitPriceCost(fitPriceCosts: any[], equipment: any): any | null {
577
+ if (!Array.isArray(fitPriceCosts) || fitPriceCosts.length === 0 || !equipment) {
578
+ return null;
579
+ }
580
+
581
+ const equipmentMakeId = equipment.Make_Id?.toString?.();
582
+ const equipmentModelId = equipment.Model_Id?.toString?.();
583
+ const equipmentVarId = equipment.Var_Id?.toString?.() || equipment.Variant_Id?.toString?.();
584
+
585
+ // Try Make + Model + Variant
586
+ if (equipmentMakeId && equipmentModelId && equipmentVarId) {
587
+ const match = fitPriceCosts.find((fpc: any) => {
588
+ if (!Array.isArray(fpc.Fit) || fpc.Fit.length === 0) return false;
589
+ return fpc.Fit.some((fit: any) =>
590
+ fit.Make_Id?.toString?.() === equipmentMakeId &&
591
+ fit.Model_Id?.toString?.() === equipmentModelId &&
592
+ fit.Var_Id?.toString?.() === equipmentVarId
593
+ );
594
+ });
595
+ if (match) return match;
596
+ }
597
+
598
+ // Try Make + Model
599
+ if (equipmentMakeId && equipmentModelId) {
600
+ const match = fitPriceCosts.find((fpc: any) => {
601
+ if (!Array.isArray(fpc.Fit) || fpc.Fit.length === 0) return false;
602
+ return fpc.Fit.some((fit: any) =>
603
+ fit.Make_Id?.toString?.() === equipmentMakeId &&
604
+ fit.Model_Id?.toString?.() === equipmentModelId &&
605
+ !fit.Var_Id
606
+ );
607
+ });
608
+ if (match) return match;
609
+ }
610
+
611
+ // Try Make only
612
+ if (equipmentMakeId) {
613
+ const match = fitPriceCosts.find((fpc: any) => {
614
+ if (!Array.isArray(fpc.Fit) || fpc.Fit.length === 0) return false;
615
+ return fpc.Fit.some((fit: any) =>
616
+ fit.Make_Id?.toString?.() === equipmentMakeId &&
617
+ !fit.Model_Id
618
+ );
619
+ });
620
+ if (match) return match;
621
+ }
622
+
623
+ // Try generic (no Make/Model/Variant specified = applies to all)
624
+ const genericMatch = fitPriceCosts.find((fpc: any) => {
625
+ if (!Array.isArray(fpc.Fit) || fpc.Fit.length === 0) return true; // No fit = applies to all
626
+ return fpc.Fit.some((fit: any) => !fit.Make_Id && !fit.Model_Id && !fit.Var_Id);
627
+ });
628
+
629
+ return genericMatch || null;
630
+ }
631
+
632
+ /**
633
+ * Applies tier adjustment to sale pricing
634
+ */
635
+ function applyTierAdjustmentForSale(
636
+ baseSale: any,
637
+ tier: QuantityTier
638
+ ): number | null {
639
+ if (IsNotNull(tier.Price)) {
640
+ return tier.Price ?? null;
641
+ }
642
+
643
+ if (IsNotNull(tier.Disc)) {
644
+ const basePrice = baseSale?.Price ?? 0;
645
+ return CalcDiscPrice(basePrice, tier.Disc ?? 0, false);
646
+ }
647
+
648
+ return baseSale?.Price ?? null;
649
+ }