@openmrs/esm-billing-app 1.0.2-pre.86 → 1.0.2-pre.861

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 (214) hide show
  1. package/.eslintrc +16 -2
  2. package/README.md +54 -9
  3. package/__mocks__/bills.mock.ts +12 -0
  4. package/__mocks__/react-i18next.js +6 -5
  5. package/dist/1119.js +1 -1
  6. package/dist/1146.js +1 -2
  7. package/dist/1146.js.map +1 -1
  8. package/dist/1197.js +1 -1
  9. package/dist/1856.js +1 -0
  10. package/dist/1856.js.map +1 -0
  11. package/dist/2146.js +1 -1
  12. package/dist/2177.js +2 -0
  13. package/dist/2177.js.LICENSE.txt +9 -0
  14. package/dist/2177.js.map +1 -0
  15. package/dist/2524.js +1 -0
  16. package/dist/2524.js.map +1 -0
  17. package/dist/2690.js +1 -1
  18. package/dist/3041.js +1 -0
  19. package/dist/3041.js.map +1 -0
  20. package/dist/3099.js +1 -1
  21. package/dist/3584.js +1 -1
  22. package/dist/3717.js +2 -0
  23. package/dist/3717.js.map +1 -0
  24. package/dist/4055.js +1 -1
  25. package/dist/4132.js +1 -1
  26. package/dist/4225.js +1 -0
  27. package/dist/4225.js.map +1 -0
  28. package/dist/4300.js +1 -1
  29. package/dist/4335.js +1 -1
  30. package/dist/4344.js +1 -0
  31. package/dist/4344.js.map +1 -0
  32. package/dist/4618.js +1 -1
  33. package/dist/4652.js +1 -1
  34. package/dist/4724.js +1 -0
  35. package/dist/4724.js.map +1 -0
  36. package/dist/4739.js +1 -1
  37. package/dist/4739.js.map +1 -1
  38. package/dist/4944.js +1 -1
  39. package/dist/5173.js +1 -1
  40. package/dist/5241.js +1 -1
  41. package/dist/5422.js +1 -0
  42. package/dist/5422.js.map +1 -0
  43. package/dist/5442.js +1 -1
  44. package/dist/5661.js +1 -1
  45. package/dist/6022.js +1 -1
  46. package/dist/6295.js +2 -0
  47. package/dist/{6525.js.LICENSE.txt → 6295.js.LICENSE.txt} +16 -4
  48. package/dist/6295.js.map +1 -0
  49. package/dist/6468.js +1 -1
  50. package/dist/6540.js +1 -1
  51. package/dist/6540.js.map +1 -1
  52. package/dist/6606.js +1 -0
  53. package/dist/6606.js.map +1 -0
  54. package/dist/6679.js +1 -1
  55. package/dist/6840.js +1 -1
  56. package/dist/6859.js +1 -1
  57. package/dist/7097.js +1 -1
  58. package/dist/7159.js +1 -1
  59. package/dist/723.js +1 -1
  60. package/dist/7617.js +1 -1
  61. package/dist/795.js +1 -1
  62. package/dist/8163.js +1 -1
  63. package/dist/8349.js +1 -1
  64. package/dist/8618.js +1 -1
  65. package/dist/890.js +1 -1
  66. package/dist/9214.js +1 -1
  67. package/dist/9538.js +1 -1
  68. package/dist/9569.js +1 -1
  69. package/dist/961.js +1 -1
  70. package/dist/961.js.map +1 -1
  71. package/dist/986.js +1 -1
  72. package/dist/9879.js +1 -1
  73. package/dist/9895.js +1 -1
  74. package/dist/9900.js +1 -1
  75. package/dist/9913.js +1 -1
  76. package/dist/main.js +1 -1
  77. package/dist/main.js.map +1 -1
  78. package/dist/openmrs-esm-billing-app.js +1 -1
  79. package/dist/openmrs-esm-billing-app.js.buildmanifest.json +388 -282
  80. package/dist/openmrs-esm-billing-app.js.map +1 -1
  81. package/dist/routes.json +1 -1
  82. package/e2e/README.md +19 -18
  83. package/e2e/core/test.ts +1 -1
  84. package/e2e/fixtures/api.ts +1 -1
  85. package/e2e/specs/sample-test.spec.ts +0 -1
  86. package/e2e/support/github/Dockerfile +1 -1
  87. package/package.json +13 -10
  88. package/src/bill-history/bill-history.component.tsx +20 -28
  89. package/src/bill-history/bill-history.scss +4 -94
  90. package/src/bill-history/bill-history.test.tsx +37 -78
  91. package/src/bill-item-actions/bill-item-actions.scss +21 -5
  92. package/src/bill-item-actions/edit-bill-item.modal.tsx +225 -0
  93. package/src/bill-item-actions/edit-bill-item.test.tsx +214 -40
  94. package/src/billable-services/bill-waiver/bill-selection.component.tsx +5 -5
  95. package/src/billable-services/bill-waiver/bill-waiver-form.component.tsx +28 -32
  96. package/src/billable-services/bill-waiver/patient-bills.component.tsx +7 -7
  97. package/src/billable-services/bill-waiver/utils.ts +13 -3
  98. package/src/billable-services/billable-service.resource.ts +42 -26
  99. package/src/billable-services/billable-services-home.component.tsx +4 -4
  100. package/src/billable-services/billable-services.component.tsx +149 -148
  101. package/src/billable-services/billable-services.scss +3 -0
  102. package/src/billable-services/billable-services.test.tsx +6 -49
  103. package/src/billable-services/cash-point/add-cash-point.modal.tsx +168 -0
  104. package/src/billable-services/cash-point/cash-point-configuration.component.tsx +19 -193
  105. package/src/billable-services/cash-point/cash-point-configuration.scss +1 -5
  106. package/src/billable-services/create-edit/add-billable-service.component.tsx +365 -299
  107. package/src/billable-services/create-edit/add-billable-service.scss +7 -68
  108. package/src/billable-services/create-edit/add-billable-service.test.tsx +167 -81
  109. package/src/billable-services/create-edit/edit-billable-service.modal.tsx +51 -0
  110. package/src/billable-services/dashboard/service-metrics.component.tsx +11 -3
  111. package/src/billable-services/payment-modes/add-payment-mode.modal.tsx +121 -0
  112. package/src/billable-services/payment-modes/delete-payment-mode.modal.tsx +74 -0
  113. package/src/billable-services/payment-modes/payment-modes-config.component.tsx +125 -0
  114. package/src/billable-services/{payyment-modes → payment-modes}/payment-modes-config.scss +5 -4
  115. package/src/billing-dashboard/billing-dashboard.scss +1 -1
  116. package/src/billing-form/billing-checkin-form.component.tsx +21 -17
  117. package/src/billing-form/billing-checkin-form.test.tsx +99 -26
  118. package/src/billing-form/billing-form.component.tsx +222 -292
  119. package/src/billing-form/billing-form.scss +143 -0
  120. package/src/billing-form/visit-attributes/visit-attributes-form.component.tsx +1 -1
  121. package/src/billing.resource.ts +69 -74
  122. package/src/bills-table/bills-table.component.tsx +3 -3
  123. package/src/bills-table/bills-table.test.tsx +98 -54
  124. package/src/config-schema.ts +52 -24
  125. package/src/dashboard.meta.ts +4 -2
  126. package/src/helpers/functions.ts +5 -4
  127. package/src/index.ts +17 -6
  128. package/src/invoice/invoice-table.component.tsx +36 -70
  129. package/src/invoice/invoice-table.scss +8 -5
  130. package/src/invoice/invoice-table.test.tsx +273 -62
  131. package/src/invoice/invoice.component.tsx +39 -32
  132. package/src/invoice/invoice.scss +11 -4
  133. package/src/invoice/invoice.test.tsx +324 -120
  134. package/src/invoice/payments/invoice-breakdown/invoice-breakdown.scss +9 -9
  135. package/src/invoice/payments/payment-form/payment-form.component.tsx +43 -34
  136. package/src/invoice/payments/payment-form/payment-form.scss +5 -6
  137. package/src/invoice/payments/payment-form/payment-form.test.tsx +216 -66
  138. package/src/invoice/payments/payment-history/payment-history.component.tsx +6 -4
  139. package/src/invoice/payments/payment-history/payment-history.test.tsx +9 -14
  140. package/src/invoice/payments/payments.component.tsx +55 -67
  141. package/src/invoice/payments/payments.scss +4 -3
  142. package/src/invoice/payments/payments.test.tsx +282 -0
  143. package/src/invoice/payments/utils.ts +15 -27
  144. package/src/invoice/printable-invoice/print-receipt.component.tsx +3 -2
  145. package/src/invoice/printable-invoice/print-receipt.test.tsx +14 -25
  146. package/src/invoice/printable-invoice/printable-footer.component.tsx +2 -2
  147. package/src/invoice/printable-invoice/printable-footer.test.tsx +4 -13
  148. package/src/invoice/printable-invoice/printable-invoice-header.component.tsx +12 -11
  149. package/src/invoice/printable-invoice/printable-invoice-header.test.tsx +16 -14
  150. package/src/invoice/printable-invoice/printable-invoice.component.tsx +20 -34
  151. package/src/left-panel-link.test.tsx +1 -4
  152. package/src/metrics-cards/metrics-cards.component.tsx +12 -2
  153. package/src/metrics-cards/metrics-cards.scss +4 -0
  154. package/src/metrics-cards/metrics-cards.test.tsx +18 -5
  155. package/src/modal/require-payment-modal.test.tsx +27 -22
  156. package/src/modal/{require-payment-modal.component.tsx → require-payment.modal.tsx} +18 -19
  157. package/src/routes.json +25 -7
  158. package/src/types/index.ts +81 -23
  159. package/translations/am.json +125 -74
  160. package/translations/ar.json +126 -75
  161. package/translations/ar_SY.json +126 -75
  162. package/translations/bn.json +128 -77
  163. package/translations/de.json +126 -75
  164. package/translations/en.json +126 -75
  165. package/translations/en_US.json +126 -75
  166. package/translations/es.json +125 -74
  167. package/translations/es_MX.json +126 -75
  168. package/translations/fr.json +131 -80
  169. package/translations/he.json +125 -74
  170. package/translations/hi.json +126 -75
  171. package/translations/hi_IN.json +126 -75
  172. package/translations/id.json +126 -75
  173. package/translations/it.json +152 -101
  174. package/translations/ka.json +126 -75
  175. package/translations/km.json +125 -74
  176. package/translations/ku.json +126 -75
  177. package/translations/ky.json +126 -75
  178. package/translations/lg.json +126 -75
  179. package/translations/ne.json +126 -75
  180. package/translations/pl.json +126 -75
  181. package/translations/pt.json +126 -75
  182. package/translations/pt_BR.json +126 -75
  183. package/translations/qu.json +126 -75
  184. package/translations/ro_RO.json +216 -165
  185. package/translations/ru_RU.json +126 -75
  186. package/translations/si.json +126 -75
  187. package/translations/sw.json +126 -75
  188. package/translations/sw_KE.json +126 -75
  189. package/translations/tr.json +126 -75
  190. package/translations/tr_TR.json +126 -75
  191. package/translations/uk.json +126 -75
  192. package/translations/uz.json +126 -75
  193. package/translations/uz@Latn.json +126 -75
  194. package/translations/uz_UZ.json +126 -75
  195. package/translations/vi.json +126 -75
  196. package/translations/zh.json +126 -75
  197. package/translations/zh_CN.json +158 -107
  198. package/dist/1146.js.LICENSE.txt +0 -21
  199. package/dist/2352.js +0 -1
  200. package/dist/2352.js.map +0 -1
  201. package/dist/246.js +0 -1
  202. package/dist/246.js.map +0 -1
  203. package/dist/6525.js +0 -2
  204. package/dist/6525.js.map +0 -1
  205. package/dist/8556.js +0 -2
  206. package/dist/8556.js.map +0 -1
  207. package/dist/8638.js +0 -1
  208. package/dist/8638.js.map +0 -1
  209. package/dist/9968.js +0 -1
  210. package/dist/9968.js.map +0 -1
  211. package/src/bill-item-actions/edit-bill-item.component.tsx +0 -221
  212. package/src/billable-services/payyment-modes/payment-modes-config.component.tsx +0 -280
  213. package/src/invoice/payments/payments.component.test.tsx +0 -121
  214. /package/dist/{8556.js.LICENSE.txt → 3717.js.LICENSE.txt} +0 -0
