@adtrackify/at-service-common 4.0.1 → 4.0.2

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 (759) hide show
  1. package/dist/cjs/__tests__/clients/acuity-client.spec.d.ts +1 -1
  2. package/dist/cjs/__tests__/clients/acuity-client.spec.js +43 -43
  3. package/dist/cjs/__tests__/clients/cross-platform-compression.spec.d.ts +1 -1
  4. package/dist/cjs/__tests__/clients/cross-platform-compression.spec.js +354 -354
  5. package/dist/cjs/__tests__/clients/dynamodb-client.spec.d.ts +1 -1
  6. package/dist/cjs/__tests__/clients/dynamodb-client.spec.js +194 -194
  7. package/dist/cjs/__tests__/clients/sqs-bundled-client.spec.d.ts +1 -1
  8. package/dist/cjs/__tests__/clients/sqs-bundled-client.spec.js +931 -931
  9. package/dist/cjs/__tests__/clients/sqs-bundling-contracts.spec.d.ts +1 -1
  10. package/dist/cjs/__tests__/clients/sqs-bundling-contracts.spec.js +563 -563
  11. package/dist/cjs/__tests__/clients/sqs-client.spec.d.ts +1 -1
  12. package/dist/cjs/__tests__/clients/sqs-client.spec.js +191 -191
  13. package/dist/cjs/__tests__/clients/sqs-unbundle.spec.d.ts +1 -1
  14. package/dist/cjs/__tests__/clients/sqs-unbundle.spec.js +1357 -1357
  15. package/dist/cjs/__tests__/db/contact-enrichments-db-service.spec.d.ts +1 -1
  16. package/dist/cjs/__tests__/db/contact-enrichments-db-service.spec.js +68 -68
  17. package/dist/cjs/__tests__/db/destinations-db-service.spec.d.ts +1 -1
  18. package/dist/cjs/__tests__/db/destinations-db-service.spec.js +125 -125
  19. package/dist/cjs/__tests__/db/products-db-service.spec.d.ts +1 -0
  20. package/dist/cjs/__tests__/db/products-db-service.spec.js +90 -0
  21. package/dist/cjs/__tests__/db/products-db-service.spec.js.map +1 -0
  22. package/dist/cjs/__tests__/db/shared-read-db-services.spec.d.ts +1 -1
  23. package/dist/cjs/__tests__/db/shared-read-db-services.spec.js +89 -89
  24. package/dist/cjs/__tests__/db/shopify-app-installs-db-service.spec.d.ts +1 -1
  25. package/dist/cjs/__tests__/db/shopify-app-installs-db-service.spec.js +104 -104
  26. package/dist/cjs/__tests__/db/subscriptions-db-service.spec.d.ts +1 -1
  27. package/dist/cjs/__tests__/db/subscriptions-db-service.spec.js +95 -95
  28. package/dist/cjs/__tests__/db/user-accounts-db-service.spec.d.ts +1 -1
  29. package/dist/cjs/__tests__/db/user-accounts-db-service.spec.js +76 -76
  30. package/dist/cjs/__tests__/helpers/account-users-helper.spec.d.ts +1 -1
  31. package/dist/cjs/__tests__/helpers/account-users-helper.spec.js +220 -220
  32. package/dist/cjs/__tests__/helpers/acuity-helper.spec.d.ts +1 -1
  33. package/dist/cjs/__tests__/helpers/acuity-helper.spec.js +69 -69
  34. package/dist/cjs/__tests__/helpers/api-key-auth-helper.spec.d.ts +1 -1
  35. package/dist/cjs/__tests__/helpers/api-key-auth-helper.spec.js +82 -82
  36. package/dist/cjs/__tests__/identity-cache/identity-cache-db-service.spec.d.ts +1 -1
  37. package/dist/cjs/__tests__/identity-cache/identity-cache-db-service.spec.js +676 -676
  38. package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service.spec.d.ts +1 -1
  39. package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js +1140 -1140
  40. package/dist/cjs/__tests__/identity-cache/identity-cache-tier-routing.spec.d.ts +1 -1
  41. package/dist/cjs/__tests__/identity-cache/identity-cache-tier-routing.spec.js +851 -851
  42. package/dist/cjs/__tests__/identity-cache/identity-cache-tier-routing.spec.js.map +1 -1
  43. package/dist/cjs/__tests__/identity-cache/trait-merging-and-staleness.spec.d.ts +1 -1
  44. package/dist/cjs/__tests__/identity-cache/trait-merging-and-staleness.spec.js +1060 -1060
  45. package/dist/cjs/__tests__/identity-cache/volatile-traits-optimization.spec.d.ts +1 -1
  46. package/dist/cjs/__tests__/identity-cache/volatile-traits-optimization.spec.js +818 -818
  47. package/dist/cjs/__tests__/integration/sqs-bundling-roundtrip.spec.d.ts +1 -1
  48. package/dist/cjs/__tests__/integration/sqs-bundling-roundtrip.spec.js +584 -584
  49. package/dist/cjs/__tests__/libs/compress-decompress.spec.d.ts +1 -1
  50. package/dist/cjs/__tests__/libs/compress-decompress.spec.js +16 -16
  51. package/dist/cjs/__tests__/libs/contacts.spec.d.ts +1 -1
  52. package/dist/cjs/__tests__/libs/contacts.spec.js +294 -294
  53. package/dist/cjs/__tests__/libs/currency.spec.d.ts +1 -1
  54. package/dist/cjs/__tests__/libs/currency.spec.js +220 -220
  55. package/dist/cjs/__tests__/libs/dates.spec.d.ts +1 -1
  56. package/dist/cjs/__tests__/libs/dates.spec.js +130 -130
  57. package/dist/cjs/__tests__/libs/domain.spec.d.ts +1 -1
  58. package/dist/cjs/__tests__/libs/domain.spec.js +107 -107
  59. package/dist/cjs/__tests__/libs/numbers.spec.d.ts +1 -1
  60. package/dist/cjs/__tests__/libs/numbers.spec.js +261 -261
  61. package/dist/cjs/__tests__/s3-client/s3-client.spec.d.ts +1 -1
  62. package/dist/cjs/__tests__/s3-client/s3-client.spec.js +33 -33
  63. package/dist/cjs/__tests__/services/acuity-api-service.spec.d.ts +1 -1
  64. package/dist/cjs/__tests__/services/acuity-api-service.spec.js +71 -71
  65. package/dist/cjs/__tests__/services/cost/cost-calculation-types.spec.d.ts +1 -0
  66. package/dist/cjs/__tests__/services/cost/cost-calculation-types.spec.js +24 -0
  67. package/dist/cjs/__tests__/services/cost/cost-calculation-types.spec.js.map +1 -0
  68. package/dist/cjs/__tests__/services/cost/cost-calculator-service.spec.d.ts +1 -0
  69. package/dist/cjs/__tests__/services/cost/cost-calculator-service.spec.js +3320 -0
  70. package/dist/cjs/__tests__/services/cost/cost-calculator-service.spec.js.map +1 -0
  71. package/dist/cjs/__tests__/services/cost/cost-currency-service.spec.d.ts +1 -0
  72. package/dist/cjs/__tests__/services/cost/cost-currency-service.spec.js +115 -0
  73. package/dist/cjs/__tests__/services/cost/cost-currency-service.spec.js.map +1 -0
  74. package/dist/cjs/__tests__/services/cost/cost-filter-service.spec.d.ts +1 -0
  75. package/dist/cjs/__tests__/services/cost/cost-filter-service.spec.js +469 -0
  76. package/dist/cjs/__tests__/services/cost/cost-filter-service.spec.js.map +1 -0
  77. package/dist/cjs/__tests__/services/cost/order-cost/order-cost-resolution-service.spec.d.ts +1 -0
  78. package/dist/cjs/__tests__/services/cost/order-cost/order-cost-resolution-service.spec.js +207 -0
  79. package/dist/cjs/__tests__/services/cost/order-cost/order-cost-resolution-service.spec.js.map +1 -0
  80. package/dist/cjs/__tests__/services/currency-exchange-rate-lookup-service.spec.d.ts +1 -0
  81. package/dist/cjs/__tests__/services/currency-exchange-rate-lookup-service.spec.js +35 -0
  82. package/dist/cjs/__tests__/services/currency-exchange-rate-lookup-service.spec.js.map +1 -0
  83. package/dist/cjs/__tests__/services/email-verification/contact-email-verification-service.spec.d.ts +1 -1
  84. package/dist/cjs/__tests__/services/email-verification/contact-email-verification-service.spec.js +93 -93
  85. package/dist/cjs/__tests__/services/email-verification/email-verification-service.spec.d.ts +1 -1
  86. package/dist/cjs/__tests__/services/email-verification/email-verification-service.spec.js +57 -57
  87. package/dist/cjs/__tests__/shopify/shopify-graphql-transformer.spec.d.ts +1 -1
  88. package/dist/cjs/__tests__/shopify/shopify-graphql-transformer.spec.js +35 -35
  89. package/dist/cjs/__tests__/unit/libs/api-router/public-api-router.spec.d.ts +1 -1
  90. package/dist/cjs/__tests__/unit/libs/api-router/public-api-router.spec.js +181 -181
  91. package/dist/cjs/__tests__/unit/libs/api-router/route-matcher.spec.d.ts +1 -1
  92. package/dist/cjs/__tests__/unit/libs/api-router/route-matcher.spec.js +69 -69
  93. package/dist/cjs/__tests__/utils/custom-measure-formula-utils.spec.d.ts +1 -1
  94. package/dist/cjs/__tests__/utils/custom-measure-formula-utils.spec.js +139 -139
  95. package/dist/cjs/clients/generic/cognito-client.d.ts +23 -23
  96. package/dist/cjs/clients/generic/cognito-client.js +209 -209
  97. package/dist/cjs/clients/generic/dynamodb-client.d.ts +20 -20
  98. package/dist/cjs/clients/generic/dynamodb-client.js +235 -235
  99. package/dist/cjs/clients/generic/eventbridge-client.d.ts +14 -14
  100. package/dist/cjs/clients/generic/eventbridge-client.js +51 -51
  101. package/dist/cjs/clients/generic/http-client.d.ts +14 -14
  102. package/dist/cjs/clients/generic/http-client.js +61 -61
  103. package/dist/cjs/clients/generic/index.d.ts +13 -13
  104. package/dist/cjs/clients/generic/index.js +29 -29
  105. package/dist/cjs/clients/generic/lambda-invoke-client.d.ts +10 -10
  106. package/dist/cjs/clients/generic/lambda-invoke-client.js +39 -39
  107. package/dist/cjs/clients/generic/location-client.d.ts +8 -8
  108. package/dist/cjs/clients/generic/location-client.js +31 -31
  109. package/dist/cjs/clients/generic/redis-client.d.ts +33 -33
  110. package/dist/cjs/clients/generic/redis-client.js +191 -191
  111. package/dist/cjs/clients/generic/s3-client.d.ts +23 -23
  112. package/dist/cjs/clients/generic/s3-client.js +216 -216
  113. package/dist/cjs/clients/generic/singlestore-db-client.d.ts +14 -14
  114. package/dist/cjs/clients/generic/singlestore-db-client.js +67 -67
  115. package/dist/cjs/clients/generic/sqs-bundled-client.d.ts +15 -15
  116. package/dist/cjs/clients/generic/sqs-bundled-client.js +311 -311
  117. package/dist/cjs/clients/generic/sqs-bundled-client.types.d.ts +53 -53
  118. package/dist/cjs/clients/generic/sqs-bundled-client.types.js +17 -17
  119. package/dist/cjs/clients/generic/sqs-client.d.ts +53 -53
  120. package/dist/cjs/clients/generic/sqs-client.js +285 -285
  121. package/dist/cjs/clients/generic/sqs-unbundle.d.ts +32 -32
  122. package/dist/cjs/clients/generic/sqs-unbundle.js +144 -144
  123. package/dist/cjs/clients/index.d.ts +3 -3
  124. package/dist/cjs/clients/index.js +19 -19
  125. package/dist/cjs/clients/internal-api/accounts-client.d.ts +91 -91
  126. package/dist/cjs/clients/internal-api/accounts-client.js +129 -129
  127. package/dist/cjs/clients/internal-api/cache-lambda-client.d.ts +26 -26
  128. package/dist/cjs/clients/internal-api/cache-lambda-client.js +89 -89
  129. package/dist/cjs/clients/internal-api/db-management-client.d.ts +18 -18
  130. package/dist/cjs/clients/internal-api/db-management-client.js +36 -36
  131. package/dist/cjs/clients/internal-api/destinations-client.d.ts +34 -34
  132. package/dist/cjs/clients/internal-api/destinations-client.js +79 -79
  133. package/dist/cjs/clients/internal-api/event-collector-client.d.ts +20 -20
  134. package/dist/cjs/clients/internal-api/event-collector-client.js +36 -36
  135. package/dist/cjs/clients/internal-api/identity-client.d.ts +31 -31
  136. package/dist/cjs/clients/internal-api/identity-client.js +91 -91
  137. package/dist/cjs/clients/internal-api/index.d.ts +9 -9
  138. package/dist/cjs/clients/internal-api/index.js +25 -25
  139. package/dist/cjs/clients/internal-api/shopify-app-install-client.d.ts +37 -37
  140. package/dist/cjs/clients/internal-api/shopify-app-install-client.js +81 -81
  141. package/dist/cjs/clients/internal-api/subscriptions-client.d.ts +26 -26
  142. package/dist/cjs/clients/internal-api/subscriptions-client.js +77 -77
  143. package/dist/cjs/clients/internal-api/users-auth-client.d.ts +35 -35
  144. package/dist/cjs/clients/internal-api/users-auth-client.js +110 -110
  145. package/dist/cjs/clients/third-party/acuity-client.d.ts +10 -10
  146. package/dist/cjs/clients/third-party/acuity-client.js +40 -40
  147. package/dist/cjs/clients/third-party/emailable-client.d.ts +7 -7
  148. package/dist/cjs/clients/third-party/emailable-client.js +25 -25
  149. package/dist/cjs/clients/third-party/exchange-rate-api-client.d.ts +17 -17
  150. package/dist/cjs/clients/third-party/exchange-rate-api-client.js +19 -19
  151. package/dist/cjs/clients/third-party/index.d.ts +5 -5
  152. package/dist/cjs/clients/third-party/index.js +21 -21
  153. package/dist/cjs/clients/third-party/loops-client.d.ts +10 -10
  154. package/dist/cjs/clients/third-party/loops-client.js +30 -30
  155. package/dist/cjs/clients/third-party/shopify/graphql-order-queries.d.ts +25 -25
  156. package/dist/cjs/clients/third-party/shopify/graphql-order-queries.js +4 -4
  157. package/dist/cjs/clients/third-party/shopify/graphql-product-queries.d.ts +2 -2
  158. package/dist/cjs/clients/third-party/shopify/graphql-product-queries.js +5 -5
  159. package/dist/cjs/clients/third-party/shopify/shopify-graphql-client.d.ts +10 -10
  160. package/dist/cjs/clients/third-party/shopify/shopify-graphql-client.js +161 -161
  161. package/dist/cjs/clients/third-party/shopify-client.d.ts +29 -29
  162. package/dist/cjs/clients/third-party/shopify-client.js +146 -146
  163. package/dist/cjs/constants/index.d.ts +1 -1
  164. package/dist/cjs/constants/index.js +17 -17
  165. package/dist/cjs/constants/sqs.d.ts +20 -20
  166. package/dist/cjs/constants/sqs.js +26 -26
  167. package/dist/cjs/helpers/account-users-helper.d.ts +2 -2
  168. package/dist/cjs/helpers/account-users-helper.js +22 -22
  169. package/dist/cjs/helpers/acuity-helper.d.ts +4 -4
  170. package/dist/cjs/helpers/acuity-helper.js +56 -56
  171. package/dist/cjs/helpers/api-key-auth-helper.d.ts +9 -9
  172. package/dist/cjs/helpers/api-key-auth-helper.js +40 -40
  173. package/dist/cjs/helpers/api-key-authorizer-helper.d.ts +36 -36
  174. package/dist/cjs/helpers/api-key-authorizer-helper.js +87 -87
  175. package/dist/cjs/helpers/identity-cache-helper.d.ts +30 -30
  176. package/dist/cjs/helpers/identity-cache-helper.js +253 -253
  177. package/dist/cjs/helpers/identity-cache-helper.js.map +1 -1
  178. package/dist/cjs/helpers/index.d.ts +10 -10
  179. package/dist/cjs/helpers/index.js +26 -26
  180. package/dist/cjs/helpers/input-validation-helper.d.ts +3 -3
  181. package/dist/cjs/helpers/input-validation-helper.js +22 -22
  182. package/dist/cjs/helpers/logging-helper.d.ts +16 -16
  183. package/dist/cjs/helpers/logging-helper.js +84 -84
  184. package/dist/cjs/helpers/response-helper.d.ts +18 -18
  185. package/dist/cjs/helpers/response-helper.js +43 -43
  186. package/dist/cjs/helpers/shopify-helper.d.ts +9 -9
  187. package/dist/cjs/helpers/shopify-helper.js +26 -26
  188. package/dist/cjs/helpers/sqs-utils.d.ts +6 -6
  189. package/dist/cjs/helpers/sqs-utils.js +14 -14
  190. package/dist/cjs/index.d.ts +7 -7
  191. package/dist/cjs/index.js +23 -23
  192. package/dist/cjs/libs/api-router/index.d.ts +2 -2
  193. package/dist/cjs/libs/api-router/index.js +18 -18
  194. package/dist/cjs/libs/api-router/public-api-router.d.ts +3 -3
  195. package/dist/cjs/libs/api-router/public-api-router.js +36 -36
  196. package/dist/cjs/libs/api-router/route-matcher.d.ts +21 -21
  197. package/dist/cjs/libs/api-router/route-matcher.js +36 -36
  198. package/dist/cjs/libs/click-id-parser.d.ts +23 -23
  199. package/dist/cjs/libs/click-id-parser.js +49 -49
  200. package/dist/cjs/libs/compression.d.ts +2 -2
  201. package/dist/cjs/libs/compression.js +33 -33
  202. package/dist/cjs/libs/contacts.d.ts +7 -7
  203. package/dist/cjs/libs/contacts.js +152 -152
  204. package/dist/cjs/libs/cookie.d.ts +17 -17
  205. package/dist/cjs/libs/cookie.js +76 -76
  206. package/dist/cjs/libs/crypto.d.ts +4 -4
  207. package/dist/cjs/libs/crypto.js +25 -25
  208. package/dist/cjs/libs/csv.d.ts +2 -2
  209. package/dist/cjs/libs/csv.js +35 -35
  210. package/dist/cjs/libs/currency.d.ts +1 -1
  211. package/dist/cjs/libs/currency.js +29 -29
  212. package/dist/cjs/libs/dates.d.ts +12 -12
  213. package/dist/cjs/libs/dates.js +96 -96
  214. package/dist/cjs/libs/domain.d.ts +2 -2
  215. package/dist/cjs/libs/domain.js +38 -38
  216. package/dist/cjs/libs/emails.d.ts +8 -8
  217. package/dist/cjs/libs/emails.js +154 -154
  218. package/dist/cjs/libs/http-error.d.ts +21 -21
  219. package/dist/cjs/libs/http-error.js +63 -63
  220. package/dist/cjs/libs/http-status-codes.d.ts +58 -58
  221. package/dist/cjs/libs/http-status-codes.js +62 -62
  222. package/dist/cjs/libs/index.d.ts +19 -19
  223. package/dist/cjs/libs/index.js +35 -35
  224. package/dist/cjs/libs/numbers.d.ts +1 -1
  225. package/dist/cjs/libs/numbers.js +15 -15
  226. package/dist/cjs/libs/referrer-parser/index.d.ts +2 -2
  227. package/dist/cjs/libs/referrer-parser/index.js +18 -18
  228. package/dist/cjs/libs/referrer-parser/referrer-data.d.ts +9 -9
  229. package/dist/cjs/libs/referrer-parser/referrer-data.js +3307 -3307
  230. package/dist/cjs/libs/referrer-parser/referrer-parser-util.d.ts +20 -20
  231. package/dist/cjs/libs/referrer-parser/referrer-parser-util.js +131 -131
  232. package/dist/cjs/libs/strings.d.ts +3 -3
  233. package/dist/cjs/libs/strings.js +46 -46
  234. package/dist/cjs/libs/traits.d.ts +6 -6
  235. package/dist/cjs/libs/traits.js +65 -65
  236. package/dist/cjs/libs/url.d.ts +1 -1
  237. package/dist/cjs/libs/url.js +13 -13
  238. package/dist/cjs/services/acuity-api-service.d.ts +9 -9
  239. package/dist/cjs/services/acuity-api-service.js +73 -73
  240. package/dist/cjs/services/cache/generic-cached-object.d.ts +5 -5
  241. package/dist/cjs/services/cache/generic-cached-object.js +2 -2
  242. package/dist/cjs/services/cache/index.d.ts +1 -1
  243. package/dist/cjs/services/cache/index.js +17 -17
  244. package/dist/cjs/services/cache/product-cache-service.d.ts +21 -21
  245. package/dist/cjs/services/cache/product-cache-service.js +76 -76
  246. package/dist/cjs/services/cost/cost-calculation-types.d.ts +69 -0
  247. package/dist/cjs/services/cost/cost-calculation-types.js +20 -0
  248. package/dist/cjs/services/cost/cost-calculation-types.js.map +1 -0
  249. package/dist/cjs/services/cost/cost-calculator-service.d.ts +24 -0
  250. package/dist/cjs/services/cost/cost-calculator-service.js +457 -0
  251. package/dist/cjs/services/cost/cost-calculator-service.js.map +1 -0
  252. package/dist/cjs/services/cost/cost-currency-service.d.ts +6 -0
  253. package/dist/cjs/services/cost/cost-currency-service.js +88 -0
  254. package/dist/cjs/services/cost/cost-currency-service.js.map +1 -0
  255. package/dist/cjs/services/cost/cost-filter-service.d.ts +10 -0
  256. package/dist/cjs/services/cost/cost-filter-service.js +122 -0
  257. package/dist/cjs/services/cost/cost-filter-service.js.map +1 -0
  258. package/dist/cjs/services/cost/index.d.ts +5 -0
  259. package/dist/cjs/services/cost/index.js +22 -0
  260. package/dist/cjs/services/cost/index.js.map +1 -0
  261. package/dist/cjs/services/cost/order-cost/index.d.ts +2 -0
  262. package/dist/cjs/services/cost/order-cost/index.js +19 -0
  263. package/dist/cjs/services/cost/order-cost/index.js.map +1 -0
  264. package/dist/cjs/services/cost/order-cost/order-cost-resolution-service.d.ts +23 -0
  265. package/dist/cjs/services/cost/order-cost/order-cost-resolution-service.js +362 -0
  266. package/dist/cjs/services/cost/order-cost/order-cost-resolution-service.js.map +1 -0
  267. package/dist/cjs/services/cost/order-cost/order-cost-resolution-types.d.ts +37 -0
  268. package/dist/cjs/services/cost/order-cost/order-cost-resolution-types.js +3 -0
  269. package/dist/cjs/services/cost/order-cost/order-cost-resolution-types.js.map +1 -0
  270. package/dist/cjs/services/currency-exchange-rate-lookup-service.d.ts +12 -11
  271. package/dist/cjs/services/currency-exchange-rate-lookup-service.js +94 -66
  272. package/dist/cjs/services/currency-exchange-rate-lookup-service.js.map +1 -1
  273. package/dist/cjs/services/db/accounts-db-service.d.ts +9 -9
  274. package/dist/cjs/services/db/accounts-db-service.js +33 -33
  275. package/dist/cjs/services/db/api-keys-db-service.d.ts +10 -10
  276. package/dist/cjs/services/db/api-keys-db-service.js +36 -36
  277. package/dist/cjs/services/db/contact-enrichments-db-service.d.ts +15 -15
  278. package/dist/cjs/services/db/contact-enrichments-db-service.js +94 -94
  279. package/dist/cjs/services/db/currency-exchange-rates-db-service.d.ts +21 -21
  280. package/dist/cjs/services/db/currency-exchange-rates-db-service.js +39 -39
  281. package/dist/cjs/services/db/custom-measures-db-service.d.ts +14 -14
  282. package/dist/cjs/services/db/custom-measures-db-service.js +48 -48
  283. package/dist/cjs/services/db/destinations-db-service.d.ts +13 -13
  284. package/dist/cjs/services/db/destinations-db-service.js +74 -74
  285. package/dist/cjs/services/db/identity-cache-db-service.d.ts +28 -28
  286. package/dist/cjs/services/db/identity-cache-db-service.js +320 -320
  287. package/dist/cjs/services/db/identity-cache-dynamodb-service.d.ts +44 -44
  288. package/dist/cjs/services/db/identity-cache-dynamodb-service.js +734 -734
  289. package/dist/cjs/services/db/identity-cache-dynamodb-service.js.map +1 -1
  290. package/dist/cjs/services/db/index.d.ts +19 -17
  291. package/dist/cjs/services/db/index.js +35 -33
  292. package/dist/cjs/services/db/index.js.map +1 -1
  293. package/dist/cjs/services/db/log-events-db-service.d.ts +11 -11
  294. package/dist/cjs/services/db/log-events-db-service.js +181 -181
  295. package/dist/cjs/services/db/pixels-db-service.d.ts +8 -8
  296. package/dist/cjs/services/db/pixels-db-service.js +35 -35
  297. package/dist/cjs/services/db/products-db-service-types.d.ts +10 -0
  298. package/dist/cjs/services/db/products-db-service-types.js +3 -0
  299. package/dist/cjs/services/db/products-db-service-types.js.map +1 -0
  300. package/dist/cjs/services/db/products-db-service.d.ts +19 -0
  301. package/dist/cjs/services/db/products-db-service.js +282 -0
  302. package/dist/cjs/services/db/products-db-service.js.map +1 -0
  303. package/dist/cjs/services/db/purchasable-contacts-db-service.d.ts +9 -9
  304. package/dist/cjs/services/db/purchasable-contacts-db-service.js +43 -43
  305. package/dist/cjs/services/db/purchased-contacts/index.d.ts +2 -2
  306. package/dist/cjs/services/db/purchased-contacts/index.js +18 -18
  307. package/dist/cjs/services/db/purchased-contacts/purchased-contacts-db-service.d.ts +18 -18
  308. package/dist/cjs/services/db/purchased-contacts/purchased-contacts-db-service.js +152 -152
  309. package/dist/cjs/services/db/purchased-contacts/types.d.ts +11 -11
  310. package/dist/cjs/services/db/purchased-contacts/types.js +2 -2
  311. package/dist/cjs/services/db/shopify-app-installs-db-service.d.ts +10 -10
  312. package/dist/cjs/services/db/shopify-app-installs-db-service.js +52 -52
  313. package/dist/cjs/services/db/shopify-products-cache-db-service.d.ts +16 -16
  314. package/dist/cjs/services/db/shopify-products-cache-db-service.js +73 -73
  315. package/dist/cjs/services/db/subscriptions-db-service.d.ts +11 -11
  316. package/dist/cjs/services/db/subscriptions-db-service.js +38 -38
  317. package/dist/cjs/services/db/tracking-events-db-service.d.ts +21 -21
  318. package/dist/cjs/services/db/tracking-events-db-service.js +188 -188
  319. package/dist/cjs/services/db/user-accounts-db-service.d.ts +7 -7
  320. package/dist/cjs/services/db/user-accounts-db-service.js +17 -17
  321. package/dist/cjs/services/email-verification/contact-email-verification-service.d.ts +7 -7
  322. package/dist/cjs/services/email-verification/contact-email-verification-service.js +101 -101
  323. package/dist/cjs/services/email-verification/email-verification-service.d.ts +19 -19
  324. package/dist/cjs/services/email-verification/email-verification-service.js +131 -131
  325. package/dist/cjs/services/email-verification/index.d.ts +2 -2
  326. package/dist/cjs/services/email-verification/index.js +18 -18
  327. package/dist/cjs/services/eventbridge-integration-service.d.ts +9 -9
  328. package/dist/cjs/services/eventbridge-integration-service.js +28 -28
  329. package/dist/cjs/services/events/index.d.ts +3 -3
  330. package/dist/cjs/services/events/index.js +19 -19
  331. package/dist/cjs/services/events/log-event-service.d.ts +19 -19
  332. package/dist/cjs/services/events/log-event-service.js +77 -77
  333. package/dist/cjs/services/events/metric-event-service.d.ts +9 -9
  334. package/dist/cjs/services/events/metric-event-service.js +49 -49
  335. package/dist/cjs/services/events/tracking-event-sqs-service.d.ts +8 -8
  336. package/dist/cjs/services/events/tracking-event-sqs-service.js +34 -34
  337. package/dist/cjs/services/generic-cache-service.d.ts +7 -7
  338. package/dist/cjs/services/generic-cache-service.js +33 -33
  339. package/dist/cjs/services/index.d.ts +11 -10
  340. package/dist/cjs/services/index.js +27 -26
  341. package/dist/cjs/services/index.js.map +1 -1
  342. package/dist/cjs/services/ipdata-lookup-service.d.ts +20 -20
  343. package/dist/cjs/services/ipdata-lookup-service.js +112 -112
  344. package/dist/cjs/services/shopify/index.d.ts +2 -2
  345. package/dist/cjs/services/shopify/index.js +18 -18
  346. package/dist/cjs/services/shopify/products/index.d.ts +1 -1
  347. package/dist/cjs/services/shopify/products/index.js +17 -17
  348. package/dist/cjs/services/shopify/products/shopify-products-serviceV2.d.ts +17 -17
  349. package/dist/cjs/services/shopify/products/shopify-products-serviceV2.js +112 -112
  350. package/dist/cjs/services/shopify/shopify-graphql-transformer.d.ts +8 -8
  351. package/dist/cjs/services/shopify/shopify-graphql-transformer.js +141 -141
  352. package/dist/cjs/types/acuity-types.d.ts +74 -74
  353. package/dist/cjs/types/acuity-types.js +2 -2
  354. package/dist/cjs/types/api-response.d.ts +6 -6
  355. package/dist/cjs/types/api-response.js +2 -2
  356. package/dist/cjs/types/index.d.ts +4 -4
  357. package/dist/cjs/types/index.js +33 -33
  358. package/dist/cjs/types/internal-events/event-detail-types.d.ts +20 -20
  359. package/dist/cjs/types/internal-events/event-detail-types.js +27 -27
  360. package/dist/cjs/types/internal-events/index.d.ts +1 -1
  361. package/dist/cjs/types/internal-events/index.js +17 -17
  362. package/dist/cjs/types/shopify-graphql-types/admin.generated.d.ts +123 -123
  363. package/dist/cjs/types/shopify-graphql-types/admin.generated.js +2 -2
  364. package/dist/cjs/types/shopify-graphql-types/admin.types.d.ts +26289 -26289
  365. package/dist/cjs/types/shopify-graphql-types/admin.types.js +5311 -5311
  366. package/dist/cjs/types/shopify-graphql-types/index.d.ts +2 -2
  367. package/dist/cjs/types/shopify-graphql-types/index.js +18 -18
  368. package/dist/cjs/types/shopify-rest-types.d.ts +767 -767
  369. package/dist/cjs/types/shopify-rest-types.js +2 -2
  370. package/dist/cjs/utils/compression.d.ts +36 -36
  371. package/dist/cjs/utils/compression.js +198 -198
  372. package/dist/cjs/utils/custom-measure-formula-utils.d.ts +6 -6
  373. package/dist/cjs/utils/custom-measure-formula-utils.js +209 -209
  374. package/dist/cjs/utils/index.d.ts +4 -4
  375. package/dist/cjs/utils/index.js +20 -20
  376. package/dist/cjs/utils/retry-envelope.d.ts +12 -12
  377. package/dist/cjs/utils/retry-envelope.js +28 -28
  378. package/dist/cjs/utils/size.d.ts +2 -2
  379. package/dist/cjs/utils/size.js +49 -49
  380. package/dist/esm/__tests__/clients/acuity-client.spec.d.ts +1 -1
  381. package/dist/esm/__tests__/clients/acuity-client.spec.js +41 -41
  382. package/dist/esm/__tests__/clients/cross-platform-compression.spec.d.ts +1 -1
  383. package/dist/esm/__tests__/clients/cross-platform-compression.spec.js +329 -329
  384. package/dist/esm/__tests__/clients/dynamodb-client.spec.d.ts +1 -1
  385. package/dist/esm/__tests__/clients/dynamodb-client.spec.js +192 -192
  386. package/dist/esm/__tests__/clients/sqs-bundled-client.spec.d.ts +1 -1
  387. package/dist/esm/__tests__/clients/sqs-bundled-client.spec.js +906 -906
  388. package/dist/esm/__tests__/clients/sqs-bundling-contracts.spec.d.ts +1 -1
  389. package/dist/esm/__tests__/clients/sqs-bundling-contracts.spec.js +538 -538
  390. package/dist/esm/__tests__/clients/sqs-client.spec.d.ts +1 -1
  391. package/dist/esm/__tests__/clients/sqs-client.spec.js +189 -189
  392. package/dist/esm/__tests__/clients/sqs-unbundle.spec.d.ts +1 -1
  393. package/dist/esm/__tests__/clients/sqs-unbundle.spec.js +1355 -1355
  394. package/dist/esm/__tests__/db/contact-enrichments-db-service.spec.d.ts +1 -1
  395. package/dist/esm/__tests__/db/contact-enrichments-db-service.spec.js +66 -66
  396. package/dist/esm/__tests__/db/destinations-db-service.spec.d.ts +1 -1
  397. package/dist/esm/__tests__/db/destinations-db-service.spec.js +123 -123
  398. package/dist/esm/__tests__/db/products-db-service.spec.d.ts +1 -0
  399. package/dist/esm/__tests__/db/products-db-service.spec.js +88 -0
  400. package/dist/esm/__tests__/db/products-db-service.spec.js.map +1 -0
  401. package/dist/esm/__tests__/db/shared-read-db-services.spec.d.ts +1 -1
  402. package/dist/esm/__tests__/db/shared-read-db-services.spec.js +87 -87
  403. package/dist/esm/__tests__/db/shopify-app-installs-db-service.spec.d.ts +1 -1
  404. package/dist/esm/__tests__/db/shopify-app-installs-db-service.spec.js +102 -102
  405. package/dist/esm/__tests__/db/subscriptions-db-service.spec.d.ts +1 -1
  406. package/dist/esm/__tests__/db/subscriptions-db-service.spec.js +93 -93
  407. package/dist/esm/__tests__/db/user-accounts-db-service.spec.d.ts +1 -1
  408. package/dist/esm/__tests__/db/user-accounts-db-service.spec.js +74 -74
  409. package/dist/esm/__tests__/helpers/account-users-helper.spec.d.ts +1 -1
  410. package/dist/esm/__tests__/helpers/account-users-helper.spec.js +218 -218
  411. package/dist/esm/__tests__/helpers/acuity-helper.spec.d.ts +1 -1
  412. package/dist/esm/__tests__/helpers/acuity-helper.spec.js +67 -67
  413. package/dist/esm/__tests__/helpers/api-key-auth-helper.spec.d.ts +1 -1
  414. package/dist/esm/__tests__/helpers/api-key-auth-helper.spec.js +80 -80
  415. package/dist/esm/__tests__/identity-cache/identity-cache-db-service.spec.d.ts +1 -1
  416. package/dist/esm/__tests__/identity-cache/identity-cache-db-service.spec.js +674 -674
  417. package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service.spec.d.ts +1 -1
  418. package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js +1138 -1138
  419. package/dist/esm/__tests__/identity-cache/identity-cache-tier-routing.spec.d.ts +1 -1
  420. package/dist/esm/__tests__/identity-cache/identity-cache-tier-routing.spec.js +849 -849
  421. package/dist/esm/__tests__/identity-cache/identity-cache-tier-routing.spec.js.map +1 -1
  422. package/dist/esm/__tests__/identity-cache/trait-merging-and-staleness.spec.d.ts +1 -1
  423. package/dist/esm/__tests__/identity-cache/trait-merging-and-staleness.spec.js +1058 -1058
  424. package/dist/esm/__tests__/identity-cache/volatile-traits-optimization.spec.d.ts +1 -1
  425. package/dist/esm/__tests__/identity-cache/volatile-traits-optimization.spec.js +816 -816
  426. package/dist/esm/__tests__/integration/sqs-bundling-roundtrip.spec.d.ts +1 -1
  427. package/dist/esm/__tests__/integration/sqs-bundling-roundtrip.spec.js +582 -582
  428. package/dist/esm/__tests__/libs/compress-decompress.spec.d.ts +1 -1
  429. package/dist/esm/__tests__/libs/compress-decompress.spec.js +14 -14
  430. package/dist/esm/__tests__/libs/contacts.spec.d.ts +1 -1
  431. package/dist/esm/__tests__/libs/contacts.spec.js +292 -292
  432. package/dist/esm/__tests__/libs/currency.spec.d.ts +1 -1
  433. package/dist/esm/__tests__/libs/currency.spec.js +218 -218
  434. package/dist/esm/__tests__/libs/dates.spec.d.ts +1 -1
  435. package/dist/esm/__tests__/libs/dates.spec.js +128 -128
  436. package/dist/esm/__tests__/libs/domain.spec.d.ts +1 -1
  437. package/dist/esm/__tests__/libs/domain.spec.js +105 -105
  438. package/dist/esm/__tests__/libs/numbers.spec.d.ts +1 -1
  439. package/dist/esm/__tests__/libs/numbers.spec.js +259 -259
  440. package/dist/esm/__tests__/s3-client/s3-client.spec.d.ts +1 -1
  441. package/dist/esm/__tests__/s3-client/s3-client.spec.js +31 -31
  442. package/dist/esm/__tests__/services/acuity-api-service.spec.d.ts +1 -1
  443. package/dist/esm/__tests__/services/acuity-api-service.spec.js +69 -69
  444. package/dist/esm/__tests__/services/cost/cost-calculation-types.spec.d.ts +1 -0
  445. package/dist/esm/__tests__/services/cost/cost-calculation-types.spec.js +22 -0
  446. package/dist/esm/__tests__/services/cost/cost-calculation-types.spec.js.map +1 -0
  447. package/dist/esm/__tests__/services/cost/cost-calculator-service.spec.d.ts +1 -0
  448. package/dist/esm/__tests__/services/cost/cost-calculator-service.spec.js +3318 -0
  449. package/dist/esm/__tests__/services/cost/cost-calculator-service.spec.js.map +1 -0
  450. package/dist/esm/__tests__/services/cost/cost-currency-service.spec.d.ts +1 -0
  451. package/dist/esm/__tests__/services/cost/cost-currency-service.spec.js +113 -0
  452. package/dist/esm/__tests__/services/cost/cost-currency-service.spec.js.map +1 -0
  453. package/dist/esm/__tests__/services/cost/cost-filter-service.spec.d.ts +1 -0
  454. package/dist/esm/__tests__/services/cost/cost-filter-service.spec.js +467 -0
  455. package/dist/esm/__tests__/services/cost/cost-filter-service.spec.js.map +1 -0
  456. package/dist/esm/__tests__/services/cost/order-cost/order-cost-resolution-service.spec.d.ts +1 -0
  457. package/dist/esm/__tests__/services/cost/order-cost/order-cost-resolution-service.spec.js +205 -0
  458. package/dist/esm/__tests__/services/cost/order-cost/order-cost-resolution-service.spec.js.map +1 -0
  459. package/dist/esm/__tests__/services/currency-exchange-rate-lookup-service.spec.d.ts +1 -0
  460. package/dist/esm/__tests__/services/currency-exchange-rate-lookup-service.spec.js +33 -0
  461. package/dist/esm/__tests__/services/currency-exchange-rate-lookup-service.spec.js.map +1 -0
  462. package/dist/esm/__tests__/services/email-verification/contact-email-verification-service.spec.d.ts +1 -1
  463. package/dist/esm/__tests__/services/email-verification/contact-email-verification-service.spec.js +91 -91
  464. package/dist/esm/__tests__/services/email-verification/email-verification-service.spec.d.ts +1 -1
  465. package/dist/esm/__tests__/services/email-verification/email-verification-service.spec.js +55 -55
  466. package/dist/esm/__tests__/shopify/shopify-graphql-transformer.spec.d.ts +1 -1
  467. package/dist/esm/__tests__/shopify/shopify-graphql-transformer.spec.js +33 -33
  468. package/dist/esm/__tests__/unit/libs/api-router/public-api-router.spec.d.ts +1 -1
  469. package/dist/esm/__tests__/unit/libs/api-router/public-api-router.spec.js +156 -156
  470. package/dist/esm/__tests__/unit/libs/api-router/route-matcher.spec.d.ts +1 -1
  471. package/dist/esm/__tests__/unit/libs/api-router/route-matcher.spec.js +67 -67
  472. package/dist/esm/__tests__/utils/custom-measure-formula-utils.spec.d.ts +1 -1
  473. package/dist/esm/__tests__/utils/custom-measure-formula-utils.spec.js +137 -137
  474. package/dist/esm/clients/generic/cognito-client.d.ts +23 -23
  475. package/dist/esm/clients/generic/cognito-client.js +204 -204
  476. package/dist/esm/clients/generic/dynamodb-client.d.ts +20 -20
  477. package/dist/esm/clients/generic/dynamodb-client.js +231 -231
  478. package/dist/esm/clients/generic/eventbridge-client.d.ts +14 -14
  479. package/dist/esm/clients/generic/eventbridge-client.js +47 -47
  480. package/dist/esm/clients/generic/http-client.d.ts +14 -14
  481. package/dist/esm/clients/generic/http-client.js +53 -53
  482. package/dist/esm/clients/generic/index.d.ts +13 -13
  483. package/dist/esm/clients/generic/index.js +13 -13
  484. package/dist/esm/clients/generic/lambda-invoke-client.d.ts +10 -10
  485. package/dist/esm/clients/generic/lambda-invoke-client.js +35 -35
  486. package/dist/esm/clients/generic/location-client.d.ts +8 -8
  487. package/dist/esm/clients/generic/location-client.js +27 -27
  488. package/dist/esm/clients/generic/redis-client.d.ts +33 -33
  489. package/dist/esm/clients/generic/redis-client.js +184 -184
  490. package/dist/esm/clients/generic/s3-client.d.ts +23 -23
  491. package/dist/esm/clients/generic/s3-client.js +209 -209
  492. package/dist/esm/clients/generic/singlestore-db-client.d.ts +14 -14
  493. package/dist/esm/clients/generic/singlestore-db-client.js +40 -40
  494. package/dist/esm/clients/generic/sqs-bundled-client.d.ts +15 -15
  495. package/dist/esm/clients/generic/sqs-bundled-client.js +307 -307
  496. package/dist/esm/clients/generic/sqs-bundled-client.types.d.ts +53 -53
  497. package/dist/esm/clients/generic/sqs-bundled-client.types.js +14 -14
  498. package/dist/esm/clients/generic/sqs-client.d.ts +53 -53
  499. package/dist/esm/clients/generic/sqs-client.js +281 -281
  500. package/dist/esm/clients/generic/sqs-unbundle.d.ts +32 -32
  501. package/dist/esm/clients/generic/sqs-unbundle.js +137 -137
  502. package/dist/esm/clients/index.d.ts +3 -3
  503. package/dist/esm/clients/index.js +3 -3
  504. package/dist/esm/clients/internal-api/accounts-client.d.ts +91 -91
  505. package/dist/esm/clients/internal-api/accounts-client.js +125 -125
  506. package/dist/esm/clients/internal-api/cache-lambda-client.d.ts +26 -26
  507. package/dist/esm/clients/internal-api/cache-lambda-client.js +85 -85
  508. package/dist/esm/clients/internal-api/db-management-client.d.ts +18 -18
  509. package/dist/esm/clients/internal-api/db-management-client.js +32 -32
  510. package/dist/esm/clients/internal-api/destinations-client.d.ts +34 -34
  511. package/dist/esm/clients/internal-api/destinations-client.js +75 -75
  512. package/dist/esm/clients/internal-api/event-collector-client.d.ts +20 -20
  513. package/dist/esm/clients/internal-api/event-collector-client.js +32 -32
  514. package/dist/esm/clients/internal-api/identity-client.d.ts +31 -31
  515. package/dist/esm/clients/internal-api/identity-client.js +87 -87
  516. package/dist/esm/clients/internal-api/index.d.ts +9 -9
  517. package/dist/esm/clients/internal-api/index.js +9 -9
  518. package/dist/esm/clients/internal-api/shopify-app-install-client.d.ts +37 -37
  519. package/dist/esm/clients/internal-api/shopify-app-install-client.js +77 -77
  520. package/dist/esm/clients/internal-api/subscriptions-client.d.ts +26 -26
  521. package/dist/esm/clients/internal-api/subscriptions-client.js +73 -73
  522. package/dist/esm/clients/internal-api/users-auth-client.d.ts +35 -35
  523. package/dist/esm/clients/internal-api/users-auth-client.js +106 -106
  524. package/dist/esm/clients/third-party/acuity-client.d.ts +10 -10
  525. package/dist/esm/clients/third-party/acuity-client.js +36 -36
  526. package/dist/esm/clients/third-party/emailable-client.d.ts +7 -7
  527. package/dist/esm/clients/third-party/emailable-client.js +21 -21
  528. package/dist/esm/clients/third-party/exchange-rate-api-client.d.ts +17 -17
  529. package/dist/esm/clients/third-party/exchange-rate-api-client.js +15 -15
  530. package/dist/esm/clients/third-party/index.d.ts +5 -5
  531. package/dist/esm/clients/third-party/index.js +5 -5
  532. package/dist/esm/clients/third-party/loops-client.d.ts +10 -10
  533. package/dist/esm/clients/third-party/loops-client.js +26 -26
  534. package/dist/esm/clients/third-party/shopify/graphql-order-queries.d.ts +25 -25
  535. package/dist/esm/clients/third-party/shopify/graphql-order-queries.js +1 -1
  536. package/dist/esm/clients/third-party/shopify/graphql-product-queries.d.ts +2 -2
  537. package/dist/esm/clients/third-party/shopify/graphql-product-queries.js +2 -2
  538. package/dist/esm/clients/third-party/shopify/shopify-graphql-client.d.ts +10 -10
  539. package/dist/esm/clients/third-party/shopify/shopify-graphql-client.js +157 -157
  540. package/dist/esm/clients/third-party/shopify-client.d.ts +29 -29
  541. package/dist/esm/clients/third-party/shopify-client.js +142 -142
  542. package/dist/esm/constants/index.d.ts +1 -1
  543. package/dist/esm/constants/index.js +1 -1
  544. package/dist/esm/constants/sqs.d.ts +20 -20
  545. package/dist/esm/constants/sqs.js +22 -22
  546. package/dist/esm/helpers/account-users-helper.d.ts +2 -2
  547. package/dist/esm/helpers/account-users-helper.js +18 -18
  548. package/dist/esm/helpers/acuity-helper.d.ts +4 -4
  549. package/dist/esm/helpers/acuity-helper.js +51 -51
  550. package/dist/esm/helpers/api-key-auth-helper.d.ts +9 -9
  551. package/dist/esm/helpers/api-key-auth-helper.js +35 -35
  552. package/dist/esm/helpers/api-key-authorizer-helper.d.ts +36 -36
  553. package/dist/esm/helpers/api-key-authorizer-helper.js +83 -83
  554. package/dist/esm/helpers/identity-cache-helper.d.ts +30 -30
  555. package/dist/esm/helpers/identity-cache-helper.js +248 -248
  556. package/dist/esm/helpers/identity-cache-helper.js.map +1 -1
  557. package/dist/esm/helpers/index.d.ts +10 -10
  558. package/dist/esm/helpers/index.js +10 -10
  559. package/dist/esm/helpers/input-validation-helper.d.ts +3 -3
  560. package/dist/esm/helpers/input-validation-helper.js +18 -18
  561. package/dist/esm/helpers/logging-helper.d.ts +16 -16
  562. package/dist/esm/helpers/logging-helper.js +56 -56
  563. package/dist/esm/helpers/response-helper.d.ts +18 -18
  564. package/dist/esm/helpers/response-helper.js +37 -37
  565. package/dist/esm/helpers/shopify-helper.d.ts +9 -9
  566. package/dist/esm/helpers/shopify-helper.js +21 -21
  567. package/dist/esm/helpers/sqs-utils.d.ts +6 -6
  568. package/dist/esm/helpers/sqs-utils.js +9 -9
  569. package/dist/esm/index.d.ts +7 -7
  570. package/dist/esm/index.js +7 -7
  571. package/dist/esm/libs/api-router/index.d.ts +2 -2
  572. package/dist/esm/libs/api-router/index.js +2 -2
  573. package/dist/esm/libs/api-router/public-api-router.d.ts +3 -3
  574. package/dist/esm/libs/api-router/public-api-router.js +32 -32
  575. package/dist/esm/libs/api-router/route-matcher.d.ts +21 -21
  576. package/dist/esm/libs/api-router/route-matcher.js +30 -30
  577. package/dist/esm/libs/click-id-parser.d.ts +23 -23
  578. package/dist/esm/libs/click-id-parser.js +45 -45
  579. package/dist/esm/libs/compression.d.ts +2 -2
  580. package/dist/esm/libs/compression.js +25 -25
  581. package/dist/esm/libs/contacts.d.ts +7 -7
  582. package/dist/esm/libs/contacts.js +143 -143
  583. package/dist/esm/libs/cookie.d.ts +17 -17
  584. package/dist/esm/libs/cookie.js +70 -70
  585. package/dist/esm/libs/crypto.d.ts +4 -4
  586. package/dist/esm/libs/crypto.js +15 -15
  587. package/dist/esm/libs/csv.d.ts +2 -2
  588. package/dist/esm/libs/csv.js +30 -30
  589. package/dist/esm/libs/currency.d.ts +1 -1
  590. package/dist/esm/libs/currency.js +22 -22
  591. package/dist/esm/libs/dates.d.ts +12 -12
  592. package/dist/esm/libs/dates.js +83 -83
  593. package/dist/esm/libs/domain.d.ts +2 -2
  594. package/dist/esm/libs/domain.js +33 -33
  595. package/dist/esm/libs/emails.d.ts +8 -8
  596. package/dist/esm/libs/emails.js +146 -146
  597. package/dist/esm/libs/http-error.d.ts +21 -21
  598. package/dist/esm/libs/http-error.js +59 -59
  599. package/dist/esm/libs/http-status-codes.d.ts +58 -58
  600. package/dist/esm/libs/http-status-codes.js +59 -59
  601. package/dist/esm/libs/index.d.ts +19 -19
  602. package/dist/esm/libs/index.js +19 -19
  603. package/dist/esm/libs/numbers.d.ts +1 -1
  604. package/dist/esm/libs/numbers.js +11 -11
  605. package/dist/esm/libs/referrer-parser/index.d.ts +2 -2
  606. package/dist/esm/libs/referrer-parser/index.js +2 -2
  607. package/dist/esm/libs/referrer-parser/referrer-data.d.ts +9 -9
  608. package/dist/esm/libs/referrer-parser/referrer-data.js +3304 -3304
  609. package/dist/esm/libs/referrer-parser/referrer-parser-util.d.ts +20 -20
  610. package/dist/esm/libs/referrer-parser/referrer-parser-util.js +124 -124
  611. package/dist/esm/libs/strings.d.ts +3 -3
  612. package/dist/esm/libs/strings.js +40 -40
  613. package/dist/esm/libs/traits.d.ts +6 -6
  614. package/dist/esm/libs/traits.js +54 -54
  615. package/dist/esm/libs/url.d.ts +1 -1
  616. package/dist/esm/libs/url.js +9 -9
  617. package/dist/esm/services/acuity-api-service.d.ts +9 -9
  618. package/dist/esm/services/acuity-api-service.js +69 -69
  619. package/dist/esm/services/cache/generic-cached-object.d.ts +5 -5
  620. package/dist/esm/services/cache/generic-cached-object.js +1 -1
  621. package/dist/esm/services/cache/index.d.ts +1 -1
  622. package/dist/esm/services/cache/index.js +1 -1
  623. package/dist/esm/services/cache/product-cache-service.d.ts +21 -21
  624. package/dist/esm/services/cache/product-cache-service.js +68 -68
  625. package/dist/esm/services/cost/cost-calculation-types.d.ts +69 -0
  626. package/dist/esm/services/cost/cost-calculation-types.js +16 -0
  627. package/dist/esm/services/cost/cost-calculation-types.js.map +1 -0
  628. package/dist/esm/services/cost/cost-calculator-service.d.ts +24 -0
  629. package/dist/esm/services/cost/cost-calculator-service.js +451 -0
  630. package/dist/esm/services/cost/cost-calculator-service.js.map +1 -0
  631. package/dist/esm/services/cost/cost-currency-service.d.ts +6 -0
  632. package/dist/esm/services/cost/cost-currency-service.js +85 -0
  633. package/dist/esm/services/cost/cost-currency-service.js.map +1 -0
  634. package/dist/esm/services/cost/cost-filter-service.d.ts +10 -0
  635. package/dist/esm/services/cost/cost-filter-service.js +119 -0
  636. package/dist/esm/services/cost/cost-filter-service.js.map +1 -0
  637. package/dist/esm/services/cost/index.d.ts +5 -0
  638. package/dist/esm/services/cost/index.js +6 -0
  639. package/dist/esm/services/cost/index.js.map +1 -0
  640. package/dist/esm/services/cost/order-cost/index.d.ts +2 -0
  641. package/dist/esm/services/cost/order-cost/index.js +3 -0
  642. package/dist/esm/services/cost/order-cost/index.js.map +1 -0
  643. package/dist/esm/services/cost/order-cost/order-cost-resolution-service.d.ts +23 -0
  644. package/dist/esm/services/cost/order-cost/order-cost-resolution-service.js +356 -0
  645. package/dist/esm/services/cost/order-cost/order-cost-resolution-service.js.map +1 -0
  646. package/dist/esm/services/cost/order-cost/order-cost-resolution-types.d.ts +37 -0
  647. package/dist/esm/services/cost/order-cost/order-cost-resolution-types.js +2 -0
  648. package/dist/esm/services/cost/order-cost/order-cost-resolution-types.js.map +1 -0
  649. package/dist/esm/services/currency-exchange-rate-lookup-service.d.ts +12 -11
  650. package/dist/esm/services/currency-exchange-rate-lookup-service.js +90 -62
  651. package/dist/esm/services/currency-exchange-rate-lookup-service.js.map +1 -1
  652. package/dist/esm/services/db/accounts-db-service.d.ts +9 -9
  653. package/dist/esm/services/db/accounts-db-service.js +29 -29
  654. package/dist/esm/services/db/api-keys-db-service.d.ts +10 -10
  655. package/dist/esm/services/db/api-keys-db-service.js +32 -32
  656. package/dist/esm/services/db/contact-enrichments-db-service.d.ts +15 -15
  657. package/dist/esm/services/db/contact-enrichments-db-service.js +90 -90
  658. package/dist/esm/services/db/currency-exchange-rates-db-service.d.ts +21 -21
  659. package/dist/esm/services/db/currency-exchange-rates-db-service.js +35 -35
  660. package/dist/esm/services/db/custom-measures-db-service.d.ts +14 -14
  661. package/dist/esm/services/db/custom-measures-db-service.js +44 -44
  662. package/dist/esm/services/db/destinations-db-service.d.ts +13 -13
  663. package/dist/esm/services/db/destinations-db-service.js +70 -70
  664. package/dist/esm/services/db/identity-cache-db-service.d.ts +28 -28
  665. package/dist/esm/services/db/identity-cache-db-service.js +313 -313
  666. package/dist/esm/services/db/identity-cache-dynamodb-service.d.ts +44 -44
  667. package/dist/esm/services/db/identity-cache-dynamodb-service.js +727 -727
  668. package/dist/esm/services/db/identity-cache-dynamodb-service.js.map +1 -1
  669. package/dist/esm/services/db/index.d.ts +19 -17
  670. package/dist/esm/services/db/index.js +19 -17
  671. package/dist/esm/services/db/index.js.map +1 -1
  672. package/dist/esm/services/db/log-events-db-service.d.ts +11 -11
  673. package/dist/esm/services/db/log-events-db-service.js +177 -177
  674. package/dist/esm/services/db/pixels-db-service.d.ts +8 -8
  675. package/dist/esm/services/db/pixels-db-service.js +31 -31
  676. package/dist/esm/services/db/products-db-service-types.d.ts +10 -0
  677. package/dist/esm/services/db/products-db-service-types.js +2 -0
  678. package/dist/esm/services/db/products-db-service-types.js.map +1 -0
  679. package/dist/esm/services/db/products-db-service.d.ts +19 -0
  680. package/dist/esm/services/db/products-db-service.js +278 -0
  681. package/dist/esm/services/db/products-db-service.js.map +1 -0
  682. package/dist/esm/services/db/purchasable-contacts-db-service.d.ts +9 -9
  683. package/dist/esm/services/db/purchasable-contacts-db-service.js +39 -39
  684. package/dist/esm/services/db/purchased-contacts/index.d.ts +2 -2
  685. package/dist/esm/services/db/purchased-contacts/index.js +2 -2
  686. package/dist/esm/services/db/purchased-contacts/purchased-contacts-db-service.d.ts +18 -18
  687. package/dist/esm/services/db/purchased-contacts/purchased-contacts-db-service.js +148 -148
  688. package/dist/esm/services/db/purchased-contacts/types.d.ts +11 -11
  689. package/dist/esm/services/db/purchased-contacts/types.js +1 -1
  690. package/dist/esm/services/db/shopify-app-installs-db-service.d.ts +10 -10
  691. package/dist/esm/services/db/shopify-app-installs-db-service.js +48 -48
  692. package/dist/esm/services/db/shopify-products-cache-db-service.d.ts +16 -16
  693. package/dist/esm/services/db/shopify-products-cache-db-service.js +66 -66
  694. package/dist/esm/services/db/subscriptions-db-service.d.ts +11 -11
  695. package/dist/esm/services/db/subscriptions-db-service.js +34 -34
  696. package/dist/esm/services/db/tracking-events-db-service.d.ts +21 -21
  697. package/dist/esm/services/db/tracking-events-db-service.js +184 -184
  698. package/dist/esm/services/db/user-accounts-db-service.d.ts +7 -7
  699. package/dist/esm/services/db/user-accounts-db-service.js +13 -13
  700. package/dist/esm/services/email-verification/contact-email-verification-service.d.ts +7 -7
  701. package/dist/esm/services/email-verification/contact-email-verification-service.js +97 -97
  702. package/dist/esm/services/email-verification/email-verification-service.d.ts +19 -19
  703. package/dist/esm/services/email-verification/email-verification-service.js +127 -127
  704. package/dist/esm/services/email-verification/index.d.ts +2 -2
  705. package/dist/esm/services/email-verification/index.js +2 -2
  706. package/dist/esm/services/eventbridge-integration-service.d.ts +9 -9
  707. package/dist/esm/services/eventbridge-integration-service.js +24 -24
  708. package/dist/esm/services/events/index.d.ts +3 -3
  709. package/dist/esm/services/events/index.js +3 -3
  710. package/dist/esm/services/events/log-event-service.d.ts +19 -19
  711. package/dist/esm/services/events/log-event-service.js +73 -73
  712. package/dist/esm/services/events/metric-event-service.d.ts +9 -9
  713. package/dist/esm/services/events/metric-event-service.js +45 -45
  714. package/dist/esm/services/events/tracking-event-sqs-service.d.ts +8 -8
  715. package/dist/esm/services/events/tracking-event-sqs-service.js +30 -30
  716. package/dist/esm/services/generic-cache-service.d.ts +7 -7
  717. package/dist/esm/services/generic-cache-service.js +29 -29
  718. package/dist/esm/services/index.d.ts +11 -10
  719. package/dist/esm/services/index.js +11 -10
  720. package/dist/esm/services/index.js.map +1 -1
  721. package/dist/esm/services/ipdata-lookup-service.d.ts +20 -20
  722. package/dist/esm/services/ipdata-lookup-service.js +108 -108
  723. package/dist/esm/services/shopify/index.d.ts +2 -2
  724. package/dist/esm/services/shopify/index.js +2 -2
  725. package/dist/esm/services/shopify/products/index.d.ts +1 -1
  726. package/dist/esm/services/shopify/products/index.js +1 -1
  727. package/dist/esm/services/shopify/products/shopify-products-serviceV2.d.ts +17 -17
  728. package/dist/esm/services/shopify/products/shopify-products-serviceV2.js +108 -108
  729. package/dist/esm/services/shopify/shopify-graphql-transformer.d.ts +8 -8
  730. package/dist/esm/services/shopify/shopify-graphql-transformer.js +138 -138
  731. package/dist/esm/types/acuity-types.d.ts +74 -74
  732. package/dist/esm/types/acuity-types.js +1 -1
  733. package/dist/esm/types/api-response.d.ts +6 -6
  734. package/dist/esm/types/api-response.js +1 -1
  735. package/dist/esm/types/index.d.ts +4 -4
  736. package/dist/esm/types/index.js +4 -4
  737. package/dist/esm/types/internal-events/event-detail-types.d.ts +20 -20
  738. package/dist/esm/types/internal-events/event-detail-types.js +24 -24
  739. package/dist/esm/types/internal-events/index.d.ts +1 -1
  740. package/dist/esm/types/internal-events/index.js +1 -1
  741. package/dist/esm/types/shopify-graphql-types/admin.generated.d.ts +123 -123
  742. package/dist/esm/types/shopify-graphql-types/admin.generated.js +1 -1
  743. package/dist/esm/types/shopify-graphql-types/admin.types.d.ts +26289 -26289
  744. package/dist/esm/types/shopify-graphql-types/admin.types.js +5299 -5299
  745. package/dist/esm/types/shopify-graphql-types/index.d.ts +2 -2
  746. package/dist/esm/types/shopify-graphql-types/index.js +2 -2
  747. package/dist/esm/types/shopify-rest-types.d.ts +767 -767
  748. package/dist/esm/types/shopify-rest-types.js +1 -1
  749. package/dist/esm/utils/compression.d.ts +36 -36
  750. package/dist/esm/utils/compression.js +187 -187
  751. package/dist/esm/utils/custom-measure-formula-utils.d.ts +6 -6
  752. package/dist/esm/utils/custom-measure-formula-utils.js +201 -201
  753. package/dist/esm/utils/index.d.ts +4 -4
  754. package/dist/esm/utils/index.js +4 -4
  755. package/dist/esm/utils/retry-envelope.d.ts +12 -12
  756. package/dist/esm/utils/retry-envelope.js +22 -22
  757. package/dist/esm/utils/size.d.ts +2 -2
  758. package/dist/esm/utils/size.js +44 -44
  759. package/package.json +134 -134
