@kenyaemr/esm-billing-app 5.4.1-pre.2108 → 5.4.1-pre.2113

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.
package/dist/routes.json CHANGED
@@ -1 +1 @@
1
- {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"kenyaemr":"^19.0.0"},"pages":[{"component":"billableServicesHome","route":"billable-services"},{"component":"requirePaymentModal","routeRegex":"^patient/.+/chart","online":true,"offline":false}],"extensions":[{"component":"billingDashboardLink","name":"billing-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"billing","title":"billing","slot":"billing-dashboard-slot"}},{"component":"benefitsPackageDashboardLink","name":"benefits-package-dashboard-link","slot":"patient-chart-dashboard-slot","meta":{"name":"benefits-package","slot":"patient-chart-benefits-dashboard-slot","path":"insurance-benefits","columns":1,"columnSpan":1},"featureFlag":"healthInformationExchange"},{"component":"benefitsPackage","name":"benefits-package","slot":"patient-chart-benefits-dashboard-slot"},{"component":"root","name":"billing-dashboard-root","slot":"billing-dashboard-slot"},{"component":"benefitsEligibilyRequestForm","name":"benefits-eligibility-request-form"},{"component":"benefitsPreAuthForm","name":"benefits-pre-auth-form"},{"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","layoutMode":"anchored"}},{"name":"billing-check-in-form","slot":"extra-visit-attribute-slot","component":"billingCheckInForm"},{"name":"require-billing-modal","component":"requirePaymentModal"},{"name":"patient-banner-billing-tags","component":"visitAttributeTags","slot":"patient-banner-tags-slot","order":2},{"name":"initiate-payment-modal","component":"initiatePaymentDialog"},{"name":"delete-billableservice-modal","component":"deleteBillableServiceModal"},{"name":"refund-bill-modal","component":"refundBillModal"},{"name":"delete-bill-modal","component":"deleteBillModal"},{"name":"lab-order-billable-item","component":"labOrder","slot":"top-of-lab-order-form-slot"},{"name":"procedure-order-billable-item","component":"procedureOrder","slot":"top-of-procedure-order-form-slot"},{"name":"imaging-order-billable-item","component":"imagingOrder","slot":"top-of-imaging-order-form-slot"},{"name":"price-info-order","component":"priceInfoOrder"},{"name":"drug-order-billable-item","component":"drugOrder","slot":"medication-info-slot"},{"name":"order-action-button","component":"orderActionButton","slots":["prescription-action-button-slot","imaging-orders-action","procedure-orders-action","tests-ordered-actions-slot"],"order":0},{"component":"billingOverviewLink","name":"billing-overview-link","order":0,"slot":"billing-dashboard-link-slot"},{"component":"paymentHistoryLink","name":"payment-history-link","slot":"billing-dashboard-link-slot"},{"component":"paymentPointsLink","name":"payment-points-link","slot":"billing-dashboard-link-slot"},{"component":"paymentModesLink","name":"payment-modes-link","slot":"billing-dashboard-link-slot"},{"component":"billManagerLink","name":"bill-manager-link","slot":"billing-dashboard-link-slot"},{"component":"chargeableItemsLink","name":"chargeable-items-link","slot":"billing-dashboard-link-slot"},{"component":"billableExemptionsLink","name":"billable-exemptions-link","slot":"billing-dashboard-link-slot"},{"component":"claimsManagementSideNavGroup","name":"claims-management-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"claims-management","title":"Claims management Overview","slot":"case-management-slot"},"featureFlag":"healthInformationExchange"},{"component":"claimsManagementOverviewDashboardLink","name":"claims-management-overview-link","order":0,"slot":"claims-management-dashboard-link-slot"},{"component":"preAuthRequestsDashboardLink","name":"preauthrequest-overview-link","slot":"claims-management-dashboard-link-slot"},{"component":"claimsOverview","name":"claims-overview-dashboard-link","slot":"claims-management-overview-slot"},{"component":"waiveBillActionButton","name":"waive-bill-action-button","slot":"bill-actions-slot"},{"component":"deleteBillActionButton","name":"delete-bill-action-button","slot":"bill-actions-slot"},{"component":"refundLineItem","name":"refund-line-item","slot":"bill-actions-overflow-menu-slot"},{"name":"edit-line-item","component":"editLineItem","slot":"bill-actions-overflow-menu-slot"},{"name":"cancel-line-item","component":"cancelLineItem","slot":"bill-actions-overflow-menu-slot"}],"workspaces":[{"name":"create-bill-workspace","component":"createBillWorkspace","title":"Create Bill Workspace","type":"other-form"},{"name":"waive-bill-form","component":"waiveBillForm","title":"Waive Bill Form","type":"other-form"},{"name":"edit-bill-form","component":"editBillForm","title":"Edit Bill Form","type":"other-form"},{"name":"billable-service-form","component":"addServiceForm","title":"Create Charge Item Form","type":"other-form"},{"name":"commodity-form","component":"addCommodityForm","title":"Create Charge Item Form","type":"other-form"},{"name":"billing-form","component":"billingForm","title":"Billing Form","type":"other-form","width":"extra-wide"},{"name":"payment-mode-workspace","component":"paymentModeWorkspace","title":"Payment Mode Workspace","type":"other-form"},{"name":"cancel-bill-workspace","component":"cancelBillWorkspace","title":"Cancel Bill Workspace","type":"other-form"},{"name":"bill-deposit-workspace","component":"billDepositWorkspace","title":"Bill Deposit Workspace","type":"other-form"}],"modals":[{"name":"create-payment-point","component":"createPaymentPoint"},{"name":"clock-out-modal","component":"clockOut"},{"name":"bulk-import-billable-services-modal","component":"bulkImportBillableServicesModal"},{"name":"delete-payment-mode-modal","component":"deletePaymentModeModal"},{"name":"manage-claim-request-modal","component":"manageClaimRequestModal"},{"name":"paid-bill-receipt-print-preview-modal","component":"paidBillReceiptPrintPreviewModal"},{"name":"clock-in-modal","component":"clockIn"},{"name":"create-bill-item-modal","component":"createBillItemModal"}],"version":"5.4.1-pre.2108"}
1
+ {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"kenyaemr":"^19.0.0"},"pages":[{"component":"billableServicesHome","route":"billable-services"},{"component":"requirePaymentModal","routeRegex":"^patient/.+/chart","online":true,"offline":false}],"extensions":[{"component":"billingDashboardLink","name":"billing-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"billing","title":"billing","slot":"billing-dashboard-slot"}},{"component":"benefitsPackageDashboardLink","name":"benefits-package-dashboard-link","slot":"patient-chart-dashboard-slot","meta":{"name":"benefits-package","slot":"patient-chart-benefits-dashboard-slot","path":"insurance-benefits","columns":1,"columnSpan":1},"featureFlag":"healthInformationExchange"},{"component":"benefitsPackage","name":"benefits-package","slot":"patient-chart-benefits-dashboard-slot"},{"component":"root","name":"billing-dashboard-root","slot":"billing-dashboard-slot"},{"component":"benefitsEligibilyRequestForm","name":"benefits-eligibility-request-form"},{"component":"benefitsPreAuthForm","name":"benefits-pre-auth-form"},{"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","layoutMode":"anchored"}},{"name":"billing-check-in-form","slot":"extra-visit-attribute-slot","component":"billingCheckInForm"},{"name":"require-billing-modal","component":"requirePaymentModal"},{"name":"patient-banner-billing-tags","component":"visitAttributeTags","slot":"patient-banner-tags-slot","order":2},{"name":"initiate-payment-modal","component":"initiatePaymentDialog"},{"name":"delete-billableservice-modal","component":"deleteBillableServiceModal"},{"name":"refund-bill-modal","component":"refundBillModal"},{"name":"delete-bill-modal","component":"deleteBillModal"},{"name":"lab-order-billable-item","component":"labOrder","slot":"top-of-lab-order-form-slot"},{"name":"procedure-order-billable-item","component":"procedureOrder","slot":"top-of-procedure-order-form-slot"},{"name":"imaging-order-billable-item","component":"imagingOrder","slot":"top-of-imaging-order-form-slot"},{"name":"price-info-order","component":"priceInfoOrder"},{"name":"drug-order-billable-item","component":"drugOrder","slot":"medication-info-slot"},{"name":"order-action-button","component":"orderActionButton","slots":["prescription-action-button-slot","imaging-orders-action","procedure-orders-action","tests-ordered-actions-slot"],"order":0},{"component":"billingOverviewLink","name":"billing-overview-link","order":0,"slot":"billing-dashboard-link-slot"},{"component":"paymentHistoryLink","name":"payment-history-link","slot":"billing-dashboard-link-slot"},{"component":"paymentPointsLink","name":"payment-points-link","slot":"billing-dashboard-link-slot"},{"component":"paymentModesLink","name":"payment-modes-link","slot":"billing-dashboard-link-slot"},{"component":"billManagerLink","name":"bill-manager-link","slot":"billing-dashboard-link-slot"},{"component":"chargeableItemsLink","name":"chargeable-items-link","slot":"billing-dashboard-link-slot"},{"component":"billableExemptionsLink","name":"billable-exemptions-link","slot":"billing-dashboard-link-slot"},{"component":"claimsManagementSideNavGroup","name":"claims-management-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"claims-management","title":"Claims management Overview","slot":"case-management-slot"},"featureFlag":"healthInformationExchange"},{"component":"claimsManagementOverviewDashboardLink","name":"claims-management-overview-link","order":0,"slot":"claims-management-dashboard-link-slot"},{"component":"preAuthRequestsDashboardLink","name":"preauthrequest-overview-link","slot":"claims-management-dashboard-link-slot"},{"component":"claimsOverview","name":"claims-overview-dashboard-link","slot":"claims-management-overview-slot"},{"component":"waiveBillActionButton","name":"waive-bill-action-button","slot":"bill-actions-slot"},{"component":"deleteBillActionButton","name":"delete-bill-action-button","slot":"bill-actions-slot"},{"component":"refundLineItem","name":"refund-line-item","slot":"bill-actions-overflow-menu-slot"},{"name":"edit-line-item","component":"editLineItem","slot":"bill-actions-overflow-menu-slot"},{"name":"cancel-line-item","component":"cancelLineItem","slot":"bill-actions-overflow-menu-slot"}],"workspaces":[{"name":"create-bill-workspace","component":"createBillWorkspace","title":"Create Bill Workspace","type":"other-form"},{"name":"waive-bill-form","component":"waiveBillForm","title":"Waive Bill Form","type":"other-form"},{"name":"edit-bill-form","component":"editBillForm","title":"Edit Bill Form","type":"other-form"},{"name":"billable-service-form","component":"addServiceForm","title":"Create Charge Item Form","type":"other-form"},{"name":"commodity-form","component":"addCommodityForm","title":"Create Charge Item Form","type":"other-form"},{"name":"billing-form","component":"billingForm","title":"Billing Form","type":"other-form","width":"extra-wide"},{"name":"payment-mode-workspace","component":"paymentModeWorkspace","title":"Payment Mode Workspace","type":"other-form"},{"name":"cancel-bill-workspace","component":"cancelBillWorkspace","title":"Cancel Bill Workspace","type":"other-form"},{"name":"bill-deposit-workspace","component":"billDepositWorkspace","title":"Bill Deposit Workspace","type":"other-form"}],"modals":[{"name":"create-payment-point","component":"createPaymentPoint"},{"name":"clock-out-modal","component":"clockOut"},{"name":"bulk-import-billable-services-modal","component":"bulkImportBillableServicesModal"},{"name":"delete-payment-mode-modal","component":"deletePaymentModeModal"},{"name":"manage-claim-request-modal","component":"manageClaimRequestModal"},{"name":"paid-bill-receipt-print-preview-modal","component":"paidBillReceiptPrintPreviewModal"},{"name":"clock-in-modal","component":"clockIn"},{"name":"create-bill-item-modal","component":"createBillItemModal"}],"version":"5.4.1-pre.2113"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenyaemr/esm-billing-app",
3
- "version": "5.4.1-pre.2108",
3
+ "version": "5.4.1-pre.2113",
4
4
  "description": "Billing app for KenyaEMR",
