@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,20 +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 { getDefaultsFromConfigSchema, showModal, useConfig } from '@openmrs/esm-framework';
5
- import { type MappedBill } from '../types';
6
+ import { BillStatus, RefundStatus, type BillRefund, type MappedBill } from '../types';
6
7
  import { configSchema, type BillingConfig } from '../config-schema';
7
8
  import InvoiceTable from './invoice-table.component';
8
9
 
9
- const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
10
- const mockShowModal = jest.mocked(showModal);
10
+ const mockUseConfig = vi.mocked(useConfig<BillingConfig>);
11
+ const mockShowModal = vi.mocked(showModal);
11
12
 
12
- jest.mock('../helpers', () => ({
13
- convertToCurrency: jest.fn((price) => `USD ${price}`),
13
+ vi.mock('../helpers', () => ({
14
+ convertToCurrency: vi.fn((price) => `USD ${price}`),
15
+ }));
16
+
17
+ vi.mock('../discounts/discounts.resource', () => ({
18
+ useBillDiscounts: () => ({ discounts: [], isLoading: false, error: null, mutate: vi.fn() }),
14
19
  }));
15
20
 
16
21
  describe('InvoiceTable', () => {
17
- const mockOnMutate = jest.fn();
22
+ const mockOnMutate = vi.fn();
18
23
 
19
24
  const defaultBill: MappedBill = {
20
25
  uuid: 'bill-uuid',
@@ -25,7 +30,7 @@ describe('InvoiceTable', () => {
25
30
  {
26
31
  uuid: '1',
27
32
  item: 'Item 1',
28
- paymentStatus: 'PAID',
33
+ status: 'PAID',
29
34
  quantity: 1,
30
35
  price: 100,
31
36
  display: '',
@@ -40,7 +45,7 @@ describe('InvoiceTable', () => {
40
45
  {
41
46
  uuid: '2',
42
47
  item: 'Item 2',
43
- paymentStatus: 'PENDING',
48
+ status: 'PENDING',
44
49
  quantity: 2,
45
50
  price: 200,
46
51
  display: '',
@@ -68,6 +73,7 @@ describe('InvoiceTable', () => {
68
73
  billingService: 'billing-service-uuid',
69
74
  payments: [],
70
75
  totalAmount: 300,
76
+ netAmount: 300,
71
77
  tenderedAmount: 300,
72
78
  };
73
79
 
@@ -127,7 +133,7 @@ describe('InvoiceTable', () => {
127
133
  {
128
134
  uuid: '1',
129
135
  item: 'Service A',
130
- paymentStatus: 'PENDING',
136
+ status: 'PENDING',
131
137
  quantity: 3,
132
138
  price: 100,
133
139
  display: '',
@@ -293,7 +299,7 @@ describe('InvoiceTable', () => {
293
299
  {
294
300
  uuid: '1',
295
301
  item: 'Free Service',
296
- paymentStatus: 'PAID',
302
+ status: 'PAID',
297
303
  quantity: 1,
298
304
  price: 0,
299
305
  display: '',
@@ -321,7 +327,7 @@ describe('InvoiceTable', () => {
321
327
  {
322
328
  uuid: '1',
323
329
  item: 'Service',
324
- paymentStatus: 'PENDING',
330
+ status: 'PENDING',
325
331
  quantity: 0,
326
332
  price: 100,
327
333
  display: '',
@@ -350,7 +356,7 @@ describe('InvoiceTable', () => {
350
356
  uuid: '1',
351
357
  item: 'Item Name',
352
358
  billableService: 'Billable Service Name',
353
- paymentStatus: 'PAID',
359
+ status: 'PAID',
354
360
  quantity: 1,
355
361
  price: 100,
356
362
  display: '',
@@ -365,7 +371,7 @@ describe('InvoiceTable', () => {
365
371
  uuid: '2',
366
372
  item: 'Item Without Billable',
367
373
  billableService: '',
368
- paymentStatus: 'PENDING',
374
+ status: 'PENDING',
369
375
  quantity: 1,
370
376
  price: 200,
371
377
  display: '',
@@ -409,4 +415,150 @@ describe('InvoiceTable', () => {
409
415
  expect(searchInput).toBeInTheDocument();
410
416
  expect(searchInput).toBeVisible();
411
417
  });
418
+
419
+ describe('viewOnly mode', () => {
420
+ it('hides the search box', () => {
421
+ render(<InvoiceTable bill={defaultBill} viewOnly />);
422
+ expect(screen.queryByPlaceholderText(/search this table/i)).not.toBeInTheDocument();
423
+ });
424
+
425
+ it('hides the actions column header', () => {
426
+ render(<InvoiceTable bill={defaultBill} viewOnly />);
427
+ // 6 data column headers; no 7th actions header
428
+ expect(screen.getAllByRole('columnheader')).toHaveLength(6);
429
+ });
430
+
431
+ it('hides per-row action menus', () => {
432
+ render(<InvoiceTable bill={defaultBill} viewOnly />);
433
+ expect(screen.queryAllByRole('button', { name: /options/i })).toHaveLength(0);
434
+ });
435
+
436
+ it('hides the filter helper text in the empty state', () => {
437
+ const emptyBill: MappedBill = { ...defaultBill, lineItems: [] };
438
+ render(<InvoiceTable bill={emptyBill} viewOnly />);
439
+ expect(screen.getByText(/no matching items to display/i)).toBeInTheDocument();
440
+ expect(screen.queryByText(/check the filters above/i)).not.toBeInTheDocument();
441
+ });
442
+ });
443
+
444
+ describe('line-item refund eligibility', () => {
445
+ const makeRefund = (overrides: Partial<BillRefund> = {}): BillRefund => ({
446
+ uuid: 'r-1',
447
+ billUuid: 'bill-uuid',
448
+ lineItemUuid: null,
449
+ refundAmount: 100,
450
+ reason: 'overcharged',
451
+ initiator: { uuid: 'u1', display: 'cashier' },
452
+ approver: null,
453
+ completer: null,
454
+ dateApproved: null,
455
+ dateCompleted: null,
456
+ dateCreated: '2024-01-01',
457
+ status: RefundStatus.REQUESTED,
458
+ voided: false,
459
+ ...overrides,
460
+ });
461
+
462
+ const paidBill: MappedBill = { ...defaultBill, status: BillStatus.PAID, totalAmount: 300, tenderedAmount: 300 };
463
+
464
+ it('shows the refund action for an eligible line item on a PAID bill', async () => {
465
+ const user = userEvent.setup();
466
+ render(<InvoiceTable bill={paidBill} onMutate={mockOnMutate} />);
467
+ await user.click(screen.getByTestId('action-menu-1'));
468
+ expect(screen.getByTestId('request-refund-button-1')).toBeInTheDocument();
469
+ });
470
+
471
+ it('hides the refund action for a line item that already has an active refund', async () => {
472
+ const user = userEvent.setup();
473
+ render(
474
+ <InvoiceTable bill={{ ...paidBill, refunds: [makeRefund({ lineItemUuid: '1' })] }} onMutate={mockOnMutate} />,
475
+ );
476
+ await user.click(screen.getByTestId('action-menu-1'));
477
+ expect(screen.queryByTestId('request-refund-button-1')).not.toBeInTheDocument();
478
+ });
479
+
480
+ it('hides the refund action for all line items when there is an active bill-level refund', async () => {
481
+ const user = userEvent.setup();
482
+ render(
483
+ <InvoiceTable bill={{ ...paidBill, refunds: [makeRefund({ lineItemUuid: null })] }} onMutate={mockOnMutate} />,
484
+ );
485
+ await user.click(screen.getByTestId('action-menu-1'));
486
+ expect(screen.queryByTestId('request-refund-button-1')).not.toBeInTheDocument();
487
+ });
488
+
489
+ it('shows the refund action for a line item when bill is REFUND_REQUESTED due to a different line item', async () => {
490
+ const user = userEvent.setup();
491
+ const bill: MappedBill = {
492
+ ...paidBill,
493
+ status: BillStatus.REFUND_REQUESTED,
494
+ refunds: [makeRefund({ uuid: 'r-1', lineItemUuid: '1' })],
495
+ };
496
+ render(<InvoiceTable bill={bill} onMutate={mockOnMutate} />);
497
+ await user.click(screen.getByTestId('action-menu-2'));
498
+ expect(screen.getByTestId('request-refund-button-2')).toBeInTheDocument();
499
+ });
500
+
501
+ it('hides the refund action for the line item that triggered REFUND_REQUESTED status', async () => {
502
+ const user = userEvent.setup();
503
+ const bill: MappedBill = {
504
+ ...paidBill,
505
+ status: BillStatus.REFUND_REQUESTED,
506
+ refunds: [makeRefund({ uuid: 'r-1', lineItemUuid: '1' })],
507
+ };
508
+ render(<InvoiceTable bill={bill} onMutate={mockOnMutate} />);
509
+ await user.click(screen.getByTestId('action-menu-1'));
510
+ expect(screen.queryByTestId('request-refund-button-1')).not.toBeInTheDocument();
511
+ });
512
+
513
+ it('hides all line-item refund actions when there is an active bill-level refund on a REFUND_REQUESTED bill', async () => {
514
+ const user = userEvent.setup();
515
+ const bill: MappedBill = {
516
+ ...paidBill,
517
+ status: BillStatus.REFUND_REQUESTED,
518
+ refunds: [makeRefund({ lineItemUuid: null })],
519
+ };
520
+ render(<InvoiceTable bill={bill} onMutate={mockOnMutate} />);
521
+ await user.click(screen.getByTestId('action-menu-1'));
522
+ expect(screen.queryByTestId('request-refund-button-1')).not.toBeInTheDocument();
523
+ });
524
+ });
525
+
526
+ describe('per-line-item discount action', () => {
527
+ it('opens the request-discount modal with lineItem scope', async () => {
528
+ const user = userEvent.setup();
529
+ const billPosted: MappedBill = {
530
+ ...defaultBill,
531
+ status: 'POSTED',
532
+ lineItems: [
533
+ {
534
+ uuid: 'li1',
535
+ item: 'Consultation',
536
+ status: 'PENDING',
537
+ quantity: 1,
538
+ price: 1000,
539
+ display: '',
540
+ voided: false,
541
+ voidReason: '',
542
+ billableService: '',
543
+ priceName: '',
544
+ priceUuid: '',
545
+ lineItemOrder: 0,
546
+ resourceVersion: '',
547
+ },
548
+ ],
549
+ totalAmount: 1000,
550
+ };
551
+ render(<InvoiceTable bill={billPosted} onMutate={mockOnMutate} />);
552
+
553
+ await user.click(screen.getByTestId('action-menu-li1'));
554
+ await user.click(screen.getByText(/request discount/i));
555
+
556
+ expect(mockShowModal).toHaveBeenCalledWith(
557
+ 'request-discount-modal',
558
+ expect.objectContaining({
559
+ lineItem: expect.objectContaining({ uuid: 'li1', display: 'Consultation' }),
560
+ }),
561
+ );
562
+ });
563
+ });
412
564
  });
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from 'react';
2
- import { Button, InlineLoading } from '@carbon/react';
2
+ import { Button, InlineLoading, Tooltip } from '@carbon/react';
3
3
  import { Add, Printer } from '@carbon/react/icons';
4
4
  import { useParams } from 'react-router-dom';
5
5
  import { useReactToPrint } from 'react-to-print';
@@ -9,6 +9,7 @@ import {
9
9
  ExtensionSlot,
10
10
  formatDate,
11
11
  launchWorkspace2,
12
+ navigate,
12
13
  parseDate,
13
14
  showModal,
14
15
  showSnackbar,
@@ -22,7 +23,9 @@ import InvoiceTable from './invoice-table.component';
22
23
  import Payments from './payments/payments.component';
23
24
  import PrintReceipt from './printable-invoice/print-receipt.component';
24
25
  import PrintableInvoice from './printable-invoice/printable-invoice.component';
25
- import { BillStatus } from '../types';
26
+ import { BillDiscountStatus, BillStatus, RefundStatus } from '../types';
27
+ import DiscountsTable from '../discounts/discounts-table.component';
28
+ import RefundsTable from '../refunds/refunds-table.component';
26
29
  import styles from './invoice.scss';
27
30
 
28
31
  interface InvoiceDetailsProps {
@@ -41,6 +44,64 @@ const Invoice: React.FC = () => {
41
44
  const onBeforeGetContentResolve = useRef<(() => void) | null>(null);
42
45
  const { defaultCurrency } = useConfig<BillingConfig>();
43
46
 
47
+ const discounts = (bill?.discounts ?? []).filter((d) => !d.voided);
48
+ const billLevelDiscountExists = discounts.some((d) => !d.lineItemUuid);
49
+ const lineItemDiscountExists = discounts.some((d) => !!d.lineItemUuid);
50
+ const billStatusEligible = bill?.status === BillStatus.PENDING || bill?.status === BillStatus.POSTED;
51
+ const showRequestDiscountButton = !!bill && billStatusEligible && !billLevelDiscountExists;
52
+ const hasApprovedDiscount = discounts.some((d) => d.status === BillDiscountStatus.APPROVED);
53
+
54
+ const refunds = (bill?.refunds ?? []).filter((r) => !r.voided);
55
+ const billStatusRefundEligible = bill?.status === BillStatus.PAID || bill?.status === BillStatus.PARTIALLY_REFUNDED;
56
+ const activeRefunds = refunds.filter(
57
+ (r) => r.status === RefundStatus.REQUESTED || r.status === RefundStatus.APPROVED,
58
+ );
59
+ const activeBillLevelRefund = activeRefunds.some((r) => !r.lineItemUuid);
60
+ const activeLineRefundUuids = new Set(activeRefunds.flatMap((r) => (r.lineItemUuid ? [r.lineItemUuid] : [])));
61
+ const showRequestRefundButton = !!bill && billStatusRefundEligible && !activeBillLevelRefund;
62
+
63
+ const handleRequestRefund = () => {
64
+ if (!bill) return;
65
+ if (bill.netAmount == null) {
66
+ showSnackbar({
67
+ title: t('refundUnavailable', 'Refund unavailable'),
68
+ subtitle: t('refundUnavailableSubtitle', 'Bill amount could not be determined. Please reload and try again.'),
69
+ kind: 'error',
70
+ });
71
+ return;
72
+ }
73
+ const totalAlreadyRefunded = refunds
74
+ .filter((r) => r.status === RefundStatus.APPROVED || r.status === RefundStatus.COMPLETED)
75
+ .reduce((s, r) => s + r.refundAmount, 0);
76
+ const remainingRefundable = bill.netAmount - totalAlreadyRefunded;
77
+ const dispose = showModal('request-refund-modal', {
78
+ bill: {
79
+ uuid: bill.uuid,
80
+ total: bill.totalAmount ?? 0,
81
+ amountAfterDiscount: bill.netAmount,
82
+ receiptNumber: bill.receiptNumber,
83
+ lineItemCount: bill.lineItems?.length ?? 0,
84
+ },
85
+ remainingRefundable: Math.max(0, remainingRefundable),
86
+ onMutate: () => mutate(),
87
+ closeModal: () => dispose(),
88
+ });
89
+ };
90
+
91
+ const handleRequestDiscount = () => {
92
+ const dispose = showModal('request-discount-modal', {
93
+ bill: {
94
+ uuid: bill!.uuid,
95
+ total: bill!.totalAmount ?? 0,
96
+ amountDue: Math.max(0, (bill!.netAmount ?? bill!.totalAmount ?? 0) - (bill!.tenderedAmount ?? 0)),
97
+ receiptNumber: bill!.receiptNumber,
98
+ lineItemCount: bill!.lineItems?.length ?? 0,
99
+ },
100
+ onMutate: () => mutate(),
101
+ closeModal: () => dispose(),
102
+ });
103
+ };
104
+
44
105
  const handleAfterPrint = useCallback(() => {
45
106
  onBeforeGetContentResolve.current = null;
46
107
  setIsPrinting(false);
@@ -63,6 +124,14 @@ const Invoice: React.FC = () => {
63
124
  });
64
125
  };
65
126
 
127
+ const handleDeleteBill = () => {
128
+ const dispose = showModal('delete-bill-confirmation-modal', {
129
+ bill,
130
+ onSuccess: () => navigate({ to: window.getOpenmrsSpaBase() + 'home/billing' }),
131
+ closeModal: () => dispose(),
132
+ });
133
+ };
134
+
66
135
  const handlePrint = useReactToPrint({
67
136
  contentRef: componentRef,
68
137
  documentTitle: `Invoice ${bill?.receiptNumber} - ${patient?.name?.[0]?.given?.join(' ')} ${patient?.name?.[0].family}`,
@@ -87,17 +156,28 @@ const Invoice: React.FC = () => {
87
156
  /**
88
157
  * t('totalAmount', 'Total amount')
89
158
  * t('amountTendered', 'Amount tendered')
90
- * t('invoiceNumber', 'Invoice #')
159
+ * t('invoiceNumber', 'Invoice number')
91
160
  * t('dateAndTime', 'Date and time')
92
161
  * t('invoiceStatus', 'Invoice status')
93
162
  */
94
- const invoiceDetails = {
163
+ const invoiceDetails: Record<string, string | number | undefined> = {
164
+ [t('dateBillCreated', 'Date bill created')]: bill?.dateCreated
165
+ ? formatDate(parseDate(bill.dateCreated), { mode: 'wide' })
166
+ : '--',
95
167
  [t('totalAmount', 'Total amount')]: convertToCurrency(bill?.totalAmount, defaultCurrency),
168
+ ...(hasApprovedDiscount
169
+ ? {
170
+ [t('discount', 'Discount')]:
171
+ `- ${convertToCurrency((bill?.totalAmount ?? 0) - (bill?.netAmount ?? 0), defaultCurrency)}`,
172
+ [t('netAmount', 'Net amount')]: convertToCurrency(bill?.netAmount, defaultCurrency),
173
+ }
174
+ : {}),
96
175
  [t('amountTendered', 'Amount tendered')]: convertToCurrency(bill?.tenderedAmount, defaultCurrency),
176
+ [t('amountDue', 'Amount due')]: convertToCurrency(
177
+ bill ? (bill.netAmount ?? 0) - (bill.tenderedAmount ?? 0) : undefined,
178
+ defaultCurrency,
179
+ ),
97
180
  [t('invoiceNumber', 'Invoice number')]: bill?.receiptNumber,
98
- [t('dateAndTime', 'Date and time')]: bill?.dateCreated
99
- ? formatDate(parseDate(bill.dateCreated), { mode: 'wide' })
100
- : '--',
101
181
  [t('invoiceStatus', 'Invoice status')]: bill?.status,
102
182
  };
103
183
 
@@ -129,6 +209,25 @@ const Invoice: React.FC = () => {
129
209
  <InlineLoading status="active" />
130
210
  </span>
131
211
  )}
212
+ {showRequestDiscountButton && (
213
+ <Button kind="tertiary" onClick={handleRequestDiscount} disabled={lineItemDiscountExists}>
214
+ {t('requestDiscount', 'Request discount')}
215
+ </Button>
216
+ )}
217
+ {showRequestRefundButton &&
218
+ (activeLineRefundUuids.size > 0 ? (
219
+ <Tooltip
220
+ align="bottom"
221
+ label={t('refundInProgress', 'A refund is already in progress for one or more line items')}>
222
+ <Button kind="tertiary" onClick={handleRequestRefund} disabled>
223
+ {t('requestRefund', 'Request refund')}
224
+ </Button>
225
+ </Tooltip>
226
+ ) : (
227
+ <Button kind="tertiary" onClick={handleRequestRefund}>
228
+ {t('requestRefund', 'Request refund')}
229
+ </Button>
230
+ ))}
132
231
  {bill?.status === BillStatus.PENDING && (
133
232
  <>
134
233
  <Button
@@ -143,6 +242,9 @@ const Invoice: React.FC = () => {
143
242
  }>
144
243
  {t('addItemsToBill', 'Add items to bill')}
145
244
  </Button>
245
+ <Button kind="danger--ghost" onClick={handleDeleteBill}>
246
+ {t('deleteBill', 'Delete bill')}
247
+ </Button>
146
248
  <Button kind="primary" onClick={handleFinalizeBill}>
147
249
  {t('finalizeBill', 'Finalize bill')}
148
250
  </Button>
@@ -167,6 +269,8 @@ const Invoice: React.FC = () => {
167
269
 
168
270
  <div className={styles.invoiceContent}>
169
271
  <InvoiceTable bill={bill} isLoadingBill={isLoadingBill} onMutate={mutate} />
272
+ {bill && <DiscountsTable bill={bill} />}
273
+ {bill && <RefundsTable bill={bill} onMutate={mutate} />}
170
274
  <Payments bill={bill} mutate={mutate} />
171
275
  </div>
172
276