@openmrs/esm-billing-app 1.1.2-pre.9 → 1.2.0

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 (390) hide show
  1. package/.turbo/cache/31f1dfc7f71601df-meta.json +1 -0
  2. package/.turbo/cache/31f1dfc7f71601df.tar.zst +0 -0
  3. package/.turbo/turbo-build.log +13 -42
  4. package/__mocks__/bills.mock.ts +3 -2
  5. package/dist/1480.js +1 -0
  6. package/dist/1480.js.map +1 -0
  7. package/dist/1564.js +1 -0
  8. package/dist/1564.js.map +1 -0
  9. package/dist/1578.js +1 -0
  10. package/dist/1578.js.map +1 -0
  11. package/dist/1646.js +1 -0
  12. package/dist/1646.js.map +1 -0
  13. package/dist/1869.js +1 -0
  14. package/dist/1869.js.map +1 -0
  15. package/dist/1877.js +1 -0
  16. package/dist/1877.js.map +1 -0
  17. package/dist/1899.js +1 -0
  18. package/dist/1899.js.map +1 -0
  19. package/dist/196.js +2 -0
  20. package/dist/196.js.map +1 -0
  21. package/dist/2250.js +43 -0
  22. package/dist/2250.js.map +1 -0
  23. package/dist/2269.js +1 -0
  24. package/dist/2269.js.map +1 -0
  25. package/dist/2317.js +1 -0
  26. package/dist/2317.js.map +1 -0
  27. package/dist/2416.js +1 -0
  28. package/dist/2416.js.map +1 -0
  29. package/dist/2489.js +1 -0
  30. package/dist/2489.js.map +1 -0
  31. package/dist/282.js +1 -0
  32. package/dist/282.js.map +1 -0
  33. package/dist/2881.js +1 -0
  34. package/dist/2881.js.map +1 -0
  35. package/dist/2997.js +1 -0
  36. package/dist/2997.js.map +1 -0
  37. package/dist/3378.js +1 -0
  38. package/dist/3378.js.map +1 -0
  39. package/dist/3379.js +1 -0
  40. package/dist/3379.js.map +1 -0
  41. package/dist/3784.js +1 -0
  42. package/dist/3784.js.map +1 -0
  43. package/dist/3963.js +1 -0
  44. package/dist/3963.js.map +1 -0
  45. package/dist/4106.js +1 -0
  46. package/dist/4106.js.map +1 -0
  47. package/dist/4111.js +1 -0
  48. package/dist/4111.js.map +1 -0
  49. package/dist/434.js +1 -0
  50. package/dist/434.js.map +1 -0
  51. package/dist/4348.js +1 -0
  52. package/dist/4348.js.map +1 -0
  53. package/dist/4383.js +1 -0
  54. package/dist/4383.js.map +1 -0
  55. package/dist/4658.js +1 -0
  56. package/dist/4658.js.map +1 -0
  57. package/dist/4870.js +1 -0
  58. package/dist/4870.js.map +1 -0
  59. package/dist/4928.js +1 -0
  60. package/dist/4928.js.map +1 -0
  61. package/dist/5098.js +1 -0
  62. package/dist/5098.js.map +1 -0
  63. package/dist/5117.js +1 -0
  64. package/dist/5117.js.map +1 -0
  65. package/dist/5132.js +1 -0
  66. package/dist/5132.js.map +1 -0
  67. package/dist/5145.js +1 -0
  68. package/dist/5145.js.map +1 -0
  69. package/dist/5390.js +1 -0
  70. package/dist/5390.js.map +1 -0
  71. package/dist/5503.js +1 -0
  72. package/dist/5503.js.map +1 -0
  73. package/dist/556.js +1 -0
  74. package/dist/556.js.map +1 -0
  75. package/dist/5644.js +1 -0
  76. package/dist/5644.js.map +1 -0
  77. package/dist/5898.js +1 -0
  78. package/dist/5898.js.map +1 -0
  79. package/dist/5940.js +1 -0
  80. package/dist/5940.js.map +1 -0
  81. package/dist/6047.js +1 -0
  82. package/dist/6047.js.map +1 -0
  83. package/dist/6237.js +1 -0
  84. package/dist/6237.js.map +1 -0
  85. package/dist/6362.js +1 -0
  86. package/dist/6362.js.map +1 -0
  87. package/dist/6371.js +1 -0
  88. package/dist/6371.js.map +1 -0
  89. package/dist/6377.js +1 -0
  90. package/dist/6377.js.map +1 -0
  91. package/dist/6444.js +1 -0
  92. package/dist/6444.js.map +1 -0
  93. package/dist/6508.js +1 -0
  94. package/dist/6508.js.map +1 -0
  95. package/dist/6594.js +1 -0
  96. package/dist/6594.js.map +1 -0
  97. package/dist/6724.js +1 -0
  98. package/dist/6724.js.map +1 -0
  99. package/dist/6904.js +1 -0
  100. package/dist/6904.js.map +1 -0
  101. package/dist/7045.js +1 -0
  102. package/dist/7045.js.map +1 -0
  103. package/dist/7175.js +1 -0
  104. package/dist/7175.js.map +1 -0
  105. package/dist/7182.js +1 -0
  106. package/dist/7182.js.map +1 -0
  107. package/dist/7247.js +1 -0
  108. package/dist/7247.js.map +1 -0
  109. package/dist/7742.js +1 -0
  110. package/dist/7742.js.map +1 -0
  111. package/dist/7912.js +1 -0
  112. package/dist/7912.js.map +1 -0
  113. package/dist/8358.js +1 -0
  114. package/dist/8358.js.map +1 -0
  115. package/dist/8359.js +1 -0
  116. package/dist/8359.js.map +1 -0
  117. package/dist/8695.js +1 -0
  118. package/dist/8695.js.map +1 -0
  119. package/dist/903.js +1 -0
  120. package/dist/903.js.map +1 -0
  121. package/dist/9072.js +1 -0
  122. package/dist/9072.js.map +1 -0
  123. package/dist/9414.js +1 -0
  124. package/dist/9414.js.map +1 -0
  125. package/dist/9655.js +11 -0
  126. package/dist/9655.js.map +1 -0
  127. package/dist/9806.js +1 -0
  128. package/dist/9806.js.map +1 -0
  129. package/dist/990.js +1 -0
  130. package/dist/990.js.map +1 -0
  131. package/dist/main.js +17 -2
  132. package/dist/main.js.map +1 -1
  133. package/dist/openmrs-esm-billing-app.js +6 -1
  134. package/dist/openmrs-esm-billing-app.js.buildmanifest.json +643 -436
  135. package/dist/openmrs-esm-billing-app.js.map +1 -1
  136. package/dist/routes.json +1 -1
  137. package/e2e/commands/billing-operations.ts +21 -0
  138. package/e2e/commands/types.ts +9 -1
  139. package/e2e/pages/discounts-page.ts +75 -0
  140. package/e2e/pages/index.ts +1 -0
  141. package/e2e/pages/invoice-page.ts +7 -7
  142. package/e2e/specs/bill-discounts.spec.ts +255 -0
  143. package/e2e/specs/billing-dashboard.spec.ts +3 -3
  144. package/e2e/specs/billing-patient-chart.spec.ts +2 -2
  145. package/package.json +13 -22
  146. package/rspack.config.js +1 -0
  147. package/src/bill-history/bill-action-menu.component.tsx +20 -2
  148. package/src/bill-history/bill-history.test.tsx +23 -22
  149. package/src/bill-item-actions/edit-bill-item.modal.tsx +1 -1
  150. package/src/bill-item-actions/edit-bill-item.test.tsx +29 -27
  151. package/src/billable-services/billable-service-form/billable-service-form.test.tsx +74 -73
  152. package/src/billable-services/billable-services-home.component.tsx +4 -2
  153. package/src/billable-services/billable-services.test.tsx +8 -7
  154. package/src/billable-services/dashboard/dashboard.test.tsx +3 -2
  155. package/src/billable-services-admin-card-link.test.tsx +2 -1
  156. package/src/billing-dashboard/billing-dashboard.test.tsx +19 -3
  157. package/src/billing-form/billing-checkin-form.component.tsx +7 -3
  158. package/src/billing-form/billing-checkin-form.test.tsx +22 -21
  159. package/src/billing-form/billing-form.resource.test.ts +7 -6
  160. package/src/billing-form/billing-form.test.tsx +77 -40
  161. package/src/billing-form/billing-form.workspace.tsx +25 -6
  162. package/src/billing-form/visit-attributes/visit-attributes-form.component.tsx +6 -2
  163. package/src/billing.resource.test.ts +43 -41
  164. package/src/billing.resource.ts +65 -16
  165. package/src/bills-table/bills-table.component.tsx +15 -4
  166. package/src/bills-table/bills-table.test.tsx +72 -71
  167. package/src/config-schema.ts +0 -7
  168. package/src/discounts/admin/discount-requests-left-panel-link.component.tsx +43 -0
  169. package/src/discounts/admin/discount-requests.component.tsx +316 -0
  170. package/src/discounts/admin/discount-requests.scss +133 -0
  171. package/src/discounts/admin/discount-requests.test.tsx +104 -0
  172. package/src/discounts/admin/review-bill-discounts/bill-line-items-table/bill-line-items-table.component.tsx +42 -0
  173. package/src/discounts/admin/review-bill-discounts/bill-line-items-table/bill-line-items-table.scss +76 -0
  174. package/src/discounts/admin/review-bill-discounts/bill-payments-table/bill-payments-table.component.tsx +50 -0
  175. package/src/discounts/admin/review-bill-discounts/bill-payments-table/bill-payments-table.scss +63 -0
  176. package/src/discounts/admin/review-bill-discounts/bill-receipt-rail/bill-receipt-rail.component.tsx +73 -0
  177. package/src/discounts/admin/review-bill-discounts/bill-receipt-rail/bill-receipt-rail.scss +54 -0
  178. package/src/discounts/admin/review-bill-discounts/bill-totals-summary/bill-totals-summary.component.tsx +95 -0
  179. package/src/discounts/admin/review-bill-discounts/bill-totals-summary/bill-totals-summary.scss +128 -0
  180. package/src/discounts/admin/review-bill-discounts/discount-card/discount-card.component.tsx +158 -0
  181. package/src/discounts/admin/review-bill-discounts/discount-card/discount-card.scss +164 -0
  182. package/src/discounts/admin/review-bill-discounts/discount-review-stack/discount-review-stack.component.tsx +86 -0
  183. package/src/discounts/admin/review-bill-discounts/discount-review-stack/discount-review-stack.scss +40 -0
  184. package/src/discounts/admin/review-bill-discounts/review-bill-discounts.modal.scss +14 -0
  185. package/src/discounts/admin/review-bill-discounts/review-bill-discounts.modal.test.tsx +153 -0
  186. package/src/discounts/admin/review-bill-discounts/review-bill-discounts.modal.tsx +167 -0
  187. package/src/discounts/admin/review-bill-discounts/review-bill-discounts.utils.ts +42 -0
  188. package/src/discounts/discounts-table.component.tsx +109 -0
  189. package/src/discounts/discounts-table.scss +37 -0
  190. package/src/discounts/discounts-table.test.tsx +67 -0
  191. package/src/discounts/discounts.resource.ts +71 -0
  192. package/src/discounts/request-discount.modal.scss +88 -0
  193. package/src/discounts/request-discount.modal.test.tsx +161 -0
  194. package/src/discounts/request-discount.modal.tsx +253 -0
  195. package/src/index.ts +52 -21
  196. package/src/invoice/invoice-table.component.tsx +116 -18
  197. package/src/invoice/invoice-table.test.tsx +165 -13
  198. package/src/invoice/invoice.component.tsx +111 -7
  199. package/src/invoice/invoice.test.tsx +366 -66
  200. package/src/invoice/line-item-action-menu.component.tsx +31 -1
  201. package/src/invoice/payments/payment-form/payment-form.test.tsx +20 -19
  202. package/src/invoice/payments/payment-history/payment-history.test.tsx +13 -10
  203. package/src/invoice/payments/payments.component.tsx +20 -6
  204. package/src/invoice/payments/payments.test.tsx +88 -23
  205. package/src/invoice/printable-invoice/print-receipt.test.tsx +10 -28
  206. package/src/invoice/printable-invoice/printable-footer.test.tsx +5 -4
  207. package/src/invoice/printable-invoice/printable-invoice-header.component.tsx +3 -3
  208. package/src/invoice/printable-invoice/printable-invoice-header.test.tsx +26 -11
  209. package/src/invoice/printable-invoice/printable-invoice.component.tsx +38 -15
  210. package/src/left-panel-link.test.tsx +3 -3
  211. package/src/metrics-cards/metrics-cards.test.tsx +11 -10
  212. package/src/modal/delete-bill-confirmation.modal.test.tsx +134 -0
  213. package/src/modal/delete-bill-confirmation.modal.tsx +98 -0
  214. package/src/modal/delete-line-item-confirmation.modal.test.tsx +11 -9
  215. package/src/modal/finalize-bill-confirmation.modal.test.tsx +10 -8
  216. package/src/modal/require-payment-modal.test.tsx +12 -11
  217. package/src/payment-status-tag/payment-status-tag.component.tsx +50 -0
  218. package/src/payment-status-tag/payment-status-tag.scss +6 -0
  219. package/src/payment-status-tag/payment-status-tag.test.tsx +113 -0
  220. package/src/refunds/admin/refund-requests-left-panel-link.component.tsx +43 -0
  221. package/src/refunds/admin/refund-requests.component.tsx +324 -0
  222. package/src/refunds/admin/refund-requests.scss +133 -0
  223. package/src/refunds/admin/refund-requests.test.tsx +99 -0
  224. package/src/refunds/admin/review-bill-refunds/bill-line-items-table/bill-line-items-table.component.tsx +42 -0
  225. package/src/refunds/admin/review-bill-refunds/bill-line-items-table/bill-line-items-table.scss +76 -0
  226. package/src/refunds/admin/review-bill-refunds/bill-payments-table/bill-payments-table.component.tsx +50 -0
  227. package/src/refunds/admin/review-bill-refunds/bill-payments-table/bill-payments-table.scss +63 -0
  228. package/src/refunds/admin/review-bill-refunds/bill-receipt-rail/bill-receipt-rail.component.tsx +84 -0
  229. package/src/refunds/admin/review-bill-refunds/bill-receipt-rail/bill-receipt-rail.scss +54 -0
  230. package/src/refunds/admin/review-bill-refunds/bill-totals-summary/bill-totals-summary.component.tsx +83 -0
  231. package/src/refunds/admin/review-bill-refunds/bill-totals-summary/bill-totals-summary.scss +65 -0
  232. package/src/refunds/admin/review-bill-refunds/refund-card/refund-card.component.tsx +170 -0
  233. package/src/refunds/admin/review-bill-refunds/refund-card/refund-card.scss +155 -0
  234. package/src/refunds/admin/review-bill-refunds/refund-review-stack/refund-review-stack.component.tsx +86 -0
  235. package/src/refunds/admin/review-bill-refunds/refund-review-stack/refund-review-stack.scss +40 -0
  236. package/src/refunds/admin/review-bill-refunds/review-bill-refunds.modal.scss +14 -0
  237. package/src/refunds/admin/review-bill-refunds/review-bill-refunds.modal.test.tsx +313 -0
  238. package/src/refunds/admin/review-bill-refunds/review-bill-refunds.modal.tsx +188 -0
  239. package/src/refunds/admin/review-bill-refunds/review-bill-refunds.utils.ts +66 -0
  240. package/src/refunds/refunds-table.component.tsx +137 -0
  241. package/src/refunds/refunds-table.scss +37 -0
  242. package/src/refunds/refunds-table.test.tsx +105 -0
  243. package/src/refunds/refunds.resource.test.ts +44 -0
  244. package/src/refunds/refunds.resource.ts +42 -0
  245. package/src/refunds/refunds.types.test.ts +15 -0
  246. package/src/refunds/request-refund.modal.scss +84 -0
  247. package/src/refunds/request-refund.modal.test.tsx +204 -0
  248. package/src/refunds/request-refund.modal.tsx +218 -0
  249. package/src/routes.json +36 -2
  250. package/src/types/index.ts +116 -1
  251. package/src/visit-bills/visit-bills-panel.component.tsx +151 -0
  252. package/src/visit-bills/visit-bills-panel.scss +31 -0
  253. package/src/visit-bills/visit-bills-panel.test.tsx +113 -0
  254. package/tools/empty-module.ts +1 -0
  255. package/tools/setup-tests.ts +9 -9
  256. package/translations/am.json +154 -16
  257. package/translations/ar.json +154 -16
  258. package/translations/ar_SY.json +154 -16
  259. package/translations/bn.json +154 -16
  260. package/translations/cs.json +154 -16
  261. package/translations/de.json +154 -16
  262. package/translations/en.json +154 -16
  263. package/translations/en_US.json +154 -16
  264. package/translations/es.json +154 -16
  265. package/translations/es_MX.json +154 -16
  266. package/translations/fr.json +154 -16
  267. package/translations/he.json +154 -16
  268. package/translations/hi.json +154 -16
  269. package/translations/hi_IN.json +154 -16
  270. package/translations/id.json +154 -16
  271. package/translations/it.json +154 -16
  272. package/translations/ka.json +154 -16
  273. package/translations/km.json +154 -16
  274. package/translations/ku.json +154 -16
  275. package/translations/ky.json +154 -16
  276. package/translations/lg.json +154 -16
  277. package/translations/ne.json +154 -16
  278. package/translations/pl.json +154 -16
  279. package/translations/pt.json +154 -16
  280. package/translations/pt_BR.json +154 -16
  281. package/translations/qu.json +154 -16
  282. package/translations/ro_RO.json +154 -16
  283. package/translations/ru_RU.json +154 -16
  284. package/translations/si.json +154 -16
  285. package/translations/sq.json +154 -16
  286. package/translations/sw.json +154 -16
  287. package/translations/sw_KE.json +154 -16
  288. package/translations/tr.json +154 -16
  289. package/translations/tr_TR.json +154 -16
  290. package/translations/uk.json +154 -16
  291. package/translations/uz.json +154 -16
  292. package/translations/uz@Latn.json +154 -16
  293. package/translations/uz_UZ.json +154 -16
  294. package/translations/vi.json +154 -16
  295. package/translations/zh.json +154 -16
  296. package/translations/zh_CN.json +179 -41
  297. package/translations/zh_TW.json +154 -16
  298. package/tsconfig.json +3 -3
  299. package/vitest.config.js +28 -0
  300. package/.turbo/cache/4e30f71f570fc412-meta.json +0 -1
  301. package/.turbo/cache/4e30f71f570fc412.tar.zst +0 -0
  302. package/dist/1119.js +0 -1
  303. package/dist/1197.js +0 -1
  304. package/dist/1435.js +0 -1
  305. package/dist/1435.js.map +0 -1
  306. package/dist/1807.js +0 -1
  307. package/dist/1807.js.map +0 -1
  308. package/dist/2146.js +0 -1
  309. package/dist/2177.js +0 -2
  310. package/dist/2177.js.LICENSE.txt +0 -9
  311. package/dist/2177.js.map +0 -1
  312. package/dist/2690.js +0 -1
  313. package/dist/2704.js +0 -1
  314. package/dist/2704.js.map +0 -1
  315. package/dist/3002.js +0 -1
  316. package/dist/3002.js.map +0 -1
  317. package/dist/3041.js +0 -1
  318. package/dist/3041.js.map +0 -1
  319. package/dist/3099.js +0 -1
  320. package/dist/3184.js +0 -2
  321. package/dist/3184.js.LICENSE.txt +0 -14
  322. package/dist/3184.js.map +0 -1
  323. package/dist/3584.js +0 -1
  324. package/dist/4055.js +0 -1
  325. package/dist/4132.js +0 -1
  326. package/dist/4225.js +0 -1
  327. package/dist/4225.js.map +0 -1
  328. package/dist/4300.js +0 -1
  329. package/dist/4335.js +0 -1
  330. package/dist/439.js +0 -1
  331. package/dist/4618.js +0 -1
  332. package/dist/4652.js +0 -1
  333. package/dist/4944.js +0 -1
  334. package/dist/5173.js +0 -1
  335. package/dist/5241.js +0 -1
  336. package/dist/5422.js +0 -1
  337. package/dist/5422.js.map +0 -1
  338. package/dist/5442.js +0 -1
  339. package/dist/5661.js +0 -1
  340. package/dist/6022.js +0 -1
  341. package/dist/6404.js +0 -1
  342. package/dist/6404.js.map +0 -1
  343. package/dist/6468.js +0 -1
  344. package/dist/6540.js +0 -2
  345. package/dist/6540.js.LICENSE.txt +0 -9
  346. package/dist/6540.js.map +0 -1
  347. package/dist/6589.js +0 -1
  348. package/dist/6606.js +0 -1
  349. package/dist/6606.js.map +0 -1
  350. package/dist/6679.js +0 -1
  351. package/dist/6792.js +0 -1
  352. package/dist/6792.js.map +0 -1
  353. package/dist/6840.js +0 -1
  354. package/dist/6859.js +0 -1
  355. package/dist/7097.js +0 -1
  356. package/dist/7159.js +0 -1
  357. package/dist/723.js +0 -1
  358. package/dist/7255.js +0 -1
  359. package/dist/7255.js.map +0 -1
  360. package/dist/7617.js +0 -1
  361. package/dist/795.js +0 -1
  362. package/dist/8163.js +0 -1
  363. package/dist/8341.js +0 -2
  364. package/dist/8341.js.LICENSE.txt +0 -52
  365. package/dist/8341.js.map +0 -1
  366. package/dist/8349.js +0 -1
  367. package/dist/8371.js +0 -1
  368. package/dist/8421.js +0 -1
  369. package/dist/8421.js.map +0 -1
  370. package/dist/8618.js +0 -1
  371. package/dist/890.js +0 -1
  372. package/dist/9214.js +0 -1
  373. package/dist/9538.js +0 -1
  374. package/dist/9569.js +0 -1
  375. package/dist/961.js +0 -2
  376. package/dist/961.js.LICENSE.txt +0 -19
  377. package/dist/961.js.map +0 -1
  378. package/dist/986.js +0 -1
  379. package/dist/9879.js +0 -1
  380. package/dist/9895.js +0 -1
  381. package/dist/9900.js +0 -1
  382. package/dist/9913.js +0 -1
  383. package/dist/main.js.LICENSE.txt +0 -62
  384. package/src/billable-services/bill-waiver/bill-selection.component.tsx +0 -76
  385. package/src/billable-services/bill-waiver/bill-waiver-form.component.tsx +0 -107
  386. package/src/billable-services/bill-waiver/bill-waiver-form.scss +0 -34
  387. package/src/billable-services/bill-waiver/bill-waiver.component.tsx +0 -34
  388. package/src/billable-services/bill-waiver/bill-waiver.scss +0 -10
  389. package/src/billable-services/bill-waiver/patient-bills.component.tsx +0 -134
  390. package/webpack.config.js +0 -1
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
3
  import { screen, render } from '@testing-library/react';