5
5
  "browser": "dist/kenyaemr-esm-billing-app.js",
6
6
  "main": "src/index.ts",
@@ -34,6 +34,7 @@ type ClaimsTableProps = {
34
34
 
35
35
  const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, isLoadingBill, onSelectItem }) => {
36
36
  const { t } = useTranslation();
37
+ const { lineItems } = bill;
37
38
  const layout = useLayoutType();
38
39
  const responsiveSize = isDesktop(layout) ? 'sm' : 'lg';
39
40
  const [selectedLineItems, setSelectedLineItems] = useState<LineItem[]>([]);
@@ -42,33 +43,20 @@ const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, is
42
43
  const [currentPage, setCurrentPage] = useState(1);
43
44
  const [pageSize, setPageSize] = useState(10);
44
45
 
45
- // Filter line items that are paid and paid through Insurance
46
- const insurancePaidLineItems = useMemo(() => {
47
- return (bill.lineItems || []).filter((lineItem) => {
48
- // Check if payment status is PAID
49
- const isPaid = lineItem.paymentStatus === 'PAID';
50
-
51
- // Check if there's an Insurance payment for this line item
52
- const hasInsurancePayment = bill.payments?.some(
53
- (payment) => payment.billLineItem?.uuid === lineItem.uuid && payment.instanceType?.name === 'Insurance',
54
- );
55
-
56
- return isPaid && hasInsurancePayment;
57
- });
58
- }, [bill.lineItems, bill.payments]);
46
+ const paidLineItems = useMemo(() => (lineItems || []).filter((item) => item.paymentStatus === 'PAID'), [lineItems]);
59
47
 
60
48
  const filteredLineItems = useMemo(() => {
61
49
  if (!debouncedSearchTerm) {
62
- return insurancePaidLineItems;
50
+ return paidLineItems;
63
51
  }
64
52
 
65
53
  return fuzzy
66
- .filter(debouncedSearchTerm, insurancePaidLineItems, {
67
- extract: (lineItem: LineItem) => `${lineItem.item || lineItem.billableService}`,
54
+ .filter(debouncedSearchTerm, paidLineItems, {
55
+ extract: (lineItem: LineItem) => `${lineItem.item}`,
68
56
  })
69
57
  .sort((r1, r2) => r1.score - r2.score)
70
58
  .map((result) => result.original);
71
- }, [debouncedSearchTerm, insurancePaidLineItems]);
59
+ }, [debouncedSearchTerm, paidLineItems]);
72
60
 
73
61
  const paginatedLineItems = useMemo(() => {
74
62
  const startIndex = (currentPage - 1) * pageSize;
@@ -81,20 +69,11 @@ const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, is
81
69
  { header: t('serialNo', 'Serial No'), key: 'serialno' },
82
70
  { header: t('billItem', 'Bill Item'), key: 'inventoryname' },
83
71
  { header: t('status', 'Status'), key: 'status' },
84
- { header: t('paymentMethod', 'Payment Method'), key: 'paymentMethod' },
85
72
  { header: t('totalAmount', 'Total amount'), key: 'total' },
86
73
  { header: t('billCreationDate', 'Bill creation date'), key: 'dateofbillcreation' },
87
74
  ];
88
75
 
89
- const processBillItem = (item) => {
90
- const itemName = item?.item || item?.billableService;
91
- return itemName?.split(':')[1]?.trim() || itemName;
92
- };
93
-
94
- const getPaymentMethod = (lineItemUuid: string) => {
95
- const payment = bill.payments?.find((p) => p.billLineItem?.uuid === lineItemUuid);
96
- return payment?.instanceType?.name || 'Unknown';
97
- };
76
+ const processBillItem = (item) => (item?.item || item?.billableService)?.split(':')[1];
98
77
 
99
78
  const tableRows: Array<typeof DataTableRow> = useMemo(
100
79
  () =>
@@ -105,12 +84,11 @@ const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, is
105
84
  inventoryname: processBillItem(item),
106
85
  serialno: bill.receiptNumber,
107
86
  status: item.paymentStatus,
108
- paymentMethod: getPaymentMethod(item.uuid),
109
87
  total: item.price * item.quantity,
110
88
  dateofbillcreation: formatDate(new Date(bill.dateCreated), { mode: 'standard' }),
111
89
  };
112
90
  }) ?? [],
