@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
@@ -0,0 +1,218 @@
1
+ import React, { useState } from 'react';
2
+ import {
3
+ Button,
4
+ InlineLoading,
5
+ ModalBody,
6
+ ModalFooter,
7
+ ModalHeader,
8
+ NumberInput,
9
+ Stack,
10
+ TextArea,
11
+ } from '@carbon/react';
12
+ import { useTranslation } from 'react-i18next';
13
+ import { getCoreTranslation, showSnackbar, useConfig } from '@openmrs/esm-framework';
14
+ import type { BillingConfig } from '../config-schema';
15
+ import { convertToCurrency } from '../helpers';
16
+ import { requestRefund } from './refunds.resource';
17
+ import styles from './request-refund.modal.scss';
18
+
19
+ interface Props {
20
+ closeModal: () => void;
21
+ bill: {
22
+ uuid: string;
23
+ total: number;
24
+ amountAfterDiscount: number;
25
+ receiptNumber?: string;
26
+ lineItemCount?: number;
27
+ };
28
+ lineItem?: {
29
+ uuid: string;
30
+ display: string;
31
+ total: number;
32
+ quantity?: number;
33
+ price?: number;
34
+ };
35
+ remainingRefundable: number;
36
+ onMutate?: () => void;
37
+ }
38
+
39
+ const RequestRefundModal: React.FC<Props> = ({ closeModal, bill, lineItem, remainingRefundable, onMutate }) => {
40
+ const { t } = useTranslation();
41
+ const { defaultCurrency } = useConfig<BillingConfig>();
42
+ const [amount, setAmount] = useState<number | null>(null);
43
+ const [reason, setReason] = useState('');
44
+ const [submitting, setSubmitting] = useState(false);
45
+
46
+ const isLineItem = !!lineItem;
47
+ const scopeTotal = lineItem?.total ?? bill.total;
48
+ const alreadyRefunded = isLineItem
49
+ ? scopeTotal - remainingRefundable
50
+ : bill.amountAfterDiscount - remainingRefundable;
51
+ const trimmedReason = reason.trim();
52
+
53
+ const reasonError = reason.length > 1000 ? t('reasonTooLong', 'Reason cannot exceed 1000 characters') : null;
54
+
55
+ const amountError = (() => {
56
+ if (amount == null || Number.isNaN(amount)) return null;
57
+ if (remainingRefundable <= 0) return t('noRefundableAmount', 'No refundable amount remaining on this bill');
58
+ if (amount <= 0) return t('amountMustBePositive', 'Amount must be greater than 0');
59
+ if (amount > remainingRefundable)
60
+ return t('amountExceedsRefundable', 'Amount cannot exceed {{max}}', {
61
+ max: convertToCurrency(remainingRefundable, defaultCurrency),
62
+ });
63
+ return null;
64
+ })();
65
+
66
+ const canSubmit =
67
+ !submitting && amount != null && amount > 0 && trimmedReason.length > 0 && !reasonError && !amountError;
68
+
69
+ const handleSubmit = async () => {
70
+ if (!canSubmit || amount == null) return;
71
+ setSubmitting(true);
72
+ try {
73
+ await requestRefund({
74
+ bill: bill.uuid,
75
+ lineItem: lineItem?.uuid,
76
+ refundAmount: amount,
77
+ reason: trimmedReason,
78
+ });
79
+ onMutate?.();
80
+ showSnackbar({
81
+ title: t('refundRequested', 'Refund request submitted'),
82
+ subtitle: t('refundPendingReview', 'An admin will review your request'),
83
+ kind: 'success',
84
+ });
85
+ closeModal();
86
+ } catch (err: unknown) {
87
+ const subtitle = (err as any)?.responseBody?.error?.message ?? (err instanceof Error ? err.message : undefined);
88
+ showSnackbar({
89
+ title: t('refundRequestFailed', 'Could not submit refund request'),
90
+ subtitle,
91
+ kind: 'error',
92
+ });
93
+ } finally {
94
+ setSubmitting(false);
95
+ }
96
+ };
97
+
98
+ return (
99
+ <>
100
+ <ModalHeader closeModal={closeModal} title={t('requestRefund', 'Request refund')} />
101
+ <ModalBody className={styles.modalBody}>
102
+ <Stack gap={5}>
103
+ <section className={styles.scopePanel} aria-label={t('refundScope', 'Refund scope')}>
104
+ <div className={styles.scopePrimary}>
105
+ {isLineItem ? lineItem.display : t('wholeBillScope', 'Whole bill')}
106
+ </div>
107
+ <ul className={styles.scopeMeta}>
108
+ {bill.receiptNumber && <li>{t('invoiceLabel', 'Invoice {{number}}', { number: bill.receiptNumber })}</li>}
109
+ {isLineItem && lineItem.quantity != null && lineItem.price != null && (
110
+ <li>
111
+ {t('qtyTimesPrice', 'Qty {{quantity}} × {{price}}', {
112
+ quantity: lineItem.quantity,
113
+ price: convertToCurrency(lineItem.price, defaultCurrency),
114
+ })}
115
+ </li>
116
+ )}
117
+ {!isLineItem && bill.lineItemCount != null && (
118
+ <li>{t('lineItemCount', '{{count}} line items', { count: bill.lineItemCount })}</li>
119
+ )}
120
+ <li className={styles.scopeTotal}>
121
+ {isLineItem
122
+ ? t('lineTotalLabel', 'Line total {{total}}', {
123
+ total: convertToCurrency(scopeTotal, defaultCurrency),
124
+ })
125
+ : t('billTotalLabel', 'Bill total {{total}}', {
126
+ total: convertToCurrency(scopeTotal, defaultCurrency),
127
+ })}
128
+ </li>
129
+ </ul>
130
+ </section>
131
+
132
+ <NumberInput
133
+ id="refund-amount"
134
+ label={t('refundAmountLabel', 'Refund amount ({{currency}})', { currency: defaultCurrency })}
135
+ allowEmpty
136
+ min={0}
137
+ max={remainingRefundable}
138
+ value={amount ?? ''}
139
+ invalid={!!amountError}
140
+ invalidText={amountError ?? ''}
141
+ onChange={(_e, { value: v }) => {
142
+ if (v === '' || v == null) {
143
+ setAmount(null);
144
+ return;
145
+ }
146
+ const n = typeof v === 'string' ? Number.parseFloat(v) : v;
147
+ setAmount(Number.isNaN(n) ? null : n);
148
+ }}
149
+ />
150
+
151
+ {amount != null && amount > 0 && !amountError && (
152
+ <section className={styles.summaryCard} aria-label={t('refundSummary', 'Refund summary')}>
153
+ <span className={styles.summaryLabel}>
154
+ {isLineItem ? t('lineTotal', 'Line total') : t('billTotal', 'Bill total')}
155
+ </span>
156
+ <span className={styles.summaryValue}>
157
+ {convertToCurrency(isLineItem ? scopeTotal : bill.amountAfterDiscount, defaultCurrency)}
158
+ </span>
159
+
160
+ {alreadyRefunded > 0 && (
161
+ <>
162
+ <span className={styles.summaryLabel}>{t('alreadyRefunded', 'Already refunded')}</span>
163
+ <span className={`${styles.summaryValue} ${styles.summaryRefund}`}>
164
+ −{convertToCurrency(alreadyRefunded, defaultCurrency)}
165
+ </span>
166
+ </>
167
+ )}
168
+
169
+ <span className={styles.summaryLabel}>{t('refundAmount', 'Refund amount')}</span>
170
+ <span className={`${styles.summaryValue} ${styles.summaryRefund}`}>
171
+ −{convertToCurrency(amount, defaultCurrency)}
172
+ </span>
173
+
174
+ <span className={styles.summaryDivider} aria-hidden="true" />
175
+
176
+ <span className={styles.summaryTotalLabel}>{t('amountAfterRefund', 'Amount after refund')}</span>
177
+ <span className={styles.summaryTotalValue}>
178
+ {convertToCurrency(remainingRefundable - amount, defaultCurrency)}
179
+ </span>
180
+ </section>
181
+ )}
182
+
183
+ <TextArea
184
+ id="refund-reason"
185
+ labelText={t('reason', 'Reason')}
186
+ placeholder={
187
+ isLineItem
188
+ ? t('lineItemReasonPlaceholder', 'e.g., duplicate charge, wrong item billed')
189
+ : t('billReasonPlaceholder', 'e.g., payment made in error, duplicate payment')
190
+ }
191
+ maxCount={1000}
192
+ enableCounter
193
+ required
194
+ rows={3}
195
+ value={reason}
196
+ invalid={!!reasonError}
197
+ invalidText={reasonError ?? ''}
198
+ onChange={(e) => setReason(e.target.value)}
199
+ />
200
+ </Stack>
201
+ </ModalBody>
202
+ <ModalFooter>
203
+ <Button kind="secondary" onClick={closeModal}>
204
+ {getCoreTranslation('cancel')}
205
+ </Button>
206
+ <Button kind="primary" onClick={handleSubmit} disabled={!canSubmit}>
207
+ {submitting ? (
208
+ <InlineLoading description={t('submitting', 'Submitting') + '...'} />
209
+ ) : (
210
+ <span>{t('submitRequest', 'Submit request')}</span>
211
+ )}
212
+ </Button>
213
+ </ModalFooter>
214
+ </>
215
+ );
216
+ };
217
+
218
+ export default RequestRefundModal;
package/src/routes.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.openmrs.org/routes.schema.json",
3
3
  "backendDependencies": {
4
- "billing": ">=2.0.0",
4
+ "billing": ">=2.3.0-0",
5
5
  "webservices.rest": ">=2.24.0"
6
6
  },