@@ -0,0 +1,3320 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const at_tracking_event_types_1 = require("@adtrackify/at-tracking-event-types");
4
+ const cost_calculator_service_1 = require("../../../services/cost/cost-calculator-service");
5
+ jest.mock('../../../helpers/logging-helper', () => ({
6
+ Logger: { debug: jest.fn(), info: jest.fn(), warn: jest.fn(), error: jest.fn() },
7
+ }));
8
+ const emptyRatesMap = {};
9
+ const createLineItem = (overrides = {}) => ({
10
+ lineItemId: '1',
11
+ quantity: 1,
12
+ refundedQuantity: 0,
13
+ filterContext: {
14
+ tags: [],
15
+ productId: '100',
16
+ variantId: '200',
17
+ },
18
+ ...overrides,
19
+ });
20
+ const createContext = (overrides = {}) => ({
21
+ orderCreatedAt: '2024-06-15T10:00:00Z',
22
+ totalPrice: 100,
23
+ grossSalePrice: 120,
24
+ netSalePrice: 90,
25
+ totalProductCost: 40,
26
+ totalShipping: 10,
27
+ totalWeight: 500,
28
+ totalQuantity: 3,
29
+ hasRefund: false,
30
+ orderFilterContext: {
31
+ tags: [],
32
+ paymentGateways: ['stripe'],
33
+ shippingTitles: ['Standard'],
34
+ },
35
+ lineItems: [createLineItem()],
36
+ ...overrides,
37
+ });
38
+ const createEntry = (type, overrides = {}) => ({
39
+ id: 'e1',
40
+ name: 'Test Entry',
41
+ type,
42
+ flatAmount: 5,
43
+ percentRate: 0,
44
+ currency: 'USD',
45
+ category: 'ops',
46
+ channel: 'all',
47
+ createdAt: '2024-01-01T00:00:00Z',
48
+ updatedAt: '2024-01-01T00:00:00Z',
49
+ ...overrides,
50
+ });
51
+ const createConfigs = (overrides = {}) => ({
52
+ globalProductOverride: null,
53
+ order: null,
54
+ gateway: null,
55
+ shipping: null,
56
+ ...overrides,
57
+ });
58
+ describe('CostCalculatorService', () => {
59
+ describe('isDateInRange', () => {
60
+ it('should return true when no date bounds', () => {
61
+ expect(cost_calculator_service_1.CostCalculatorService.isDateInRange('2024-06-15T10:00:00Z')).toBe(true);
62
+ });
63
+ it('should return true when order date is within range', () => {
64
+ expect(cost_calculator_service_1.CostCalculatorService.isDateInRange('2024-06-15T10:00:00Z', '2024-01-01T00:00:00Z', '2024-12-31T23:59:59Z')).toBe(true);
65
+ });
66
+ it('should return false when order date is before effectiveFrom', () => {
67
+ expect(cost_calculator_service_1.CostCalculatorService.isDateInRange('2023-06-15T10:00:00Z', '2024-01-01T00:00:00Z')).toBe(false);
68
+ });
69
+ it('should return false when order date is after effectiveTo', () => {
70
+ expect(cost_calculator_service_1.CostCalculatorService.isDateInRange('2025-06-15T10:00:00Z', undefined, '2024-12-31T23:59:59Z')).toBe(false);
71
+ });
72
+ it('should return true for invalid order date', () => {
73
+ expect(cost_calculator_service_1.CostCalculatorService.isDateInRange('invalid-date', '2024-01-01T00:00:00Z')).toBe(true);
74
+ });
75
+ });
76
+ describe('findMatchingWeightTierAmount', () => {
77
+ it('should return 0 for empty tiers', () => {
78
+ expect(cost_calculator_service_1.CostCalculatorService.findMatchingWeightTierAmount([], 500, undefined)).toBe(0);
79
+ expect(cost_calculator_service_1.CostCalculatorService.findMatchingWeightTierAmount(undefined, 500, undefined)).toBe(0);
80
+ });
81
+ it('should find correct tier', () => {
82
+ const tiers = [
83
+ { weightMin: 0, weightMax: 500, flatAmount: 5 },
84
+ { weightMin: 501, weightMax: 1000, flatAmount: 10 },
85
+ { weightMin: 1001, weightMax: null, flatAmount: 15 },
86
+ ];
87
+ expect(cost_calculator_service_1.CostCalculatorService.findMatchingWeightTierAmount(tiers, 250, undefined)).toBe(5);
88
+ expect(cost_calculator_service_1.CostCalculatorService.findMatchingWeightTierAmount(tiers, 750, undefined)).toBe(10);
89
+ expect(cost_calculator_service_1.CostCalculatorService.findMatchingWeightTierAmount(tiers, 2000, undefined)).toBe(15);
90
+ });
91
+ it('should return 0 when no tier matches', () => {
92
+ const tiers = [{ weightMin: 100, weightMax: 200, flatAmount: 5 }];
93
+ expect(cost_calculator_service_1.CostCalculatorService.findMatchingWeightTierAmount(tiers, 50, undefined)).toBe(0);
94
+ });
95
+ it('should convert weight from grams to OUNCES before matching', () => {
96
+ const tiers = [
97
+ { weightMin: 0, weightMax: 5, flatAmount: 2 },
98
+ { weightMin: 5.01, weightMax: null, flatAmount: 8 },
99
+ ];
100
+ expect(cost_calculator_service_1.CostCalculatorService.findMatchingWeightTierAmount(tiers, 100, at_tracking_event_types_1.WEIGHT_UNIT.OUNCES)).toBe(2);
101
+ expect(cost_calculator_service_1.CostCalculatorService.findMatchingWeightTierAmount(tiers, 500, at_tracking_event_types_1.WEIGHT_UNIT.OUNCES)).toBe(8);
102
+ });
103
+ it('should return 0 when tier has null flatAmount', () => {
104
+ const tiers = [{ weightMin: 0, weightMax: 1000, flatAmount: null }];
105
+ expect(cost_calculator_service_1.CostCalculatorService.findMatchingWeightTierAmount(tiers, 500, undefined)).toBe(0);
106
+ });
107
+ });
108
+ describe('findQuantityTierAmount', () => {
109
+ it('should return 0 for empty tiers', () => {
110
+ expect(cost_calculator_service_1.CostCalculatorService.findQuantityTierAmount([], 5)).toBe(0);
111
+ expect(cost_calculator_service_1.CostCalculatorService.findQuantityTierAmount(undefined, 5)).toBe(0);
112
+ });
113
+ it('should find correct tier', () => {
114
+ const tiers = [
115
+ { quantityMin: 1, quantityMax: 10, flatAmount: 2 },
116
+ { quantityMin: 11, quantityMax: null, flatAmount: 1 },
117
+ ];
118
+ expect(cost_calculator_service_1.CostCalculatorService.findQuantityTierAmount(tiers, 5)).toBe(2);
119
+ expect(cost_calculator_service_1.CostCalculatorService.findQuantityTierAmount(tiers, 20)).toBe(1);
120
+ });
121
+ it('should return 0 when quantity is below all tiers', () => {
122
+ const tiers = [
123
+ { quantityMin: 5, quantityMax: 10, flatAmount: 2 },
124
+ { quantityMin: 11, quantityMax: null, flatAmount: 1 },
125
+ ];
126
+ expect(cost_calculator_service_1.CostCalculatorService.findQuantityTierAmount(tiers, 2)).toBe(0);
127
+ });
128
+ it('should return 0 when tier has null flatAmount', () => {
129
+ const tiers = [{ quantityMin: 1, quantityMax: 10, flatAmount: null }];
130
+ expect(cost_calculator_service_1.CostCalculatorService.findQuantityTierAmount(tiers, 5)).toBe(0);
131
+ });
132
+ });
133
+ describe('calculateHandlingCost', () => {
134
+ const initLineItemCosts = (lineItemIds = ['1']) => {
135
+ const result = {};
136
+ for (const id of lineItemIds) {
137
+ result[id] = { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 };
138
+ }
139
+ return result;
140
+ };
141
+ it('should return 0 when no globalProductOverride', () => {
142
+ const configs = createConfigs();
143
+ const lineItems = [createLineItem({ quantity: 1 })];
144
+ const liCosts = initLineItemCosts();
145
+ expect(cost_calculator_service_1.CostCalculatorService.calculateHandlingCost(configs, lineItems, liCosts, 'USD', emptyRatesMap)).toBe(0);
146
+ });
147
+ it('should apply flat fee per line item (not multiplied by quantity)', () => {
148
+ const configs = createConfigs({
149
+ globalProductOverride: { globalHandlingFee: 2, currency: 'USD' },
150
+ });
151
+ const lineItems = [createLineItem({ lineItemId: '1', quantity: 3 })];
152
+ const liCosts = initLineItemCosts();
153
+ const result = cost_calculator_service_1.CostCalculatorService.calculateHandlingCost(configs, lineItems, liCosts, 'USD', emptyRatesMap);
154
+ expect(result).toBe(2);
155
+ expect(liCosts['1'].totalLineItemHandlingCost).toBe(2);
156
+ });
157
+ it('should sum across multiple line items', () => {
158
+ const configs = createConfigs({
159
+ globalProductOverride: { globalHandlingFee: 2, currency: 'USD' },
160
+ });
161
+ const lineItems = [
162
+ createLineItem({ lineItemId: '1', quantity: 2 }),
163
+ createLineItem({ lineItemId: '2', quantity: 3 }),
164
+ ];
165
+ const liCosts = initLineItemCosts(['1', '2']);
166
+ const result = cost_calculator_service_1.CostCalculatorService.calculateHandlingCost(configs, lineItems, liCosts, 'USD', emptyRatesMap);
167
+ expect(result).toBe(4);
168
+ expect(liCosts['1'].totalLineItemHandlingCost).toBe(2);
169
+ expect(liCosts['2'].totalLineItemHandlingCost).toBe(2);
170
+ });
171
+ it('should convert currency when config currency differs from order currency', () => {
172
+ const configs = createConfigs({
173
+ globalProductOverride: { globalHandlingFee: 10, currency: 'EUR' },
174
+ });
175
+ const rates = { EUR: { USD: 1.1 } };
176
+ const lineItems = [createLineItem({ quantity: 1 })];
177
+ const liCosts = initLineItemCosts();
178
+ const result = cost_calculator_service_1.CostCalculatorService.calculateHandlingCost(configs, lineItems, liCosts, 'USD', rates);
179
+ expect(result).toBeCloseTo(11);
180
+ });
181
+ it('should return fee without conversion when config has no currency', () => {
182
+ const configs = createConfigs({
183
+ globalProductOverride: { globalHandlingFee: 5 },
184
+ });
185
+ const lineItems = [createLineItem({ quantity: 1 })];
186
+ const liCosts = initLineItemCosts();
187
+ expect(cost_calculator_service_1.CostCalculatorService.calculateHandlingCost(configs, lineItems, liCosts, 'USD', emptyRatesMap)).toBe(5);
188
+ });
189
+ it('should add global handling fee on top of existing per-variant handling cost', () => {
190
+ const configs = createConfigs({
191
+ globalProductOverride: { globalHandlingFee: 2, currency: 'USD' },
192
+ });
193
+ const lineItems = [createLineItem({ lineItemId: '1', quantity: 3 })];
194
+ const liCosts = initLineItemCosts();
195
+ liCosts['1'].totalLineItemHandlingCost = 4.5;
196
+ cost_calculator_service_1.CostCalculatorService.calculateHandlingCost(configs, lineItems, liCosts, 'USD', emptyRatesMap);
197
+ expect(liCosts['1'].totalLineItemHandlingCost).toBe(6.5);
198
+ });
199
+ });
200
+ describe('calculateGatewayCost', () => {
201
+ it('should return 0 when no gateway config', () => {
202
+ const configs = createConfigs();
203
+ expect(cost_calculator_service_1.CostCalculatorService.calculateGatewayCost(configs, ['stripe'], 100, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(0);
204
+ });
205
+ it('should return 0 when no matching gateway', () => {
206
+ const configs = createConfigs({
207
+ gateway: {
208
+ entries: [{ gatewayName: 'paypal', flatAmount: 0.30, percentRate: 2.9, currency: 'USD', createdAt: '', updatedAt: '' }],
209
+ },
210
+ });
211
+ expect(cost_calculator_service_1.CostCalculatorService.calculateGatewayCost(configs, ['stripe'], 100, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(0);
212
+ });
213
+ it('should calculate flat + percent for matching gateway', () => {
214
+ const configs = createConfigs({
215
+ gateway: {
216
+ entries: [{ gatewayName: 'stripe', flatAmount: 0.30, percentRate: 2.9, currency: 'USD', createdAt: '', updatedAt: '' }],
217
+ },
218
+ });
219
+ const result = cost_calculator_service_1.CostCalculatorService.calculateGatewayCost(configs, ['stripe'], 100, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap);
220
+ expect(result).toBeCloseTo(3.20);
221
+ });
222
+ it('should be case-insensitive for gateway name', () => {
223
+ const configs = createConfigs({
224
+ gateway: {
225
+ entries: [{ gatewayName: 'Stripe', flatAmount: 1, percentRate: 0, currency: 'USD', createdAt: '', updatedAt: '' }],
226
+ },
227
+ });
228
+ expect(cost_calculator_service_1.CostCalculatorService.calculateGatewayCost(configs, ['stripe'], 100, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(1);
229
+ });
230
+ it('should respect date range', () => {
231
+ const configs = createConfigs({
232
+ gateway: {
233
+ entries: [{
234
+ gatewayName: 'stripe', flatAmount: 1, percentRate: 0, currency: 'USD',
235
+ effectiveFrom: '2025-01-01T00:00:00Z', createdAt: '', updatedAt: '',
236
+ }],
237
+ },
238
+ });
239
+ expect(cost_calculator_service_1.CostCalculatorService.calculateGatewayCost(configs, ['stripe'], 100, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(0);
240
+ });
241
+ it('should convert flat amount from entry currency', () => {
242
+ const configs = createConfigs({
243
+ gateway: {
244
+ entries: [{ gatewayName: 'stripe', flatAmount: 1, percentRate: 0, currency: 'EUR', createdAt: '', updatedAt: '' }],
245
+ },
246
+ });
247
+ const rates = { EUR: { USD: 1.1 } };
248
+ const result = cost_calculator_service_1.CostCalculatorService.calculateGatewayCost(configs, ['stripe'], 100, '2024-06-15T10:00:00Z', 'USD', rates);
249
+ expect(result).toBeCloseTo(1.1);
250
+ });
251
+ it('should pick first matching gateway when multiple entries exist', () => {
252
+ const configs = createConfigs({
253
+ gateway: {
254
+ entries: [
255
+ { gatewayName: 'stripe', flatAmount: 0.30, percentRate: 2.9, currency: 'USD', createdAt: '', updatedAt: '' },
256
+ { gatewayName: 'stripe', flatAmount: 0.50, percentRate: 3.5, currency: 'USD', createdAt: '', updatedAt: '' },
257
+ ],
258
+ },
259
+ });
260
+ const result = cost_calculator_service_1.CostCalculatorService.calculateGatewayCost(configs, ['stripe'], 100, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap);
261
+ expect(result).toBeCloseTo(3.20);
262
+ });
263
+ it('should respect effectiveTo date on gateway entry', () => {
264
+ const configs = createConfigs({
265
+ gateway: {
266
+ entries: [{
267
+ gatewayName: 'stripe', flatAmount: 1, percentRate: 0, currency: 'USD',
268
+ effectiveTo: '2024-01-01T00:00:00Z', createdAt: '', updatedAt: '',
269
+ }],
270
+ },
271
+ });
272
+ expect(cost_calculator_service_1.CostCalculatorService.calculateGatewayCost(configs, ['stripe'], 100, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(0);
273
+ });
274
+ it('should match gateway when order has multiple gateways', () => {
275
+ const configs = createConfigs({
276
+ gateway: {
277
+ entries: [{ gatewayName: 'paypal', flatAmount: 0.50, percentRate: 3.0, currency: 'USD', createdAt: '', updatedAt: '' }],
278
+ },
279
+ });
280
+ const result = cost_calculator_service_1.CostCalculatorService.calculateGatewayCost(configs, ['stripe', 'paypal'], 100, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap);
281
+ expect(result).toBeCloseTo(3.50);
282
+ });
283
+ });
284
+ describe('calculateShippingCost', () => {
285
+ it('should return 0 when no shipping config', () => {
286
+ const configs = createConfigs();
287
+ expect(cost_calculator_service_1.CostCalculatorService.calculateShippingCost(configs, 10, 'US', 500, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(0);
288
+ });
289
+ it('should return shopify amount for SHOPIFY_CHARGES method', () => {
290
+ const configs = createConfigs({
291
+ shipping: { method: at_tracking_event_types_1.SHIPPING_METHOD.SHOPIFY_CHARGES, currency: 'USD' },
292
+ });
293
+ expect(cost_calculator_service_1.CostCalculatorService.calculateShippingCost(configs, 15.50, 'US', 500, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(15.50);
294
+ });
295
+ it('should return fixed rate for FIXED_RATE method', () => {
296
+ const configs = createConfigs({
297
+ shipping: { method: at_tracking_event_types_1.SHIPPING_METHOD.FIXED_RATE, fixedRateAmount: 8.99, currency: 'USD' },
298
+ });
299
+ expect(cost_calculator_service_1.CostCalculatorService.calculateShippingCost(configs, 10, 'US', 500, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(8.99);
300
+ });
301
+ it('should convert fixed rate from shipping currency', () => {
302
+ const configs = createConfigs({
303
+ shipping: { method: at_tracking_event_types_1.SHIPPING_METHOD.FIXED_RATE, fixedRateAmount: 10, currency: 'GBP' },
304
+ });
305
+ const rates = { GBP: { USD: 1.27 } };
306
+ const result = cost_calculator_service_1.CostCalculatorService.calculateShippingCost(configs, 0, 'US', 500, '2024-06-15T10:00:00Z', 'USD', rates);
307
+ expect(result).toBeCloseTo(12.7);
308
+ });
309
+ it('should match shipping profile by country', () => {
310
+ const configs = createConfigs({
311
+ shipping: {
312
+ method: at_tracking_event_types_1.SHIPPING_METHOD.SHIPPING_PROFILES,
313
+ currency: 'USD',
314
+ profiles: [
315
+ {
316
+ id: 'p1', name: 'US Profile', countries: ['US'], isDefault: false,
317
+ rateType: at_tracking_event_types_1.SHIPPING_RATE_TYPE.FLAT, flatAmount: 5, currency: 'USD',
318
+ createdAt: '', updatedAt: '',
319
+ },
320
+ {
321
+ id: 'p2', name: 'Default', countries: [], isDefault: true,
322
+ rateType: at_tracking_event_types_1.SHIPPING_RATE_TYPE.FLAT, flatAmount: 15, currency: 'USD',
323
+ createdAt: '', updatedAt: '',
324
+ },
325
+ ],
326
+ },
327
+ });
328
+ expect(cost_calculator_service_1.CostCalculatorService.calculateShippingCost(configs, 0, 'US', 500, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(5);
329
+ });
330
+ it('should fall back to default profile', () => {
331
+ const configs = createConfigs({
332
+ shipping: {
333
+ method: at_tracking_event_types_1.SHIPPING_METHOD.SHIPPING_PROFILES,
334
+ currency: 'USD',
335
+ profiles: [
336
+ {
337
+ id: 'p1', name: 'US Profile', countries: ['US'], isDefault: false,
338
+ rateType: at_tracking_event_types_1.SHIPPING_RATE_TYPE.FLAT, flatAmount: 5, currency: 'USD',
339
+ createdAt: '', updatedAt: '',
340
+ },
341
+ {
342
+ id: 'p2', name: 'Default', countries: [], isDefault: true,
343
+ rateType: at_tracking_event_types_1.SHIPPING_RATE_TYPE.FLAT, flatAmount: 15, currency: 'USD',
344
+ createdAt: '', updatedAt: '',
345
+ },
346
+ ],
347
+ },
348
+ });
349
+ expect(cost_calculator_service_1.CostCalculatorService.calculateShippingCost(configs, 0, 'CA', 500, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(15);
350
+ });
351
+ it('should handle weight-tiered profile', () => {
352
+ const configs = createConfigs({
353
+ shipping: {
354
+ method: at_tracking_event_types_1.SHIPPING_METHOD.SHIPPING_PROFILES,
355
+ currency: 'USD',
356
+ profiles: [{
357
+ id: 'p1', name: 'Weight', countries: ['US'], isDefault: false,
358
+ rateType: at_tracking_event_types_1.SHIPPING_RATE_TYPE.WEIGHT_TIERED, currency: 'USD',
359
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.GRAMS,
360
+ weightTiers: [
361
+ { weightMin: 0, weightMax: 500, flatAmount: 5 },
362
+ { weightMin: 501, weightMax: null, flatAmount: 10 },
363
+ ],
364
+ createdAt: '', updatedAt: '',
365
+ }],
366
+ },
367
+ });
368
+ expect(cost_calculator_service_1.CostCalculatorService.calculateShippingCost(configs, 0, 'US', 300, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(5);
369
+ expect(cost_calculator_service_1.CostCalculatorService.calculateShippingCost(configs, 0, 'US', 700, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(10);
370
+ });
371
+ it('should return 0 when no shipping profile matches (no country match, no default)', () => {
372
+ const configs = createConfigs({
373
+ shipping: {
374
+ method: at_tracking_event_types_1.SHIPPING_METHOD.SHIPPING_PROFILES,
375
+ currency: 'USD',
376
+ profiles: [{
377
+ id: 'p1', name: 'US Only', countries: ['US'], isDefault: false,
378
+ rateType: at_tracking_event_types_1.SHIPPING_RATE_TYPE.FLAT, flatAmount: 5, currency: 'USD',
379
+ createdAt: '', updatedAt: '',
380
+ }],
381
+ },
382
+ });
383
+ expect(cost_calculator_service_1.CostCalculatorService.calculateShippingCost(configs, 0, 'CA', 500, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(0);
384
+ });
385
+ it('should skip expired shipping profile and fall back to default', () => {
386
+ const configs = createConfigs({
387
+ shipping: {
388
+ method: at_tracking_event_types_1.SHIPPING_METHOD.SHIPPING_PROFILES,
389
+ currency: 'USD',
390
+ profiles: [
391
+ {
392
+ id: 'p1', name: 'US Expired', countries: ['US'], isDefault: false,
393
+ rateType: at_tracking_event_types_1.SHIPPING_RATE_TYPE.FLAT, flatAmount: 5, currency: 'USD',
394
+ effectiveTo: '2024-01-01T00:00:00Z',
395
+ createdAt: '', updatedAt: '',
396
+ },
397
+ {
398
+ id: 'p2', name: 'Default', countries: [], isDefault: true,
399
+ rateType: at_tracking_event_types_1.SHIPPING_RATE_TYPE.FLAT, flatAmount: 12, currency: 'USD',
400
+ createdAt: '', updatedAt: '',
401
+ },
402
+ ],
403
+ },
404
+ });
405
+ expect(cost_calculator_service_1.CostCalculatorService.calculateShippingCost(configs, 0, 'US', 500, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(12);
406
+ });
407
+ it('should convert weight-tiered shipping profile amount from profile currency', () => {
408
+ const configs = createConfigs({
409
+ shipping: {
410
+ method: at_tracking_event_types_1.SHIPPING_METHOD.SHIPPING_PROFILES,
411
+ currency: 'EUR',
412
+ profiles: [{
413
+ id: 'p1', name: 'EU Weight', countries: ['DE'], isDefault: false,
414
+ rateType: at_tracking_event_types_1.SHIPPING_RATE_TYPE.WEIGHT_TIERED, currency: 'EUR',
415
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.GRAMS,
416
+ weightTiers: [
417
+ { weightMin: 0, weightMax: 500, flatAmount: 5 },
418
+ { weightMin: 501, weightMax: null, flatAmount: 10 },
419
+ ],
420
+ createdAt: '', updatedAt: '',
421
+ }],
422
+ },
423
+ });
424
+ const rates = { EUR: { USD: 1.1 } };
425
+ expect(cost_calculator_service_1.CostCalculatorService.calculateShippingCost(configs, 0, 'DE', 300, '2024-06-15T10:00:00Z', 'USD', rates)).toBeCloseTo(5.5);
426
+ });
427
+ it('should be case-insensitive for shipping profile country matching', () => {
428
+ const configs = createConfigs({
429
+ shipping: {
430
+ method: at_tracking_event_types_1.SHIPPING_METHOD.SHIPPING_PROFILES,
431
+ currency: 'USD',
432
+ profiles: [{
433
+ id: 'p1', name: 'US', countries: ['us'], isDefault: false,
434
+ rateType: at_tracking_event_types_1.SHIPPING_RATE_TYPE.FLAT, flatAmount: 7, currency: 'USD',
435
+ createdAt: '', updatedAt: '',
436
+ }],
437
+ },
438
+ });
439
+ expect(cost_calculator_service_1.CostCalculatorService.calculateShippingCost(configs, 0, 'US', 500, '2024-06-15T10:00:00Z', 'USD', emptyRatesMap)).toBe(7);
440
+ });
441
+ });
442
+ describe('calculateOtherCostEntry', () => {
443
+ const initLineItemCosts = () => ({
444
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
445
+ });
446
+ it('should calculate PER_ORDER flat amount as order-level cost', () => {
447
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 3 });
448
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext(), initLineItemCosts(), 'USD', emptyRatesMap);
449
+ expect(result.orderLevel).toBe(3);
450
+ expect(result.lineItemLevel).toBe(0);
451
+ });
452
+ it('should calculate PER_LINE_ITEM with quantity as line-item-level cost', () => {
453
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { flatAmount: 2 });
454
+ const liCosts = initLineItemCosts();
455
+ const context = createContext({ lineItems: [createLineItem({ quantity: 3 })] });
456
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap);
457
+ expect(result.orderLevel).toBe(0);
458
+ expect(result.lineItemLevel).toBe(6);
459
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(6);
460
+ });
461
+ it('should accumulate PER_LINE_ITEM costs across multiple line items as line-item-level', () => {
462
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { flatAmount: 2 });
463
+ const liCosts = {
464
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
465
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
466
+ '3': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
467
+ };
468
+ const context = createContext({
469
+ lineItems: [
470
+ createLineItem({ lineItemId: '1', quantity: 3 }),
471
+ createLineItem({ lineItemId: '2', quantity: 1 }),
472
+ createLineItem({ lineItemId: '3', quantity: 5 }),
473
+ ],
474
+ });
475
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap);
476
+ expect(result.orderLevel).toBe(0);
477
+ expect(result.lineItemLevel).toBe(18);
478
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(6);
479
+ expect(liCosts['2'].totalLineItemOtherCost).toBe(2);
480
+ expect(liCosts['3'].totalLineItemOtherCost).toBe(10);
481
+ });
482
+ it('should calculate BY_ORDER_WEIGHT with flat amount as order-level cost', () => {
483
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, { flatAmount: 0.01 });
484
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 500 }), initLineItemCosts(), 'USD', emptyRatesMap);
485
+ expect(result.orderLevel).toBe(5);
486
+ expect(result.lineItemLevel).toBe(0);
487
+ });
488
+ it('should calculate BY_ORDER_WEIGHT with weight tiers in KILOGRAMS (totalWeight is grams from Shopify)', () => {
489
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
490
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.KILOGRAMS,
491
+ weightIntervals: [
492
+ { weightMin: 0, weightMax: 5, flatAmount: 3 },
493
+ { weightMin: 5.01, weightMax: 20, flatAmount: 8 },
494
+ { weightMin: 20.01, weightMax: null, flatAmount: 15 },
495
+ ],
496
+ });
497
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 2000 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(3);
498
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 10000 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(8);
499
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 25000 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(15);
500
+ });
501
+ it('should calculate BY_ORDER_WEIGHT with weight tiers in POUNDS', () => {
502
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
503
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.POUNDS,
504
+ weightIntervals: [
505
+ { weightMin: 0, weightMax: 10, flatAmount: 5 },
506
+ { weightMin: 10.01, weightMax: null, flatAmount: 12 },
507
+ ],
508
+ });
509
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 2000 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
510
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 10000 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(12);
511
+ });
512
+ it('should calculate BY_ORDER_WEIGHT with weight tiers in GRAMS (no conversion needed)', () => {
513
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
514
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.GRAMS,
515
+ weightIntervals: [
516
+ { weightMin: 0, weightMax: 1000, flatAmount: 2 },
517
+ { weightMin: 1001, weightMax: null, flatAmount: 7 },
518
+ ],
519
+ });
520
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 500 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(2);
521
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 1500 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(7);
522
+ });
523
+ it('should calculate BY_ORDER_WEIGHT with weight tiers and no weightUnit (treats as grams)', () => {
524
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
525
+ weightUnit: undefined,
526
+ weightIntervals: [
527
+ { weightMin: 0, weightMax: 1000, flatAmount: 4 },
528
+ { weightMin: 1001, weightMax: null, flatAmount: 9 },
529
+ ],
530
+ });
531
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 500 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(4);
532
+ });
533
+ it('should calculate BY_ORDER_WEIGHT with flat amount and KILOGRAMS unit (converts weight before multiplying)', () => {
534
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
535
+ flatAmount: 0.50,
536
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.KILOGRAMS,
537
+ });
538
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 2000 }), initLineItemCosts(), 'USD', emptyRatesMap);
539
+ expect(result.orderLevel).toBe(1);
540
+ expect(result.lineItemLevel).toBe(0);
541
+ });
542
+ it('should calculate BY_ORDER_WEIGHT with flat amount and POUNDS unit (converts weight before multiplying)', () => {
543
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
544
+ flatAmount: 2.00,
545
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.POUNDS,
546
+ });
547
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 453.592 }), initLineItemCosts(), 'USD', emptyRatesMap);
548
+ expect(result.orderLevel).toBeCloseTo(2);
549
+ expect(result.lineItemLevel).toBe(0);
550
+ });
551
+ it('should calculate BY_LINE_ITEM_QUANTITY with flat amount as line-item-level cost', () => {
552
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, { flatAmount: 0.50 });
553
+ const liCosts = initLineItemCosts();
554
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ lineItems: [createLineItem({ quantity: 10 })] }), liCosts, 'USD', emptyRatesMap);
555
+ expect(result.orderLevel).toBe(0);
556
+ expect(result.lineItemLevel).toBe(5);
557
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(5);
558
+ });
559
+ it('should calculate BY_LINE_ITEM_QUANTITY with tiers per line item quantity', () => {
560
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, {
561
+ quantityIntervals: [
562
+ { quantityMin: 1, quantityMax: 5, flatAmount: 3 },
563
+ { quantityMin: 6, quantityMax: null, flatAmount: 2 },
564
+ ],
565
+ });
566
+ const liCosts1 = initLineItemCosts();
567
+ const result1 = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ lineItems: [createLineItem({ quantity: 3 })] }), liCosts1, 'USD', emptyRatesMap);
568
+ expect(result1.lineItemLevel).toBe(3);
569
+ expect(result1.orderLevel).toBe(0);
570
+ const liCosts2 = initLineItemCosts();
571
+ const result2 = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ lineItems: [createLineItem({ quantity: 10 })] }), liCosts2, 'USD', emptyRatesMap);
572
+ expect(result2.lineItemLevel).toBe(2);
573
+ expect(result2.orderLevel).toBe(0);
574
+ });
575
+ it('should calculate BY_LINE_ITEM_QUANTITY with multiple line items at different tiers', () => {
576
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, {
577
+ quantityIntervals: [
578
+ { quantityMin: 1, quantityMax: 5, flatAmount: 3 },
579
+ { quantityMin: 6, quantityMax: null, flatAmount: 2 },
580
+ ],
581
+ });
582
+ const liCosts = {
583
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
584
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
585
+ };
586
+ const context = createContext({
587
+ lineItems: [
588
+ createLineItem({ lineItemId: '1', quantity: 3 }),
589
+ createLineItem({ lineItemId: '2', quantity: 8 }),
590
+ ],
591
+ });
592
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap);
593
+ expect(result.lineItemLevel).toBe(5);
594
+ expect(result.orderLevel).toBe(0);
595
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(3);
596
+ expect(liCosts['2'].totalLineItemOtherCost).toBe(2);
597
+ });
598
+ it('should calculate BY_ORDER_QUANTITY with flat amount as order-level cost', () => {
599
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_QUANTITY, { flatAmount: 0.50 });
600
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalQuantity: 10 }), initLineItemCosts(), 'USD', emptyRatesMap);
601
+ expect(result.orderLevel).toBe(5);
602
+ expect(result.lineItemLevel).toBe(0);
603
+ });
604
+ it('should calculate BY_ORDER_QUANTITY with tiers as order-level cost', () => {
605
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_QUANTITY, {
606
+ quantityIntervals: [
607
+ { quantityMin: 1, quantityMax: 5, flatAmount: 3 },
608
+ { quantityMin: 6, quantityMax: null, flatAmount: 2 },
609
+ ],
610
+ });
611
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalQuantity: 3 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(3);
612
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalQuantity: 10 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(2);
613
+ });
614
+ it('should calculate BY_ORDER_QUANTITY with multiple tiers correctly', () => {
615
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_QUANTITY, {
616
+ quantityIntervals: [
617
+ { quantityMin: 1, quantityMax: 10, flatAmount: 5 },
618
+ { quantityMin: 11, quantityMax: 50, flatAmount: 3 },
619
+ { quantityMin: 51, quantityMax: null, flatAmount: 1 },
620
+ ],
621
+ });
622
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalQuantity: 1 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
623
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalQuantity: 10 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
624
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalQuantity: 25 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(3);
625
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalQuantity: 100 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(1);
626
+ });
627
+ it('should return 0 for BY_ORDER_QUANTITY when quantity does not match any tier', () => {
628
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_QUANTITY, {
629
+ quantityIntervals: [
630
+ { quantityMin: 5, quantityMax: 10, flatAmount: 3 },
631
+ ],
632
+ });
633
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalQuantity: 2 }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
634
+ });
635
+ it('should calculate BY_ORDER_QUANTITY with empty quantityIntervals using flat amount', () => {
636
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_QUANTITY, { flatAmount: 2, quantityIntervals: [] });
637
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalQuantity: 7 }), initLineItemCosts(), 'USD', emptyRatesMap);
638
+ expect(result.orderLevel).toBe(14);
639
+ });
640
+ it('should return 0 for BY_LINE_ITEM_QUANTITY when line item quantity is below all tiers', () => {
641
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, {
642
+ quantityIntervals: [{ quantityMin: 5, quantityMax: 10, flatAmount: 3 }],
643
+ });
644
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ lineItems: [createLineItem({ quantity: 2 })] }), initLineItemCosts(), 'USD', emptyRatesMap);
645
+ expect(result.lineItemLevel).toBe(0);
646
+ expect(result.orderLevel).toBe(0);
647
+ });
648
+ it('should apply BY_LINE_ITEM_QUANTITY filters per line item, skipping non-matching ones', () => {
649
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, {
650
+ flatAmount: 1,
651
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
652
+ });
653
+ const liCosts = {
654
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
655
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
656
+ };
657
+ const context = createContext({
658
+ lineItems: [
659
+ createLineItem({ lineItemId: '1', quantity: 4, filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike' } }),
660
+ createLineItem({ lineItemId: '2', quantity: 6, filterContext: { productId: 'P2', variantId: 'V2', tags: [], vendor: 'Adidas' } }),
661
+ ],
662
+ });
663
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap);
664
+ expect(result.lineItemLevel).toBe(4);
665
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(4);
666
+ expect(liCosts['2'].totalLineItemOtherCost).toBe(0);
667
+ });
668
+ it('should convert flat amount for BY_ORDER_QUANTITY', () => {
669
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_QUANTITY, { flatAmount: 5, currency: 'GBP' });
670
+ const rates = { GBP: { USD: 1.27 } };
671
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalQuantity: 10 }), initLineItemCosts(), 'USD', rates);
672
+ expect(result.orderLevel).toBeCloseTo(63.5);
673
+ expect(result.lineItemLevel).toBe(0);
674
+ });
675
+ it('should convert tier amount currency for BY_ORDER_QUANTITY', () => {
676
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_QUANTITY, {
677
+ currency: 'EUR',
678
+ quantityIntervals: [{ quantityMin: 1, quantityMax: null, flatAmount: 10 }],
679
+ });
680
+ const rates = { EUR: { USD: 1.1 } };
681
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalQuantity: 5 }), initLineItemCosts(), 'USD', rates);
682
+ expect(result.orderLevel).toBeCloseTo(11);
683
+ });
684
+ it('should return 0 for BY_ORDER_QUANTITY when order filters do not match', () => {
685
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_QUANTITY, {
686
+ flatAmount: 5,
687
+ filters: { orderTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['wholesale'] } },
688
+ });
689
+ const context = createContext({
690
+ totalQuantity: 10,
691
+ orderFilterContext: { tags: ['retail'], paymentGateways: [], shippingTitles: [] },
692
+ });
693
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
694
+ });
695
+ it('should calculate PER_REFUND_ORDER when hasRefund as order-level cost', () => {
696
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_ORDER, { flatAmount: 5 });
697
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ hasRefund: true }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
698
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ hasRefund: false }), initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
699
+ });
700
+ it('should calculate PER_REFUND_LINE_ITEM as line-item-level cost', () => {
701
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_LINE_ITEM, { flatAmount: 2 });
702
+ const liCosts = initLineItemCosts();
703
+ const context = createContext({
704
+ hasRefund: true,
705
+ lineItems: [createLineItem({ refundedQuantity: 2 })],
706
+ });
707
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap);
708
+ expect(result.orderLevel).toBe(0);
709
+ expect(result.lineItemLevel).toBe(4);
710
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(4);
711
+ });
712
+ it('should skip PER_REFUND_LINE_ITEM when refundedQuantity is 0', () => {
713
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_LINE_ITEM, { flatAmount: 2 });
714
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext(), initLineItemCosts(), 'USD', emptyRatesMap);
715
+ expect(result.orderLevel).toBe(0);
716
+ expect(result.lineItemLevel).toBe(0);
717
+ });
718
+ it('should only count refunded line items for PER_REFUND_LINE_ITEM with mixed refund/non-refund', () => {
719
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_LINE_ITEM, { flatAmount: 3 });
720
+ const liCosts = {
721
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
722
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
723
+ '3': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
724
+ };
725
+ const context = createContext({
726
+ hasRefund: true,
727
+ lineItems: [
728
+ createLineItem({ lineItemId: '1', refundedQuantity: 2 }),
729
+ createLineItem({ lineItemId: '2', refundedQuantity: 0 }),
730
+ createLineItem({ lineItemId: '3', refundedQuantity: 1 }),
731
+ ],
732
+ });
733
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap);
734
+ expect(result.lineItemLevel).toBe(9);
735
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(6);
736
+ expect(liCosts['2'].totalLineItemOtherCost).toBe(0);
737
+ expect(liCosts['3'].totalLineItemOtherCost).toBe(3);
738
+ });
739
+ it('should calculate PCT_COGS as order-level cost', () => {
740
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_COGS, { percentRate: 10 });
741
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalProductCost: 50 }), initLineItemCosts(), 'USD', emptyRatesMap);
742
+ expect(result.orderLevel).toBe(5);
743
+ });
744
+ it('should calculate PCT_SHIPPING as order-level cost', () => {
745
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_SHIPPING, { percentRate: 5 });
746
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalShipping: 20 }), initLineItemCosts(), 'USD', emptyRatesMap);
747
+ expect(result.orderLevel).toBe(1);
748
+ });
749
+ it('should calculate PCT_TOTAL_SALES as order-level cost', () => {
750
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, { percentRate: 2 });
751
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalPrice: 200 }), initLineItemCosts(), 'USD', emptyRatesMap);
752
+ expect(result.orderLevel).toBe(4);
753
+ });
754
+ it('should calculate PCT_GROSS_SALES as order-level cost', () => {
755
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_GROSS_SALES, { percentRate: 3 });
756
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ grossSalePrice: 150 }), initLineItemCosts(), 'USD', emptyRatesMap);
757
+ expect(result.orderLevel).toBeCloseTo(4.5);
758
+ });
759
+ it('should calculate PCT_NET_SALES as order-level cost', () => {
760
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_NET_SALES, { percentRate: 4 });
761
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ netSalePrice: 80 }), initLineItemCosts(), 'USD', emptyRatesMap);
762
+ expect(result.orderLevel).toBe(3.2);
763
+ });
764
+ it('should return 0 for PCT_COGS when totalProductCost is 0', () => {
765
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_COGS, { percentRate: 10 });
766
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalProductCost: 0 }), initLineItemCosts(), 'USD', emptyRatesMap);
767
+ expect(result.orderLevel).toBe(0);
768
+ });
769
+ it('should return 0 for PCT_SHIPPING when totalShipping is 0', () => {
770
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_SHIPPING, { percentRate: 5 });
771
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalShipping: 0 }), initLineItemCosts(), 'USD', emptyRatesMap);
772
+ expect(result.orderLevel).toBe(0);
773
+ });
774
+ it('should return 0 for PER_ORDER when flatAmount is 0', () => {
775
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 0 });
776
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext(), initLineItemCosts(), 'USD', emptyRatesMap);
777
+ expect(result.orderLevel).toBe(0);
778
+ });
779
+ it('should return 0 for PER_LINE_ITEM when quantity is 0', () => {
780
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { flatAmount: 2 });
781
+ const context = createContext({ lineItems: [createLineItem({ quantity: 0 })] });
782
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap);
783
+ expect(result.lineItemLevel).toBe(0);
784
+ });
785
+ it('should return 0 for unknown entry type', () => {
786
+ const entry = createEntry('UNKNOWN_TYPE', { flatAmount: 5 });
787
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext(), initLineItemCosts(), 'USD', emptyRatesMap);
788
+ expect(result.orderLevel).toBe(0);
789
+ expect(result.lineItemLevel).toBe(0);
790
+ });
791
+ it('should convert flat amount for BY_ORDER_WEIGHT (flat * weight mode)', () => {
792
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, { flatAmount: 2, currency: 'EUR' });
793
+ const rates = { EUR: { USD: 1.1 } };
794
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 500 }), initLineItemCosts(), 'USD', rates);
795
+ expect(result.orderLevel).toBeCloseTo(1100);
796
+ });
797
+ it('should convert tier amount for BY_ORDER_WEIGHT with tiers + currency', () => {
798
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
799
+ currency: 'EUR',
800
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.GRAMS,
801
+ weightIntervals: [
802
+ { weightMin: 0, weightMax: 500, flatAmount: 5 },
803
+ { weightMin: 501, weightMax: null, flatAmount: 10 },
804
+ ],
805
+ });
806
+ const rates = { EUR: { USD: 1.1 } };
807
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ totalWeight: 300 }), initLineItemCosts(), 'USD', rates).orderLevel).toBeCloseTo(5.5);
808
+ });
809
+ it('should convert flat amount for BY_LINE_ITEM_QUANTITY', () => {
810
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, { flatAmount: 5, currency: 'GBP' });
811
+ const rates = { GBP: { USD: 1.27 } };
812
+ const liCosts = initLineItemCosts();
813
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext({ lineItems: [createLineItem({ quantity: 10 })] }), liCosts, 'USD', rates);
814
+ expect(result.lineItemLevel).toBeCloseTo(63.5);
815
+ expect(result.orderLevel).toBe(0);
816
+ });
817
+ it('should return 0 when order-level filters do not match', () => {
818
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
819
+ flatAmount: 5,
820
+ filters: { countries: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['US'] } },
821
+ });
822
+ const context = createContext({ orderFilterContext: { tags: [], paymentGateways: [], shippingTitles: [], shipCountryCode: 'CA' } });
823
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap);
824
+ expect(result.orderLevel).toBe(0);
825
+ });
826
+ it('should convert flat amount from entry currency', () => {
827
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 10, currency: 'EUR' });
828
+ const rates = { EUR: { USD: 1.1 } };
829
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, createContext(), initLineItemCosts(), 'USD', rates);
830
+ expect(result.orderLevel).toBeCloseTo(11);
831
+ });
832
+ it('should return 0 for PER_ORDER when product filter does not match any line item', () => {
833
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
834
+ flatAmount: 5,
835
+ filters: { products: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['P999'] } },
836
+ });
837
+ const context = createContext({
838
+ lineItems: [createLineItem({ filterContext: { productId: 'P100', variantId: 'V1', tags: [] } })],
839
+ });
840
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
841
+ });
842
+ it('should apply PER_ORDER when product filter matches a line item', () => {
843
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
844
+ flatAmount: 5,
845
+ filters: { products: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['P100'] } },
846
+ });
847
+ const context = createContext({
848
+ lineItems: [createLineItem({ filterContext: { productId: 'P100', variantId: 'V1', tags: [] } })],
849
+ });
850
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
851
+ });
852
+ it('should apply PER_ORDER when at least one of multiple line items matches product filter', () => {
853
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
854
+ flatAmount: 5,
855
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
856
+ });
857
+ const context = createContext({
858
+ lineItems: [
859
+ createLineItem({ lineItemId: '1', filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Adidas' } }),
860
+ createLineItem({ lineItemId: '2', filterContext: { productId: 'P2', variantId: 'V2', tags: [], vendor: 'Nike' } }),
861
+ ],
862
+ });
863
+ const liCosts = {
864
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
865
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
866
+ };
867
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap).orderLevel).toBe(5);
868
+ });
869
+ it('should return 0 for BY_ORDER_WEIGHT when productTags filter does not match any line item', () => {
870
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
871
+ flatAmount: 0.01,
872
+ filters: { productTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['fragile'] } },
873
+ });
874
+ const context = createContext({
875
+ totalWeight: 500,
876
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: ['lightweight'] } })],
877
+ });
878
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
879
+ });
880
+ it('should apply BY_ORDER_WEIGHT when productTags filter matches a line item', () => {
881
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
882
+ flatAmount: 0.01,
883
+ filters: { productTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['fragile'] } },
884
+ });
885
+ const context = createContext({
886
+ totalWeight: 500,
887
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: ['fragile'] } })],
888
+ });
889
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
890
+ });
891
+ it('should return 0 for BY_LINE_ITEM_QUANTITY when productVendors filter does not match', () => {
892
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, {
893
+ flatAmount: 0.5,
894
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
895
+ });
896
+ const context = createContext({
897
+ lineItems: [createLineItem({ quantity: 10, filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Adidas' } })],
898
+ });
899
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).lineItemLevel).toBe(0);
900
+ });
901
+ it('should apply BY_LINE_ITEM_QUANTITY when productVendors filter matches', () => {
902
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, {
903
+ flatAmount: 0.5,
904
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
905
+ });
906
+ const context = createContext({
907
+ lineItems: [createLineItem({ quantity: 10, filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike' } })],
908
+ });
909
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).lineItemLevel).toBe(5);
910
+ });
911
+ it('should return 0 for PCT_GROSS_SALES when productTypes filter does not match', () => {
912
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_GROSS_SALES, {
913
+ percentRate: 3,
914
+ filters: { productTypes: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Electronics'] } },
915
+ });
916
+ const context = createContext({
917
+ grossSalePrice: 150,
918
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], productType: 'Clothing' } })],
919
+ });
920
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
921
+ });
922
+ it('should apply PCT_GROSS_SALES when productTypes filter matches', () => {
923
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_GROSS_SALES, {
924
+ percentRate: 3,
925
+ filters: { productTypes: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Electronics'] } },
926
+ });
927
+ const context = createContext({
928
+ grossSalePrice: 150,
929
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], productType: 'Electronics' } })],
930
+ });
931
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBeCloseTo(4.5);
932
+ });
933
+ it('should return 0 for PCT_NET_SALES when productCategories filter does not match', () => {
934
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_NET_SALES, {
935
+ percentRate: 5,
936
+ filters: { productCategories: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Home > Kitchen'] } },
937
+ });
938
+ const context = createContext({
939
+ netSalePrice: 80,
940
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], category: 'Sports' } })],
941
+ });
942
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
943
+ });
944
+ it('should return 0 for PCT_TOTAL_SALES when products filter does not match', () => {
945
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, {
946
+ percentRate: 2,
947
+ filters: { products: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['P999'] } },
948
+ });
949
+ const context = createContext({
950
+ totalPrice: 200,
951
+ lineItems: [createLineItem({ filterContext: { productId: 'P100', variantId: 'V1', tags: [] } })],
952
+ });
953
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
954
+ });
955
+ it('should return 0 for PCT_COGS when product filter does not match', () => {
956
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_COGS, {
957
+ percentRate: 10,
958
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Apple'] } },
959
+ });
960
+ const context = createContext({
961
+ totalProductCost: 50,
962
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Samsung' } })],
963
+ });
964
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
965
+ });
966
+ it('should return 0 for PCT_SHIPPING when product filter does not match', () => {
967
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_SHIPPING, {
968
+ percentRate: 5,
969
+ filters: { products: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['P500'] } },
970
+ });
971
+ const context = createContext({
972
+ totalShipping: 20,
973
+ lineItems: [createLineItem({ filterContext: { productId: 'P100', variantId: 'V1', tags: [] } })],
974
+ });
975
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
976
+ });
977
+ it('should return 0 for PER_REFUND_ORDER when product filter does not match', () => {
978
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_ORDER, {
979
+ flatAmount: 5,
980
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
981
+ });
982
+ const context = createContext({
983
+ hasRefund: true,
984
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Adidas' } })],
985
+ });
986
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
987
+ });
988
+ it('should apply PER_REFUND_ORDER when product filter matches and hasRefund', () => {
989
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_ORDER, {
990
+ flatAmount: 5,
991
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
992
+ });
993
+ const context = createContext({
994
+ hasRefund: true,
995
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike' } })],
996
+ });
997
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
998
+ });
999
+ it('should only apply PER_LINE_ITEM cost to line items matching product filter', () => {
1000
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, {
1001
+ flatAmount: 2,
1002
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
1003
+ });
1004
+ const liCosts = {
1005
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1006
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1007
+ };
1008
+ const context = createContext({
1009
+ lineItems: [
1010
+ createLineItem({ lineItemId: '1', quantity: 3, filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike' } }),
1011
+ createLineItem({ lineItemId: '2', quantity: 2, filterContext: { productId: 'P2', variantId: 'V2', tags: [], vendor: 'Adidas' } }),
1012
+ ],
1013
+ });
1014
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap);
1015
+ expect(result.lineItemLevel).toBe(6);
1016
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(6);
1017
+ expect(liCosts['2'].totalLineItemOtherCost).toBe(0);
1018
+ });
1019
+ it('should return 0 for PER_LINE_ITEM when no line items match product filter', () => {
1020
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, {
1021
+ flatAmount: 2,
1022
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
1023
+ });
1024
+ const context = createContext({
1025
+ lineItems: [
1026
+ createLineItem({ lineItemId: '1', quantity: 3, filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Adidas' } }),
1027
+ ],
1028
+ });
1029
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).lineItemLevel).toBe(0);
1030
+ });
1031
+ it('should only apply PER_REFUND_LINE_ITEM cost to refunded line items matching product filter', () => {
1032
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_LINE_ITEM, {
1033
+ flatAmount: 3,
1034
+ filters: { products: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['P1'] } },
1035
+ });
1036
+ const liCosts = {
1037
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1038
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1039
+ };
1040
+ const context = createContext({
1041
+ hasRefund: true,
1042
+ lineItems: [
1043
+ createLineItem({ lineItemId: '1', refundedQuantity: 2, filterContext: { productId: 'P1', variantId: 'V1', tags: [] } }),
1044
+ createLineItem({ lineItemId: '2', refundedQuantity: 1, filterContext: { productId: 'P2', variantId: 'V2', tags: [] } }),
1045
+ ],
1046
+ });
1047
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap);
1048
+ expect(result.lineItemLevel).toBe(6);
1049
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(6);
1050
+ expect(liCosts['2'].totalLineItemOtherCost).toBe(0);
1051
+ });
1052
+ it('should check both order and product filters for PER_ORDER (both must pass)', () => {
1053
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1054
+ flatAmount: 5,
1055
+ filters: {
1056
+ countries: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['US'] },
1057
+ productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] },
1058
+ },
1059
+ });
1060
+ const contextPass = createContext({
1061
+ orderFilterContext: { tags: [], paymentGateways: [], shippingTitles: [], shipCountryCode: 'US' },
1062
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike' } })],
1063
+ });
1064
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, contextPass, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
1065
+ const contextProductFail = createContext({
1066
+ orderFilterContext: { tags: [], paymentGateways: [], shippingTitles: [], shipCountryCode: 'US' },
1067
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Adidas' } })],
1068
+ });
1069
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, contextProductFail, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
1070
+ const contextOrderFail = createContext({
1071
+ orderFilterContext: { tags: [], paymentGateways: [], shippingTitles: [], shipCountryCode: 'CA' },
1072
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike' } })],
1073
+ });
1074
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, contextOrderFail, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
1075
+ });
1076
+ it('should return 0 for BY_ORDER_WEIGHT when order-level country filter does not match', () => {
1077
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
1078
+ flatAmount: 0.01,
1079
+ filters: { countries: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['US'] } },
1080
+ });
1081
+ const context = createContext({
1082
+ totalWeight: 500,
1083
+ orderFilterContext: { tags: [], paymentGateways: [], shippingTitles: [], shipCountryCode: 'CA' },
1084
+ });
1085
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
1086
+ });
1087
+ it('should return 0 for BY_LINE_ITEM_QUANTITY when orderTags filter does not match', () => {
1088
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, {
1089
+ flatAmount: 0.5,
1090
+ filters: { orderTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['wholesale'] } },
1091
+ });
1092
+ const context = createContext({
1093
+ lineItems: [createLineItem({ quantity: 10 })],
1094
+ orderFilterContext: { tags: ['retail'], paymentGateways: [], shippingTitles: [] },
1095
+ });
1096
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).lineItemLevel).toBe(0);
1097
+ });
1098
+ it('should return 0 for PCT_GROSS_SALES when orderPaymentGateways filter does not match', () => {
1099
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_GROSS_SALES, {
1100
+ percentRate: 3,
1101
+ filters: { orderPaymentGateways: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['stripe'] } },
1102
+ });
1103
+ const context = createContext({
1104
+ grossSalePrice: 150,
1105
+ orderFilterContext: { tags: [], paymentGateways: ['paypal'], shippingTitles: [] },
1106
+ });
1107
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
1108
+ });
1109
+ it('should apply PCT_GROSS_SALES when orderPaymentGateways filter matches', () => {
1110
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_GROSS_SALES, {
1111
+ percentRate: 3,
1112
+ filters: { orderPaymentGateways: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['stripe'] } },
1113
+ });
1114
+ const context = createContext({
1115
+ grossSalePrice: 150,
1116
+ orderFilterContext: { tags: [], paymentGateways: ['stripe'], shippingTitles: [] },
1117
+ });
1118
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBeCloseTo(4.5);
1119
+ });
1120
+ it('should return 0 for PER_LINE_ITEM when orderShippingTitles filter does not match', () => {
1121
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, {
1122
+ flatAmount: 2,
1123
+ filters: { orderShippingTitles: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Express'] } },
1124
+ });
1125
+ const context = createContext({
1126
+ orderFilterContext: { tags: [], paymentGateways: [], shippingTitles: ['Standard'] },
1127
+ lineItems: [createLineItem({ quantity: 3 })],
1128
+ });
1129
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).lineItemLevel).toBe(0);
1130
+ });
1131
+ it('should return 0 for PCT_COGS when orderSourceNames filter does not match', () => {
1132
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_COGS, {
1133
+ percentRate: 10,
1134
+ filters: { orderSourceNames: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['web'] } },
1135
+ });
1136
+ const context = createContext({
1137
+ totalProductCost: 50,
1138
+ orderFilterContext: { tags: [], paymentGateways: [], shippingTitles: [], sourceName: 'pos' },
1139
+ });
1140
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
1141
+ });
1142
+ it('should apply PER_ORDER when EXCLUDE product filter does not match any line item vendor', () => {
1143
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1144
+ flatAmount: 5,
1145
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.EXCLUDE, values: ['Nike'] } },
1146
+ });
1147
+ const context = createContext({
1148
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Adidas' } })],
1149
+ });
1150
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
1151
+ });
1152
+ it('should return 0 for PER_ORDER when EXCLUDE product filter matches all line items', () => {
1153
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1154
+ flatAmount: 5,
1155
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.EXCLUDE, values: ['Nike'] } },
1156
+ });
1157
+ const context = createContext({
1158
+ lineItems: [
1159
+ createLineItem({ lineItemId: '1', filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike' } }),
1160
+ ],
1161
+ });
1162
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
1163
+ });
1164
+ it('should apply PER_ORDER when product filter mode is ALL', () => {
1165
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1166
+ flatAmount: 5,
1167
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.ALL, values: [] } },
1168
+ });
1169
+ const context = createContext({
1170
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'anything' } })],
1171
+ });
1172
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
1173
+ });
1174
+ it('should return 0 for PER_ORDER when multiple product filters set and no single line item satisfies all', () => {
1175
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1176
+ flatAmount: 5,
1177
+ filters: {
1178
+ productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] },
1179
+ productTypes: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Shoes'] },
1180
+ },
1181
+ });
1182
+ const context = createContext({
1183
+ lineItems: [
1184
+ createLineItem({ lineItemId: '1', filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike', productType: 'Hats' } }),
1185
+ createLineItem({ lineItemId: '2', filterContext: { productId: 'P2', variantId: 'V2', tags: [], vendor: 'Adidas', productType: 'Shoes' } }),
1186
+ ],
1187
+ });
1188
+ const liCosts = {
1189
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1190
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1191
+ };
1192
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap).orderLevel).toBe(0);
1193
+ });
1194
+ it('should apply PER_ORDER when one line item satisfies all product filters', () => {
1195
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1196
+ flatAmount: 5,
1197
+ filters: {
1198
+ productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] },
1199
+ productTypes: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Shoes'] },
1200
+ },
1201
+ });
1202
+ const context = createContext({
1203
+ lineItems: [
1204
+ createLineItem({ lineItemId: '1', filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Adidas', productType: 'Hats' } }),
1205
+ createLineItem({ lineItemId: '2', filterContext: { productId: 'P2', variantId: 'V2', tags: [], vendor: 'Nike', productType: 'Shoes' } }),
1206
+ ],
1207
+ });
1208
+ const liCosts = {
1209
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1210
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1211
+ };
1212
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap).orderLevel).toBe(5);
1213
+ });
1214
+ it('should match PER_ORDER with productId_variantId composite filter', () => {
1215
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1216
+ flatAmount: 5,
1217
+ filters: { products: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['P100_V200'] } },
1218
+ });
1219
+ const matchContext = createContext({
1220
+ lineItems: [createLineItem({ filterContext: { productId: 'P100', variantId: 'V200', tags: [] } })],
1221
+ });
1222
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, matchContext, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
1223
+ const noMatchContext = createContext({
1224
+ lineItems: [createLineItem({ filterContext: { productId: 'P100', variantId: 'V999', tags: [] } })],
1225
+ });
1226
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, noMatchContext, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
1227
+ });
1228
+ it('should convert currency AND evaluate product filters for PER_ORDER', () => {
1229
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1230
+ flatAmount: 10,
1231
+ currency: 'EUR',
1232
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
1233
+ });
1234
+ const rates = { EUR: { USD: 1.1 } };
1235
+ const matchContext = createContext({
1236
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike' } })],
1237
+ });
1238
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, matchContext, initLineItemCosts(), 'USD', rates).orderLevel).toBeCloseTo(11);
1239
+ const noMatchContext = createContext({
1240
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Adidas' } })],
1241
+ });
1242
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, noMatchContext, initLineItemCosts(), 'USD', rates).orderLevel).toBe(0);
1243
+ });
1244
+ it('should evaluate weight tiers AND product filters for BY_ORDER_WEIGHT', () => {
1245
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
1246
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.KILOGRAMS,
1247
+ weightIntervals: [
1248
+ { weightMin: 0, weightMax: 5, flatAmount: 3 },
1249
+ { weightMin: 5.01, weightMax: null, flatAmount: 8 },
1250
+ ],
1251
+ filters: { productCategories: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Heavy Items'] } },
1252
+ });
1253
+ const matchContext = createContext({
1254
+ totalWeight: 2000,
1255
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], category: 'Heavy Items' } })],
1256
+ });
1257
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, matchContext, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(3);
1258
+ const noMatchContext = createContext({
1259
+ totalWeight: 2000,
1260
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], category: 'Light Items' } })],
1261
+ });
1262
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, noMatchContext, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
1263
+ });
1264
+ it('should evaluate quantity tiers AND product filters for BY_LINE_ITEM_QUANTITY', () => {
1265
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, {
1266
+ quantityIntervals: [
1267
+ { quantityMin: 1, quantityMax: 10, flatAmount: 2 },
1268
+ { quantityMin: 11, quantityMax: null, flatAmount: 1 },
1269
+ ],
1270
+ filters: { productTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['bulk'] } },
1271
+ });
1272
+ const matchContext = createContext({
1273
+ lineItems: [createLineItem({ quantity: 5, filterContext: { productId: 'P1', variantId: 'V1', tags: ['bulk'] } })],
1274
+ });
1275
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, matchContext, initLineItemCosts(), 'USD', emptyRatesMap).lineItemLevel).toBe(2);
1276
+ const noMatchContext = createContext({
1277
+ lineItems: [createLineItem({ quantity: 5, filterContext: { productId: 'P1', variantId: 'V1', tags: ['single'] } })],
1278
+ });
1279
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, noMatchContext, initLineItemCosts(), 'USD', emptyRatesMap).lineItemLevel).toBe(0);
1280
+ });
1281
+ it('should apply PER_LINE_ITEM to line items NOT matching EXCLUDE product filter', () => {
1282
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, {
1283
+ flatAmount: 2,
1284
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.EXCLUDE, values: ['Nike'] } },
1285
+ });
1286
+ const liCosts = {
1287
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1288
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1289
+ };
1290
+ const context = createContext({
1291
+ lineItems: [
1292
+ createLineItem({ lineItemId: '1', quantity: 3, filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike' } }),
1293
+ createLineItem({ lineItemId: '2', quantity: 2, filterContext: { productId: 'P2', variantId: 'V2', tags: [], vendor: 'Adidas' } }),
1294
+ ],
1295
+ });
1296
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap);
1297
+ expect(result.lineItemLevel).toBe(4);
1298
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(0);
1299
+ expect(liCosts['2'].totalLineItemOtherCost).toBe(4);
1300
+ });
1301
+ it('should apply PER_LINE_ITEM selectively when multiple product filters used', () => {
1302
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, {
1303
+ flatAmount: 1,
1304
+ filters: {
1305
+ productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] },
1306
+ productTypes: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Shoes'] },
1307
+ },
1308
+ });
1309
+ const liCosts = {
1310
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1311
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1312
+ '3': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1313
+ };
1314
+ const context = createContext({
1315
+ lineItems: [
1316
+ createLineItem({ lineItemId: '1', quantity: 2, filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike', productType: 'Shoes' } }),
1317
+ createLineItem({ lineItemId: '2', quantity: 3, filterContext: { productId: 'P2', variantId: 'V2', tags: [], vendor: 'Nike', productType: 'Hats' } }),
1318
+ createLineItem({ lineItemId: '3', quantity: 1, filterContext: { productId: 'P3', variantId: 'V3', tags: [], vendor: 'Adidas', productType: 'Shoes' } }),
1319
+ ],
1320
+ });
1321
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap);
1322
+ expect(result.lineItemLevel).toBe(2);
1323
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(2);
1324
+ expect(liCosts['2'].totalLineItemOtherCost).toBe(0);
1325
+ expect(liCosts['3'].totalLineItemOtherCost).toBe(0);
1326
+ });
1327
+ it('should return 0 for PER_REFUND_ORDER when product filter matches but hasRefund is false', () => {
1328
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_ORDER, {
1329
+ flatAmount: 5,
1330
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
1331
+ });
1332
+ const context = createContext({
1333
+ hasRefund: false,
1334
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike' } })],
1335
+ });
1336
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
1337
+ });
1338
+ it('should apply PER_REFUND_LINE_ITEM only to refunded line items NOT matching EXCLUDE filter', () => {
1339
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_LINE_ITEM, {
1340
+ flatAmount: 3,
1341
+ filters: { products: { mode: at_tracking_event_types_1.COST_FILTER_MODE.EXCLUDE, values: ['P1'] } },
1342
+ });
1343
+ const liCosts = {
1344
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1345
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1346
+ };
1347
+ const context = createContext({
1348
+ hasRefund: true,
1349
+ lineItems: [
1350
+ createLineItem({ lineItemId: '1', refundedQuantity: 2, filterContext: { productId: 'P1', variantId: 'V1', tags: [] } }),
1351
+ createLineItem({ lineItemId: '2', refundedQuantity: 1, filterContext: { productId: 'P2', variantId: 'V2', tags: [] } }),
1352
+ ],
1353
+ });
1354
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap);
1355
+ expect(result.lineItemLevel).toBe(3);
1356
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(0);
1357
+ expect(liCosts['2'].totalLineItemOtherCost).toBe(3);
1358
+ });
1359
+ it('should apply PER_ORDER when EXCLUDE order filter does not match', () => {
1360
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1361
+ flatAmount: 5,
1362
+ filters: { orderTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.EXCLUDE, values: ['test'] } },
1363
+ });
1364
+ const context = createContext({
1365
+ orderFilterContext: { tags: ['vip'], paymentGateways: [], shippingTitles: [] },
1366
+ });
1367
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
1368
+ });
1369
+ it('should return 0 for PER_ORDER when EXCLUDE order filter matches', () => {
1370
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1371
+ flatAmount: 5,
1372
+ filters: { orderTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.EXCLUDE, values: ['test'] } },
1373
+ });
1374
+ const context = createContext({
1375
+ orderFilterContext: { tags: ['test'], paymentGateways: [], shippingTitles: [] },
1376
+ });
1377
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
1378
+ });
1379
+ it('should handle undefined filter context fields (missing vendor, productType, category)', () => {
1380
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1381
+ flatAmount: 5,
1382
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
1383
+ });
1384
+ const context = createContext({
1385
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [] } })],
1386
+ });
1387
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
1388
+ });
1389
+ it('should handle EXCLUDE filter with undefined filter context field (undefined treated as not matching)', () => {
1390
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1391
+ flatAmount: 5,
1392
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.EXCLUDE, values: ['Nike'] } },
1393
+ });
1394
+ const context = createContext({
1395
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [] } })],
1396
+ });
1397
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
1398
+ });
1399
+ it('should return 0 for PER_ORDER when INCLUDE productTags filter and line item has empty tags', () => {
1400
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1401
+ flatAmount: 5,
1402
+ filters: { productTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['sale'] } },
1403
+ });
1404
+ const context = createContext({
1405
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [] } })],
1406
+ });
1407
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(0);
1408
+ });
1409
+ it('should apply PER_ORDER when EXCLUDE productTags filter and line item has empty tags', () => {
1410
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1411
+ flatAmount: 5,
1412
+ filters: { productTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.EXCLUDE, values: ['sale'] } },
1413
+ });
1414
+ const context = createContext({
1415
+ lineItems: [createLineItem({ filterContext: { productId: 'P1', variantId: 'V1', tags: [] } })],
1416
+ });
1417
+ expect(cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, initLineItemCosts(), 'USD', emptyRatesMap).orderLevel).toBe(5);
1418
+ });
1419
+ it('should apply PER_LINE_ITEM only to line items matching product filter when order filter also passes', () => {
1420
+ const entry = createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, {
1421
+ flatAmount: 2,
1422
+ filters: {
1423
+ countries: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['US'] },
1424
+ productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] },
1425
+ },
1426
+ });
1427
+ const liCosts = {
1428
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1429
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1430
+ };
1431
+ const context = createContext({
1432
+ orderFilterContext: { tags: [], paymentGateways: [], shippingTitles: [], shipCountryCode: 'US' },
1433
+ lineItems: [
1434
+ createLineItem({ lineItemId: '1', quantity: 3, filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike' } }),
1435
+ createLineItem({ lineItemId: '2', quantity: 2, filterContext: { productId: 'P2', variantId: 'V2', tags: [], vendor: 'Adidas' } }),
1436
+ ],
1437
+ });
1438
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCostEntry(entry, context, liCosts, 'USD', emptyRatesMap);
1439
+ expect(result.lineItemLevel).toBe(6);
1440
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(6);
1441
+ expect(liCosts['2'].totalLineItemOtherCost).toBe(0);
1442
+ });
1443
+ });
1444
+ describe('calculateOtherCost', () => {
1445
+ it('should return 0 when no order config', () => {
1446
+ const configs = createConfigs();
1447
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCost(configs, createContext(), {}, 'USD', emptyRatesMap);
1448
+ expect(result.total).toBe(0);
1449
+ expect(result.orderLevel).toBe(0);
1450
+ expect(result.lineItemLevel).toBe(0);
1451
+ });
1452
+ it('should sum multiple entries and separate order-level costs', () => {
1453
+ const configs = createConfigs({
1454
+ order: {
1455
+ entries: [
1456
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 3 }),
1457
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 2 }),
1458
+ ],
1459
+ },
1460
+ });
1461
+ const liCosts = { '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 } };
1462
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCost(configs, createContext(), liCosts, 'USD', emptyRatesMap);
1463
+ expect(result.total).toBe(5);
1464
+ expect(result.orderLevel).toBe(5);
1465
+ expect(result.lineItemLevel).toBe(0);
1466
+ });
1467
+ it('should accumulate lineItemCosts from multiple PER_LINE_ITEM entries', () => {
1468
+ const configs = createConfigs({
1469
+ order: {
1470
+ entries: [
1471
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { id: 'e1', flatAmount: 2 }),
1472
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { id: 'e2', flatAmount: 3 }),
1473
+ ],
1474
+ },
1475
+ });
1476
+ const context = createContext({
1477
+ lineItems: [createLineItem({ lineItemId: '1', quantity: 2 })],
1478
+ });
1479
+ const liCosts = {
1480
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1481
+ };
1482
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCost(configs, context, liCosts, 'USD', emptyRatesMap);
1483
+ expect(result.total).toBe(10);
1484
+ expect(result.orderLevel).toBe(0);
1485
+ expect(result.lineItemLevel).toBe(10);
1486
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(10);
1487
+ });
1488
+ it('should skip entries outside date range', () => {
1489
+ const configs = createConfigs({
1490
+ order: {
1491
+ entries: [
1492
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 3, effectiveFrom: '2025-01-01T00:00:00Z' }),
1493
+ ],
1494
+ },
1495
+ });
1496
+ const liCosts = {};
1497
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCost(configs, createContext(), liCosts, 'USD', emptyRatesMap);
1498
+ expect(result.total).toBe(0);
1499
+ });
1500
+ it('should only sum entries whose filters pass (mixed filter results)', () => {
1501
+ const configs = createConfigs({
1502
+ order: {
1503
+ entries: [
1504
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1505
+ id: 'e1', flatAmount: 3,
1506
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
1507
+ }),
1508
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1509
+ id: 'e2', flatAmount: 7,
1510
+ filters: { products: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['P999'] } },
1511
+ }),
1512
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_GROSS_SALES, {
1513
+ id: 'e3', percentRate: 2,
1514
+ }),
1515
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1516
+ id: 'e4', flatAmount: 10,
1517
+ filters: { countries: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['CA'] } },
1518
+ }),
1519
+ ],
1520
+ },
1521
+ });
1522
+ const context = createContext({
1523
+ grossSalePrice: 120,
1524
+ orderFilterContext: { tags: [], paymentGateways: ['stripe'], shippingTitles: ['Standard'], shipCountryCode: 'US' },
1525
+ lineItems: [createLineItem({ filterContext: { productId: 'P100', variantId: 'V1', tags: [], vendor: 'Nike' } })],
1526
+ });
1527
+ const liCosts = { '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 } };
1528
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCost(configs, context, liCosts, 'USD', emptyRatesMap);
1529
+ expect(result.total).toBeCloseTo(5.4);
1530
+ expect(result.orderLevel).toBeCloseTo(5.4);
1531
+ });
1532
+ it('should sum PER_ORDER and PER_LINE_ITEM entries together and separate by level', () => {
1533
+ const configs = createConfigs({
1534
+ order: {
1535
+ entries: [
1536
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'e1', flatAmount: 5 }),
1537
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { id: 'e2', flatAmount: 2 }),
1538
+ ],
1539
+ },
1540
+ });
1541
+ const context = createContext({
1542
+ lineItems: [
1543
+ createLineItem({ lineItemId: '1', quantity: 3 }),
1544
+ createLineItem({ lineItemId: '2', quantity: 2 }),
1545
+ ],
1546
+ });
1547
+ const liCosts = {
1548
+ '1': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1549
+ '2': { totalLineItemHandlingCost: 0, totalLineItemOtherCost: 0 },
1550
+ };
1551
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOtherCost(configs, context, liCosts, 'USD', emptyRatesMap);
1552
+ expect(result.total).toBe(15);
1553
+ expect(result.orderLevel).toBe(5);
1554
+ expect(result.lineItemLevel).toBe(10);
1555
+ expect(liCosts['1'].totalLineItemOtherCost).toBe(6);
1556
+ expect(liCosts['2'].totalLineItemOtherCost).toBe(4);
1557
+ });
1558
+ });
1559
+ describe('calculateLineItemHandlingCosts', () => {
1560
+ it('should return 0 for no handling fee', () => {
1561
+ const result = cost_calculator_service_1.CostCalculatorService.calculateLineItemHandlingCosts([createLineItem()]);
1562
+ expect(result['1']).toBe(0);
1563
+ });
1564
+ it('should multiply handling fee by quantity', () => {
1565
+ const result = cost_calculator_service_1.CostCalculatorService.calculateLineItemHandlingCosts([
1566
+ createLineItem({ lineItemId: '1', quantity: 3, variantHandlingFee: 1.5 }),
1567
+ ]);
1568
+ expect(result['1']).toBe(4.5);
1569
+ });
1570
+ it('should handle multiple line items', () => {
1571
+ const result = cost_calculator_service_1.CostCalculatorService.calculateLineItemHandlingCosts([
1572
+ createLineItem({ lineItemId: '1', quantity: 2, variantHandlingFee: 1 }),
1573
+ createLineItem({ lineItemId: '2', quantity: 1, variantHandlingFee: 3 }),
1574
+ ]);
1575
+ expect(result['1']).toBe(2);
1576
+ expect(result['2']).toBe(3);
1577
+ });
1578
+ });
1579
+ describe('calculateOrderCosts', () => {
1580
+ it('should return default result when configs is null', () => {
1581
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(null, createContext(), 'USD', emptyRatesMap);
1582
+ expect(result.orderLevelHandlingCost).toBe(0);
1583
+ expect(result.lineItemLevelHandlingCost).toBe(0);
1584
+ expect(result.totalHandlingCost).toBe(0);
1585
+ expect(result.totalOtherCost).toBe(0);
1586
+ expect(result.orderLevelOtherCost).toBe(0);
1587
+ expect(result.lineItemLevelOtherCost).toBe(0);
1588
+ expect(result.totalGatewayCost).toBe(0);
1589
+ expect(result.totalShippingCost).toBe(0);
1590
+ });
1591
+ it('should calculate all cost types together', () => {
1592
+ const configs = createConfigs({
1593
+ globalProductOverride: { globalHandlingFee: 2 },
1594
+ gateway: {
1595
+ entries: [{ gatewayName: 'stripe', flatAmount: 0.30, percentRate: 2.9, currency: 'USD', createdAt: '', updatedAt: '' }],
1596
+ },
1597
+ shipping: { method: at_tracking_event_types_1.SHIPPING_METHOD.SHOPIFY_CHARGES, currency: 'USD' },
1598
+ order: {
1599
+ entries: [createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 1 })],
1600
+ },
1601
+ });
1602
+ const context = createContext({
1603
+ totalShipping: 10,
1604
+ lineItems: [createLineItem({ variantHandlingFee: 0.5, quantity: 2 })],
1605
+ });
1606
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1607
+ expect(result.orderLevelHandlingCost).toBe(0);
1608
+ expect(result.lineItemLevelHandlingCost).toBe(3);
1609
+ expect(result.totalHandlingCost).toBe(3);
1610
+ expect(result.totalGatewayCost).toBeCloseTo(3.20);
1611
+ expect(result.totalShippingCost).toBe(10);
1612
+ expect(result.totalOtherCost).toBe(1);
1613
+ expect(result.orderLevelOtherCost).toBe(1);
1614
+ expect(result.lineItemLevelOtherCost).toBe(0);
1615
+ expect(result.lineItemCosts['1'].totalLineItemHandlingCost).toBe(3);
1616
+ });
1617
+ it('should handle partial configs', () => {
1618
+ const configs = createConfigs({
1619
+ gateway: {
1620
+ entries: [{ gatewayName: 'stripe', flatAmount: 0.30, percentRate: 0, currency: 'USD', createdAt: '', updatedAt: '' }],
1621
+ },
1622
+ });
1623
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
1624
+ expect(result.orderLevelHandlingCost).toBe(0);
1625
+ expect(result.lineItemLevelHandlingCost).toBe(0);
1626
+ expect(result.totalHandlingCost).toBe(0);
1627
+ expect(result.totalGatewayCost).toBe(0.30);
1628
+ expect(result.totalShippingCost).toBe(0);
1629
+ expect(result.totalOtherCost).toBe(0);
1630
+ });
1631
+ it('should respect product filters in calculateOrderCosts integration', () => {
1632
+ const configs = createConfigs({
1633
+ order: {
1634
+ entries: [
1635
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1636
+ id: 'e1', flatAmount: 5,
1637
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Nike'] } },
1638
+ }),
1639
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1640
+ id: 'e2', flatAmount: 10,
1641
+ filters: { productVendors: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['Puma'] } },
1642
+ }),
1643
+ ],
1644
+ },
1645
+ });
1646
+ const context = createContext({
1647
+ lineItems: [
1648
+ createLineItem({
1649
+ lineItemId: '1',
1650
+ quantity: 1,
1651
+ filterContext: { productId: 'P1', variantId: 'V1', tags: [], vendor: 'Nike' },
1652
+ }),
1653
+ ],
1654
+ });
1655
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1656
+ expect(result.totalOtherCost).toBe(5);
1657
+ expect(result.orderLevelOtherCost).toBe(5);
1658
+ });
1659
+ it('should accumulate all cost types with multiple line items and separate by level', () => {
1660
+ const configs = createConfigs({
1661
+ globalProductOverride: { globalHandlingFee: 1, currency: 'USD' },
1662
+ gateway: {
1663
+ entries: [{ gatewayName: 'stripe', flatAmount: 0.30, percentRate: 2.9, currency: 'USD', createdAt: '', updatedAt: '' }],
1664
+ },
1665
+ shipping: { method: at_tracking_event_types_1.SHIPPING_METHOD.FIXED_RATE, fixedRateAmount: 5, currency: 'USD' },
1666
+ order: {
1667
+ entries: [
1668
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'e1', flatAmount: 3 }),
1669
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { id: 'e2', flatAmount: 1 }),
1670
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, { id: 'e3', percentRate: 2 }),
1671
+ ],
1672
+ },
1673
+ });
1674
+ const context = createContext({
1675
+ totalPrice: 200,
1676
+ lineItems: [
1677
+ createLineItem({ lineItemId: '1', quantity: 3, variantHandlingFee: 0.5 }),
1678
+ createLineItem({ lineItemId: '2', quantity: 2, variantHandlingFee: 1 }),
1679
+ ],
1680
+ });
1681
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1682
+ expect(result.orderLevelHandlingCost).toBe(0);
1683
+ expect(result.lineItemLevelHandlingCost).toBe(5.5);
1684
+ expect(result.totalHandlingCost).toBe(5.5);
1685
+ expect(result.totalGatewayCost).toBeCloseTo(6.10);
1686
+ expect(result.totalShippingCost).toBe(5);
1687
+ expect(result.totalOtherCost).toBe(12);
1688
+ expect(result.orderLevelOtherCost).toBe(7);
1689
+ expect(result.lineItemLevelOtherCost).toBe(5);
1690
+ expect(result.lineItemCosts['1'].totalLineItemOtherCost).toBe(3);
1691
+ expect(result.lineItemCosts['2'].totalLineItemOtherCost).toBe(2);
1692
+ expect(result.lineItemCosts['1'].totalLineItemHandlingCost).toBe(2.5);
1693
+ expect(result.lineItemCosts['2'].totalLineItemHandlingCost).toBe(3);
1694
+ });
1695
+ it('should maintain decimal precision with Big.js', () => {
1696
+ const configs = createConfigs({
1697
+ order: {
1698
+ entries: [
1699
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, { percentRate: 33.33 }),
1700
+ ],
1701
+ },
1702
+ });
1703
+ const context = createContext({ totalPrice: 99.99 });
1704
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1705
+ expect(result.totalOtherCost).toBeCloseTo(33.326667, 4);
1706
+ });
1707
+ });
1708
+ describe('calculateOtherCost - category routing', () => {
1709
+ it('should route COGS category costs to totalOtherCogs', () => {
1710
+ const configs = createConfigs({
1711
+ order: {
1712
+ entries: [
1713
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'cogs1', flatAmount: 10, category: 'cogs' }),
1714
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'cogs2', flatAmount: 5, category: 'cogs' }),
1715
+ ],
1716
+ },
1717
+ });
1718
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
1719
+ expect(result.categoryCosts.totalOtherCogs).toBe(15);
1720
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(0);
1721
+ expect(result.categoryCosts.otherTransactionCost).toBe(0);
1722
+ expect(result.categoryCosts.marketingCost).toBe(0);
1723
+ expect(result.categoryCosts.agencyFeesCost).toBe(0);
1724
+ expect(result.categoryCosts.opexCost).toBe(0);
1725
+ expect(result.categoryCosts.totalOtherCost).toBe(0);
1726
+ });
1727
+ it('should route FULFILLMENT category costs to otherFulfillmentCost', () => {
1728
+ const configs = createConfigs({
1729
+ order: {
1730
+ entries: [
1731
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'ful1', flatAmount: 8, category: 'fulfillment' }),
1732
+ ],
1733
+ },
1734
+ });
1735
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
1736
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(8);
1737
+ expect(result.categoryCosts.totalOtherCogs).toBe(0);
1738
+ });
1739
+ it('should route TRANSACTION category costs to otherTransactionCost', () => {
1740
+ const configs = createConfigs({
1741
+ order: {
1742
+ entries: [
1743
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, { id: 'tx1', percentRate: 3, category: 'transaction' }),
1744
+ ],
1745
+ },
1746
+ });
1747
+ const context = createContext({ totalPrice: 100 });
1748
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1749
+ expect(result.categoryCosts.otherTransactionCost).toBe(3);
1750
+ });
1751
+ it('should route MARKETING category costs to marketingCost', () => {
1752
+ const configs = createConfigs({
1753
+ order: {
1754
+ entries: [
1755
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'mkt1', flatAmount: 25, category: 'marketing' }),
1756
+ ],
1757
+ },
1758
+ });
1759
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
1760
+ expect(result.categoryCosts.marketingCost).toBe(25);
1761
+ });
1762
+ it('should route AGENCY_FEES category costs to agencyFeesCost', () => {
1763
+ const configs = createConfigs({
1764
+ order: {
1765
+ entries: [
1766
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_GROSS_SALES, { id: 'agency1', percentRate: 15, category: 'agency_fees' }),
1767
+ ],
1768
+ },
1769
+ });
1770
+ const context = createContext({ grossSalePrice: 200 });
1771
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1772
+ expect(result.categoryCosts.agencyFeesCost).toBe(30);
1773
+ });
1774
+ it('should route OPEX category costs to opexCost', () => {
1775
+ const configs = createConfigs({
1776
+ order: {
1777
+ entries: [
1778
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'opex1', flatAmount: 12, category: 'opex' }),
1779
+ ],
1780
+ },
1781
+ });
1782
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
1783
+ expect(result.categoryCosts.opexCost).toBe(12);
1784
+ });
1785
+ it('should route OTHER category costs to totalOtherCost', () => {
1786
+ const configs = createConfigs({
1787
+ order: {
1788
+ entries: [
1789
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'other1', flatAmount: 7, category: 'other' }),
1790
+ ],
1791
+ },
1792
+ });
1793
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
1794
+ expect(result.categoryCosts.totalOtherCost).toBe(7);
1795
+ });
1796
+ it('should route legacy payment_fees to TRANSACTION (otherTransactionCost)', () => {
1797
+ const configs = createConfigs({
1798
+ order: {
1799
+ entries: [
1800
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'legacy1', flatAmount: 2.5, category: 'payment_fees' }),
1801
+ ],
1802
+ },
1803
+ });
1804
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
1805
+ expect(result.categoryCosts.otherTransactionCost).toBe(2.5);
1806
+ expect(result.categoryCosts.totalOtherCost).toBe(0);
1807
+ });
1808
+ it('should route legacy packaging to FULFILLMENT (otherFulfillmentCost)', () => {
1809
+ const configs = createConfigs({
1810
+ order: {
1811
+ entries: [
1812
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'legacy2', flatAmount: 1.75, category: 'packaging' }),
1813
+ ],
1814
+ },
1815
+ });
1816
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
1817
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(1.75);
1818
+ expect(result.categoryCosts.totalOtherCost).toBe(0);
1819
+ });
1820
+ it('should route unknown category to totalOtherCost', () => {
1821
+ const configs = createConfigs({
1822
+ order: {
1823
+ entries: [
1824
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'unknown1', flatAmount: 3, category: 'unknown_category_xyz' }),
1825
+ ],
1826
+ },
1827
+ });
1828
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
1829
+ expect(result.categoryCosts.totalOtherCost).toBe(3);
1830
+ });
1831
+ it('should route undefined/null category to totalOtherCost', () => {
1832
+ const configs = createConfigs({
1833
+ order: {
1834
+ entries: [
1835
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'nocat1', flatAmount: 4, category: undefined }),
1836
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'nocat2', flatAmount: 6, category: null }),
1837
+ ],
1838
+ },
1839
+ });
1840
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
1841
+ expect(result.categoryCosts.totalOtherCost).toBe(10);
1842
+ });
1843
+ it('should accumulate costs across multiple entries of the same category', () => {
1844
+ const configs = createConfigs({
1845
+ order: {
1846
+ entries: [
1847
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'ful1', flatAmount: 5, category: 'fulfillment' }),
1848
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'ful2', flatAmount: 3, category: 'fulfillment' }),
1849
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { id: 'ful3', flatAmount: 2, category: 'fulfillment' }),
1850
+ ],
1851
+ },
1852
+ });
1853
+ const context = createContext({
1854
+ lineItems: [
1855
+ createLineItem({ lineItemId: '1', quantity: 2 }),
1856
+ createLineItem({ lineItemId: '2', quantity: 3 }),
1857
+ ],
1858
+ });
1859
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1860
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(18);
1861
+ });
1862
+ it('should route costs to multiple categories correctly', () => {
1863
+ const configs = createConfigs({
1864
+ order: {
1865
+ entries: [
1866
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'cogs1', flatAmount: 10, category: 'cogs' }),
1867
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'ful1', flatAmount: 5, category: 'fulfillment' }),
1868
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, { id: 'tx1', percentRate: 2, category: 'transaction' }),
1869
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'mkt1', flatAmount: 15, category: 'marketing' }),
1870
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'agency1', flatAmount: 8, category: 'agency_fees' }),
1871
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'opex1', flatAmount: 20, category: 'opex' }),
1872
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'other1', flatAmount: 3, category: 'other' }),
1873
+ ],
1874
+ },
1875
+ });
1876
+ const context = createContext({ totalPrice: 100 });
1877
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1878
+ expect(result.categoryCosts.totalOtherCogs).toBe(10);
1879
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(5);
1880
+ expect(result.categoryCosts.otherTransactionCost).toBe(2);
1881
+ expect(result.categoryCosts.marketingCost).toBe(15);
1882
+ expect(result.categoryCosts.agencyFeesCost).toBe(8);
1883
+ expect(result.categoryCosts.opexCost).toBe(20);
1884
+ expect(result.categoryCosts.totalOtherCost).toBe(3);
1885
+ expect(result.totalOtherCost).toBe(3);
1886
+ });
1887
+ it('should handle line-item level costs in category routing', () => {
1888
+ const configs = createConfigs({
1889
+ order: {
1890
+ entries: [
1891
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { id: 'cogs_li', flatAmount: 2, category: 'cogs' }),
1892
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { id: 'ful_li', flatAmount: 1.5, category: 'fulfillment' }),
1893
+ ],
1894
+ },
1895
+ });
1896
+ const context = createContext({
1897
+ lineItems: [
1898
+ createLineItem({ lineItemId: '1', quantity: 3 }),
1899
+ createLineItem({ lineItemId: '2', quantity: 2 }),
1900
+ ],
1901
+ });
1902
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1903
+ expect(result.categoryCosts.totalOtherCogs).toBe(10);
1904
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(7.5);
1905
+ });
1906
+ it('should handle PCT_COGS entry type with category routing', () => {
1907
+ const configs = createConfigs({
1908
+ order: {
1909
+ entries: [
1910
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_COGS, { id: 'pct_cogs', percentRate: 10, category: 'cogs' }),
1911
+ ],
1912
+ },
1913
+ });
1914
+ const context = createContext({ totalProductCost: 50 });
1915
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1916
+ expect(result.categoryCosts.totalOtherCogs).toBe(5);
1917
+ });
1918
+ it('should handle PCT_SHIPPING entry type routed to fulfillment', () => {
1919
+ const configs = createConfigs({
1920
+ order: {
1921
+ entries: [
1922
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_SHIPPING, { id: 'pct_ship', percentRate: 20, category: 'fulfillment' }),
1923
+ ],
1924
+ },
1925
+ });
1926
+ const context = createContext({ totalShipping: 25 });
1927
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1928
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(5);
1929
+ });
1930
+ it('should handle refund costs with category routing', () => {
1931
+ const configs = createConfigs({
1932
+ order: {
1933
+ entries: [
1934
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_ORDER, { id: 'refund1', flatAmount: 15, category: 'opex' }),
1935
+ ],
1936
+ },
1937
+ });
1938
+ const context = createContext({ hasRefund: true });
1939
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1940
+ expect(result.categoryCosts.opexCost).toBe(15);
1941
+ });
1942
+ it('should not apply refund costs when hasRefund is false', () => {
1943
+ const configs = createConfigs({
1944
+ order: {
1945
+ entries: [
1946
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_ORDER, { id: 'refund1', flatAmount: 15, category: 'opex' }),
1947
+ ],
1948
+ },
1949
+ });
1950
+ const context = createContext({ hasRefund: false });
1951
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1952
+ expect(result.categoryCosts.opexCost).toBe(0);
1953
+ });
1954
+ it('should respect date ranges in category cost routing', () => {
1955
+ const configs = createConfigs({
1956
+ order: {
1957
+ entries: [
1958
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1959
+ id: 'valid',
1960
+ flatAmount: 10,
1961
+ category: 'marketing',
1962
+ effectiveFrom: '2024-01-01T00:00:00Z',
1963
+ effectiveTo: '2024-12-31T23:59:59Z',
1964
+ }),
1965
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
1966
+ id: 'expired',
1967
+ flatAmount: 20,
1968
+ category: 'marketing',
1969
+ effectiveFrom: '2023-01-01T00:00:00Z',
1970
+ effectiveTo: '2023-12-31T23:59:59Z',
1971
+ }),
1972
+ ],
1973
+ },
1974
+ });
1975
+ const context = createContext({ orderCreatedAt: '2024-06-15T10:00:00Z' });
1976
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
1977
+ expect(result.categoryCosts.marketingCost).toBe(10);
1978
+ });
1979
+ it('should return default categoryCosts when configs is null', () => {
1980
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(null, createContext(), 'USD', emptyRatesMap);
1981
+ expect(result.categoryCosts).toEqual({
1982
+ totalOtherCogs: 0,
1983
+ otherFulfillmentCost: 0,
1984
+ otherTransactionCost: 0,
1985
+ marketingCost: 0,
1986
+ agencyFeesCost: 0,
1987
+ opexCost: 0,
1988
+ totalOtherCost: 0,
1989
+ });
1990
+ });
1991
+ it('should return default categoryCosts when order config is null', () => {
1992
+ const configs = createConfigs({ order: null });
1993
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
1994
+ expect(result.categoryCosts).toEqual({
1995
+ totalOtherCogs: 0,
1996
+ otherFulfillmentCost: 0,
1997
+ otherTransactionCost: 0,
1998
+ marketingCost: 0,
1999
+ agencyFeesCost: 0,
2000
+ opexCost: 0,
2001
+ totalOtherCost: 0,
2002
+ });
2003
+ });
2004
+ it('should handle mixed legacy and new categories', () => {
2005
+ const configs = createConfigs({
2006
+ order: {
2007
+ entries: [
2008
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'new_tx', flatAmount: 5, category: 'transaction' }),
2009
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'legacy_pf', flatAmount: 3, category: 'payment_fees' }),
2010
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'new_ful', flatAmount: 4, category: 'fulfillment' }),
2011
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'legacy_pkg', flatAmount: 2, category: 'packaging' }),
2012
+ ],
2013
+ },
2014
+ });
2015
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2016
+ expect(result.categoryCosts.otherTransactionCost).toBe(8);
2017
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(6);
2018
+ });
2019
+ it('should handle weight-based costs with category routing', () => {
2020
+ const configs = createConfigs({
2021
+ order: {
2022
+ entries: [
2023
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
2024
+ id: 'weight1',
2025
+ flatAmount: 0.01,
2026
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.GRAMS,
2027
+ category: 'fulfillment',
2028
+ }),
2029
+ ],
2030
+ },
2031
+ });
2032
+ const context = createContext({ totalWeight: 1000 });
2033
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2034
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(10);
2035
+ });
2036
+ it('should handle quantity-based costs with category routing', () => {
2037
+ const configs = createConfigs({
2038
+ order: {
2039
+ entries: [
2040
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, {
2041
+ id: 'qty1',
2042
+ flatAmount: 0.5,
2043
+ category: 'fulfillment',
2044
+ }),
2045
+ ],
2046
+ },
2047
+ });
2048
+ const context = createContext({ lineItems: [createLineItem({ quantity: 10 })] });
2049
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2050
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(5);
2051
+ });
2052
+ it('should handle BY_ORDER_QUANTITY costs with category routing', () => {
2053
+ const configs = createConfigs({
2054
+ order: {
2055
+ entries: [
2056
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_QUANTITY, {
2057
+ id: 'qty-order',
2058
+ quantityIntervals: [
2059
+ { quantityMin: 1, quantityMax: 5, flatAmount: 10 },
2060
+ { quantityMin: 6, quantityMax: null, flatAmount: 5 },
2061
+ ],
2062
+ category: 'cogs',
2063
+ }),
2064
+ ],
2065
+ },
2066
+ });
2067
+ const context = createContext({
2068
+ lineItems: [
2069
+ createLineItem({ lineItemId: '1', quantity: 4 }),
2070
+ createLineItem({ lineItemId: '2', quantity: 3 }),
2071
+ ],
2072
+ totalQuantity: 7,
2073
+ });
2074
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2075
+ expect(result.categoryCosts.totalOtherCogs).toBe(5);
2076
+ expect(result.orderLevelOtherCost).toBe(5);
2077
+ expect(result.lineItemLevelOtherCost).toBe(0);
2078
+ });
2079
+ it('should continue processing other entries when one entry fails', () => {
2080
+ const configs = createConfigs({
2081
+ order: {
2082
+ entries: [
2083
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'good1', flatAmount: 10, category: 'cogs' }),
2084
+ {
2085
+ ...createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT),
2086
+ id: 'problematic',
2087
+ weightIntervals: [{ weightMin: NaN, weightMax: null, flatAmount: 5 }],
2088
+ category: 'fulfillment',
2089
+ },
2090
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'good2', flatAmount: 5, category: 'marketing' }),
2091
+ ],
2092
+ },
2093
+ });
2094
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2095
+ expect(result.categoryCosts.totalOtherCogs).toBe(10);
2096
+ expect(result.categoryCosts.marketingCost).toBe(5);
2097
+ });
2098
+ });
2099
+ describe('calculateOrderCosts - category costs integration', () => {
2100
+ it('should calculate all cost types with category routing in full integration', () => {
2101
+ const configs = createConfigs({
2102
+ globalProductOverride: { globalHandlingFee: 1.5, currency: 'USD' },
2103
+ gateway: {
2104
+ entries: [{ gatewayName: 'stripe', flatAmount: 0.30, percentRate: 2.9, currency: 'USD', createdAt: '', updatedAt: '' }],
2105
+ },
2106
+ shipping: { method: at_tracking_event_types_1.SHIPPING_METHOD.FIXED_RATE, fixedRateAmount: 7, currency: 'USD' },
2107
+ order: {
2108
+ entries: [
2109
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'cogs1', flatAmount: 5, category: 'cogs' }),
2110
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'ful1', flatAmount: 3, category: 'fulfillment' }),
2111
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, { id: 'tx1', percentRate: 1, category: 'transaction' }),
2112
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { id: 'mkt1', flatAmount: 2, category: 'marketing' }),
2113
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'agency1', flatAmount: 10, category: 'agency_fees' }),
2114
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'opex1', flatAmount: 8, category: 'opex' }),
2115
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'other1', flatAmount: 4, category: 'other' }),
2116
+ ],
2117
+ },
2118
+ });
2119
+ const context = createContext({
2120
+ totalPrice: 200,
2121
+ lineItems: [
2122
+ createLineItem({ lineItemId: '1', quantity: 2, variantHandlingFee: 0.5 }),
2123
+ createLineItem({ lineItemId: '2', quantity: 3, variantHandlingFee: 0.75 }),
2124
+ ],
2125
+ });
2126
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2127
+ expect(result.lineItemLevelHandlingCost).toBe(6.25);
2128
+ expect(result.totalHandlingCost).toBe(6.25);
2129
+ expect(result.totalGatewayCost).toBeCloseTo(6.10);
2130
+ expect(result.totalShippingCost).toBe(7);
2131
+ expect(result.categoryCosts.totalOtherCogs).toBe(5);
2132
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(3);
2133
+ expect(result.categoryCosts.otherTransactionCost).toBe(2);
2134
+ expect(result.categoryCosts.marketingCost).toBe(10);
2135
+ expect(result.categoryCosts.agencyFeesCost).toBe(10);
2136
+ expect(result.categoryCosts.opexCost).toBe(8);
2137
+ expect(result.categoryCosts.totalOtherCost).toBe(4);
2138
+ expect(result.totalOtherCost).toBe(4);
2139
+ });
2140
+ it('should properly separate order-level and line-item-level costs with categories', () => {
2141
+ const configs = createConfigs({
2142
+ order: {
2143
+ entries: [
2144
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'order_cogs', flatAmount: 10, category: 'cogs' }),
2145
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { id: 'li_cogs', flatAmount: 2, category: 'cogs' }),
2146
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'order_ful', flatAmount: 5, category: 'fulfillment' }),
2147
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { id: 'li_ful', flatAmount: 1, category: 'fulfillment' }),
2148
+ ],
2149
+ },
2150
+ });
2151
+ const context = createContext({
2152
+ lineItems: [
2153
+ createLineItem({ lineItemId: '1', quantity: 3 }),
2154
+ createLineItem({ lineItemId: '2', quantity: 2 }),
2155
+ ],
2156
+ });
2157
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2158
+ expect(result.orderLevelOtherCost).toBe(15);
2159
+ expect(result.lineItemLevelOtherCost).toBe(15);
2160
+ expect(result.categoryCosts.totalOtherCogs).toBe(20);
2161
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(10);
2162
+ expect(result.lineItemCosts['1'].totalLineItemOtherCost).toBe(9);
2163
+ expect(result.lineItemCosts['2'].totalLineItemOtherCost).toBe(6);
2164
+ });
2165
+ });
2166
+ describe('defensive coding - malformed input handling', () => {
2167
+ describe('empty and missing entries', () => {
2168
+ it('should handle empty entries array', () => {
2169
+ const configs = createConfigs({
2170
+ order: { entries: [] },
2171
+ });
2172
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2173
+ expect(result.categoryCosts.totalOtherCogs).toBe(0);
2174
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(0);
2175
+ expect(result.categoryCosts.totalOtherCost).toBe(0);
2176
+ expect(result.totalOtherCost).toBe(0);
2177
+ });
2178
+ it('should handle undefined entries', () => {
2179
+ const configs = createConfigs({
2180
+ order: { entries: undefined },
2181
+ });
2182
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2183
+ expect(result.categoryCosts).toEqual({
2184
+ totalOtherCogs: 0,
2185
+ otherFulfillmentCost: 0,
2186
+ otherTransactionCost: 0,
2187
+ marketingCost: 0,
2188
+ agencyFeesCost: 0,
2189
+ opexCost: 0,
2190
+ totalOtherCost: 0,
2191
+ });
2192
+ });
2193
+ it('should handle null entries', () => {
2194
+ const configs = createConfigs({
2195
+ order: { entries: null },
2196
+ });
2197
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2198
+ expect(result.categoryCosts).toEqual({
2199
+ totalOtherCogs: 0,
2200
+ otherFulfillmentCost: 0,
2201
+ otherTransactionCost: 0,
2202
+ marketingCost: 0,
2203
+ agencyFeesCost: 0,
2204
+ opexCost: 0,
2205
+ totalOtherCost: 0,
2206
+ });
2207
+ });
2208
+ });
2209
+ describe('malformed entry data', () => {
2210
+ it('should handle entry with undefined flatAmount', () => {
2211
+ const configs = createConfigs({
2212
+ order: {
2213
+ entries: [
2214
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: undefined, category: 'cogs' }),
2215
+ ],
2216
+ },
2217
+ });
2218
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2219
+ expect(result.categoryCosts.totalOtherCogs).toBe(0);
2220
+ });
2221
+ it('should handle entry with null flatAmount', () => {
2222
+ const configs = createConfigs({
2223
+ order: {
2224
+ entries: [
2225
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: null, category: 'cogs' }),
2226
+ ],
2227
+ },
2228
+ });
2229
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2230
+ expect(result.categoryCosts.totalOtherCogs).toBe(0);
2231
+ });
2232
+ it('should handle entry with NaN flatAmount', () => {
2233
+ const configs = createConfigs({
2234
+ order: {
2235
+ entries: [
2236
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: NaN, category: 'fulfillment' }),
2237
+ ],
2238
+ },
2239
+ });
2240
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2241
+ expect(typeof result.categoryCosts.otherFulfillmentCost).toBe('number');
2242
+ });
2243
+ it('should handle entry with negative flatAmount', () => {
2244
+ const configs = createConfigs({
2245
+ order: {
2246
+ entries: [
2247
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: -10, category: 'cogs' }),
2248
+ ],
2249
+ },
2250
+ });
2251
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2252
+ expect(result.categoryCosts.totalOtherCogs).toBe(-10);
2253
+ });
2254
+ it('should handle entry with Infinity flatAmount', () => {
2255
+ const configs = createConfigs({
2256
+ order: {
2257
+ entries: [
2258
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: Infinity, category: 'marketing' }),
2259
+ ],
2260
+ },
2261
+ });
2262
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2263
+ expect(typeof result.categoryCosts.marketingCost).toBe('number');
2264
+ });
2265
+ it('should handle entry with string flatAmount', () => {
2266
+ const configs = createConfigs({
2267
+ order: {
2268
+ entries: [
2269
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: '10.50', category: 'opex' }),
2270
+ ],
2271
+ },
2272
+ });
2273
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2274
+ expect(result.categoryCosts.opexCost).toBe(10.5);
2275
+ });
2276
+ });
2277
+ describe('malformed percent rates', () => {
2278
+ it('should handle undefined percentRate', () => {
2279
+ const configs = createConfigs({
2280
+ order: {
2281
+ entries: [
2282
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, { percentRate: undefined, category: 'transaction' }),
2283
+ ],
2284
+ },
2285
+ });
2286
+ const context = createContext({ totalPrice: 100 });
2287
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2288
+ expect(result.categoryCosts.otherTransactionCost).toBe(0);
2289
+ });
2290
+ it('should handle null percentRate', () => {
2291
+ const configs = createConfigs({
2292
+ order: {
2293
+ entries: [
2294
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_GROSS_SALES, { percentRate: null, category: 'agency_fees' }),
2295
+ ],
2296
+ },
2297
+ });
2298
+ const context = createContext({ grossSalePrice: 200 });
2299
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2300
+ expect(result.categoryCosts.agencyFeesCost).toBe(0);
2301
+ });
2302
+ it('should handle negative percentRate', () => {
2303
+ const configs = createConfigs({
2304
+ order: {
2305
+ entries: [
2306
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_NET_SALES, { percentRate: -5, category: 'marketing' }),
2307
+ ],
2308
+ },
2309
+ });
2310
+ const context = createContext({ netSalePrice: 100 });
2311
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2312
+ expect(result.categoryCosts.marketingCost).toBe(-5);
2313
+ });
2314
+ it('should handle percentRate over 100', () => {
2315
+ const configs = createConfigs({
2316
+ order: {
2317
+ entries: [
2318
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_COGS, { percentRate: 150, category: 'fulfillment' }),
2319
+ ],
2320
+ },
2321
+ });
2322
+ const context = createContext({ totalProductCost: 50 });
2323
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2324
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(75);
2325
+ });
2326
+ });
2327
+ describe('malformed context data', () => {
2328
+ it('should handle zero totalPrice for percent calculations', () => {
2329
+ const configs = createConfigs({
2330
+ order: {
2331
+ entries: [
2332
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, { percentRate: 10, category: 'transaction' }),
2333
+ ],
2334
+ },
2335
+ });
2336
+ const context = createContext({ totalPrice: 0 });
2337
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2338
+ expect(result.categoryCosts.otherTransactionCost).toBe(0);
2339
+ });
2340
+ it('should handle negative totalPrice', () => {
2341
+ const configs = createConfigs({
2342
+ order: {
2343
+ entries: [
2344
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, { percentRate: 10, category: 'cogs' }),
2345
+ ],
2346
+ },
2347
+ });
2348
+ const context = createContext({ totalPrice: -100 });
2349
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2350
+ expect(result.categoryCosts.totalOtherCogs).toBe(-10);
2351
+ });
2352
+ it('should handle empty lineItems array', () => {
2353
+ const configs = createConfigs({
2354
+ order: {
2355
+ entries: [
2356
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { flatAmount: 5, category: 'fulfillment' }),
2357
+ ],
2358
+ },
2359
+ });
2360
+ const context = createContext({ lineItems: [] });
2361
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2362
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(0);
2363
+ expect(result.lineItemCosts).toEqual({});
2364
+ });
2365
+ it('should handle lineItems with zero quantity', () => {
2366
+ const configs = createConfigs({
2367
+ order: {
2368
+ entries: [
2369
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { flatAmount: 5, category: 'marketing' }),
2370
+ ],
2371
+ },
2372
+ });
2373
+ const context = createContext({
2374
+ lineItems: [createLineItem({ quantity: 0 })],
2375
+ });
2376
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2377
+ expect(result.categoryCosts.marketingCost).toBe(0);
2378
+ });
2379
+ it('should handle undefined orderCreatedAt', () => {
2380
+ const configs = createConfigs({
2381
+ order: {
2382
+ entries: [
2383
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
2384
+ flatAmount: 10,
2385
+ category: 'cogs',
2386
+ effectiveFrom: '2024-01-01T00:00:00Z',
2387
+ effectiveTo: '2024-12-31T23:59:59Z',
2388
+ }),
2389
+ ],
2390
+ },
2391
+ });
2392
+ const context = createContext({ orderCreatedAt: undefined });
2393
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2394
+ expect(typeof result.categoryCosts.totalOtherCogs).toBe('number');
2395
+ });
2396
+ it('should handle invalid orderCreatedAt format', () => {
2397
+ const configs = createConfigs({
2398
+ order: {
2399
+ entries: [
2400
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
2401
+ flatAmount: 10,
2402
+ category: 'opex',
2403
+ effectiveFrom: '2024-01-01T00:00:00Z',
2404
+ }),
2405
+ ],
2406
+ },
2407
+ });
2408
+ const context = createContext({ orderCreatedAt: 'not-a-date' });
2409
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2410
+ expect(result.categoryCosts.opexCost).toBe(10);
2411
+ });
2412
+ });
2413
+ describe('currency edge cases', () => {
2414
+ it('should handle empty currency string', () => {
2415
+ const configs = createConfigs({
2416
+ order: {
2417
+ entries: [
2418
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 10, currency: '', category: 'cogs' }),
2419
+ ],
2420
+ },
2421
+ });
2422
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2423
+ expect(result.categoryCosts.totalOtherCogs).toBe(10);
2424
+ });
2425
+ it('should handle null order currency', () => {
2426
+ const configs = createConfigs({
2427
+ order: {
2428
+ entries: [
2429
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 10, category: 'fulfillment' }),
2430
+ ],
2431
+ },
2432
+ });
2433
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), null, emptyRatesMap);
2434
+ expect(typeof result.categoryCosts.otherFulfillmentCost).toBe('number');
2435
+ });
2436
+ it('should handle missing exchange rates', () => {
2437
+ const configs = createConfigs({
2438
+ order: {
2439
+ entries: [
2440
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 10, currency: 'EUR', category: 'transaction' }),
2441
+ ],
2442
+ },
2443
+ });
2444
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', {});
2445
+ expect(typeof result.categoryCosts.otherTransactionCost).toBe('number');
2446
+ });
2447
+ });
2448
+ describe('weight and quantity tier edge cases', () => {
2449
+ it('should fall back to flat mode when weightIntervals array is empty', () => {
2450
+ const configs = createConfigs({
2451
+ order: {
2452
+ entries: [
2453
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
2454
+ weightIntervals: [],
2455
+ flatAmount: 5,
2456
+ category: 'fulfillment',
2457
+ }),
2458
+ ],
2459
+ },
2460
+ });
2461
+ const context = createContext({ totalWeight: 1000 });
2462
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2463
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(5000);
2464
+ });
2465
+ it('should fall back to flat mode when quantityIntervals array is empty', () => {
2466
+ const configs = createConfigs({
2467
+ order: {
2468
+ entries: [
2469
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, {
2470
+ quantityIntervals: [],
2471
+ flatAmount: 5,
2472
+ category: 'cogs',
2473
+ }),
2474
+ ],
2475
+ },
2476
+ });
2477
+ const context = createContext({ lineItems: [createLineItem({ quantity: 10 })] });
2478
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2479
+ expect(result.categoryCosts.totalOtherCogs).toBe(50);
2480
+ });
2481
+ it('should handle weight outside all tier ranges', () => {
2482
+ const configs = createConfigs({
2483
+ order: {
2484
+ entries: [
2485
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
2486
+ weightIntervals: [
2487
+ { weightMin: 0, weightMax: 100, flatAmount: 5 },
2488
+ { weightMin: 101, weightMax: 500, flatAmount: 10 },
2489
+ ],
2490
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.GRAMS,
2491
+ category: 'fulfillment',
2492
+ }),
2493
+ ],
2494
+ },
2495
+ });
2496
+ const context = createContext({ totalWeight: 1000 });
2497
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2498
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(0);
2499
+ });
2500
+ it('should handle zero totalWeight', () => {
2501
+ const configs = createConfigs({
2502
+ order: {
2503
+ entries: [
2504
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
2505
+ flatAmount: 0.01,
2506
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.GRAMS,
2507
+ category: 'marketing',
2508
+ }),
2509
+ ],
2510
+ },
2511
+ });
2512
+ const context = createContext({ totalWeight: 0 });
2513
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2514
+ expect(result.categoryCosts.marketingCost).toBe(0);
2515
+ });
2516
+ it('should handle negative totalWeight', () => {
2517
+ const configs = createConfigs({
2518
+ order: {
2519
+ entries: [
2520
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
2521
+ flatAmount: 0.01,
2522
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.GRAMS,
2523
+ category: 'opex',
2524
+ }),
2525
+ ],
2526
+ },
2527
+ });
2528
+ const context = createContext({ totalWeight: -100 });
2529
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2530
+ expect(typeof result.categoryCosts.opexCost).toBe('number');
2531
+ });
2532
+ });
2533
+ describe('filter context edge cases', () => {
2534
+ it('should handle undefined orderFilterContext', () => {
2535
+ const configs = createConfigs({
2536
+ order: {
2537
+ entries: [
2538
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 10, category: 'cogs' }),
2539
+ ],
2540
+ },
2541
+ });
2542
+ const context = createContext({ orderFilterContext: undefined });
2543
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2544
+ expect(typeof result.categoryCosts.totalOtherCogs).toBe('number');
2545
+ });
2546
+ it('should handle empty tags array in filter context', () => {
2547
+ const configs = createConfigs({
2548
+ order: {
2549
+ entries: [
2550
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
2551
+ flatAmount: 10,
2552
+ category: 'fulfillment',
2553
+ filters: {
2554
+ orderTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['VIP'] },
2555
+ },
2556
+ }),
2557
+ ],
2558
+ },
2559
+ });
2560
+ const context = createContext({
2561
+ orderFilterContext: { tags: [], paymentGateways: [], shippingTitles: [] },
2562
+ });
2563
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2564
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(0);
2565
+ });
2566
+ });
2567
+ describe('date range edge cases', () => {
2568
+ it('should handle entry with effectiveFrom after effectiveTo', () => {
2569
+ const configs = createConfigs({
2570
+ order: {
2571
+ entries: [
2572
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
2573
+ flatAmount: 10,
2574
+ category: 'transaction',
2575
+ effectiveFrom: '2025-01-01T00:00:00Z',
2576
+ effectiveTo: '2024-01-01T00:00:00Z',
2577
+ }),
2578
+ ],
2579
+ },
2580
+ });
2581
+ const context = createContext({ orderCreatedAt: '2024-06-15T10:00:00Z' });
2582
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2583
+ expect(result.categoryCosts.otherTransactionCost).toBe(0);
2584
+ });
2585
+ it('should handle entry with same effectiveFrom and effectiveTo', () => {
2586
+ const configs = createConfigs({
2587
+ order: {
2588
+ entries: [
2589
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
2590
+ flatAmount: 10,
2591
+ category: 'agency_fees',
2592
+ effectiveFrom: '2024-06-15T10:00:00Z',
2593
+ effectiveTo: '2024-06-15T10:00:00Z',
2594
+ }),
2595
+ ],
2596
+ },
2597
+ });
2598
+ const context = createContext({ orderCreatedAt: '2024-06-15T10:00:00Z' });
2599
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2600
+ expect(result.categoryCosts.agencyFeesCost).toBe(10);
2601
+ });
2602
+ it('should handle invalid effectiveFrom format', () => {
2603
+ const configs = createConfigs({
2604
+ order: {
2605
+ entries: [
2606
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
2607
+ flatAmount: 10,
2608
+ category: 'opex',
2609
+ effectiveFrom: 'invalid-date',
2610
+ }),
2611
+ ],
2612
+ },
2613
+ });
2614
+ const context = createContext({ orderCreatedAt: '2024-06-15T10:00:00Z' });
2615
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2616
+ expect(result.categoryCosts.opexCost).toBe(10);
2617
+ });
2618
+ });
2619
+ describe('concurrent category accumulation', () => {
2620
+ it('should correctly accumulate when multiple entries have same ID but different categories', () => {
2621
+ const configs = createConfigs({
2622
+ order: {
2623
+ entries: [
2624
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'same-id', flatAmount: 10, category: 'cogs' }),
2625
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'same-id', flatAmount: 5, category: 'fulfillment' }),
2626
+ ],
2627
+ },
2628
+ });
2629
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2630
+ expect(result.categoryCosts.totalOtherCogs).toBe(10);
2631
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(5);
2632
+ });
2633
+ it('should handle maximum number of entries without performance degradation', () => {
2634
+ const entries = Array.from({ length: 100 }, (_, i) => createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
2635
+ id: `entry-${i}`,
2636
+ flatAmount: 1,
2637
+ category: ['cogs', 'fulfillment', 'transaction', 'marketing', 'agency_fees', 'opex', 'other'][i % 7],
2638
+ }));
2639
+ const configs = createConfigs({
2640
+ order: { entries },
2641
+ });
2642
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2643
+ const totalCost = result.categoryCosts.totalOtherCogs +
2644
+ result.categoryCosts.otherFulfillmentCost +
2645
+ result.categoryCosts.otherTransactionCost +
2646
+ result.categoryCosts.marketingCost +
2647
+ result.categoryCosts.agencyFeesCost +
2648
+ result.categoryCosts.opexCost +
2649
+ result.categoryCosts.totalOtherCost;
2650
+ expect(totalCost).toBe(100);
2651
+ });
2652
+ });
2653
+ });
2654
+ describe('Accounting Identity Tests', () => {
2655
+ describe('sum of category costs equals order + line-item level costs', () => {
2656
+ const sumCategoryCosts = (categoryCosts) => {
2657
+ return (categoryCosts.totalOtherCogs +
2658
+ categoryCosts.otherFulfillmentCost +
2659
+ categoryCosts.otherTransactionCost +
2660
+ categoryCosts.marketingCost +
2661
+ categoryCosts.agencyFeesCost +
2662
+ categoryCosts.opexCost +
2663
+ categoryCosts.totalOtherCost);
2664
+ };
2665
+ it('should maintain identity with single order-level COGS entry', () => {
2666
+ const configs = createConfigs({
2667
+ order: {
2668
+ entries: [
2669
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 25, category: 'cogs' }),
2670
+ ],
2671
+ },
2672
+ });
2673
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2674
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2675
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2676
+ expect(sumOfCategories).toBe(inputTotal);
2677
+ expect(sumOfCategories).toBe(25);
2678
+ });
2679
+ it('should maintain identity with single line-item-level entry', () => {
2680
+ const lineItems = [
2681
+ createLineItem({ lineItemId: 'li-1', quantity: 3 }),
2682
+ createLineItem({ lineItemId: 'li-2', quantity: 2 }),
2683
+ ];
2684
+ const context = createContext({ lineItems, totalQuantity: 5 });
2685
+ const configs = createConfigs({
2686
+ order: {
2687
+ entries: [
2688
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { flatAmount: 4, category: 'fulfillment' }),
2689
+ ],
2690
+ },
2691
+ });
2692
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2693
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2694
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2695
+ expect(sumOfCategories).toBe(inputTotal);
2696
+ expect(sumOfCategories).toBe(20);
2697
+ });
2698
+ it('should maintain identity with mixed order-level and line-item-level entries across all categories', () => {
2699
+ const lineItems = [
2700
+ createLineItem({ lineItemId: 'li-1', quantity: 2 }),
2701
+ createLineItem({ lineItemId: 'li-2', quantity: 3, refundedQuantity: 1 }),
2702
+ ];
2703
+ const context = createContext({
2704
+ lineItems,
2705
+ totalQuantity: 5,
2706
+ totalWeight: 1000,
2707
+ totalProductCost: 50,
2708
+ totalShipping: 10,
2709
+ totalPrice: 100,
2710
+ grossSalePrice: 120,
2711
+ netSalePrice: 90,
2712
+ hasRefund: true,
2713
+ });
2714
+ const configs = createConfigs({
2715
+ order: {
2716
+ entries: [
2717
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 10, category: 'cogs' }),
2718
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, { flatAmount: 2, category: 'fulfillment' }),
2719
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, { percentRate: 5, category: 'transaction' }),
2720
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, { flatAmount: 1, category: 'marketing' }),
2721
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_ORDER, { flatAmount: 15, category: 'agency_fees' }),
2722
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_COGS, { percentRate: 10, category: 'opex' }),
2723
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_LINE_ITEM, { flatAmount: 3, category: 'other' }),
2724
+ ],
2725
+ },
2726
+ });
2727
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2728
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2729
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2730
+ expect(sumOfCategories).toBe(inputTotal);
2731
+ expect(result.categoryCosts.totalOtherCogs).toBe(10);
2732
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(10);
2733
+ expect(result.categoryCosts.otherTransactionCost).toBe(5);
2734
+ expect(result.categoryCosts.marketingCost).toBe(5);
2735
+ expect(result.categoryCosts.agencyFeesCost).toBe(15);
2736
+ expect(result.categoryCosts.opexCost).toBe(5);
2737
+ expect(result.categoryCosts.totalOtherCost).toBe(3);
2738
+ expect(sumOfCategories).toBe(53);
2739
+ });
2740
+ it('should maintain identity when entries are filtered out by date range', () => {
2741
+ const configs = createConfigs({
2742
+ order: {
2743
+ entries: [
2744
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
2745
+ flatAmount: 10,
2746
+ category: 'cogs',
2747
+ effectiveFrom: '2024-01-01T00:00:00Z',
2748
+ effectiveTo: '2024-12-31T23:59:59Z',
2749
+ }),
2750
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
2751
+ flatAmount: 20,
2752
+ category: 'fulfillment',
2753
+ effectiveFrom: '2025-01-01T00:00:00Z',
2754
+ }),
2755
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
2756
+ flatAmount: 30,
2757
+ category: 'marketing',
2758
+ effectiveFrom: '2023-01-01T00:00:00Z',
2759
+ effectiveTo: '2023-12-31T23:59:59Z',
2760
+ }),
2761
+ ],
2762
+ },
2763
+ });
2764
+ const context = createContext({ orderCreatedAt: '2024-06-15T10:00:00Z' });
2765
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2766
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2767
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2768
+ expect(sumOfCategories).toBe(inputTotal);
2769
+ expect(sumOfCategories).toBe(10);
2770
+ expect(result.categoryCosts.totalOtherCogs).toBe(10);
2771
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(0);
2772
+ expect(result.categoryCosts.marketingCost).toBe(0);
2773
+ });
2774
+ it('should maintain identity when entries are filtered out by cost filters', () => {
2775
+ const lineItems = [
2776
+ createLineItem({
2777
+ lineItemId: 'li-1',
2778
+ quantity: 2,
2779
+ filterContext: { tags: ['sale'], productId: '100', variantId: '200' },
2780
+ }),
2781
+ createLineItem({
2782
+ lineItemId: 'li-2',
2783
+ quantity: 3,
2784
+ filterContext: { tags: ['premium'], productId: '101', variantId: '201' },
2785
+ }),
2786
+ ];
2787
+ const context = createContext({
2788
+ lineItems,
2789
+ totalQuantity: 5,
2790
+ orderFilterContext: {
2791
+ tags: ['wholesale'],
2792
+ paymentGateways: ['stripe'],
2793
+ shippingTitles: ['Standard'],
2794
+ },
2795
+ });
2796
+ const configs = createConfigs({
2797
+ order: {
2798
+ entries: [
2799
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
2800
+ flatAmount: 10,
2801
+ category: 'cogs',
2802
+ filters: { orderTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['wholesale'] } },
2803
+ }),
2804
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
2805
+ flatAmount: 20,
2806
+ category: 'fulfillment',
2807
+ filters: { orderTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['retail'] } },
2808
+ }),
2809
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, {
2810
+ flatAmount: 5,
2811
+ category: 'transaction',
2812
+ filters: { productTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['sale'] } },
2813
+ }),
2814
+ ],
2815
+ },
2816
+ });
2817
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2818
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2819
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2820
+ expect(sumOfCategories).toBe(inputTotal);
2821
+ expect(result.categoryCosts.totalOtherCogs).toBe(10);
2822
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(0);
2823
+ expect(result.categoryCosts.otherTransactionCost).toBe(10);
2824
+ expect(sumOfCategories).toBe(20);
2825
+ });
2826
+ it('should maintain identity with multiple entries in same category', () => {
2827
+ const configs = createConfigs({
2828
+ order: {
2829
+ entries: [
2830
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'e1', flatAmount: 10, category: 'cogs' }),
2831
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'e2', flatAmount: 15, category: 'cogs' }),
2832
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'e3', flatAmount: 5, category: 'cogs' }),
2833
+ ],
2834
+ },
2835
+ });
2836
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2837
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2838
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2839
+ expect(sumOfCategories).toBe(inputTotal);
2840
+ expect(result.categoryCosts.totalOtherCogs).toBe(30);
2841
+ expect(sumOfCategories).toBe(30);
2842
+ });
2843
+ it('should maintain identity with legacy category mappings', () => {
2844
+ const configs = createConfigs({
2845
+ order: {
2846
+ entries: [
2847
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 10, category: 'payment_fees' }),
2848
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 20, category: 'packaging' }),
2849
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 5, category: 'transaction' }),
2850
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 8, category: 'fulfillment' }),
2851
+ ],
2852
+ },
2853
+ });
2854
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2855
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2856
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2857
+ expect(sumOfCategories).toBe(inputTotal);
2858
+ expect(result.categoryCosts.otherTransactionCost).toBe(15);
2859
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(28);
2860
+ expect(sumOfCategories).toBe(43);
2861
+ });
2862
+ it('should maintain identity with empty config', () => {
2863
+ const configs = createConfigs({
2864
+ order: { entries: [] },
2865
+ });
2866
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
2867
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2868
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2869
+ expect(sumOfCategories).toBe(inputTotal);
2870
+ expect(sumOfCategories).toBe(0);
2871
+ });
2872
+ it('should maintain identity with null config', () => {
2873
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(null, createContext(), 'USD', emptyRatesMap);
2874
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2875
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2876
+ expect(sumOfCategories).toBe(inputTotal);
2877
+ expect(sumOfCategories).toBe(0);
2878
+ });
2879
+ it('should maintain identity with currency conversion', () => {
2880
+ const exchangeRates = {
2881
+ USD: { EUR: 0.85, GBP: 0.73 },
2882
+ EUR: { USD: 1.18, GBP: 0.86 },
2883
+ };
2884
+ const configs = createConfigs({
2885
+ order: {
2886
+ entries: [
2887
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 10, category: 'cogs', currency: 'EUR' }),
2888
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 20, category: 'fulfillment', currency: 'GBP' }),
2889
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 15, category: 'marketing', currency: 'USD' }),
2890
+ ],
2891
+ },
2892
+ });
2893
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', exchangeRates);
2894
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2895
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2896
+ expect(sumOfCategories).toBeCloseTo(inputTotal, 5);
2897
+ expect(result.categoryCosts.totalOtherCogs).toBeCloseTo(10 * 1.18, 5);
2898
+ expect(result.categoryCosts.marketingCost).toBe(15);
2899
+ });
2900
+ it('should maintain identity with percentage-based entries', () => {
2901
+ const context = createContext({
2902
+ totalProductCost: 100,
2903
+ totalShipping: 50,
2904
+ totalPrice: 200,
2905
+ grossSalePrice: 250,
2906
+ netSalePrice: 180,
2907
+ });
2908
+ const configs = createConfigs({
2909
+ order: {
2910
+ entries: [
2911
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_COGS, { percentRate: 10, category: 'cogs' }),
2912
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_SHIPPING, { percentRate: 20, category: 'fulfillment' }),
2913
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, { percentRate: 5, category: 'transaction' }),
2914
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_GROSS_SALES, { percentRate: 8, category: 'marketing' }),
2915
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_NET_SALES, { percentRate: 3, category: 'opex' }),
2916
+ ],
2917
+ },
2918
+ });
2919
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2920
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2921
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2922
+ expect(sumOfCategories).toBeCloseTo(inputTotal, 5);
2923
+ expect(result.categoryCosts.totalOtherCogs).toBe(10);
2924
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(10);
2925
+ expect(result.categoryCosts.otherTransactionCost).toBe(10);
2926
+ expect(result.categoryCosts.marketingCost).toBe(20);
2927
+ expect(result.categoryCosts.opexCost).toBe(5.4);
2928
+ expect(sumOfCategories).toBe(55.4);
2929
+ });
2930
+ it('should maintain identity with weight-based entries', () => {
2931
+ const context = createContext({ totalWeight: 1500 });
2932
+ const configs = createConfigs({
2933
+ order: {
2934
+ entries: [
2935
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
2936
+ flatAmount: 0.5,
2937
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.KILOGRAMS,
2938
+ category: 'fulfillment',
2939
+ }),
2940
+ ],
2941
+ },
2942
+ });
2943
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2944
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2945
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2946
+ expect(sumOfCategories).toBeCloseTo(inputTotal, 5);
2947
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(0.75);
2948
+ });
2949
+ it('should maintain identity with weight-tiered entries', () => {
2950
+ const context = createContext({ totalWeight: 750 });
2951
+ const configs = createConfigs({
2952
+ order: {
2953
+ entries: [
2954
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
2955
+ category: 'fulfillment',
2956
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.GRAMS,
2957
+ weightIntervals: [
2958
+ { weightMin: 0, weightMax: 500, flatAmount: 5 },
2959
+ { weightMin: 501, weightMax: 1000, flatAmount: 8 },
2960
+ { weightMin: 1001, weightMax: null, flatAmount: 12 },
2961
+ ],
2962
+ }),
2963
+ ],
2964
+ },
2965
+ });
2966
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2967
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2968
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2969
+ expect(sumOfCategories).toBeCloseTo(inputTotal, 5);
2970
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(8);
2971
+ });
2972
+ it('should maintain identity with quantity-tiered entries', () => {
2973
+ const context = createContext({
2974
+ lineItems: [
2975
+ createLineItem({ lineItemId: '1', quantity: 5 }),
2976
+ createLineItem({ lineItemId: '2', quantity: 15 }),
2977
+ ],
2978
+ });
2979
+ const configs = createConfigs({
2980
+ order: {
2981
+ entries: [
2982
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, {
2983
+ category: 'fulfillment',
2984
+ quantityIntervals: [
2985
+ { quantityMin: 1, quantityMax: 10, flatAmount: 10 },
2986
+ { quantityMin: 11, quantityMax: 50, flatAmount: 8 },
2987
+ { quantityMin: 51, quantityMax: null, flatAmount: 5 },
2988
+ ],
2989
+ }),
2990
+ ],
2991
+ },
2992
+ });
2993
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
2994
+ const sumOfCategories = sumCategoryCosts(result.categoryCosts);
2995
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
2996
+ expect(sumOfCategories).toBeCloseTo(inputTotal, 5);
2997
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(18);
2998
+ });
2999
+ });
3000
+ describe('no double-counting between categories', () => {
3001
+ it('should route each entry to exactly one category', () => {
3002
+ const configs = createConfigs({
3003
+ order: {
3004
+ entries: [
3005
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { id: 'e1', flatAmount: 100, category: 'cogs' }),
3006
+ ],
3007
+ },
3008
+ });
3009
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
3010
+ expect(result.categoryCosts.totalOtherCogs).toBe(100);
3011
+ expect(result.categoryCosts.otherFulfillmentCost).toBe(0);
3012
+ expect(result.categoryCosts.otherTransactionCost).toBe(0);
3013
+ expect(result.categoryCosts.marketingCost).toBe(0);
3014
+ expect(result.categoryCosts.agencyFeesCost).toBe(0);
3015
+ expect(result.categoryCosts.opexCost).toBe(0);
3016
+ expect(result.categoryCosts.totalOtherCost).toBe(0);
3017
+ });
3018
+ it('should not double-count when entry matches multiple conditions', () => {
3019
+ const lineItems = [
3020
+ createLineItem({
3021
+ lineItemId: 'li-1',
3022
+ quantity: 2,
3023
+ refundedQuantity: 1,
3024
+ filterContext: { tags: ['sale', 'premium'], productId: '100', variantId: '200', vendor: 'acme' },
3025
+ }),
3026
+ ];
3027
+ const context = createContext({
3028
+ lineItems,
3029
+ totalQuantity: 2,
3030
+ hasRefund: true,
3031
+ orderFilterContext: {
3032
+ tags: ['wholesale', 'vip'],
3033
+ paymentGateways: ['stripe', 'paypal'],
3034
+ shippingTitles: ['Express', 'Standard'],
3035
+ },
3036
+ });
3037
+ const configs = createConfigs({
3038
+ order: {
3039
+ entries: [
3040
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
3041
+ flatAmount: 50,
3042
+ category: 'transaction',
3043
+ filters: {
3044
+ orderTags: { mode: at_tracking_event_types_1.COST_FILTER_MODE.INCLUDE, values: ['wholesale', 'vip'] },
3045
+ },
3046
+ }),
3047
+ ],
3048
+ },
3049
+ });
3050
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
3051
+ expect(result.categoryCosts.otherTransactionCost).toBe(50);
3052
+ expect(result.orderLevelOtherCost + result.lineItemLevelOtherCost).toBe(50);
3053
+ });
3054
+ });
3055
+ describe('handling/gateway/shipping costs are separate from category costs', () => {
3056
+ it('should not include handling costs in category cost sum', () => {
3057
+ const lineItems = [
3058
+ createLineItem({ lineItemId: 'li-1', quantity: 2, variantHandlingFee: 5 }),
3059
+ createLineItem({ lineItemId: 'li-2', quantity: 1, variantHandlingFee: 3 }),
3060
+ ];
3061
+ const configs = createConfigs({
3062
+ globalProductOverride: { globalHandlingFee: 2, currency: 'USD' },
3063
+ order: {
3064
+ entries: [
3065
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 10, category: 'cogs' }),
3066
+ ],
3067
+ },
3068
+ });
3069
+ const context = createContext({ lineItems, totalQuantity: 3 });
3070
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
3071
+ const sumOfCategories = result.categoryCosts.totalOtherCogs +
3072
+ result.categoryCosts.otherFulfillmentCost +
3073
+ result.categoryCosts.otherTransactionCost +
3074
+ result.categoryCosts.marketingCost +
3075
+ result.categoryCosts.agencyFeesCost +
3076
+ result.categoryCosts.opexCost +
3077
+ result.categoryCosts.totalOtherCost;
3078
+ expect(sumOfCategories).toBe(10);
3079
+ expect(result.totalHandlingCost).toBe(17);
3080
+ expect(result.lineItemLevelHandlingCost).toBe(17);
3081
+ });
3082
+ it('should not include gateway costs in category cost sum', () => {
3083
+ const configs = createConfigs({
3084
+ gateway: {
3085
+ entries: [
3086
+ { id: 'g1', name: 'Stripe', gatewayName: 'stripe', flatAmount: 0.30, percentRate: 2.9, currency: 'USD' },
3087
+ ],
3088
+ },
3089
+ order: {
3090
+ entries: [
3091
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 10, category: 'fulfillment' }),
3092
+ ],
3093
+ },
3094
+ });
3095
+ const context = createContext({ totalPrice: 100 });
3096
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', emptyRatesMap);
3097
+ const sumOfCategories = result.categoryCosts.totalOtherCogs +
3098
+ result.categoryCosts.otherFulfillmentCost +
3099
+ result.categoryCosts.otherTransactionCost +
3100
+ result.categoryCosts.marketingCost +
3101
+ result.categoryCosts.agencyFeesCost +
3102
+ result.categoryCosts.opexCost +
3103
+ result.categoryCosts.totalOtherCost;
3104
+ expect(sumOfCategories).toBe(10);
3105
+ expect(result.totalGatewayCost).toBe(3.2);
3106
+ });
3107
+ it('should not include shipping costs in category cost sum', () => {
3108
+ const configs = createConfigs({
3109
+ shipping: {
3110
+ method: at_tracking_event_types_1.SHIPPING_METHOD.FIXED_RATE,
3111
+ fixedRateAmount: 15,
3112
+ currency: 'USD',
3113
+ },
3114
+ order: {
3115
+ entries: [
3116
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, { flatAmount: 10, category: 'marketing' }),
3117
+ ],
3118
+ },
3119
+ });
3120
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, createContext(), 'USD', emptyRatesMap);
3121
+ const sumOfCategories = result.categoryCosts.totalOtherCogs +
3122
+ result.categoryCosts.otherFulfillmentCost +
3123
+ result.categoryCosts.otherTransactionCost +
3124
+ result.categoryCosts.marketingCost +
3125
+ result.categoryCosts.agencyFeesCost +
3126
+ result.categoryCosts.opexCost +
3127
+ result.categoryCosts.totalOtherCost;
3128
+ expect(sumOfCategories).toBe(10);
3129
+ expect(result.totalShippingCost).toBe(15);
3130
+ });
3131
+ });
3132
+ describe('stress test: large realistic order with all cost types', () => {
3133
+ it('should maintain accounting identity with complex realistic order', () => {
3134
+ const lineItems = [
3135
+ createLineItem({
3136
+ lineItemId: 'li-1',
3137
+ quantity: 5,
3138
+ refundedQuantity: 0,
3139
+ variantHandlingFee: 1.50,
3140
+ filterContext: { tags: ['electronics'], productId: '100', variantId: '200', vendor: 'Samsung' },
3141
+ }),
3142
+ createLineItem({
3143
+ lineItemId: 'li-2',
3144
+ quantity: 3,
3145
+ refundedQuantity: 1,
3146
+ variantHandlingFee: 0.75,
3147
+ filterContext: { tags: ['accessories'], productId: '101', variantId: '201', vendor: 'Generic' },
3148
+ }),
3149
+ createLineItem({
3150
+ lineItemId: 'li-3',
3151
+ quantity: 2,
3152
+ refundedQuantity: 0,
3153
+ variantHandlingFee: 2.00,
3154
+ filterContext: { tags: ['premium', 'electronics'], productId: '102', variantId: '202', vendor: 'Apple' },
3155
+ }),
3156
+ ];
3157
+ const context = createContext({
3158
+ orderCreatedAt: '2024-06-15T10:00:00Z',
3159
+ totalPrice: 850.00,
3160
+ grossSalePrice: 920.00,
3161
+ netSalePrice: 780.00,
3162
+ totalProductCost: 425.00,
3163
+ totalShipping: 25.00,
3164
+ totalWeight: 2500,
3165
+ totalQuantity: 10,
3166
+ hasRefund: true,
3167
+ orderFilterContext: {
3168
+ tags: ['online', 'marketing-campaign'],
3169
+ paymentGateways: ['stripe'],
3170
+ shippingTitles: ['Express Shipping'],
3171
+ shipCountryCode: 'US',
3172
+ },
3173
+ lineItems,
3174
+ });
3175
+ const exchangeRates = {
3176
+ USD: { EUR: 0.85, GBP: 0.73, CAD: 1.35 },
3177
+ EUR: { USD: 1.18 },
3178
+ GBP: { USD: 1.37 },
3179
+ CAD: { USD: 0.74 },
3180
+ };
3181
+ const configs = createConfigs({
3182
+ globalProductOverride: {
3183
+ globalHandlingFee: 0.50,
3184
+ currency: 'USD',
3185
+ },
3186
+ gateway: {
3187
+ entries: [
3188
+ {
3189
+ id: 'stripe',
3190
+ name: 'Stripe',
3191
+ gatewayName: 'stripe',
3192
+ flatAmount: 0.30,
3193
+ percentRate: 2.9,
3194
+ currency: 'USD',
3195
+ },
3196
+ ],
3197
+ },
3198
+ shipping: {
3199
+ method: at_tracking_event_types_1.SHIPPING_METHOD.SHIPPING_PROFILES,
3200
+ profiles: [
3201
+ {
3202
+ id: 'us-flat',
3203
+ name: 'US Flat Rate',
3204
+ countries: ['US'],
3205
+ isDefault: false,
3206
+ rateType: at_tracking_event_types_1.SHIPPING_RATE_TYPE.FLAT,
3207
+ flatAmount: 12.00,
3208
+ currency: 'USD',
3209
+ },
3210
+ {
3211
+ id: 'default',
3212
+ name: 'Default',
3213
+ countries: [],
3214
+ isDefault: true,
3215
+ rateType: at_tracking_event_types_1.SHIPPING_RATE_TYPE.WEIGHT_TIERED,
3216
+ weightUnit: at_tracking_event_types_1.WEIGHT_UNIT.KILOGRAMS,
3217
+ weightTiers: [
3218
+ { weightMin: 0, weightMax: 1, flatAmount: 8 },
3219
+ { weightMin: 1.001, weightMax: 5, flatAmount: 15 },
3220
+ { weightMin: 5.001, weightMax: null, flatAmount: 25 },
3221
+ ],
3222
+ currency: 'USD',
3223
+ },
3224
+ ],
3225
+ },
3226
+ order: {
3227
+ entries: [
3228
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
3229
+ id: 'packaging-base',
3230
+ flatAmount: 2.00,
3231
+ category: 'cogs',
3232
+ }),
3233
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_COGS, {
3234
+ id: 'inventory-shrinkage',
3235
+ percentRate: 2,
3236
+ category: 'cogs',
3237
+ }),
3238
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_LINE_ITEM, {
3239
+ id: 'pick-pack',
3240
+ flatAmount: 0.75,
3241
+ category: 'fulfillment',
3242
+ }),
3243
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_ORDER_WEIGHT, {
3244
+ id: 'packing-materials',
3245
+ flatAmount: 0.002,
3246
+ category: 'fulfillment',
3247
+ }),
3248
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_TOTAL_SALES, {
3249
+ id: 'platform-fee',
3250
+ percentRate: 1.5,
3251
+ category: 'transaction',
3252
+ }),
3253
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_GROSS_SALES, {
3254
+ id: 'affiliate-commission',
3255
+ percentRate: 5,
3256
+ category: 'marketing',
3257
+ }),
3258
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_ORDER, {
3259
+ id: 'agency-retainer-allocation',
3260
+ flatAmount: 8.00,
3261
+ currency: 'EUR',
3262
+ category: 'agency_fees',
3263
+ }),
3264
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PCT_NET_SALES, {
3265
+ id: 'overhead-allocation',
3266
+ percentRate: 3,
3267
+ category: 'opex',
3268
+ }),
3269
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_ORDER, {
3270
+ id: 'refund-processing',
3271
+ flatAmount: 5.00,
3272
+ category: 'opex',
3273
+ }),
3274
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.PER_REFUND_LINE_ITEM, {
3275
+ id: 'restocking-fee',
3276
+ flatAmount: 3.00,
3277
+ category: 'other',
3278
+ }),
3279
+ createEntry(at_tracking_event_types_1.ORDER_COST_ENTRY_TYPE.BY_LINE_ITEM_QUANTITY, {
3280
+ id: 'misc-per-unit',
3281
+ quantityIntervals: [
3282
+ { quantityMin: 1, quantityMax: 5, flatAmount: 2 },
3283
+ { quantityMin: 6, quantityMax: 20, flatAmount: 1.5 },
3284
+ { quantityMin: 21, quantityMax: null, flatAmount: 1 },
3285
+ ],
3286
+ category: 'other',
3287
+ }),
3288
+ ],
3289
+ },
3290
+ });
3291
+ const result = cost_calculator_service_1.CostCalculatorService.calculateOrderCosts(configs, context, 'USD', exchangeRates);
3292
+ const sumOfCategories = result.categoryCosts.totalOtherCogs +
3293
+ result.categoryCosts.otherFulfillmentCost +
3294
+ result.categoryCosts.otherTransactionCost +
3295
+ result.categoryCosts.marketingCost +
3296
+ result.categoryCosts.agencyFeesCost +
3297
+ result.categoryCosts.opexCost +
3298
+ result.categoryCosts.totalOtherCost;
3299
+ const inputTotal = result.orderLevelOtherCost + result.lineItemLevelOtherCost;
3300
+ expect(sumOfCategories).toBeCloseTo(inputTotal, 5);
3301
+ expect(Number.isFinite(sumOfCategories)).toBe(true);
3302
+ expect(sumOfCategories).toBeGreaterThanOrEqual(0);
3303
+ expect(Number.isFinite(result.totalHandlingCost)).toBe(true);
3304
+ expect(Number.isFinite(result.totalGatewayCost)).toBe(true);
3305
+ expect(Number.isFinite(result.totalShippingCost)).toBe(true);
3306
+ expect(result.categoryCosts.totalOtherCogs).toBeGreaterThan(0);
3307
+ expect(result.categoryCosts.otherFulfillmentCost).toBeGreaterThan(0);
3308
+ expect(result.categoryCosts.otherTransactionCost).toBeGreaterThan(0);
3309
+ expect(result.categoryCosts.marketingCost).toBeGreaterThan(0);
3310
+ expect(result.categoryCosts.agencyFeesCost).toBeGreaterThan(0);
3311
+ expect(result.categoryCosts.opexCost).toBeGreaterThan(0);
3312
+ expect(result.categoryCosts.totalOtherCost).toBeGreaterThan(0);
3313
+ expect(result.totalHandlingCost).toBeGreaterThan(0);
3314
+ expect(result.totalGatewayCost).toBeGreaterThan(0);
3315
+ expect(result.totalShippingCost).toBeGreaterThan(0);
3316
+ });
3317
+ });
3318
+ });
3319
+ });
3320
+ //# sourceMappingURL=cost-calculator-service.spec.js.map