113
- [bill.dateCreated, bill.receiptNumber, bill.payments, paginatedLineItems],
91
+ [bill.dateCreated, bill.receiptNumber, paginatedLineItems],
114
92
  );
115
93
 
116
94
  if (isLoadingBill) {
@@ -148,10 +126,10 @@ const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, is
148
126
  className={styles.tableContainer}
149
127
  description={
150
128
  <span className={styles.tableDescription}>
151
- <span>{t('insurancePaidItems', 'Items paid through Insurance')}</span>
129
+ <span>{t('selectitemstobeclaimed', 'Select items that are to be included in the claims')}</span>
152
130
  </span>
153
131
  }
154
- title={t('claimableItems', 'Claimable Items')}>
132
+ title={t('lineItems', 'Line items')}>
155
133
  <div className={styles.toolbarWrapper}>
156
134
  <TableToolbar {...getToolbarProps()} className={styles.tableToolbar} size={responsiveSize}>
157
135
  <TableToolbarContent className={styles.headerContainer}>
@@ -165,7 +143,7 @@ const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, is
165
143
  </TableToolbarContent>
166
144
  </TableToolbar>
167
145
  </div>
168
- <Table {...getTableProps()} aria-label="insurance claim line items" className={styles.table}>
146
+ <Table {...getTableProps()} aria-label="claim line items" className={styles.table}>
169
147
  <TableHead>
170
148
  <TableRow>
171
149
  {isSelectable ? <TableHeader /> : null}
@@ -206,13 +184,14 @@ const ClaimsTable: React.FC<ClaimsTableProps> = ({ bill, isSelectable = true, is
206
184
  <Layer>
207
185
  <Tile className={styles.filterEmptyStateTile}>
208
186
  <p className={styles.filterEmptyStateContent}>
209
- {t('noInsurancePaidItems', 'No items paid through insurance found')}
187
+ {t('noMatchingItemsToDisplay', 'No matching items to display')}
210
188
  </p>
189
+ <p className={styles.filterEmptyStateHelper}>{t('checkFilters', 'Check the filters above')}</p>
211
190
  </Tile>
212
191
  </Layer>
213
192
  </div>
214
193
  )}
215
- {filteredLineItems.length > pageSize && (
194
+ {tableRows.length > pageSize && (
216
195
  <Pagination
217
196
  forwardText="Next page"
218
197
  backwardText="Previous page"
@@ -97,8 +97,8 @@ export const useInterventions = (filters: InterventionsFilter) => {
97
97
  const { isLoading, error, data } = useSWR<FetchResponse<{ results: Array<Intervention> }>>(url, openmrsFetch);
98
98
 
99
99
  // Mapper function to transform intervention data
100
- const mapper = ({ benefitCode, name, parentBenefitCode }: Intervention): SHAIntervention => ({
101
- interventionCode: benefitCode,
100
+ const mapper = ({ code, name, parentBenefitCode }: Intervention): SHAIntervention => ({
101
+ interventionCode: code,
102
102
  interventionName: name,
103
103
  interventionPackage: parentBenefitCode,
104
104
  ...({} as any),
@@ -112,9 +112,9 @@ export const useInterventions = (filters: InterventionsFilter) => {
112
112
 
113
113
  const packageCodes = filters.package_code?.split(',').filter(Boolean) || [];
114
114
 
115
- // Filter based on criteria
116
- const filteredResults = data.data.results.filter((intervention) => {
115
+ const filteredResults = data.data.results.filter((intervention, i) => {
117
116
  // Filter by package code (only if defined)
117
+
118
118
  if (packageCodes.length > 0 && !packageCodes.includes(intervention.parentBenefitCode)) {
119
119
  return false;
120
120
  }
@@ -128,9 +128,14 @@ export const useInterventions = (filters: InterventionsFilter) => {
128
128
  return false;
129
129
  }
130
130
 
131
- // Filter by facility level
132
- if (level && intervention.levelsApplicable && !intervention.levelsApplicable.some((l) => level.includes(l))) {
133
- return false;
131
+ // // Filter by facility level
132
+ if (level && intervention.levelsApplicable && Array.isArray(intervention.levelsApplicable)) {
133
+ // Check if level starts with any of the intervention.levelsApplicable values
134
+ const levelMatched = intervention.levelsApplicable.some((appLevel) => level.startsWith(appLevel));
135
+
136
+ if (!levelMatched) {
137
+ return false;
138
+ }
134
139
  }
135
140
 
136
141
  return true; // Keep item if it passes all filters
@@ -139,12 +144,7 @@ export const useInterventions = (filters: InterventionsFilter) => {
139
144
  // Map to the required format and ensure uniqueness by interventionCode
140
145
  const mappedInterventions = filteredResults.map(mapper);
141
146
 
142
- // Remove duplicates based on interventionCode
143
- const uniqueInterventions = Array.from(
144
- new Map(mappedInterventions.map((item) => [item.interventionCode, item])),
145
- ).map(([, value]) => value);
146
-
147
- return uniqueInterventions;
147
+ return mappedInterventions;
148
148
  }, [data, filters, level]);
149
149
 
150
150
  // Map all interventions without filtering
@@ -23,7 +23,6 @@ export function usePaymentSchema(bill: MappedBill) {
23
23
  return amountDue >= 0 && value > 0;
24
24
  }, 'Amount paid should not be greater than amount due'),
25
25
  referenceCode: z.string(),
26
- lineItemUuid: z.string().uuid().nonempty({ message: 'Line item selection is required' }),
27
26
  })
28
27
  .refine(
29
28
  (data) => {
@@ -151,7 +151,7 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isSelectable = true,
151
151
  {...getRowProps({
152
152
  row,
153
153
  })}>
154
- {isSelectable && (
154
+ {rows.length > 1 && isSelectable && (
155
155
  <TableSelectRow
156
156
  aria-label="Select row"
157
157
  {...getSelectionProps({ row })}
@@ -6,33 +6,23 @@ import { Button, Dropdown, NumberInputSkeleton, TextInput, NumberInput } from '@
6
6
  import { ErrorState } from '@openmrs/esm-patient-common-lib';
7
7
  import styles from './payment-form.scss';
8
8
  import { usePaymentModes } from '../../../billing.resource';
9
- import { type LineItem, PaymentFormValue, PaymentMethod } from '../../../types';
10
- import { extractBillableName } from '../../../utils';
9
+ import { PaymentFormValue, PaymentMethod } from '../../../types';
11
10
 
12
11
  type PaymentFormProps = {
13
12
  disablePayment: boolean;
14
13
  amountDue: number;
15
- append: (obj: { method: PaymentMethod; amount: number; referenceCode: string; lineItemUuid: string }) => void;
14
+ append: (obj: { method: PaymentMethod; amount: number; referenceCode: string }) => void;
16
15
  fields: FieldArrayWithId<PaymentFormValue, 'payment', 'id'>[];
17
16
  remove: UseFieldArrayRemove;
18
- selectedLineItems: Array<LineItem>;
19
17
  };
20
18
 
21
- const PaymentForm: React.FC<PaymentFormProps> = ({
22
- disablePayment,
23
- amountDue,
24
- append,
25
- remove,
26
- fields,
27
- selectedLineItems,
28
- }) => {
19
+ const PaymentForm: React.FC<PaymentFormProps> = ({ disablePayment, amountDue, append, remove, fields }) => {
29
20
  const { t } = useTranslation();
30
21
  const {
31
22
  control,
32
23
  formState: { errors },
33
24
  setFocus,
34
25
  getValues,
35
- watch,
36
26
  } = useFormContext<PaymentFormValue>();
37
27
  const { paymentModes, isLoading, error } = usePaymentModes();
38
28
 
@@ -41,26 +31,9 @@ const PaymentForm: React.FC<PaymentFormProps> = ({
41
31
  const attributes = formValues?.payment?.[index]?.method?.attributeTypes ?? [];
42
32
  return attributes.some((attribute) => attribute.required) || attributes?.length > 0;
43
33
  };
44
- const paymentValues = watch('payment');
45
34
 
46
- const getUsedItemUuids = (currentIndex: number) => {
47
- return paymentValues
48
- .map((payment, index) => (index !== currentIndex ? payment?.lineItemUuid : null))
49
- .filter(Boolean);
50
- };
51
- const getAvailableLineItems = (currentIndex: number) => {
52
- const usedUuids = getUsedItemUuids(currentIndex);
53
- return selectedLineItems.filter((item) => item.paymentStatus === 'PENDING' && !usedUuids.includes(item.uuid));
54
- };
55
35
  const handleAppendPaymentMode = useCallback(() => {
56
- const availableItems = getAvailableLineItems(fields.length);
57
- const initialItemUuid = availableItems.length === 1 ? availableItems[0].uuid : '';
58
- append({
59
- method: null,
60
- amount: null,
61
- referenceCode: '',
62
- lineItemUuid: initialItemUuid,
63
- });
36
+ append({ method: null, amount: null, referenceCode: '' });
64
37
  setFocus(`payment.${fields.length}.method`);
65
38
  }, [append, fields.length, setFocus]);
66
39
 
@@ -80,91 +53,67 @@ const PaymentForm: React.FC<PaymentFormProps> = ({
80
53
 
81
54
  return (
82
55
  <div className={styles.container}>
83
- {fields.map((field, index) => {
84
- const availableLineItems = getAvailableLineItems(index);
85
- return (
86
- <div key={field.id} className={styles.paymentMethodContainer}>
87
- <Controller
88
- control={control}
89
- name={`payment.${index}.lineItemUuid`}
90
- render={({ field }) => (
91
- <Dropdown
92
- {...field}
93
- id="paymentLineItem"
94
- onChange={({ selectedItem }) => {
95
- field.onChange(selectedItem?.uuid);
96
- setFocus(`payment.${index}.method`);
97
- }}
98
- titleText={t('selectLineItemToPay', 'Select line item to pay')}
99
- label={t('selectLineItemToPay', 'Select line item to pay')}
100
- items={availableLineItems}
101
- itemToString={(item) => extractBillableName(item)}
102
- invalid={!!errors?.payment?.[index]?.lineItemUuid}
103
- invalidText={errors?.payment?.[index]?.lineItemUuid?.message}
104
- className={styles.lineItemDropdown}
105
- />
106
- )}
107
- />
108
- <Controller
109
- control={control}
110
- name={`payment.${index}.method`}
111
- render={({ field }) => (
112
- <Dropdown
113
- {...field}
114
- id="paymentMethod"
115
- onChange={({ selectedItem }) => {
116
- setFocus(`payment.${index}.amount`);
117
- field.onChange(selectedItem);
118
- }}
119
- titleText={t('paymentMethod', 'Payment method')}
120
- label={t('selectPaymentMethod', 'Select payment method')}
121
- items={paymentModes}
122
- itemToString={(item) => (item ? item.name : '')}
123
- invalid={!!errors?.payment?.[index]?.method}
124
- invalidText={errors?.payment?.[index]?.method?.message}
125
- />
126
- )}
127
- />
56
+ {fields.map((field, index) => (
57
+ <div key={field.id} className={styles.paymentMethodContainer}>
58
+ <Controller
59
+ control={control}
60
+ name={`payment.${index}.method`}
61
+ render={({ field }) => (
62
+ <Dropdown
63
+ {...field}
64
+ id="paymentMethod"
65
+ onChange={({ selectedItem }) => {
66
+ setFocus(`payment.${index}.amount`);
67
+ field.onChange(selectedItem);
68
+ }}
69
+ titleText={t('paymentMethod', 'Payment method')}
70
+ label={t('selectPaymentMethod', 'Select payment method')}
71
+ items={paymentModes}
72
+ itemToString={(item) => (item ? item.name : '')}
73
+ invalid={!!errors?.payment?.[index]?.method}
74
+ invalidText={errors?.payment?.[index]?.method?.message}
75
+ />
76
+ )}
77
+ />
78
+ <Controller
79
+ control={control}
80
+ name={`payment.${index}.amount`}
81
+ render={({ field }) => (
82
+ <NumberInput
83
+ {...field}
84
+ id="paymentAmount"
85
+ onChange={(e) => field.onChange(Number(e.target.value))}
86
+ invalid={!!errors?.payment?.[index]?.amount}
87
+ invalidText={errors?.payment?.[index]?.amount?.message}
88
+ label={t('amount', 'Amount')}
89
+ placeholder={t('enterAmount', 'Enter amount')}
90
+ />
91
+ )}
92
+ />
93
+ {shouldShowReferenceCode(index) && (
128
94
  <Controller
95
+ name={`payment.${index}.referenceCode`}
129
96
  control={control}
130
- name={`payment.${index}.amount`}
131
97
  render={({ field }) => (
132
- <NumberInput
98
+ <TextInput
133
99
  {...field}
134
- id="paymentAmount"
135
- onChange={(e) => field.onChange(Number(e.target.value))}
136
- invalid={!!errors?.payment?.[index]?.amount}
137
- invalidText={errors?.payment?.[index]?.amount?.message}
138
- label={t('amount', 'Amount')}
139
- placeholder={t('enterAmount', 'Enter amount')}
100
+ id="paymentReferenceCode"
101
+ labelText={t('referenceNumber', 'Reference number')}
102
+ placeholder={t('enterReferenceNumber', 'Enter ref. number')}
103
+ type="text"
104
+ invalid={!!errors?.payment?.[index]?.referenceCode}
105
+ invalidText={errors?.payment?.[index]?.referenceCode?.message}
140
106
  />
141
107
  )}
142
108
  />
143
- {shouldShowReferenceCode(index) && (
144
- <Controller
145
- name={`payment.${index}.referenceCode`}
146
- control={control}
147
- render={({ field }) => (
148
- <TextInput
149
- {...field}
150
- id="paymentReferenceCode"
151
- labelText={t('referenceNumber', 'Reference number')}
152
- placeholder={t('enterReferenceNumber', 'Enter ref. number')}
153
- type="text"
154
- invalid={!!errors?.payment?.[index]?.referenceCode}
155
- invalidText={errors?.payment?.[index]?.referenceCode?.message}
156
- />
157
- )}
158
- />
159
- )}
160
- <div className={styles.removeButtonContainer}>
161
- <TrashCan onClick={() => handleRemovePaymentMode(index)} className={styles.removeButton} size={20} />
162
- </div>
109
+ )}
110
+ <div className={styles.removeButtonContainer}>
111
+ <TrashCan onClick={() => handleRemovePaymentMode(index)} className={styles.removeButton} size={20} />
163
112
  </div>
164
- );
165
- })}
113
+ </div>
114
+ ))}
166
115
  <Button
167
- disabled={disablePayment || !selectedLineItems.length}
116
+ disabled={disablePayment}
168
117
  size="md"
169
118
  onClick={handleAppendPaymentMode}
170
119
  className={styles.paymentButtons}
@@ -19,9 +19,9 @@
19
19
 
20
20
  .paymentMethodContainer {
21
21
  display: grid;
22
- grid-template-columns: repeat(5, minmax(auto, 1fr));
22
+ grid-template-columns: repeat(4, minmax(auto, 1fr));
23
23
  align-items: flex-start;
24
- column-gap: layout.$spacing-02;
24
+ column-gap: 1rem;
25
25
  margin: 0.625rem 0;
26
26
  width: 100%;
27
27
  }
@@ -52,6 +52,3 @@
52
52
  .removeButton {
53
53
  color: colors.$red-60;
54
54
  }
55
- .lineItemDropdown {
56
- min-width: 10rem;
57
- }
@@ -67,6 +67,7 @@ const Payments: React.FC<PaymentProps> = ({ bill, selectedLineItems }) => {
67
67
  globalActiveSheet,
68
68
  );
69
69
  remove();
70
+
70
71
  processBillPayment(paymentPayload, bill.uuid).then(
71
72
  (resp) => {
72
73
  showSnackbar({
@@ -141,12 +142,7 @@ const Payments: React.FC<PaymentProps> = ({ bill, selectedLineItems }) => {
141
142
  className={styles.paymentError}
142
143
  />
143
144
  )}
144
- <PaymentForm
145
- {...formArrayMethods}
146
- selectedLineItems={selectedLineItems}
147
- disablePayment={amountDue <= 0}
148
- amountDue={amountDue}
149
- />
145
+ <PaymentForm {...formArrayMethods} disablePayment={amountDue <= 0} amountDue={amountDue} />
150
146
  </div>
151
147
  </div>
152
148
  <div className={styles.divider} />
@@ -6,7 +6,6 @@ import { mockBill, mockedActiveSheet, mockLineItems, mockPaymentModes } from '..
6
6
  import { processBillPayment, usePaymentModes } from '../../billing.resource';
7
7
  import { useClockInStatus } from '../../payment-points/use-clock-in-status';
8
8
  import Payments from './payments.component';
9
-
10
9
  const mockProcessBillPayment = processBillPayment as jest.MockedFunction<typeof processBillPayment>;
11
10
  const mockUsePaymentModes = usePaymentModes as jest.MockedFunction<typeof usePaymentModes>;
12
11
  const mockShowSnackbar = showSnackbar as jest.MockedFunction<typeof showSnackbar>;
@@ -32,7 +31,6 @@ describe('Payment', () => {
32
31
  },
33
32
  },
34
33
  };
35
-
36
34
  mockProcessBillPayment.mockRejectedValueOnce(mockFieldErrorResponse);
37
35
  mockUsePaymentModes.mockReturnValue({
38
36
  paymentModes: mockPaymentModes,
@@ -51,19 +49,10 @@ describe('Payment', () => {
51
49
  });
52
50
 
53
51
  render(<Payments bill={mockBill as any} selectedLineItems={mockLineItems} />);
54
-
55
52
  const addPaymentMethod = screen.getByRole('button', { name: /Add payment option/i });
56
53
  await user.click(addPaymentMethod);
57
-
58
- const lineItemDropdown = await screen.findByRole('combobox', { name: /Select line item to pay/i });
59
- await user.click(lineItemDropdown);
60
-
61
- const lineItemOptions = await screen.findAllByRole('option');
62
- await user.click(lineItemOptions[0]);
63
-
64
- const paymentMethodDropdown = await screen.findByRole('combobox', { name: /Payment method/i });
65
- await user.click(paymentMethodDropdown);
66
- const cashOption = await screen.findByRole('option', { name: /Cash/i });
54
+ await user.click(screen.getByRole('combobox', { name: /Payment method/i }));
55
+ const cashOption = screen.getByRole('option', { name: /Cash/i });
67
56
  await user.click(cashOption);
68
57
 
69
58
  const amountInput = screen.getByRole('spinbutton', { name: /Amount/i });
@@ -82,8 +71,6 @@ describe('Payment', () => {
82
71
  billableService: 'c15d25b9-12bb-441d-9241-cae541dd4575',
83
72
  display: 'BillLineItem',
84
73
  item: 'c15d25b9-12bb-441d-9241-cae541dd4575',
85
- itemOrServiceConceptUuid: 'c42525b9-12bb-441d-9241-cae541dd4575',
86
- serviceTypeUuid: '915d25b9-12bb-441d-9241-cae541dd4575',
87
74
  lineItemOrder: 0,
88
75
  order: null,
89
76
  paymentStatus: 'PAID',
@@ -100,8 +87,6 @@ describe('Payment', () => {
100
87
  billableService: '04be5832-5440-44d0-83d2-5c0dfd0ac7de',
101
88
  display: 'BillLineItem',
102
89
  item: '04be5832-5440-44d0-83d2-5c0dfd0ac7de',
103
- itemOrServiceConceptUuid: 'c42525b9-12bb-441d-9241-cae541dd4575',
104
- serviceTypeUuid: '915d25b9-12bb-441d-9241-cae541dd4575',
105
90
  lineItemOrder: 1,
106
91
  order: null,
107
92
  paymentStatus: 'PAID',
@@ -118,8 +103,6 @@ describe('Payment', () => {
118
103
  billableService: '3f5d0684-a280-477e-a67b-2a956a1f6dca',
119
104
  display: 'BillLineItem',
120
105
  item: '3f5d0684-a280-477e-a67b-2a956a1f6dca',
121
- itemOrServiceConceptUuid: 'c42525b9-12bb-441d-9241-cae541dd4575',
122
- serviceTypeUuid: '915d25b9-12bb-441d-9241-cae541dd4575',
123
106
  lineItemOrder: 2,
124
107
  order: null,
125
108
  paymentStatus: 'PAID',
@@ -150,13 +133,7 @@ describe('Payment', () => {
150
133
  ],
151
134
  patient: 'b2fcf02b-7ee3-4d16-a48f-576be2b103aa',
152
135
  payments: [
153
- {
154
- amount: 100,
155
- amountTendered: 100,
156
- attributes: [],
157
- instanceType: '63eff7a4-6f82-43c4-a333-dbcc58fe9f74',
158
- billLineItem: '314c25fd-2c90-4a7f-9f98-c99cd3f153e8',
159
- },
136
+ { amount: 100, amountTendered: 100, attributes: [], instanceType: '63eff7a4-6f82-43c4-a333-dbcc58fe9f74' },
160
137
  ],
161
138
  status: 'PENDING',
162
139
  },
@@ -181,27 +158,18 @@ describe('Payment', () => {
181
158
  error: null,
182
159
  mutate: jest.fn(),
183
160
  });
184
-
185
161
  render(<Payments bill={mockBill as any} selectedLineItems={mockLineItems} />);
186
-
187
162
  const addPaymentMethod = screen.getByRole('button', { name: /Add payment option/i });
188
163
  await user.click(addPaymentMethod);
189
164
 
190
- const lineItemDropdown = await screen.findByRole('combobox', { name: /Select line item to pay/i });
191
- expect(lineItemDropdown).toHaveFocus();
192
-
193
- await user.click(lineItemDropdown);
194
- const lineItemOptions = await screen.findAllByRole('option');
195
- await user.click(lineItemOptions[0]);
196
-
197
- const paymentMethodField = await screen.findByRole('combobox', { name: /Payment method/i });
198
- expect(paymentMethodField).toHaveFocus();
199
-
200
- await user.click(paymentMethodField);
201
- const cashOption = await screen.findByRole('option', { name: /Cash/i });
165
+ // Check if the payment method field is focused
166
+ expect(screen.getByRole('combobox', { name: /Payment method/i })).toHaveFocus();
167
+ await user.click(screen.getByRole('combobox', { name: /Payment method/i }));
168
+ const cashOption = screen.getByRole('option', { name: /Cash/i });
202
169
  await user.click(cashOption);
203
-
170
+ // Check if the amount field is focused
171
+ expect(screen.getByRole('spinbutton', { name: /Amount/i })).toHaveFocus();
204
172
  const amountInput = screen.getByRole('spinbutton', { name: /Amount/i });
205
- expect(amountInput).toHaveFocus();
173
+ await user.type(amountInput, '100');
206
174
  });
207
175
  });
@@ -126,7 +126,6 @@ export const createPaymentPayload = (
126
126
  value: attribute.value,
127
127
  })),
128
128
  instanceType: payment.instanceType.uuid,
129
- billLineItem: payment.billLineItem?.uuid,
130
129
  }));
131
130
 
132
131
  // Transform new payments
@@ -138,7 +137,6 @@ export const createPaymentPayload = (
138
137
  value: formValue.referenceCode,
139
138
  })),
140
139
  instanceType: formValue.method?.uuid,
141
- billLineItem: formValue.lineItemUuid,
142
140
  }));
143
141
 
144
142
  // Combine and calculate payments