7
7
  "pages": [
@@ -35,6 +35,14 @@
35
35
  "columnSpan": 4
36
36
  }
37
37
  },
38
+ {
39
+ "name": "visit-bills-panel",
40
+ "component": "visitBillsPanel",
41
+ "slot": "visit-summary-panels",
42
+ "meta": {
43
+ "title": "Bills"
44
+ }
45
+ },
38
46
  {
39
47
  "name": "billing-summary-dashboard-link",
40
48
  "component": "billingSummaryDashboardLink",
@@ -71,6 +79,12 @@
71
79
  "slot": "patient-banner-tags-slot",
72
80
  "order": 2
73
81
  },
82
+ {
83
+ "name": "patient-banner-payment-status-tag",
84
+ "component": "patientPaymentStatusTag",
85
+ "slot": "patient-banner-tags-slot",
86
+ "order": 3
87
+ },
74
88
  {
75
89
  "name": "billable-services-left-panel-link",
76
90
  "component": "billableServicesLeftPanelLink",
@@ -82,6 +96,18 @@
82
96
  "component": "billingSettingsLeftPanelMenu",
83
97
  "slot": "billable-services-left-panel-slot",
84
98
  "order": 1
99
+ },
100
+ {
101
+ "name": "discount-requests-left-panel-link",
102
+ "component": "discountRequestsLeftPanelLink",
103
+ "slot": "billable-services-left-panel-slot",
104
+ "order": 2
105
+ },
106
+ {
107
+ "name": "refund-requests-left-panel-link",
108
+ "component": "refundRequestsLeftPanelLink",
109
+ "slot": "billable-services-left-panel-slot",
110
+ "order": 3
85
111
  }