3
4
  import { useConfig } from '@openmrs/esm-framework';
4
5
  import { type BillingConfig } from '../../config-schema';
@@ -6,11 +7,11 @@ import { useDefaultFacility } from '../../billing.resource';
6
7
  import { type MappedBill } from '../../types';
7
8
  import PrintableInvoiceHeader from './printable-invoice-header.component';
8
9
 
9
- const mockUseDefaultFacility = jest.mocked(useDefaultFacility);
10
- const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
10
+ const mockUseDefaultFacility = vi.mocked(useDefaultFacility);
11
+ const mockUseConfig = vi.mocked(useConfig<BillingConfig>);
11
12
 
12
- jest.mock('../../billing.resource', () => ({
13
- useDefaultFacility: jest.fn(),
13
+ vi.mock('../../billing.resource', () => ({
14
+ useDefaultFacility: vi.fn(),
14
15
  }));
15
16
 
16
17
  const testProps = {
@@ -41,7 +42,7 @@ describe('PrintableInvoiceHeader', () => {
41
42
  mockUseDefaultFacility.mockReturnValue({ data: { display: 'MTRH', uuid: 'mtrh-uuid', links: [] } });
42
43
  });
43
44
 
44
- test('should render PrintableInvoiceHeader component', () => {
45
+ it('should render PrintableInvoiceHeader component', () => {
45
46
  render(<PrintableInvoiceHeader {...testProps} defaultFacility={defaultFacility} bill={bill} />);
46
47
  const header = screen.getByText('Invoice');
47
48
  expect(header).toBeInTheDocument();
@@ -54,13 +55,13 @@ describe('PrintableInvoiceHeader', () => {
54
55
  expect(screen.getByText(/15-May-1980/i)).toBeInTheDocument();
55
56
  });
56
57
 
57
- test('should display the logo when logo is provided', () => {
58
+ it('should display the logo when logo is provided', () => {
58
59
  render(<PrintableInvoiceHeader {...testProps} defaultFacility={defaultFacility} bill={bill} />);
59
60
  const logo = screen.getByAltText('logo');
60
61
  expect(logo).toBeInTheDocument();
61
62
  });
62
63
 
63
- test('should display the default OpenMRS SVG logo when logo src is not provided', () => {
64
+ it('should display the default OpenMRS SVG logo when logo src is not provided', () => {
64
65
  mockUseConfig.mockReturnValue({
65
66
  logo: { src: '', alt: '' },
66
67
  country: 'Kenya',
@@ -73,7 +74,7 @@ describe('PrintableInvoiceHeader', () => {
73
74
  expect(logo.tagName).toBe('svg');
74
75
  });
75
76
 
76
- test('should display logo alt text when src is empty but alt is provided', () => {
77
+ it('should display logo alt text when src is empty but alt is provided', () => {
77
78
  mockUseConfig.mockReturnValue({
78
79
  logo: { src: '', alt: 'Test Facility Logo' },
79
80
  country: 'Kenya',
@@ -84,7 +85,7 @@ describe('PrintableInvoiceHeader', () => {
84
85
  expect(screen.getByText('Test Facility Logo')).toBeInTheDocument();
85
86
  });
86
87
 
87
- test('should format birthDate correctly', () => {
88
+ it('should format birthDate correctly', () => {
88
89
  const propsWithDifferentDate = {
89
90
  patientDetails: {
90
91
  ...testProps.patientDetails,
@@ -96,7 +97,7 @@ describe('PrintableInvoiceHeader', () => {
96
97
  expect(screen.getByText(/25-Dec-1995/i)).toBeInTheDocument();
97
98
  });
98
99
 
99
- test('should not render birthDate and gender when not provided', () => {
100
+ it('should not render birthDate and gender when not provided', () => {
100
101
  const propsWithoutBirthDateAndGender = {
101
102
  patientDetails: {
102
103
  ...testProps.patientDetails,
@@ -112,9 +113,23 @@ describe('PrintableInvoiceHeader', () => {
112
113
  expect(screen.queryByText(/Gender:/i)).not.toBeInTheDocument();
113
114
  });
114
115
 
115
- test('should handle null defaultFacility gracefully', () => {
116
+ it('should handle null defaultFacility gracefully', () => {
116
117
  render(<PrintableInvoiceHeader {...testProps} defaultFacility={null} bill={bill} />);
117
118
  expect(screen.getByRole('heading', { name: /Invoice/i, level: 1 })).toBeInTheDocument();
118
119
  expect(screen.getByText(/john Doe/i)).toBeInTheDocument();
119
120
  });
121
+
122
+ it('should use netAmount to calculate amount balance when a discount is applied', () => {
123
+ const discountedBill = {
124
+ ...bill,
125
+ totalAmount: 100,
126
+ netAmount: 80,
127
+ tenderedAmount: 20,
128
+ } as MappedBill;
129
+
130
+ render(<PrintableInvoiceHeader {...testProps} defaultFacility={defaultFacility} bill={discountedBill} />);
131
+
132
+ expect(screen.getByText(': KES 60')).toBeInTheDocument();
133
+ expect(screen.queryByText(': KES 80')).not.toBeInTheDocument();
134
+ });
120
135
  });
@@ -83,12 +83,6 @@ const PrintableInvoice: React.FC<PrintableInvoiceProps> = ({ bill, patient, comp
83
83
  return [];
84
84
  }, [bill, defaultCurrency]);
85
85
 
86
- const summaryHeaders = [
87
- { key: 'total', header: t('totalAmount', 'Total Amount') },
88
- { key: 'paid', header: t('totalPaid', 'Total Paid') },
89
- { key: 'balance', header: t('amountBalance', 'Amount Balance') },
90
- ];
91
-
92
86
  const patientDetails = useMemo(() => {
93
87
  const address = patient?.address?.[0];
94
88
  const addressParts = [address?.line?.join(' '), address?.city, address?.district, address?.state].filter(Boolean);
@@ -136,14 +130,43 @@ const PrintableInvoice: React.FC<PrintableInvoiceProps> = ({ bill, patient, comp
136
130
  </TableContainer>
137
131
  )}
138
132
  </DataTable>
139
- <div className={styles.balanceContainer}>
140
- <span className={styles.itemHeading}>{t('totalAmount', 'Total Amount')}:</span>{' '}
141
- <span className={styles.itemLabel}>
142
- <strong>
143
- {defaultCurrency} {bill?.totalAmount}
144
- </strong>
145
- </span>
146
- </div>
133
+ {bill?.netAmount != null && bill.totalAmount !== bill.netAmount ? (
134
+ <>
135
+ <div className={styles.balanceContainer}>
136
+ <span className={styles.itemHeading}>{t('subtotal', 'Subtotal')}:</span>{' '}
137
+ <span className={styles.itemLabel}>
138
+ <strong>
139
+ {defaultCurrency} {bill?.totalAmount}
140
+ </strong>
141
+ </span>
142
+ </div>
143
+ <div className={styles.balanceContainer}>
144
+ <span className={styles.itemHeading}>{t('discount', 'Discount')}:</span>{' '}
145
+ <span className={styles.itemLabel}>
146
+ <strong>
147
+ - {defaultCurrency} {(bill?.totalAmount ?? 0) - (bill?.netAmount ?? 0)}
148
+ </strong>
149
+ </span>
150
+ </div>
151
+ <div className={styles.balanceContainer}>
152
+ <span className={styles.itemHeading}>{t('totalAmount', 'Total amount')}:</span>{' '}
153
+ <span className={styles.itemLabel}>
154
+ <strong>
155
+ {defaultCurrency} {bill?.netAmount}
156
+ </strong>
157
+ </span>
158
+ </div>
159
+ </>
160
+ ) : (
161
+ <div className={styles.balanceContainer}>
162
+ <span className={styles.itemHeading}>{t('totalAmount', 'Total amount')}:</span>{' '}
163
+ <span className={styles.itemLabel}>
164
+ <strong>
165
+ {defaultCurrency} {bill?.totalAmount}
166
+ </strong>
167
+ </span>
168
+ </div>
169
+ )}
147
170
  {bill?.payments?.length > 0 && (
148
171
  <div className={styles.paymentHistoryContainer}>
149
172
  <DataTable rows={paymentHistoryRows} headers={paymentHistoryHeaders} size={responsiveSize}>
@@ -184,7 +207,7 @@ const PrintableInvoice: React.FC<PrintableInvoiceProps> = ({ bill, patient, comp
184
207
  <span className={styles.itemHeading}>{t('balance', 'Balance')}:</span>
185
208
  <span className={styles.itemLabel}>
186
209
  <strong>
187
- {defaultCurrency} {bill?.totalAmount - bill?.tenderedAmount}
210
+ {defaultCurrency} {bill?.netAmount - bill?.tenderedAmount}
188
211
  </strong>
189
212
  </span>
190
213
  </div>
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
+ import { describe, expect, it } from 'vitest';
2
3
  import { render, screen } from '@testing-library/react';
3
4
  import { MemoryRouter } from 'react-router-dom';
4
5
  import { LinkExtension, createLeftPanelLink } from './left-panel-link.component';
5
- import userEvent from '@testing-library/user-event';
6
6
 
7
7
  window.getOpenmrsSpaBase = () => '/openmrs/spa/';
8
8
 
@@ -12,7 +12,7 @@ describe('LinkExtension Component', () => {
12
12
  return render(component, { wrapper: MemoryRouter });
13
13
  };
14
14
 
15
- test('renders correctly', () => {
15
+ it('renders correctly', () => {
16
16
  const config = { name: 'billing', title: 'Billing' };
17
17
  renderWithRouter(<LinkExtension config={config} />, {
18
18
  route: '/billing/6eb8d678-514d-46ad-9554-51e48d96d567',
@@ -23,7 +23,7 @@ describe('LinkExtension Component', () => {
23
23
  });
24
24
 
25
25
  describe('createLeftPanelLink Function', () => {
26
- test('returns a component that renders LinkExtension', () => {
26
+ it('returns a component that renders LinkExtension', () => {
27
27
  const config = { name: 'billing', title: 'Billing' };
28
28
  const TestComponent = createLeftPanelLink(config);
29
29
 
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
3
  import { render, screen } from '@testing-library/react';
3
4
  import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
4
5
  import { billsSummary } from '../../__mocks__/bills.mock';
@@ -7,11 +8,11 @@ import { type MappedBill } from '../types';
7
8
  import { configSchema, type BillingConfig } from '../config-schema';
8
9
  import MetricsCards from './metrics-cards.component';
9
10
 
10
- const mockUseBills = jest.mocked<typeof useBills>(useBills);
11
- const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
11
+ const mockUseBills = vi.mocked<typeof useBills>(useBills);
12
+ const mockUseConfig = vi.mocked(useConfig<BillingConfig>);
12
13
 
13
- jest.mock('../billing.resource', () => ({
14
- useBills: jest.fn(),
14
+ vi.mock('../billing.resource', () => ({
15
+ useBills: vi.fn(),
15
16
  }));
16
17
 
17
18
  describe('MetricsCards', () => {
@@ -19,31 +20,31 @@ describe('MetricsCards', () => {
19
20
  mockUseConfig.mockReturnValue({ ...getDefaultsFromConfigSchema(configSchema), defaultCurrency: 'USD' });
20
21
  });
21
22
 
22
- test('renders loading state', () => {
23
- mockUseBills.mockReturnValue({ isLoading: true, bills: [], error: null, isValidating: false, mutate: jest.fn() });
23
+ it('renders loading state', () => {
24
+ mockUseBills.mockReturnValue({ isLoading: true, bills: [], error: null, isValidating: false, mutate: vi.fn() });
24
25
  renderMetricsCards();
25
26
  expect(screen.getByText(/Loading bill metrics.../i)).toBeInTheDocument();
26
27
  });
27
28
 
28
- test('renders error state', () => {
29
+ it('renders error state', () => {
29
30
  mockUseBills.mockReturnValue({
30
31
  isLoading: false,
31
32
  bills: [],
32
33
  error: new Error('Internal server error'),
33
34
  isValidating: false,
34
- mutate: jest.fn(),
35
+ mutate: vi.fn(),
35
36
  });
36
37
  renderMetricsCards();
37
38
  expect(screen.getByText(/error state/i)).toBeInTheDocument();
38
39
  });
39
40
 
40
- test('renders metrics cards', () => {
41
+ it('renders metrics cards', () => {
41
42
  mockUseBills.mockReturnValue({
42
43
  isLoading: false,
43
44
  bills: billsSummary as unknown as MappedBill[],
44
45
  error: null,
45
46
  isValidating: false,
46
- mutate: jest.fn(),
47
+ mutate: vi.fn(),
47
48
  });
48
49
  renderMetricsCards();
49
50
  expect(screen.getByRole('heading', { name: /cumulative bills/i })).toBeInTheDocument();
@@ -0,0 +1,134 @@
1
+ import React from 'react';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { render, screen, waitFor } from '@testing-library/react';
5
+ import { showSnackbar } from '@openmrs/esm-framework';
6
+ import { deleteBill } from '../billing.resource';
7
+ import { type MappedBill } from '../types';
8
+ import DeleteBillModal from './delete-bill-confirmation.modal';
9
+
10
+ const mockDeleteBill = vi.mocked(deleteBill);
11
+ const mockShowSnackbar = vi.mocked(showSnackbar);
12
+
13
+ vi.mock('../billing.resource', () => ({
14
+ deleteBill: vi.fn(),
15
+ }));
16
+
17
+ const mockBill = {
18
+ uuid: 'bill-uuid',
19
+ receiptNumber: 'RCPT-001',
20
+ } as MappedBill;
21
+
22
+ describe('DeleteBillModal', () => {
23
+ const mockCloseModal = vi.fn();
24
+ const mockOnSuccess = vi.fn();
25
+
26
+ beforeEach(() => {
27
+ vi.clearAllMocks();
28
+ });
29
+
30
+ it('calls deleteBill with trimmed reason, fires onSuccess, shows success snackbar, and closes modal', async () => {
31
+ const user = userEvent.setup();
32
+ mockDeleteBill.mockResolvedValueOnce({} as any);
33
+
34
+ render(<DeleteBillModal closeModal={mockCloseModal} bill={mockBill} onSuccess={mockOnSuccess} />);
35
+
36
+ const deleteReasonInput = screen.getByLabelText(/Reason for deletion/i);
37
+ await user.type(deleteReasonInput, 'Test delete reason');
38
+
39
+ await user.click(screen.getByRole('button', { name: /delete/i }));
40
+
41
+ await waitFor(() => {
42
+ expect(mockDeleteBill).toHaveBeenCalledWith(mockBill.uuid, 'Test delete reason');
43
+ expect(mockOnSuccess).toHaveBeenCalled();
44
+ expect(mockShowSnackbar).toHaveBeenCalledWith({
45
+ kind: 'success',
46
+ subtitle: 'Bill deleted successfully',
47
+ title: 'Bill deleted',
48
+ });
49
+ expect(mockCloseModal).toHaveBeenCalled();
50
+ });
51
+ });
52
+
53
+ it('shows error message from responseBody when delete fails', async () => {
54
+ const user = userEvent.setup();
55
+
56
+ mockDeleteBill.mockRejectedValueOnce({
57
+ responseBody: {
58
+ error: {
59
+ message: 'Cannot delete paid bill',
60
+ },
61
+ },
62
+ });
63
+
64
+ render(<DeleteBillModal closeModal={mockCloseModal} bill={mockBill} onSuccess={mockOnSuccess} />);
65
+
66
+ const deleteReasonInput = screen.getByLabelText(/Reason for deletion/i);
67
+ await user.type(deleteReasonInput, 'Test delete reason');
68
+
69
+ await user.click(screen.getByRole('button', { name: /delete/i }));
70
+
71
+ await waitFor(() => {
72
+ expect(mockShowSnackbar).toHaveBeenCalledWith({
73
+ kind: 'error',
74
+ subtitle: 'Cannot delete paid bill',
75
+ title: 'Failed to delete bill',
76
+ });
77
+ });
78
+ });
79
+
80
+ it('shows fallback error message when error has no responseBody message', async () => {
81
+ const user = userEvent.setup();
82
+
83
+ mockDeleteBill.mockRejectedValueOnce({});
84
+
85
+ render(<DeleteBillModal closeModal={mockCloseModal} bill={mockBill} onSuccess={mockOnSuccess} />);
86
+
87
+ const deleteReasonInput = screen.getByLabelText(/Reason for deletion/i);
88
+ await user.type(deleteReasonInput, 'Test delete reason');
89
+
90
+ await user.click(screen.getByRole('button', { name: /delete/i }));
91
+
92
+ await waitFor(() => {
93
+ expect(mockShowSnackbar).toHaveBeenCalledWith({
94
+ kind: 'error',
95
+ subtitle: 'Unable to delete bill. Please try again.',
96
+ title: 'Failed to delete bill',
97
+ });
98
+ });
99
+ });
100
+
101
+ it('disables delete button when reason is empty', () => {
102
+ render(<DeleteBillModal closeModal={mockCloseModal} bill={mockBill} onSuccess={mockOnSuccess} />);
103
+
104
+ const deleteButton = screen.getByRole('button', { name: /delete/i });
105
+ expect(deleteButton).toBeDisabled();
106
+ });
107
+
108
+ it('disables delete button when reason contains only whitespace', async () => {
109
+ const user = userEvent.setup();
110
+ render(<DeleteBillModal closeModal={mockCloseModal} bill={mockBill} onSuccess={mockOnSuccess} />);
111
+
112
+ const deleteReasonInput = screen.getByLabelText(/Reason for deletion/i);
113
+ await user.type(deleteReasonInput, ' ');
114
+
115
+ const deleteButton = screen.getByRole('button', { name: /delete/i });
116
+ expect(deleteButton).toBeDisabled();
117
+ });
118
+
119
+ it('trims reason before sending to API', async () => {
120
+ const user = userEvent.setup();
121
+ mockDeleteBill.mockResolvedValueOnce({} as any);
122
+
123
+ render(<DeleteBillModal closeModal={mockCloseModal} bill={mockBill} onSuccess={mockOnSuccess} />);
124
+
125
+ const deleteReasonInput = screen.getByLabelText(/Reason for deletion/i);
126
+ await user.type(deleteReasonInput, ' Test delete reason with spaces ');
127
+
128
+ await user.click(screen.getByRole('button', { name: /delete/i }));
129
+
130
+ await waitFor(() => {
131
+ expect(mockDeleteBill).toHaveBeenCalledWith(mockBill.uuid, 'Test delete reason with spaces');
132
+ });
133
+ });
134
+ });
@@ -0,0 +1,98 @@
1
+ import React, { useState } from 'react';
2
+ import { Button, InlineLoading, ModalBody, ModalFooter, ModalHeader, Stack, TextArea } from '@carbon/react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { getCoreTranslation, showSnackbar } from '@openmrs/esm-framework';
5
+ import { deleteBill } from '../billing.resource';
6
+ import { type MappedBill } from '../types';
7
+ import styles from './delete-line-item-confirmation.scss';
8
+
9
+ interface DeleteBillModalParams {
10
+ closeModal: () => void;
11
+ bill: MappedBill;
12
+ onSuccess?: () => void;
13
+ }
14
+
15
+ const DeleteBillModal: React.FC<DeleteBillModalParams> = ({ closeModal, bill, onSuccess }) => {
16
+ const { t } = useTranslation();
17
+ const [isDeleting, setIsDeleting] = useState(false);
18
+ const [deleteReason, setDeleteReason] = useState('');
19
+
20
+ const handleDelete = async () => {
21
+ if (!bill?.uuid) {
22
+ return;
23
+ }
24
+
25
+ setIsDeleting(true);
26
+
27
+ try {
28
+ await deleteBill(bill.uuid, deleteReason.trim());
29
+
30
+ showSnackbar({
31
+ title: t('billDeleted', 'Bill deleted'),
32
+ subtitle: t('billDeleteSuccess', 'Bill deleted successfully'),
33
+ kind: 'success',
34
+ });
35
+
36
+ onSuccess?.();
37
+ closeModal();
38
+ } catch (err: any) {
39
+ const message =
40
+ err?.responseBody?.error?.message ||
41
+ err?.message ||
42
+ t('deleteBillFailedTryAgain', 'Unable to delete bill. Please try again.');
43
+
44
+ showSnackbar({
45
+ title: t('billDeleteFailed', 'Failed to delete bill'),
46
+ subtitle: message,
47
+ kind: 'error',
48
+ });
49
+ } finally {
50
+ setIsDeleting(false);
51
+ }
52
+ };
53
+
54
+ return (
55
+ <>
56
+ <ModalHeader closeModal={closeModal} title={t('deleteBill', 'Delete bill')} />
57
+
58
+ <ModalBody className={styles.modalBody}>
59
+ <Stack gap={5}>
60
+ <p>
61
+ {t(
62
+ 'deleteBillConfirmation',
63
+ 'Are you sure you want to delete bill {{receiptNumber}}? This action cannot be undone.',
64
+ { receiptNumber: bill?.receiptNumber },
65
+ )}
66
+ </p>
67
+ <TextArea
68
+ enableCounter
69
+ id="deleteBillReason"
70
+ labelText={t('deleteReason', 'Reason for deletion')}
71
+ maxCount={255}
72
+ onChange={(e) => setDeleteReason(e.target.value)}
73
+ placeholder={t('deleteBillReasonPlaceholder', 'Enter the reason for deleting this bill')}
74
+ required
75
+ rows={3}
76
+ value={deleteReason}
77
+ />
78
+ </Stack>
79
+ </ModalBody>
80
+
81
+ <ModalFooter>
82
+ <Button kind="secondary" onClick={closeModal} disabled={isDeleting}>
83
+ {getCoreTranslation('cancel')}
84
+ </Button>
85
+
86
+ <Button kind="danger" onClick={handleDelete} disabled={isDeleting || !deleteReason.trim()}>
87
+ {isDeleting ? (
88
+ <InlineLoading className={styles.spinner} description={t('deleting', 'Deleting') + '...'} />
89
+ ) : (
90
+ <span>{getCoreTranslation('delete')}</span>
91
+ )}
92
+ </Button>
93
+ </ModalFooter>
94
+ </>
95
+ );
96
+ };
97
+
98
+ export default DeleteBillModal;
@@ -1,23 +1,25 @@
1
1
  import React from 'react';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
3
  import userEvent from '@testing-library/user-event';
3
4
  import { render, screen, waitFor } from '@testing-library/react';
4
5
  import { showSnackbar } from '@openmrs/esm-framework';
5
6
  import { deleteBillItem } from '../billing.resource';
7
+ import { type LineItem } from '../types';
6
8
  import DeleteLineItem from './delete-line-item-confirmation.modal';
7
9
 
8
- const mockDeleteBillItem = jest.mocked(deleteBillItem);
9
- const mockShowSnackbar = jest.mocked(showSnackbar);
10
+ const mockDeleteBillItem = vi.mocked(deleteBillItem);
11
+ const mockShowSnackbar = vi.mocked(showSnackbar);
10
12
 
11
- jest.mock('../billing.resource', () => ({
12
- deleteBillItem: jest.fn(),
13
+ vi.mock('../billing.resource', () => ({
14
+ deleteBillItem: vi.fn(),
13
15
  }));
14
16
 
15
- const mockItem = {
17
+ const mockItem: LineItem = {
16
18
  uuid: 'item-uuid',
17
19
  quantity: 2,
18
20
  price: 100,
19
21
  billableService: 'X-Ray Service',
20
- paymentStatus: 'UNPAID',
22
+ status: 'PENDING',
21
23
  item: 'Test Service',
22
24
  display: 'Test Service',
23
25
  voided: false,
@@ -29,11 +31,11 @@ const mockItem = {
29
31
  };
30
32
 
31
33
  describe('DeleteLineItem Modal', () => {
32
- const mockCloseModal = jest.fn();
33
- const mockMutate = jest.fn();
34
+ const mockCloseModal = vi.fn();
35
+ const mockMutate = vi.fn();
34
36
 
35
37
  beforeEach(() => {
36
- jest.clearAllMocks();
38
+ vi.clearAllMocks();
37
39
  });
38
40
 
39
41
  it('renders delete confirmation modal', () => {
@@ -1,17 +1,18 @@
1
1
  import React from 'react';
2
+ import { describe, expect, it, vi } from 'vitest';
2
3
  import userEvent from '@testing-library/user-event';
3
4
  import { render, screen, waitFor } from '@testing-library/react';
4
5
  import { showSnackbar } from '@openmrs/esm-framework';
5
6
  import { finalizeBill } from '../billing.resource';
6
7
  import FinalizeBillModal from './finalize-bill-confirmation.modal';
7
8
  import type { MappedBill } from '../types';
8
- import { BillStatus } from '../types';
9
+ import { BillStatus, BillLineItemStatus } from '../types';
9
10
 
10
- const mockFinalizeBill = jest.mocked(finalizeBill);
11
- const mockShowSnackbar = jest.mocked(showSnackbar);
11
+ const mockFinalizeBill = vi.mocked(finalizeBill);
12
+ const mockShowSnackbar = vi.mocked(showSnackbar);
12
13
 
13
- jest.mock('../billing.resource', () => ({
14
- finalizeBill: jest.fn(),
14
+ vi.mock('../billing.resource', () => ({
15
+ finalizeBill: vi.fn(),
15
16
  }));
16
17
 
17
18
  const mockBill: MappedBill = {
@@ -33,19 +34,20 @@ const mockBill: MappedBill = {
33
34
  item: 'X-Ray',
34
35
  quantity: 1,
35
36
  price: 500,
36
- paymentStatus: BillStatus.PENDING,
37
+ status: BillLineItemStatus.PENDING,
37
38
  billableService: 'X-Ray Service',
38
39
  },
39
40
  ],
40
41
  billingService: 'X-Ray Service',
41
42
  payments: [],
42
43
  totalAmount: 500,
44
+ netAmount: 500,
43
45
  tenderedAmount: 0,
44
46
  };
45
47
 
46
48
  describe('FinalizeBillModal', () => {
47
- const mockCloseModal = jest.fn();
48
- const mockMutate = jest.fn();
49
+ const mockCloseModal = vi.fn();
50
+ const mockMutate = vi.fn();
49
51
 
50
52
  it('renders the confirmation modal with correct content', () => {
51
53
  render(<FinalizeBillModal closeModal={mockCloseModal} bill={mockBill} onMutate={mockMutate} />);
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
3
  import userEvent from '@testing-library/user-event';
3
4
  import { render, screen } from '@testing-library/react';
4
5
  import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
@@ -7,19 +8,19 @@ import { type MappedBill } from '../types';
7
8
  import { configSchema, type BillingConfig } from '../config-schema';
8
9
  import RequirePaymentModal from './require-payment.modal';
9
10
 
10
- const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
11
- const mockUseBills = jest.mocked<typeof useBills>(useBills);
11
+ const mockUseConfig = vi.mocked(useConfig<BillingConfig>);
12
+ const mockUseBills = vi.mocked<typeof useBills>(useBills);
12
13
 
13
- jest.mock('../billing.resource', () => ({
14
- useBills: jest.fn(),
14
+ vi.mock('../billing.resource', () => ({
15
+ useBills: vi.fn(),
15
16
  }));
16
17
 
17
- jest.mock('../helpers', () => ({
18
- convertToCurrency: (value, currency) => `${currency} ${value.toFixed(2)}`,
18
+ vi.mock('../helpers', () => ({
19
+ convertToCurrency: (value: number, currency: string) => `${currency} ${value.toFixed(2)}`,
19
20
  }));
20
21
 
21
22
  describe('RequirePaymentModal', () => {
22
- const closeModal = jest.fn();
23
+ const closeModal = vi.fn();
23
24
  const patientUuid = '12345';
24
25
 
25
26
  beforeEach(() => {
@@ -27,13 +28,13 @@ describe('RequirePaymentModal', () => {
27
28
  });
28
29
 
29
30
  it('renders correctly', () => {
30
- mockUseBills.mockReturnValue({ bills: [], isLoading: false, error: null, isValidating: false, mutate: jest.fn() });
31
+ mockUseBills.mockReturnValue({ bills: [], isLoading: false, error: null, isValidating: false, mutate: vi.fn() });
31
32
  render(<RequirePaymentModal closeModal={closeModal} patientUuid={patientUuid} />);
32
33
  expect(screen.getByText('Patient Billing Alert')).toBeInTheDocument();
33
34
  });
34
35
 
35
36
  it('displays loading state', () => {
36
- mockUseBills.mockReturnValue({ bills: [], isLoading: true, error: null, isValidating: false, mutate: jest.fn() });
37
+ mockUseBills.mockReturnValue({ bills: [], isLoading: true, error: null, isValidating: false, mutate: vi.fn() });
37
38
  render(<RequirePaymentModal closeModal={closeModal} patientUuid={patientUuid} />);
38
39
  expect(screen.getByText('Loading bill items...')).toBeInTheDocument();
39
40
  });
@@ -53,7 +54,7 @@ describe('RequirePaymentModal', () => {
53
54
  isLoading: false,
54
55
  error: null,
55
56
  isValidating: false,
56
- mutate: jest.fn(),
57
+ mutate: vi.fn(),
57
58
  });
58
59
  render(<RequirePaymentModal closeModal={closeModal} patientUuid={patientUuid} />);
59
60
  expect(screen.getByText('Service 1')).toBeInTheDocument();
@@ -62,7 +63,7 @@ describe('RequirePaymentModal', () => {
62
63
 
63
64
  it('handles closeModal', async () => {
64
65
  const user = userEvent.setup();
65
- mockUseBills.mockReturnValue({ bills: [], isLoading: false, error: null, isValidating: false, mutate: jest.fn() });
66
+ mockUseBills.mockReturnValue({ bills: [], isLoading: false, error: null, isValidating: false, mutate: vi.fn() });
66
67
  render(<RequirePaymentModal closeModal={closeModal} patientUuid={patientUuid} />);
67
68
  await user.click(screen.getByText('Cancel'));
68
69
  await user.click(screen.getByText('OK'));