@openmrs/esm-billing-app 1.0.2-pre.657 → 1.0.2-pre.661

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.
@@ -24,7 +24,7 @@
24
24
  "auxiliaryFiles": [
25
25
  "590.js.map"
26
26
  ],
27
- "hash": "3c5ed56e493a8ddc",
27
+ "hash": "414e99f458bbc26a",
28
28
  "childrenByOrder": {}
29
29
  },
30
30
  {
@@ -330,7 +330,7 @@
330
330
  "auxiliaryFiles": [
331
331
  "2352.js.map"
332
332
  ],
333
- "hash": "efb884001d4b67ef",
333
+ "hash": "422ac1657e297fb7",
334
334
  "childrenByOrder": {}
335
335
  },
336
336
  {
@@ -379,7 +379,7 @@
379
379
  "auxiliaryFiles": [
380
380
  "2747.js.map"
381
381
  ],
382
- "hash": "d795cc85f8b0dbb4",
382
+ "hash": "c47b184600598dfd",
383
383
  "childrenByOrder": {}
384
384
  },
385
385
  {
@@ -571,9 +571,9 @@
571
571
  "initial": false,
572
572
  "entry": false,
573
573
  "recorded": false,
574
- "size": 7331,
574
+ "size": 7298,
575
575
  "sizes": {
576
- "javascript": 7331
576
+ "javascript": 7298
577
577
  },
578
578
  "names": [],
579
579
  "idHints": [],
@@ -585,7 +585,7 @@
585
585
  "4300.js"
586
586
  ],
587
587
  "auxiliaryFiles": [],
588
- "hash": "32beaf3352e19df1",
588
+ "hash": "a8ba7d5cab1cf5ae",
589
589
  "childrenByOrder": {}
590
590
  },
591
591
  {
@@ -675,7 +675,7 @@
675
675
  "auxiliaryFiles": [
676
676
  "4739.js.map"
677
677
  ],
678
- "hash": "442e36091b68bbb9",
678
+ "hash": "53528d1cc7772c75",
679
679
  "childrenByOrder": {}
680
680
  },
681
681
  {
@@ -1135,7 +1135,7 @@
1135
1135
  "auxiliaryFiles": [
1136
1136
  "7692.js.map"
1137
1137
  ],
1138
- "hash": "bb9f8a0c5a09d441",
1138
+ "hash": "7c2f626c815f585d",
1139
1139
  "childrenByOrder": {}
1140
1140
  },
1141
1141
  {
@@ -1236,9 +1236,9 @@
1236
1236
  "initial": false,
1237
1237
  "entry": false,
1238
1238
  "recorded": false,
1239
- "size": 1096884,
1239
+ "size": 1091132,
1240
1240
  "sizes": {
1241
- "javascript": 1096842,
1241
+ "javascript": 1091090,
1242
1242
  "consume-shared": 42
1243
1243
  },
1244
1244
  "names": [],
@@ -1252,7 +1252,7 @@
1252
1252
  "auxiliaryFiles": [
1253
1253
  "8638.js.map"
1254
1254
  ],
1255
- "hash": "17dbec089b5e8c96",
1255
+ "hash": "d7b8b1033eef7f90",
1256
1256
  "childrenByOrder": {}
1257
1257
  },
1258
1258
  {
@@ -1260,10 +1260,10 @@
1260
1260
  "initial": true,
1261
1261
  "entry": true,
1262
1262
  "recorded": false,
1263
- "size": 5117455,
1263
+ "size": 5111703,
1264
1264
  "sizes": {
1265
1265
  "consume-shared": 210,
1266
- "javascript": 5094803,
1266
+ "javascript": 5089051,
1267
1267
  "share-init": 336,
1268
1268
  "runtime": 22106
1269
1269
  },
@@ -1280,7 +1280,7 @@
1280
1280
  "auxiliaryFiles": [
1281
1281
  "main.js.map"
1282
1282
  ],
1283
- "hash": "0dfe3aad437c29d2",
1283
+ "hash": "fc6ad4df4d9516ac",
1284
1284
  "childrenByOrder": {}
1285
1285
  },