86
112
  ],
87
113
  "modals": [
@@ -116,7 +142,15 @@
116
142
  {
117
143
  "name": "finalize-bill-confirmation-modal",
118
144
  "component": "finalizeBillConfirmationModal"
119
- }
145
+ },
146
+ {
147
+ "name": "delete-bill-confirmation-modal",
148
+ "component": "deleteBillConfirmationModal"
149
+ },
150
+ { "name": "request-discount-modal", "component": "requestDiscountModal" },
151
+ { "name": "review-bill-discounts-modal", "component": "reviewBillDiscountsModal" },
152
+ { "name": "request-refund-modal", "component": "requestRefundModal" },
153
+ { "name": "review-bill-refunds-modal", "component": "reviewBillRefundsModal" }
120
154
  ],
121
155
  "workspaces2": [
122
156
  {
@@ -5,10 +5,26 @@ export const BillStatus = {
5
5
  POSTED: 'POSTED',
6
6
  PAID: 'PAID',
7
7
  ADJUSTED: 'ADJUSTED',
8
+ PARTIALLY_REFUNDED: 'PARTIALLY_REFUNDED',
9
+ REFUNDED: 'REFUNDED',
10
+ REFUND_REQUESTED: 'REFUND_REQUESTED',
8
11
  } as const;
9
12
 
10
13
  export type BillStatus = (typeof BillStatus)[keyof typeof BillStatus];
11
14
 
15
+ export const BillLineItemStatus = {
16
+ PENDING: 'PENDING',
17
+ PAID: 'PAID',
18
+ CANCELLED: 'CANCELLED',
19
+ ADJUSTED: 'ADJUSTED',
20
+ EXEMPTED: 'EXEMPTED',
21
+ REFUND_REQUESTED: 'REFUND_REQUESTED',
22
+ REFUNDED: 'REFUNDED',
23
+ PARTIALLY_REFUNDED: 'PARTIALLY_REFUNDED',
24
+ } as const;
25
+
26
+ export type BillLineItemStatus = (typeof BillLineItemStatus)[keyof typeof BillLineItemStatus];
27
+
12
28
  export interface MappedBill {
13
29
  uuid: string;
14
30
  id: number;
@@ -25,9 +41,13 @@ export interface MappedBill {
25
41
  lineItems: Array<LineItem>;
26
42
  billingService: string;
27
43
  payments: Array<Payment>;
44
+ discounts?: Array<BillDiscount>;
45
+ refunds?: Array<BillRefund>;
28
46
  totalAmount?: number;
47
+ netAmount: number;
29
48
  tenderedAmount?: number;
30
49
  display?: string;
50
+ visitUuid?: string;
31
51
  }
32
52
 
33
53
  interface LocationLink {
@@ -65,7 +85,7 @@ interface Provider {
65
85
  export interface LineItem {
66
86
  uuid?: string;
67
87
  item?: string;
68
- paymentStatus: string;
88
+ status: BillLineItemStatus;
69
89
  billableService?: string;
70
90
  quantity: number;
71
91
  price: number;
@@ -157,7 +177,12 @@ export interface PatientInvoice {
157
177
  status: string;
158
178
  adjustmentReason: string | null;
159
179
  id: number;
180
+ visit?: { uuid: string; display: string };
160
181
  resourceVersion: string;
182
+ total: number;
183
+ amountAfterDiscount: number;
184
+ discounts: BillDiscount[];
185
+ refunds: BillRefund[];
161
186
  }
162
187
 
163
188
  export interface PatientDetails {
@@ -230,6 +255,7 @@ export type CreateBillPayload = {
230
255
  payments: Array<PaymentPayload>;
231
256
  patient: string;
232
257
  status: string;
258
+ visit?: string;
233
259
  };
234
260
 
235
261
  export type UpdateBillPayload = {
@@ -261,3 +287,92 @@ export type PaymentModePayload = {
261
287
  name: string;
262
288
  description: string;
263
289
  };
290
+
291
+ export const BillDiscountStatus = {
292
+ PENDING: 'PENDING',
293
+ APPROVED: 'APPROVED',
294
+ REJECTED: 'REJECTED',
295
+ } as const;
296
+
297
+ export type BillDiscountStatus = (typeof BillDiscountStatus)[keyof typeof BillDiscountStatus];
298
+
299
+ export const BillDiscountType = {
300
+ PERCENTAGE: 'PERCENTAGE',
301
+ FIXED_AMOUNT: 'FIXED_AMOUNT',
302
+ } as const;
303
+
304
+ export type BillDiscountType = (typeof BillDiscountType)[keyof typeof BillDiscountType];
305
+
306
+ export interface UserRef {
307
+ uuid: string;
308
+ display: string;
309
+ }
310
+
311
+ export interface BillDiscount {
312
+ uuid: string;
313
+ billUuid: string;
314
+ lineItemUuid: string | null;
315
+ discountType: BillDiscountType;
316
+ discountValue: number;
317
+ discountAmount: number;
318
+ justification: string;
319
+ initiator: UserRef;
320
+ approver: UserRef | null;
321
+ dateCreated: string;
322
+ status: BillDiscountStatus;
323
+ voided: boolean;
324
+ }
325
+
326
+ export interface RequestDiscountPayload {
327
+ bill: string;
328
+ lineItem?: string;
329
+ discountType: BillDiscountType;
330
+ discountValue: number;
331
+ justification: string;
332
+ }
333
+
334
+ export interface DecideDiscountPayload {
335
+ status: Exclude<BillDiscountStatus, 'PENDING'>;
336
+ approver: string;
337
+ }
338
+
339
+ export interface PatientPaymentStatus {
340
+ status: 'PAID' | 'UNPAID' | 'UNKNOWN';
341
+ reason: string;
342
+ }
343
+ export const RefundStatus = {
344
+ REQUESTED: 'REQUESTED',
345
+ APPROVED: 'APPROVED',
346
+ REJECTED: 'REJECTED',
347
+ COMPLETED: 'COMPLETED',
348
+ } as const;
349
+ export type RefundStatus = (typeof RefundStatus)[keyof typeof RefundStatus];
350
+
351
+ export interface BillRefund {
352
+ uuid: string;
353
+ billUuid: string;
354
+ lineItemUuid: string | null;
355
+ refundAmount: number;
356
+ reason: string;
357
+ initiator: UserRef;
358
+ approver: UserRef | null;
359
+ completer: UserRef | null;
360
+ dateApproved: string | null;
361
+ dateCompleted: string | null;
362
+ dateCreated: string;
363
+ status: RefundStatus;
364
+ voided: boolean;
365
+ }
366
+
367
+ export interface RequestRefundPayload {
368
+ bill: string;
369
+ lineItem?: string;
370
+ refundAmount: number;
371
+ reason: string;
372
+ }
373
+
374
+ export interface DecideRefundPayload {
375
+ status: Exclude<RefundStatus, 'REQUESTED'>;
376
+ approver?: string;
377
+ completer?: string;
378
+ }
@@ -0,0 +1,151 @@
1
+ import React, { useMemo } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import {
4
+ DataTable,
5
+ DataTableSkeleton,
6
+ InlineNotification,
7
+ Layer,
8
+ Table,
9
+ TableBody,
10
+ TableCell,
11
+ TableContainer,
12
+ TableExpandedRow,
13
+ TableExpandHeader,
14
+ TableExpandRow,
15
+ TableHead,
16
+ TableHeader,
17
+ TableRow,
18
+ Tile,
19
+ } from '@carbon/react';
20
+ import { EmptyCardIllustration, ErrorState, formatDate, parseDate, useConfig } from '@openmrs/esm-framework';
21
+ import { useBills } from '../billing.resource';
22
+ import { convertToCurrency } from '../helpers';
23
+ import InvoiceTable from '../invoice/invoice-table.component';
24
+ import type { BillingConfig } from '../config-schema';
25
+ import styles from './visit-bills-panel.scss';
26
+
27
+ interface VisitBillsPanelProps {
28
+ visit: { uuid: string };
29
+ patientUuid: string;
30
+ }
31
+
32
+ const VisitBillsPanel: React.FC<VisitBillsPanelProps> = ({ visit, patientUuid }) => {
33
+ const { t } = useTranslation();
34
+ const { defaultCurrency } = useConfig<BillingConfig>();
35
+ const { bills, error, isLoading, isValidating, mutate } = useBills(patientUuid, undefined, visit.uuid);
36
+ const billsByUuid = useMemo(() => new Map(bills?.map((bill) => [bill.uuid, bill])), [bills]);
37
+
38
+ const headerData = [
39
+ { header: t('billDate', 'Bill date'), key: 'billDate' },
40
+ { header: t('invoiceNumber', 'Invoice number'), key: 'invoiceNumber' },
41
+ { header: t('billedItems', 'Billed items'), key: 'billedItems' },
42
+ { header: t('billTotal', 'Bill total'), key: 'billTotal' },
43
+ ];
44
+
45
+ const setBilledItems = (bill) =>
46
+ bill?.lineItems
47
+ ?.map((item) => item?.billableService || item?.item)
48
+ .filter(Boolean)
49
+ .join(' & ') || '--';
50
+
51
+ const formatBillDate = (dateCreated: string) => {
52
+ try {
53
+ return formatDate(parseDate(dateCreated), { mode: 'wide' });
54
+ } catch {
55
+ return '--';
56
+ }
57
+ };
58
+
59
+ const rowData = bills?.map((bill) => ({
60
+ id: bill.uuid,
61
+ billDate: bill.dateCreated ? formatBillDate(bill.dateCreated) : '--',
62
+ invoiceNumber: bill.receiptNumber,
63
+ billedItems: setBilledItems(bill),
64
+ billTotal: convertToCurrency(bill.totalAmount ?? 0, defaultCurrency),
65
+ }));
66
+
67
+ if (isLoading) {
68
+ return <DataTableSkeleton showHeader={false} showToolbar={false} zebra />;
69
+ }
70
+
71
+ if (error) {
72
+ return (
73
+ <Layer>
74
+ <ErrorState error={error} headerTitle={t('billList', 'Bill list')} />
75
+ </Layer>
76
+ );
77
+ }
78
+
79
+ if (!bills?.length) {
80
+ return (
81
+ <Layer className={styles.emptyStateContainer}>
82
+ <Tile className={styles.tile}>
83
+ <div className={styles.illo}>
84
+ <EmptyCardIllustration />
85
+ </div>
86
+ <p className={styles.content}>{t('noBillsToDisplay', 'There are no bills to display.')}</p>
87
+ </Tile>
88
+ </Layer>
89
+ );
90
+ }
91
+
92
+ return (
93
+ <DataTable isSortable rows={rowData} headers={headerData} size="md" useZebraStyles>
94
+ {({
95
+ getExpandHeaderProps,
96
+ getHeaderProps,
97
+ getRowProps,
98
+ getTableContainerProps,
99
+ getTableProps,
100
+ headers,
101
+ rows,
102
+ }) => (
103
+ <TableContainer {...getTableContainerProps()}>
104
+ <Table {...getTableProps()} aria-label={t('billList', 'Bill list')}>
105
+ <TableHead>
106
+ <TableRow>
107
+ <TableExpandHeader enableToggle {...getExpandHeaderProps()} />
108
+ {headers.map((header) => (
109
+ <TableHeader key={header.key} {...getHeaderProps({ header })}>
110
+ {header.header}
111
+ </TableHeader>
112
+ ))}
113
+ </TableRow>
114
+ </TableHead>
115
+ <TableBody>
116
+ {rows.map((row) => {
117
+ const currentBill = billsByUuid.get(row.id);
118
+ return (
119
+ <React.Fragment key={row.id}>
120
+ <TableExpandRow {...getRowProps({ row })}>
121
+ {row.cells.map((cell) => (
122
+ <TableCell key={cell.id}>{cell.value}</TableCell>
123
+ ))}
124
+ </TableExpandRow>
125
+ {row.isExpanded ? (
126
+ <TableExpandedRow colSpan={headers.length + 1}>
127
+ {currentBill ? (
128
+ <InvoiceTable bill={currentBill} onMutate={mutate} isLoadingBill={isValidating} viewOnly />
129
+ ) : (
130
+ <InlineNotification
131
+ kind="error"
132
+ lowContrast
133
+ title={t('billNotFound', 'Bill details could not be loaded')}
134
+ />
135
+ )}
136
+ </TableExpandedRow>
137
+ ) : (
138
+ <TableExpandedRow className={styles.hiddenRow} colSpan={headers.length + 1} />
139
+ )}
140
+ </React.Fragment>
141
+ );
142
+ })}
143
+ </TableBody>
144
+ </Table>
145
+ </TableContainer>
146
+ )}
147
+ </DataTable>
148
+ );
149
+ };
150
+
151
+ export default VisitBillsPanel;
@@ -0,0 +1,31 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @use '@openmrs/esm-styleguide/src/vars' as *;
4
+
5
+ .emptyStateContainer {
6
+ margin: layout.$spacing-03 0;
7
+ }
8
+
9
+ .hiddenRow {
10
+ display: none;
11
+ }
12
+
13
+ .illo {
14
+ margin-top: layout.$spacing-05;
15
+ }
16
+
17
+ .content {
18
+ @include type.type-style('heading-compact-01');
19
+ color: $text-02;
20
+ margin-top: layout.$spacing-05;
21
+ margin-bottom: layout.$spacing-03;
22
+ }
23
+
24
+ .tile {
25
+ text-align: center;
26
+ border: 1px solid $ui-03;
27
+ }
28
+
29
+ .menuItem {
30
+ max-width: none;
31
+ }