@@ -1,221 +0,0 @@
1
- import React, { useEffect, useMemo, useState } from 'react';
2
- import {
3
- Button,
4
- Column,
5
- Form,
6
- InlineLoading,
7
- InlineNotification,
8
- ModalBody,
9
- ModalFooter,
10
- ModalHeader,
11
- NumberInput,
12
- TextInput,
13
- } from '@carbon/react';
14
- import { useTranslation } from 'react-i18next';
15
- import { Controller, type FieldErrors, useForm } from 'react-hook-form';
16
- import { mutate } from 'swr';
17
- import { z } from 'zod';
18
- import { zodResolver } from '@hookform/resolvers/zod';
19
- import { showSnackbar } from '@openmrs/esm-framework';
20
- import { apiBasePath } from '../constants';
21
- import { getBillableServiceUuid } from '../invoice/payments/utils';
22
- import { type LineItem, type MappedBill } from '../types';
23
- import { updateBillItems } from '../billing.resource';
24
- import { useBillableServices } from '../billable-services/billable-service.resource';
25
- import styles from './bill-item-actions.scss';
26
-
27
- interface BillLineItemProps {
28
- bill: MappedBill;
29
- item: LineItem;
30
- closeModal: () => void;
31
- }
32
-
33
- const ChangeStatus: React.FC<BillLineItemProps> = ({ bill, item, closeModal }) => {
34
- const { t } = useTranslation();
35
- const { billableServices } = useBillableServices();
36
- const [showErrorNotification, setShowErrorNotification] = useState(false);
37
- const [total, setTotal] = useState(0);
38
-
39
- const schema = useMemo(
40
- () =>
41
- z.object({
42
- quantity: z.string({ required_error: t('quantityRequired', 'Quantity is required') }),
43
- price: z.string({ required_error: t('priceIsRequired', 'Price is required') }),
44
- }),
45
- [t],
46
- );
47
-
48
- type BillLineItemForm = z.infer<typeof schema>;
49
-
50
- const onError = (errors: FieldErrors<LineItem>) => {
51
- if (errors) {
52
- setShowErrorNotification(true);
53
- }
54
- };
55
-
56
- const {
57
- control,
58
- handleSubmit,
59
- formState: { isSubmitting, errors, isDirty },
60
- watch,
61
- } = useForm<BillLineItemForm>({
62
- defaultValues: {
63
- quantity: item.quantity.toString(),
64
- price: item.price.toString(),
65
- },
66
- resolver: zodResolver(schema),
67
- });
68
-
69
- const quantity = watch('quantity');
70
- const price = watch('price');
71
-
72
- useEffect(() => {
73
- const newTotal = parseInt(quantity) * parseInt(price);
74
- setTotal(newTotal);
75
- }, [quantity, price]);
76
-
77
- const onSubmit = (data: BillLineItemForm) => {
78
- const url = `${apiBasePath}bill`;
79
-
80
- const newItem = {
81
- ...item,
82
- quantity: parseInt(data.quantity),
83
- price: parseInt(data?.price),
84
- billableService: getBillableServiceUuid(billableServices, item.billableService),
85
- item: item?.item,
86
- };
87
-
88
- const previousLineitems = bill?.lineItems
89
- .filter((currItem) => currItem.uuid !== item?.uuid)
90
- .map((currItem) => ({
91
- ...currItem,
92
- billableService: getBillableServiceUuid(billableServices, item.billableService),
93
- }));
94
- const updatedLineItems = previousLineitems.concat(newItem);
95
-
96
- const payload = {
97
- cashPoint: bill.cashPointUuid,
98
- cashier: bill.cashier.uuid,
99
- lineItems: updatedLineItems,
100
- patient: bill.patientUuid,
101
- status: bill.status,
102
- uuid: bill.uuid,
103
- };
104
- updateBillItems(payload).then(
105
- (res) => {
106
- mutate((key) => typeof key === 'string' && key.startsWith(url), undefined, { revalidate: true });
107
- showSnackbar({
108
- title: t('billItems', 'Save Bill'),
109
- subtitle: 'Bill processing has been successful',
110
- kind: 'success',
111
- timeoutInMs: 3000,
112
- });
113
- closeModal();
114
- },
115
- (error) => {
116
- showSnackbar({ title: 'Bill processing error', kind: 'error', subtitle: error?.message });
117
- },
118
- );
119
- };
120
-
121
- if (Object.keys(bill)?.length === 0) {
122
- return <ModalHeader closeModal={closeModal} title={t('billLineItemEmpty', 'This bill has no line items')} />;
123
- }
124
-
125
- if (Object.keys(bill)?.length > 0) {
126
- return (
127
- <div>
128
- <Form onSubmit={handleSubmit(onSubmit, onError)}>
129
- <ModalHeader closeModal={closeModal} title={t('editBillLineItem', 'Edit bill line item?')} />
130
- <ModalBody>
131
- <div className={styles.modalBody}>
132
- <h5>
133
- {bill?.patientName} &nbsp; · &nbsp;{bill?.cashPointName} &nbsp; · &nbsp;{bill?.receiptNumber}&nbsp;
134
- </h5>
135
- </div>
136
- <section className={styles.section}>
137
- <p className={styles.label}>
138
- {t('item', 'Item')} : {item?.billableService ? item?.billableService : item?.item}
139
- </p>
140
- <p className={styles.label}>
141
- {t('currentPrice', 'Current price')} : {item?.price}
142
- </p>
143
- <p className={styles.label}>
144
- {t('status', 'status')} : {item?.paymentStatus}
145
- </p>
146
- <Controller
147
- name="quantity"
148
- control={control}
149
- render={({ field: { onChange, onBlur, value } }) => (
150
- <NumberInput
151
- label={t('quantity', 'Quantity')}
152
- id="quantityInput"
153
- min={0}
154
- max={100}
155
- value={value}
156
- onChange={onChange}
157
- className={styles.controlField}
158
- invalid={errors.quantity?.message}
159
- invalidText={errors.quantity?.message}
160
- />
161
- )}
162
- />
163
-
164
- <Controller
165
- name="price"
166
- control={control}
167
- render={({ field: { value } }) => (
168
- <TextInput
169
- id="priceInput"
170
- labelText={t('price', 'Unit Price')}
171
- value={value}
172
- readOnly={true}
173
- className={styles.controlField}
174
- helperText="This is the unit Price for this item."
175
- />
176
- )}
177
- />
178
-
179
- <p className={styles.label}>
180
- {t('total', 'Total')} : {total}{' '}
181
- </p>
182
-
183
- {showErrorNotification && (
184
- <Column className={styles.errorContainer}>
185
- <InlineNotification
186
- lowContrast
187
- title={t('error', 'Error')}
188
- subtitle={t('pleaseRequiredFields', 'Please fill all required fields') + '.'}
189
- onClose={() => setShowErrorNotification(false)}
190
- />
191
- </Column>
192
- )}
193
- </section>
194
- </ModalBody>
195
- <ModalFooter>
196
- <Button kind="secondary" onClick={closeModal}>
197
- {t('cancel', 'Cancel')}
198
- </Button>
199
- <Button disabled={isSubmitting} type="submit">
200
- <>
201
- {isSubmitting ? (
202
- <div className={styles.inline}>
203
- <InlineLoading
204
- status="active"
205
- iconDescription={t('submitting', 'Submitting')}
206
- description={t('submitting', 'Submitting...')}
207
- />
208
- </div>
209
- ) : (
210
- t('save', 'Save')
211
- )}
212
- </>
213
- </Button>
214
- </ModalFooter>
215
- </Form>
216
- </div>
217
- );
218
- }
219
- };
220
-
221
- export default ChangeStatus;
@@ -1,280 +0,0 @@
1
- import React, { useState, useEffect, useCallback } from 'react';
2
- import {
3
- Button,
4
- DataTable,
5
- TableContainer,
6
- Table,
7
- TableHead,
8
- TableRow,
9
- TableHeader,
10
- TableBody,
11
- TableCell,
12
- Modal,
13
- TextInput,
14
- OverflowMenu,
15
- OverflowMenuItem,
16
- } from '@carbon/react';
17
- import { Add } from '@carbon/react/icons';
18
- import { useTranslation } from 'react-i18next';
19
- import { useForm, Controller } from 'react-hook-form';
20
- import { z } from 'zod';
21
- import { zodResolver } from '@hookform/resolvers/zod';
22
- import { showSnackbar, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
23
- import { CardHeader } from '@openmrs/esm-patient-common-lib';
24
- import styles from './payment-modes-config.scss';
25
-
26
- // Validation schema
27
- const paymentModeSchema = z.object({
28
- name: z.string().min(1, 'Payment Mode Name is required'),
29
- description: z.string().optional(),
30
- });
31
-
32
- type PaymentModeFormValues = z.infer<typeof paymentModeSchema>;
33
-
34
- const PaymentModesConfig: React.FC = () => {
35
- const { t } = useTranslation();
36
- const [paymentModes, setPaymentModes] = useState([]);
37
- const [isModalOpen, setIsModalOpen] = useState(false);
38
- const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
39
- const [selectedPaymentMode, setSelectedPaymentMode] = useState(null);
40
-
41
- const {
42
- control,
43
- handleSubmit,
44
- reset,
45
- formState: { errors, isSubmitting },
46
- } = useForm<PaymentModeFormValues>({
47
- resolver: zodResolver(paymentModeSchema),
48
- defaultValues: {
49
- name: '',
50
- description: '',
51
- },
52
- });
53
-
54
- const fetchPaymentModes = useCallback(async () => {
55
- try {
56
- const response = await openmrsFetch(`${restBaseUrl}/billing/paymentMode?v=full`);
57
- setPaymentModes(response.data.results || []);
58
- } catch (err) {
59
- showSnackbar({
60
- title: t('error', 'Error'),
61
- subtitle: t('errorFetchingPaymentModes', 'An error occurred while fetching payment modes.'),
62
- kind: 'error',
63
- isLowContrast: false,
64
- });
65
- }
66
- }, [t]);
67
-
68
- useEffect(() => {
69
- fetchPaymentModes();
70
- }, [fetchPaymentModes]);
71
-
72
- const onSubmit = async (data: PaymentModeFormValues) => {
73
- // Check for duplicate payment mode name
74
- const isDuplicate = paymentModes.some((mode) => mode.name.toLowerCase() === data.name.toLowerCase());
75
-
76
- if (isDuplicate) {
77
- showSnackbar({
78
- title: t('error', 'Error'),
79
- subtitle: t(
80
- 'duplicatePaymentModeError',
81
- 'A payment mode with the same name already exists. Please create another payment mode',
82
- ),
83
- kind: 'error',
84
- isLowContrast: false,
85
- });
86
- return;
87
- }
88
-
89
- try {
90
- const response = await openmrsFetch(`${restBaseUrl}/billing/paymentMode`, {
91
- method: 'POST',
92
- headers: {
93
- 'Content-Type': 'application/json',
94
- },
95
- body: JSON.stringify({
96
- name: data.name,
97
- description: data.description,
98
- }),
99
- });
100
-
101
- if (response.ok) {
102
- showSnackbar({
103
- title: t('success', 'Success'),
104
- subtitle: t('paymentModeSaved', 'Payment mode was successfully saved.'),
105
- kind: 'success',
106
- });
107
-
108
- setIsModalOpen(false);
109
- reset({ name: '', description: '' });
110
- fetchPaymentModes();
111
- } else {
112
- const errorData = response.data || {};
113
- showSnackbar({
114
- title: t('error', 'Error'),
115
- subtitle:
116
- errorData.message || t('errorSavingPaymentMode', 'An error occurred while saving the payment mode.'),
117
- kind: 'error',
118
- isLowContrast: false,
119
- });
120
- }
121
- } catch (err) {
122
- showSnackbar({
123
- title: t('error', 'Error'),
124
- subtitle: t('errorSavingPaymentMode', 'An error occurred while saving the payment mode.'),
125
- kind: 'error',
126
- isLowContrast: false,
127
- });
128
- }
129
- };
130
-
131
- const handleDelete = async () => {
132
- if (!selectedPaymentMode) return;
133
-
134
- try {
135
- await openmrsFetch(`${restBaseUrl}/billing/paymentMode/${selectedPaymentMode.uuid}`, {
136
- method: 'DELETE',
137
- });
138
-
139
- showSnackbar({
140
- title: t('success', 'Success'),
141
- subtitle: t('paymentModeDeleted', 'Payment mode was successfully deleted.'),
142
- kind: 'success',
143
- });
144
-
145
- setIsDeleteModalOpen(false);
146
- setSelectedPaymentMode(null);
147
- fetchPaymentModes();
148
- } catch (err) {
149
- showSnackbar({
150
- title: t('error', 'Error'),
151
- subtitle: t('errorDeletingPaymentMode', 'An error occurred while deleting the payment mode.'),
152
- kind: 'error',
153
- isLowContrast: false,
154
- });
155
- }
156
- };
157
-
158
- const rowData = paymentModes.map((mode) => ({
159
- id: mode.uuid,
160
- name: mode.name,
161
- description: mode.description || '--',
162
- }));
163
-
164
- const headerData = [
165
- { key: 'name', header: t('name', 'Name') },
166
- { key: 'description', header: t('description', 'Description') },
167
- { key: 'actions', header: t('actions', 'Actions') },
168
- ];
169
-
170
- return (
171
- <div className={styles.container}>
172
- <div className={styles.card}>
173
- <CardHeader title={t('paymentModeHistory', 'Payment Mode History')}>
174
- <Button renderIcon={Add} onClick={() => setIsModalOpen(true)} kind="ghost">
175
- {t('addPaymentMode', 'Add New Payment Mode')}
176
- </Button>
177
- </CardHeader>
178
- <div className={styles.historyContainer}>
179
- <DataTable rows={rowData} headers={headerData} isSortable size="lg">
180
- {({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => (
181
- <TableContainer>
182
- <Table className={styles.table} {...getTableProps()}>
183
- <TableHead>
184
- <TableRow>
185
- {headers.map((header) => (
186
- <TableHeader key={header.key} {...getHeaderProps({ header })}>
187
- {header.header}
188
- </TableHeader>
189
- ))}
190
- </TableRow>
191
- </TableHead>
192
- <TableBody>
193
- {rows.map((row) => (
194
- <TableRow key={row.id} {...getRowProps({ row })}>
195
- {row.cells.map((cell) =>
196
- cell.info.header !== 'actions' ? (
197
- <TableCell key={cell.id}>{cell.value}</TableCell>
198
- ) : (
199
- <TableCell key={cell.id}>
200
- <OverflowMenu>
201
- <OverflowMenuItem
202
- itemText={t('delete', 'Delete')}
203
- onClick={() => {
204
- const selected = paymentModes.find((p) => p.uuid === row.id);
205
- setSelectedPaymentMode(selected);
206
- setIsDeleteModalOpen(true);
207
- }}
208
- />
209
- </OverflowMenu>
210
- </TableCell>
211
- ),
212
- )}
213
- </TableRow>
214
- ))}
215
- </TableBody>
216
- </Table>
217
- </TableContainer>
218
- )}
219
- </DataTable>
220
- </div>
221
- </div>
222
-
223
- {/* Modal for Adding New Payment Mode */}
224
- <Modal
225
- open={isModalOpen}
226
- modalHeading={t('addPaymentMode', 'Add Payment Mode')}
227
- onRequestClose={() => setIsModalOpen(false)}
228
- onRequestSubmit={handleSubmit(onSubmit)}
229
- primaryButtonText={t('save', 'Save')}
230
- secondaryButtonText={t('cancel', 'Cancel')}
231
- isPrimaryButtonDisabled={isSubmitting}>
232
- <form>
233
- <Controller
234
- name="name"
235
- control={control}
236
- render={({ field }) => (
237
- <TextInput
238
- id="payment-mode-name"
239
- labelText={t('paymentModeName', 'Payment Mode Name')}
240
- placeholder={t('paymentModeNamePlaceholder', 'e.g., Cash, Credit Card')}
241
- invalid={!!errors.name}
242
- invalidText={errors.name?.message}
243
- {...field}
244
- />
245
- )}
246
- />
247
- <Controller
248
- name="description"
249
- control={control}
250
- render={({ field }) => (
251
- <TextInput
252
- id="payment-mode-description"
253
- labelText={t('description', 'Description')}
254
- placeholder={t('descriptionPlaceholder', 'e.g., Used for all cash transactions')}
255
- invalid={!!errors.description}
256
- invalidText={errors.description?.message}
257
- {...field}
258
- />
259
- )}
260
- />
261
- </form>
262
- </Modal>
263
-
264
- {/* Modal for Deleting Payment Mode */}
265
- <Modal
266
- open={isDeleteModalOpen}
267
- modalHeading={t('deletePaymentMode', 'Delete Payment Mode')}
268
- onRequestClose={() => setIsDeleteModalOpen(false)}
269
- onRequestSubmit={handleDelete}
270
- primaryButtonText={t('delete', 'Delete')}
271
- secondaryButtonText={t('cancel', 'Cancel')}
272
- primaryButtonDanger
273
- danger>
274
- <p>{t('confirmDeleteMessage', 'Are you sure you want to delete this payment mode? Proceed cautiously.')}</p>
275
- </Modal>
276
- </div>
277
- );
278
- };
279
-
280
- export default PaymentModesConfig;
@@ -1,121 +0,0 @@
1
- import React from 'react';
2
- import userEvent from '@testing-library/user-event';
3
- import { render, screen } from '@testing-library/react';
4
- import { useVisit, useConfig, navigate } from '@openmrs/esm-framework';
5
- import { useBillableServices } from '../../billable-services/billable-service.resource';
6
- import { type MappedBill, type LineItem } from '../../types';
7
- import Payments from './payments.component';
8
-
9
- // Add this mock for currency formatting
10
- const mockFormatToParts = jest.fn().mockReturnValue([{ type: 'integer', value: '1000' }]);
11
- const mockFormat = jest.fn().mockReturnValue('$1000.00');
12
- global.Intl.NumberFormat = jest.fn().mockImplementation(() => ({
13
- formatToParts: mockFormatToParts,
14
- format: mockFormat,
15
- })) as any;
16
- global.Intl.NumberFormat.supportedLocalesOf = jest.fn().mockReturnValue(['en-US']);
17
-
18
- jest.mock('../../billing.resource', () => ({
19
- processBillPayment: jest.fn(),
20
- }));
21
-
22
- jest.mock('../../billable-services/billable-service.resource', () => ({
23
- useBillableServices: jest.fn(),
24
- }));
25
-
26
- describe('Payments', () => {
27
- const mockBill: MappedBill = {
28
- uuid: 'bill-uuid',
29
- id: 1,
30
- patientUuid: 'patient-uuid',
31
- patientName: 'John Doe',
32
- cashPointUuid: 'cash-point-uuid',
33
- cashPointName: 'Main Cash Point',
34
- cashPointLocation: 'Main Hospital',
35
- cashier: {
36
- uuid: 'provider-1',
37
- display: 'Jane Doe',
38
- links: [
39
- {
40
- rel: 'self',
41
- uri: 'http://example.com/provider/1',
42
- resourceAlias: 'Jane Doe',
43
- },
44
- ],
45
- },
46
- payments: [
47
- {
48
- uuid: 'payment-1',
49
- dateCreated: new Date('2023-09-01T12:00:00Z').getTime(),
50
- amountTendered: 100,
51
- amount: 80,
52
- instanceType: {
53
- uuid: 'instance-1',
54
- name: 'Credit Card',
55
- description: 'Credit Card payment',
56
- retired: false,
57
- },
58
- attributes: [],
59
- voided: false,
60
- resourceVersion: '1.0',
61
- },
62
- {
63
- uuid: 'payment-2',
64
- dateCreated: new Date('2023-09-05T14:00:00Z').getTime(),
65
- amountTendered: 200,
66
- amount: 180,
67
- instanceType: {
68
- uuid: 'instance-2',
69
- name: 'Cash',
70
- description: 'Cash payment',
71
- retired: false,
72
- },
73
- attributes: [],
74
- voided: false,
75
- resourceVersion: '1.0',
76
- },
77
- ],
78
- receiptNumber: '12345',
79
- status: 'PAID',
80
- identifier: 'invoice-123',
81
- dateCreated: '2023-09-01T12:00:00Z',
82
- lineItems: [],
83
- billingService: 'Billing Service',
84
- };
85
-
86
- const mockMutate = jest.fn();
87
- const mockSelectedLineItems: LineItem[] = [];
88
-
89
- beforeEach(() => {
90
- jest.clearAllMocks();
91
- (useVisit as jest.Mock).mockReturnValue({ currentVisit: null });
92
- (useConfig as jest.Mock).mockReturnValue({ defaultCurrency: 'USD' });
93
- (useBillableServices as jest.Mock).mockReturnValue({ billableServices: [], isLoading: false });
94
- });
95
-
96
- it('renders payment form and history', () => {
97
- render(<Payments bill={mockBill} mutate={mockMutate} selectedLineItems={mockSelectedLineItems} />);
98
- expect(screen.getByText('Payments')).toBeInTheDocument();
99
- expect(screen.getByText('Total Amount:')).toBeInTheDocument();
100
- expect(screen.getByText('Total Tendered:')).toBeInTheDocument();
101
- });
102
-
103
- it('calculates and displays correct amounts', () => {
104
- render(<Payments bill={mockBill} mutate={mockMutate} selectedLineItems={mockSelectedLineItems} />);
105
- const amountElements = screen.getAllByText('$1000.00');
106
- expect(amountElements[amountElements.length - 3]).toBeInTheDocument();
107
- expect(amountElements[amountElements.length - 2]).toBeInTheDocument();
108
- expect(amountElements[amountElements.length - 1]).toBeInTheDocument();
109
- });
110
-
111
- it('disables Process Payment button when form is invalid', () => {
112
- render(<Payments bill={mockBill} mutate={mockMutate} selectedLineItems={mockSelectedLineItems} />);
113
- expect(screen.getByText('Process Payment')).toBeDisabled();
114
- });
115
-
116
- it('navigates to billing dashboard when Discard is clicked', async () => {
117
- render(<Payments bill={mockBill} mutate={mockMutate} selectedLineItems={mockSelectedLineItems} />);
118
- await userEvent.click(screen.getByText('Discard'));
119
- expect(navigate).toHaveBeenCalled();
120
- });
121
- });