1286
1286
  {
package/dist/routes.json CHANGED
@@ -1 +1 @@
1
- {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":">=2.24.0","fhir2":">=1.2"},"pages":[{"component":"billableServicesHome","route":"billable-services"}],"extensions":[{"component":"billingDashboardLink","name":"billing-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"billing","title":"billing","slot":"billing-dashboard-slot"},"featureFlag":"billing"},{"component":"root","name":"billing-dashboard-root","slot":"billing-dashboard-slot"},{"name":"billing-patient-summary","component":"billingPatientSummary","slot":"patient-chart-billing-dashboard-slot","order":10,"meta":{"columnSpan":4}},{"name":"billing-summary-dashboard-link","component":"billingSummaryDashboardLink","slot":"patient-chart-dashboard-slot","order":11,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-billing-dashboard-slot","path":"Billing history"},"featureFlag":"billing"},{"name":"billable-services-app-menu-item","component":"billableServicesAppMenuItem","slot":"app-menu-item-slot","meta":{"name":"Billable Services"}},{"name":"billing-checkin-form","slot":"extra-visit-attribute-slot","component":"billingCheckInForm","featureFlag":"billing"},{"slot":"system-admin-page-card-link-slot","component":"billableServicesCardLink","name":"billable-services-admin-card-link"},{"name":"patient-banner-billing-tags","component":"visitAttributeTags","slot":"patient-banner-tags-slot","order":2},{"name":"billing-home-tiles-ext","slot":"billing-home-tiles-slot","component":"serviceMetrics"},{"name":"edit-bill-line-item-dialog","component":"editBillLineItemDialog","online":true,"offline":true}],"modals":[{"name":"require-billing-modal","component":"requirePaymentModal"}],"workspaces":[{"name":"billing-form-workspace","title":"billingForm","component":"billingFormWorkspace","type":"form"}],"featureFlags":[{"flagName":"billing","label":"Billing module","description":"This feature introduces navigation links on the patient chart and home page to allow accessing the billing module features"}],"version":"1.0.2-pre.657"}
1
+ {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":">=2.24.0","fhir2":">=1.2"},"pages":[{"component":"billableServicesHome","route":"billable-services"}],"extensions":[{"component":"billingDashboardLink","name":"billing-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"billing","title":"billing","slot":"billing-dashboard-slot"},"featureFlag":"billing"},{"component":"root","name":"billing-dashboard-root","slot":"billing-dashboard-slot"},{"name":"billing-patient-summary","component":"billingPatientSummary","slot":"patient-chart-billing-dashboard-slot","order":10,"meta":{"columnSpan":4}},{"name":"billing-summary-dashboard-link","component":"billingSummaryDashboardLink","slot":"patient-chart-dashboard-slot","order":11,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-billing-dashboard-slot","path":"Billing history"},"featureFlag":"billing"},{"name":"billable-services-app-menu-item","component":"billableServicesAppMenuItem","slot":"app-menu-item-slot","meta":{"name":"Billable Services"}},{"name":"billing-checkin-form","slot":"extra-visit-attribute-slot","component":"billingCheckInForm","featureFlag":"billing"},{"slot":"system-admin-page-card-link-slot","component":"billableServicesCardLink","name":"billable-services-admin-card-link"},{"name":"patient-banner-billing-tags","component":"visitAttributeTags","slot":"patient-banner-tags-slot","order":2},{"name":"billing-home-tiles-ext","slot":"billing-home-tiles-slot","component":"serviceMetrics"},{"name":"edit-bill-line-item-dialog","component":"editBillLineItemDialog","online":true,"offline":true}],"modals":[{"name":"require-billing-modal","component":"requirePaymentModal"}],"workspaces":[{"name":"billing-form-workspace","title":"billingForm","component":"billingFormWorkspace","type":"form"}],"featureFlags":[{"flagName":"billing","label":"Billing module","description":"This feature introduces navigation links on the patient chart and home page to allow accessing the billing module features"}],"version":"1.0.2-pre.661"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-billing-app",
3
- "version": "1.0.2-pre.657",
3
+ "version": "1.0.2-pre.661",
4
4
  "description": "O3 frontend module for handling billing concerns in healthcare settings",
5
5
  "browser": "dist/openmrs-esm-billing-app.js",
6
6
  "main": "src/index.ts",
@@ -155,7 +155,7 @@ const BillHistory: React.FC<BillHistoryProps> = ({ patientUuid }) => {
155
155
  {row.isExpanded ? (
156
156
  <TableExpandedRow className={styles.expandedRow} colSpan={headers.length + 1}>
157
157
  <div className={styles.container} key={i}>
158
- <InvoiceTable bill={currentBill} isSelectable={false} />
158
+ <InvoiceTable bill={currentBill} />
159
159
  </div>
160
160
  </TableExpandedRow>
161
161
  ) : (
@@ -1,4 +1,4 @@
1
- import React, { useMemo, useState, useEffect, useCallback } from 'react';
1
+ import React, { useMemo, useState, useCallback } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import fuzzy from 'fuzzy';
4
4
  import {
@@ -13,7 +13,6 @@ import {
13
13
  TableHead,
14
14
  TableHeader,
15
15
  TableRow,
16
- TableSelectRow,
17
16
  TableToolbarSearch,
18
17
  Tile,
19
18
  type DataTableRow,
@@ -22,33 +21,23 @@ import { Edit } from '@carbon/react/icons';
22
21
  import { isDesktop, showModal, useConfig, useDebounce, useLayoutType } from '@openmrs/esm-framework';
23
22
  import { type LineItem, type MappedBill } from '../types';
24
23
  import { convertToCurrency } from '../helpers';
24
+ import type { BillingConfig } from '../config-schema';
25
25
  import styles from './invoice-table.scss';
26
26
 
27
27
  type InvoiceTableProps = {
28
28
  bill: MappedBill;
29
- isSelectable?: boolean;
30
29
  isLoadingBill?: boolean;
31
- onSelectItem?: (selectedLineItems: LineItem[]) => void;
32
30
  };
33
31
 
34
- const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isSelectable = true, isLoadingBill, onSelectItem }) => {
32
+ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isLoadingBill }) => {
35
33
  const { t } = useTranslation();
36
- const { defaultCurrency, showEditBillButton } = useConfig();
34
+ const { defaultCurrency, showEditBillButton } = useConfig<BillingConfig>();
37
35
  const layout = useLayoutType();
38
36
  const lineItems = useMemo(() => bill?.lineItems ?? [], [bill?.lineItems]);
39
- const paidLineItems = useMemo(() => lineItems?.filter((item) => item.paymentStatus === 'PAID') ?? [], [lineItems]);
40
37
  const responsiveSize = isDesktop(layout) ? 'sm' : 'lg';
41
-
42
- const [selectedLineItems, setSelectedLineItems] = useState(paidLineItems ?? []);
43
38
  const [searchTerm, setSearchTerm] = useState('');
44
39
  const debouncedSearchTerm = useDebounce(searchTerm);
45
40
 
46
- useEffect(() => {
47
- if (onSelectItem) {
48
- onSelectItem(selectedLineItems);
49
- }
50
- }, [selectedLineItems, onSelectItem]);
51
-
52
41
  const filteredLineItems = useMemo(() => {
53
42
  if (!debouncedSearchTerm) {
54
43
  return lineItems;
@@ -135,23 +124,10 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isSelectable = true,
135
124
  );
136
125
  }
137
126
 
138
- const handleRowSelection = (row: typeof DataTableRow, checked: boolean) => {
139
- const matchingRow = filteredLineItems.find((item) => item.uuid === row.id);
140
- let newSelectedLineItems;
141
-
142
- if (checked) {
143
- newSelectedLineItems = [...selectedLineItems, matchingRow];
144
- } else {
145
- newSelectedLineItems = selectedLineItems.filter((item) => item.uuid !== row.id);
146
- }
147
- setSelectedLineItems(newSelectedLineItems);
148
- onSelectItem(newSelectedLineItems);
149
- };
150
-
151
127
  return (
152
128
  <>
153
- <DataTable headers={tableHeaders} isSortable rows={tableRows} size={responsiveSize} useZebraStyles>
154
- {({ rows, headers, getRowProps, getSelectionProps, getTableProps, getToolbarProps }) => (
129
+ <DataTable headers={tableHeaders} rows={tableRows} size={responsiveSize} useZebraStyles>
130
+ {({ rows, headers, getRowProps, getTableProps }) => (
155
131
  <TableContainer
156
132
  description={
157
133
  <span className={styles.tableDescription}>
@@ -172,7 +148,6 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isSelectable = true,
172
148
  className={`${styles.invoiceTable} billingTable`}>
173
149
  <TableHead>
174
150
  <TableRow>
175
- {rows.length > 1 && isSelectable ? <TableHeader /> : null}
176
151
  {headers.map((header) => (
177
152
  <TableHeader key={header.key}>{header.header}</TableHeader>
178
153
  ))}
@@ -186,18 +161,6 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isSelectable = true,
186
161
  {...getRowProps({
187
162
  row,
188
163
  })}>
189
- {rows.length > 1 && isSelectable && (
190
- <TableSelectRow
191
- aria-label="Select row"
192
- {...getSelectionProps({ row })}
193
- disabled={tableRows[index].status === 'PAID'}
194
- onChange={(checked: boolean) => handleRowSelection(row, checked)}
195
- checked={
196
- tableRows[index].status === 'PAID' ||
197
- Boolean(selectedLineItems?.find((item) => item?.uuid === row?.id))
198
- }
199
- />
200
- )}
201
164
  {row.cells.map((cell) => (
202
165
  <TableCell key={cell.id}>{cell.value}</TableCell>
203
166
  ))}
@@ -110,17 +110,6 @@ describe('InvoiceTable', () => {
110
110
  expect(screen.getByText('Item 2')).toBeInTheDocument();
111
111
  });
112
112
 
113
- it('correctly handles row selection', async () => {
114
- const user = userEvent.setup();
115
- const onSelectItem = jest.fn();
116
- render(<InvoiceTable bill={bill} onSelectItem={onSelectItem} />);
117
-
118
- const checkboxes = screen.getAllByLabelText('Select row');
119
- await user.click(checkboxes[0]);
120
-
121
- expect(onSelectItem).toHaveBeenCalledWith([bill.lineItems[0]]);
122
- });
123
-
124
113
  it('resets isRedirecting to false after timeout', async () => {
125
114
  const user = userEvent.setup();
126
115
  render(<InvoiceTable bill={bill} />);
@@ -12,7 +12,6 @@ import PrintableInvoice from './printable-invoice/printable-invoice.component';
12
12
  import { ErrorState } from '@openmrs/esm-patient-common-lib';
13
13
  import { convertToCurrency } from '../helpers';
14
14
  import { useBill, useDefaultFacility } from '../billing.resource';
15
- import { type LineItem } from '../types';
16
15
  import type { BillingConfig } from '../config-schema';
17
16
  import styles from './invoice.scss';
18
17
 
@@ -28,13 +27,9 @@ const Invoice: React.FC = () => {
28
27
  const { patient, isLoading: isLoadingPatient } = usePatient(patientUuid);
29
28
  const { bill, isLoading: isLoadingBill, error, mutate } = useBill(billUuid);
30
29
  const [isPrinting, setIsPrinting] = useState(false);
31
- const [selectedLineItems, setSelectedLineItems] = useState<LineItem[]>([]);
32
30
  const componentRef = useRef<HTMLDivElement>(null);
33
31
  const onBeforeGetContentResolve = useRef<(() => void) | null>(null);
34
32
  const { defaultCurrency } = useConfig<BillingConfig>();
35
- const handleSelectItem = (lineItems: LineItem[]) => {
36
- setSelectedLineItems(lineItems);
37
- };
38
33
 
39
34
  const handleAfterPrint = useCallback(() => {
40
35
  onBeforeGetContentResolve.current = null;
@@ -66,11 +61,6 @@ const Invoice: React.FC = () => {
66
61
  }
67
62
  }, [isPrinting]);
68
63
 
69
- useEffect(() => {
70
- const unPaidLineItems = bill?.lineItems?.filter((item) => item.paymentStatus === 'PENDING') ?? [];
71
- setSelectedLineItems(unPaidLineItems);
72
- }, [bill?.lineItems]);
73
-
74
64
  // Do not remove this comment. Adds the translation keys for the invoice details
75
65
  /**
76
66
  * t('totalAmount', 'Total Amount')
@@ -130,12 +120,14 @@ const Invoice: React.FC = () => {
130
120
  </div>
131
121
  </div>
132
122
 
133
- <InvoiceTable bill={bill} isLoadingBill={isLoadingBill} onSelectItem={handleSelectItem} />
134
- <Payments bill={bill} mutate={mutate} selectedLineItems={selectedLineItems} />
123
+ <InvoiceTable bill={bill} isLoadingBill={isLoadingBill} />
124
+ <Payments bill={bill} mutate={mutate} />
135
125
 
136
- <div className={styles.printContainer}>
137
- <PrintableInvoice bill={bill} patient={patient} defaultFacility={data} componentRef={componentRef} />
138
- </div>
126
+ {bill && patient && (
127
+ <div className={styles.printContainer}>
128
+ <PrintableInvoice bill={bill} patient={patient} defaultFacility={data} componentRef={componentRef} />
129
+ </div>
130
+ )}
139
131
  </div>
140
132
  );
141
133
  };
@@ -10,19 +10,12 @@ import styles from './payment-form.scss';
10
10
 
11
11
  type PaymentFormProps = {
12
12
  disablePayment: boolean;
13
- clientBalance: number;
14
- isSingleLineItemSelected: boolean;
15
13
  isSingleLineItem: boolean;
16
14
  };
17
15
 
18
16
  const DEFAULT_PAYMENT = { method: '', amount: 0, referenceCode: '' };
19
17
 
20
- const PaymentForm: React.FC<PaymentFormProps> = ({
21
- disablePayment,
22
- clientBalance,
23
- isSingleLineItemSelected,
24
- isSingleLineItem,
25
- }) => {
18
+ const PaymentForm: React.FC<PaymentFormProps> = ({ disablePayment, isSingleLineItem }) => {
26
19
  const { t } = useTranslation();
27
20
  const {
28
21
  control,
@@ -119,7 +112,7 @@ const PaymentForm: React.FC<PaymentFormProps> = ({
119
112
  </div>
120
113
  ))}
121
114
  <Button
122
- disabled={disablePayment || (!isSingleLineItem && !isSingleLineItemSelected)}
115
+ disabled={disablePayment}
123
116
  size="md"
124
117
  onClick={handleAppendPaymentMode}
125
118
  className={styles.paymentButtons}
@@ -31,12 +31,7 @@ describe('PaymentForm Component', () => {
31
31
 
32
32
  render(
33
33
  <Wrapper>
34
- <PaymentForm
35
- disablePayment={false}
36
- clientBalance={100}
37
- isSingleLineItemSelected={false}
38
- isSingleLineItem={false}
39
- />
34
+ <PaymentForm disablePayment={false} isSingleLineItem={false} />
40
35
  </Wrapper>,
41
36
  );
42
37
 
@@ -53,12 +48,7 @@ describe('PaymentForm Component', () => {
53
48
 
54
49
  render(
55
50
  <Wrapper>
56
- <PaymentForm
57
- disablePayment={false}
58
- clientBalance={100}
59
- isSingleLineItemSelected={false}
60
- isSingleLineItem={false}
61
- />
51
+ <PaymentForm disablePayment={false} isSingleLineItem={false} />
62
52
  </Wrapper>,
63
53
  );
64
54
 
@@ -75,12 +65,7 @@ describe('PaymentForm Component', () => {
75
65
 
76
66
  render(
77
67
  <Wrapper>
78
- <PaymentForm
79
- disablePayment={false}
80
- clientBalance={100}
81
- isSingleLineItemSelected={false}
82
- isSingleLineItem={true}
83
- />
68
+ <PaymentForm disablePayment={false} isSingleLineItem={true} />
84
69
  </Wrapper>,
85
70
  );
86
71
 
@@ -103,12 +88,7 @@ describe('PaymentForm Component', () => {
103
88
 
104
89
  render(
105
90
  <Wrapper>
106
- <PaymentForm
107
- disablePayment={false}
108
- clientBalance={100}
109
- isSingleLineItemSelected={true}
110
- isSingleLineItem={false}
111
- />
91
+ <PaymentForm disablePayment={false} isSingleLineItem={false} />
112
92
  </Wrapper>,
113
93
  );
114
94
 
@@ -128,12 +108,7 @@ describe('PaymentForm Component', () => {
128
108
 
129
109
  render(
130
110
  <Wrapper>
131
- <PaymentForm
132
- disablePayment={true}
133
- clientBalance={100}
134
- isSingleLineItemSelected={true}
135
- isSingleLineItem={false}
136
- />
111
+ <PaymentForm disablePayment={true} isSingleLineItem={false} />
137
112
  </Wrapper>,
138
113
  );
139
114
 
@@ -151,12 +126,7 @@ describe('PaymentForm Component', () => {
151
126
 
152
127
  render(
153
128
  <Wrapper>
154
- <PaymentForm
155
- disablePayment={false}
156
- clientBalance={100}
157
- isSingleLineItemSelected={true}
158
- isSingleLineItem={false}
159
- />
129
+ <PaymentForm disablePayment={false} isSingleLineItem={false} />
160
130
  </Wrapper>,
161
131
  );
162
132
 
@@ -6,20 +6,19 @@ import { zodResolver } from '@hookform/resolvers/zod';
6
6
  import { navigate, showSnackbar, useConfig, useVisit } from '@openmrs/esm-framework';
7
7
  import { Button } from '@carbon/react';
8
8
  import { CardHeader } from '@openmrs/esm-patient-common-lib';
9
- import { type LineItem, type MappedBill } from '../../types';
10
- import { convertToCurrency } from '../../helpers';
11
- import { createPaymentPayload } from './utils';
12
- import { processBillPayment } from '../../billing.resource';
13
9
  import { InvoiceBreakDown } from './invoice-breakdown/invoice-breakdown.component';
14
10
  import PaymentHistory from './payment-history/payment-history.component';
15
11
  import PaymentForm from './payment-form/payment-form.component';
12
+ import { convertToCurrency } from '../../helpers';
13
+ import { createPaymentPayload } from './utils';
14
+ import { processBillPayment } from '../../billing.resource';
15
+ import { useBillableServices } from '../../billable-services/billable-service.resource';
16
16
  import { updateBillVisitAttribute } from './payment.resource';
17
+ import { type MappedBill } from '../../types';
17
18
  import styles from './payments.scss';
18
- import { useBillableServices } from '../../billable-services/billable-service.resource';
19
19
 
20
20
  type PaymentProps = {
21
21
  bill: MappedBill;
22
- selectedLineItems: Array<LineItem>;
23
22
  mutate: () => void;
24
23
  };
25
24
 
@@ -29,7 +28,7 @@ export type PaymentFormValue = {
29
28
  payment: Array<Payment>;
30
29
  };
31
30
 
32
- const Payments: React.FC<PaymentProps> = ({ bill, mutate, selectedLineItems }) => {
31
+ const Payments: React.FC<PaymentProps> = ({ bill, mutate }) => {
33
32
  const { t } = useTranslation();
34
33
  const { billableServices, isLoading, isValidating, error } = useBillableServices();
35
34
  const paymentSchema = z.object({
@@ -54,25 +53,23 @@ const Payments: React.FC<PaymentProps> = ({ bill, mutate, selectedLineItems }) =
54
53
  control: methods.control,
55
54
  });
56
55
 
57
- const selectedLineItemsTotal = selectedLineItems.reduce((total, item) => total + item.price * item.quantity, 0);
58
- const totalAmountTendered = formValues?.reduce((curr: number, prev) => curr + Number(prev.amount) ?? 0, 0) ?? 0;
59
- const amountDue = bill ? bill.totalAmount - selectedLineItemsTotal : 0;
60
- const clientBalance = bill ? bill.totalAmount - (bill.tenderedAmount + totalAmountTendered) : 0;
61
-
62
56
  const handleNavigateToBillingDashboard = () =>
63
57
  navigate({
64
58
  to: window.getOpenmrsSpaBase() + 'home/billing',
65
59
  });
66
60
 
61
+ const amountDue = bill.totalAmount - bill.tenderedAmount;
62
+
67
63
  const handleProcessPayment = () => {
68
64
  if (bill) {
65
+ const amountBeingTendered = formValues?.reduce((acc, curr) => acc + Number(curr.amount), 0);
66
+ const amountRemaining = amountDue - amountBeingTendered;
69
67
  const paymentPayload = createPaymentPayload(
70
68
  bill,
71
69
  bill?.patientUuid,
72
70
  formValues,
73
- amountDue,
71
+ amountRemaining,
74
72
  billableServices,
75
- selectedLineItems,
76
73
  );
77
74
  paymentPayload.payments.forEach((payment) => {
78
75
  payment.dateCreated = new Date(payment.dateCreated);
@@ -103,9 +100,6 @@ const Payments: React.FC<PaymentProps> = ({ bill, mutate, selectedLineItems }) =
103
100
  return null;
104
101
  }
105
102
 
106
- const amountDueLabel = selectedLineItems.length ? t('amountDue', 'Amount Due') : t('clientBalance', 'Client Balance');
107
- const amountDueValue = selectedLineItems.length ? amountDue : clientBalance;
108
-
109
103
  return (
110
104
  <FormProvider {...methods}>
111
105
  <div className={styles.wrapper}>
@@ -115,12 +109,7 @@ const Payments: React.FC<PaymentProps> = ({ bill, mutate, selectedLineItems }) =
115
109
  </CardHeader>
116
110
  <div>
117
111
  {bill && <PaymentHistory bill={bill} />}
118
- <PaymentForm
119
- disablePayment={clientBalance <= 0}
120
- clientBalance={clientBalance}
121
- isSingleLineItemSelected={selectedLineItems.length > 0}
122
- isSingleLineItem={bill.lineItems.length === 1}
123
- />
112
+ <PaymentForm disablePayment={amountDue <= 0} isSingleLineItem={bill.lineItems.length === 1} />
124
113
  </div>
125
114
  </div>
126
115
  <div className={styles.divider} />
@@ -131,13 +120,13 @@ const Payments: React.FC<PaymentProps> = ({ bill, mutate, selectedLineItems }) =
131
120
  />
132
121
  <InvoiceBreakDown
133
122
  label={t('totalTendered', 'Total Tendered')}
134
- value={convertToCurrency(bill?.tenderedAmount + totalAmountTendered, defaultCurrency)}
123
+ value={convertToCurrency(bill.tenderedAmount, defaultCurrency)}
135
124
  />
136
125
  <InvoiceBreakDown label={t('discount', 'Discount')} value={'--'} />
137
126
  <InvoiceBreakDown
138
- hasBalance={amountDueValue < 0}
139
- label={amountDueLabel}
140
- value={convertToCurrency(amountDueValue < 0 ? -amountDueValue : amountDueValue, defaultCurrency)}
127
+ hasBalance={amountDue < 0}
128
+ label={t('amountDue', 'Amount Due')}
129
+ value={convertToCurrency(amountDue < 0 ? -amountDue : amountDue, defaultCurrency)}
141
130
  />
142
131
  <div className={styles.processPayments}>
143
132
  <Button onClick={handleNavigateToBillingDashboard} kind="secondary">
@@ -108,14 +108,14 @@ describe('Payments', () => {
108
108
  });
109
109
 
110
110
  it('renders payment form and history', () => {
111
- render(<Payments bill={mockBill} mutate={mockMutate} selectedLineItems={mockSelectedLineItems} />);
111
+ render(<Payments bill={mockBill} mutate={mockMutate} />);
112
112
  expect(screen.getByText('Payments')).toBeInTheDocument();
113
113
  expect(screen.getByText('Total Amount:')).toBeInTheDocument();
114
114
  expect(screen.getByText('Total Tendered:')).toBeInTheDocument();
115
115
  });
116
116
 
117
117
  it('calculates and displays correct amounts', () => {
118
- render(<Payments bill={mockBill} mutate={mockMutate} selectedLineItems={mockSelectedLineItems} />);
118
+ render(<Payments bill={mockBill} mutate={mockMutate} />);
119
119
  const amountElements = screen.getAllByText('$1000.00');
120
120
  expect(amountElements[amountElements.length - 3]).toBeInTheDocument();
121
121
  expect(amountElements[amountElements.length - 2]).toBeInTheDocument();
@@ -123,12 +123,12 @@ describe('Payments', () => {
123
123
  });
124
124
 
125
125
  it('disables Process Payment button when form is invalid', () => {
126
- render(<Payments bill={mockBill} mutate={mockMutate} selectedLineItems={mockSelectedLineItems} />);
126
+ render(<Payments bill={mockBill} mutate={mockMutate} />);
127
127
  expect(screen.getByText('Process Payment')).toBeDisabled();
128
128
  });
129
129
 
130
130
  it('navigates to billing dashboard when Discard is clicked', async () => {
131
- render(<Payments bill={mockBill} mutate={mockMutate} selectedLineItems={mockSelectedLineItems} />);
131
+ render(<Payments bill={mockBill} mutate={mockMutate} />);
132
132
  await userEvent.click(screen.getByText('Discard'));
133
133
  expect(navigate).toHaveBeenCalled();
134
134
  });
@@ -1,24 +1,15 @@
1
- import { type LineItem, type MappedBill } from '../../types';
1
+ import { type MappedBill } from '../../types';
2
2
  import { type Payment } from './payments.component';
3
3
 
4
- const hasLineItem = (lineItems: Array<LineItem>, item: LineItem) => {
5
- if (lineItems?.length === 0) {
6
- return false;
7
- }
8
- const foundItem = lineItems.find((lineItem) => lineItem.uuid === item.uuid);
9
- return Boolean(foundItem);
10
- };
11
-
12
4
  export const createPaymentPayload = (
13
5
  bill: MappedBill,
14
6
  patientUuid: string,
15
7
  formValues: Array<Payment>,
16
8
  amountDue: number,
17
9
  billableServices: Array<any>,
18
- selectedLineItems: Array<LineItem>,
19
10
  ) => {
20
11
  const { cashier } = bill;
21
- const totalAmount = bill?.totalAmount;
12
+ const totalAmount = bill.totalAmount ?? 0;
22
13
  const paymentStatus = amountDue <= 0 ? 'PAID' : 'PENDING';
23
14
  const previousPayments = bill?.payments.map((payment) => ({
24
15
  amount: payment.amount,
@@ -37,30 +28,21 @@ export const createPaymentPayload = (
37
28
  }));
38
29
 
39
30
  const updatedPayments = [...newPayments, ...previousPayments];
40
- const totalAmountRendered = updatedPayments.reduce((acc, payment) => acc + payment.amountTendered, 0);
41
31
 
42
32
  const updatedLineItems = bill?.lineItems.map((lineItem) => ({
43
33
  ...lineItem,
44
34
  billableService: getBillableServiceUuid(billableServices, lineItem.billableService),
45
35
  item: processBillItem?.(lineItem),
46
- paymentStatus:
47
- bill?.lineItems.length > 1
48
- ? hasLineItem(selectedLineItems ?? [], lineItem) && totalAmountRendered >= lineItem.price * lineItem.quantity
49
- ? 'PAID'
50
- : 'PENDING'
51
- : paymentStatus,
36
+ paymentStatus: lineItem.paymentStatus === 'PAID' ? 'PAID' : paymentStatus,
52
37
  }));
53
38
 
54
- const allItemsBillPaymentStatus =
55
- updatedLineItems.filter((item) => item.paymentStatus === 'PENDING').length === 0 ? 'PAID' : 'PENDING';
56
-
57
39
  const processedPayment = {
58
40
  cashPoint: bill?.cashPointUuid,
59
41
  cashier: cashier.uuid,
60
42
  lineItems: updatedLineItems,
61
43
  payments: [...updatedPayments],
62
44
  patient: patientUuid,
63
- status: selectedLineItems?.length > 0 ? allItemsBillPaymentStatus : paymentStatus,
45
+ status: paymentStatus,
64
46
  };
65
47
 
66
48
  return processedPayment;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { type PatientDetails } from '../../types';
3
- import { type SessionLocation, useConfig } from '@openmrs/esm-framework';
3
+ import { type SessionLocation, useConfig, interpolateUrl } from '@openmrs/esm-framework';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import type { BillingConfig } from '../../config-schema';
6
6
  import styles from './printable-invoice-header.scss';
@@ -20,7 +20,7 @@ const PrintableInvoiceHeader: React.FC<PrintableInvoiceHeaderProps> = ({ patient
20
20
  <div className={styles.printableHeader}>
21
21
  <p className={styles.heading}>{t('invoice', 'Invoice')}</p>
22
22
  {logo?.src && !isEmpty(logo.src) ? (
23
- <img className={styles.img} src={logo.src} alt={logo.alt} />
23
+ <img className={styles.img} src={interpolateUrl(logo.src)} alt={logo.alt} />
24
24
  ) : logo?.alt && !isEmpty(logo.alt) ? (
25
25
  logo.alt
26
26
  ) : (
@@ -39,7 +39,7 @@ const PrintableInvoice: React.FC<PrintableInvoiceProps> = ({ bill, patient, comp
39
39
  bill?.lineItems?.map((item) => {
40
40
  return {
41
41
  id: `${item.uuid}`,
42
- billItem: item.item,
42
+ billItem: item.billableService ?? item.item,
43
43
  quantity: item.quantity,
44
44
  price: item.price,
45
45
  total: item.price * item.quantity,
@@ -54,7 +54,6 @@
54
54
  "cashPointUuidPlaceholder": "Enter UUID",
55
55
  "checkFilters": "Check the filters above",
56
56
  "clearSearchInput": "Clear search input",
57
- "clientBalance": "Client Balance",
58
57
  "confirmDeleteMessage": "Are you sure you want to delete this payment mode? Proceed cautiously.",
59
58
  "createdSuccessfully": "Billable service created successfully",
60
59
  "currentPrice": "Current price",