@kenyaemr/esm-billing-app 5.4.1-pre.1998 → 5.4.1-pre.2008

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":"retry-claim-request-modal","component":"retryClaimRequestModal"},{"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.1998"}
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":"retry-claim-request-modal","component":"retryClaimRequestModal"},{"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.2008"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenyaemr/esm-billing-app",
3
- "version": "5.4.1-pre.1998",
3
+ "version": "5.4.1-pre.2008",
4
4
  "description": "Billing app for KenyaEMR",
5
5
  "browser": "dist/kenyaemr-esm-billing-app.js",
6
6
  "main": "src/index.ts",
@@ -1,9 +1,13 @@
1
- import { OpenmrsResource, openmrsFetch } from '@openmrs/esm-framework';
1
+ import { Concept, OpenmrsResource, openmrsFetch } from '@openmrs/esm-framework';
2
2
  import useSWR from 'swr';
3
3
  import { ServiceConcept, ServiceTypesResponse } from '../types';
4
4
 
5
+ type ExtendedResponseObject = OpenmrsResource & {
6
+ concept: Concept;
7
+ };
8
+
5
9
  type ResponseObject = {
6
- results: Array<OpenmrsResource>;
10
+ results: Array<ExtendedResponseObject>;
7
11
  };
8
12
 
9
13
  export const useBillableServices = () => {
@@ -1,6 +1,7 @@
1
1
  import { useTranslation } from 'react-i18next';
2
2
  import { Order } from '@openmrs/esm-patient-common-lib';
3
3
  import { useBillStatus } from './useBillStatus';
4
+ import { useBillableServices } from '../../../billable-service.resource';
4
5
 
5
6
  export enum FulfillerStatus {
6
7
  IN_PROGRESS = 'IN_PROGRESS',
@@ -11,11 +12,25 @@ export function useLabOrderAction(order?: Order) {
11
12
  const orderUuid = order?.uuid;
12
13
  const patientUuid = order?.patient?.uuid;
13
14
  const conceptClass = order?.concept?.conceptClass?.['display'];
15
+ const { isLoading: isLoadingBillableServices, billableServices } = useBillableServices();
16
+
17
+ const serviceBillingStatus =
18
+ billableServices?.find((service) => service.concept.uuid === order.concept.uuid)?.serviceStatus === 'ENABLED';
14
19
 
15
20
  const { isLoading, hasPendingPayment, itemHasBill } = useBillStatus(orderUuid, patientUuid);
16
- const shouldShowBillModal = itemHasBill.length < 1;
21
+ const shouldShowBillModal = itemHasBill.length < 1 && serviceBillingStatus;
17
22
  const isInProgress = order?.fulfillerStatus === FulfillerStatus.IN_PROGRESS;
18
23
 
24
+ const getOrderType = () => {
25
+ if (!conceptClass) {
26
+ return 'Lab Request';
27
+ }
28
+ if (conceptClass.includes('/')) {
29
+ return conceptClass.split('/')[1].trim();
30
+ }
31
+ return conceptClass;
32
+ };
33
+
19
34
  const getButtonText = () => {
20
35
  if (hasPendingPayment) {
21
36
  return t('unsettledBill', 'Unsettled bill');
@@ -25,11 +40,11 @@ export function useLabOrderAction(order?: Order) {
25
40
  return t('bill', 'Bill');
26
41
  }
27
42
 
28
- return t('pickLabRequest', 'Pick {{orderType}} Request', { orderType: conceptClass ?? 'Lab' });
43
+ return t('pickLabRequest', 'Pick {{orderType}}', { orderType: getOrderType() ?? 'Lab Request' });
29
44
  };
30
45
 
31
46
  return {
32
- isLoading,
47
+ isLoading: isLoading && isLoadingBillableServices,
33
48
  isDisabled: hasPendingPayment,
34
49
  buttonText: getButtonText(),
35
50
  isInProgress,
@@ -25,6 +25,7 @@ export interface BillingConfig {
25
25
  patientBillsUrl: string;
26
26
  nationalIdUUID: string;
27
27
  isPDSLFacility: boolean;
28
+ mobileMoneyPaymentModeUUID: string;
28
29
  concepts: {
29
30
  emergencyPriorityConceptUuid: string;
30
31
  };
@@ -41,6 +42,11 @@ export const configSchema: ConfigSchema = {
41
42
  _description: 'A flag for PDSL facilities',
42
43
  _default: false,
43
44
  },
45
+ mobileMoneyPaymentModeUUID: {
46
+ _type: Type.UUID,
47
+ _description: 'Mobile money payment method uuid',
48
+ _default: '28989582-e8c3-46b0-96d0-c249cb06d5c6',
49
+ },
44
50
  shaIdentificationNumberUUID: {
45
51
  _type: Type.String,
46
52
  _description: 'Social Health Authority Identification Number',
@@ -82,6 +82,7 @@ const Invoice: React.FC = () => {
82
82
  const dispose = showModal('initiate-payment-modal', {
83
83
  closeModal: () => dispose(),
84
84
  bill: bill,
85
+ selectedLineItems,
85
86
  });
86
87
  };
87
88
 
@@ -1,12 +1,16 @@
1
1
  import {
2
2
  Button,
3
+ ButtonSet,
4
+ ContentSwitcher,
3
5
  Form,
4
6
  InlineNotification,
5
7
  Layer,
6
8
  Loading,
7
9
  ModalBody,
10
+ ModalFooter,
8
11
  ModalHeader,
9
12
  NumberInputSkeleton,
13
+ Switch,
10
14
  TextInput,
11
15
  } from '@carbon/react';
12
16
  import { zodResolver } from '@hookform/resolvers/zod';
@@ -20,7 +24,8 @@ import { useSystemSetting } from '../../../hooks/getMflCode';
20
24
  import { usePatientAttributes } from '../../../hooks/usePatientAttributes';
21
25
  import { useRequestStatus } from '../../../hooks/useRequestStatus';
22
26
  import { initiateStkPush } from '../../../m-pesa/mpesa-resource';
23
- import { MappedBill } from '../../../types';
27
+ import { LineItem, MappedBill } from '../../../types';
28
+ import PaymentStatusCheckerModal from '../payment-status-ckecker.modal';
24
29
  import { formatKenyanPhoneNumber } from '../utils';
25
30
  import styles from './initiate-payment.scss';
26
31
 
@@ -37,15 +42,17 @@ type FormData = z.infer<typeof initiatePaymentSchema>;
37
42
  export interface InitiatePaymentDialogProps {
38
43
  closeModal: () => void;
39
44
  bill: MappedBill;
45
+ selectedLineItems: Array<LineItem>;
40
46
  }
41
47
 
42
- const InitiatePaymentDialog: React.FC<InitiatePaymentDialogProps> = ({ closeModal, bill }) => {
48
+ const InitiatePaymentDialog: React.FC<InitiatePaymentDialogProps> = ({ closeModal, bill, selectedLineItems }) => {
43
49
  const { t } = useTranslation();
44
50
  const { phoneNumber, isLoading: isLoadingPhoneNumber } = usePatientAttributes(bill.patientUuid);
45
51
  const { mpesaAPIBaseUrl, isPDSLFacility } = useConfig<BillingConfig>();
46
52
  const { mflCodeValue } = useSystemSetting('facility.mflcode');
47
53
  const [notification, setNotification] = useState<{ type: 'error' | 'success'; message: string } | null>(null);
48
54
  const [isLoading, setIsLoading] = useState(false);
55
+ const [hasMadePayment, setHasMadepayment] = useState(false);
49
56
  const [{ requestStatus }, pollingTrigger] = useRequestStatus(setNotification, closeModal, bill);
50
57
 
51
58
  const pendingAmount = bill.totalAmount - bill.tenderedAmount;
@@ -91,12 +98,35 @@ const InitiatePaymentDialog: React.FC<InitiatePaymentDialogProps> = ({ closeModa
91
98
  setIsLoading(false);
92
99
  };
93
100
 
101
+ if (isPDSLFacility && hasMadePayment) {
102
+ return (
103
+ <PaymentStatusCheckerModal
104
+ onClose={closeModal}
105
+ paymentMade={hasMadePayment}
106
+ onPaymentMadestatusChange={setHasMadepayment}
107
+ bill={bill}
108
+ selectedLineItems={selectedLineItems}
109
+ />
110
+ );
111
+ }
112
+
94
113
  return (
95
- <div>
96
- <ModalHeader closeModal={closeModal} />
114
+ <Form>
115
+ <ModalHeader className={styles.heading} closeModal={closeModal}>
116
+ {t('paymentPayment', 'Bill Payment')}
117
+ </ModalHeader>
97
118
  <ModalBody>
98
- <Form className={styles.form}>
99
- <h4>{t('paymentPayment', 'Bill Payment')}</h4>
119
+ {isPDSLFacility && (
120
+ <ContentSwitcher
121
+ selectedIndex={hasMadePayment ? 1 : 0}
122
+ onChange={({ name }) => {
123
+ setHasMadepayment(name === 'paymentMade');
124
+ }}>
125
+ <Switch name="paymentNotMade" text={t('paymentNotMade', 'Payment not made')} />
126
+ <Switch name="paymentMade" text={t('paymentMade', 'Payments already made')} />
127
+ </ContentSwitcher>
128
+ )}
129
+ <div className={styles.form}>
100
130
  {notification && (
101
131
  <InlineNotification
102
132
  kind={notification.type}
@@ -144,28 +174,30 @@ const InitiatePaymentDialog: React.FC<InitiatePaymentDialogProps> = ({ closeModa
144
174
  )}
145
175
  />
146
176
  </section>
147
- <section>
148
- <Button kind="secondary" className={styles.buttonLayout} onClick={closeModal}>
149
- {t('cancel', 'Cancel')}
150
- </Button>
151
- <Button
152
- type="submit"
153
- className={styles.button}
154
- onClick={handleSubmit(onSubmit)}
155
- disabled={!isValid || isLoading || requestStatus === 'INITIATED'}>
156
- {isLoading ? (
157
- <>
158
- <Loading className={styles.button_spinner} withOverlay={false} small />{' '}
159
- {t('processingPayment', 'Processing Payment')}
160
- </>
161
- ) : (
162
- t('initiatePay', 'Initiate Payment')
163
- )}
164
- </Button>
165
- </section>
166
- </Form>
177
+ </div>
167
178
  </ModalBody>
168
- </div>
179
+ <ModalFooter>
180
+ <ButtonSet className={styles.buttonSet}>
181
+ <Button kind="secondary" onClick={closeModal} className={styles.button}>
182
+ {t('cancel', 'Cancel')}
183
+ </Button>
184
+ <Button
185
+ type="submit"
186
+ className={styles.button}
187
+ onClick={handleSubmit(onSubmit)}
188
+ disabled={!isValid || isLoading || requestStatus === 'INITIATED'}>
189
+ {isLoading ? (
190
+ <>
191
+ <Loading className={styles.button_spinner} withOverlay={false} small />{' '}
192
+ {t('processingPayment', 'Processing Payment')}
193
+ </>
194
+ ) : (
195
+ t('initiatePayment', 'Initiate Payment')
196
+ )}
197
+ </Button>
198
+ </ButtonSet>
199
+ </ModalFooter>
200
+ </Form>
169
201
  );
170
202
  };
171
203
 
@@ -21,18 +21,28 @@
21
21
  margin-bottom: spacing.$spacing-04;
22
22
  }
23
23
 
24
- .buttonLayout {
25
- margin-top: layout.$spacing-05;
26
- margin-bottom: layout.$spacing-01;
24
+ .button_spinner {
25
+ padding: 0;
26
+ margin-right: layout.$spacing-04;
27
27
  }
28
28
 
29
29
  .button {
30
- margin-left: layout.$spacing-04;
30
+ height: layout.$spacing-10;
31
+ display: flex;
32
+ align-content: flex-start;
33
+ align-items: baseline;
34
+ min-width: 20%;
35
+ }
36
+
37
+ .buttonSet {
38
+ padding: 0rem;
31
39
  margin-top: layout.$spacing-05;
32
- margin-bottom: layout.$spacing-01;
40
+ display: flex;
41
+ justify-content: space-between;
42
+ width: 100%;
43
+ margin-bottom: layout.$spacing-05;
33
44
  }
34
45
 
35
- .button_spinner {
36
- padding: 0;
37
- margin-right: 12px;
46
+ .heading {
47
+ @include type.type-style('heading-02');
38
48
  }
@@ -0,0 +1,136 @@
1
+ import {
2
+ Button,
3
+ ButtonSet,
4
+ ContentSwitcher,
5
+ InlineLoading,
6
+ ModalBody,
7
+ ModalFooter,
8
+ ModalHeader,
9
+ Switch,
10
+ TextInput,
11
+ } from '@carbon/react';
12
+ import { showSnackbar, useConfig } from '@openmrs/esm-framework';
13
+ import React, { useCallback, useMemo, useState } from 'react';
14
+ import { useTranslation } from 'react-i18next';
15
+ import { mutate } from 'swr';
16
+ import { processBillPayment, usePaymentModes } from '../../billing.resource';
17
+ import { BillingConfig } from '../../config-schema';
18
+ import { useClockInStatus } from '../../payment-points/use-clock-in-status';
19
+ import { LineItem, MappedBill } from '../../types';
20
+ import { checkPaymentStatus } from './payments.resource';
21
+ import styles from './payments.scss';
22
+ import { createPaymentPayload } from './utils';
23
+ type PaymentStatusCheckerModalProps = {
24
+ onClose: () => void;
25
+ paymentMade?: boolean;
26
+ onPaymentMadestatusChange?: (paid: boolean) => void;
27
+ bill: MappedBill;
28
+ selectedLineItems: Array<LineItem>;
29
+ };
30
+ const PaymentStatusCheckerModal: React.FC<PaymentStatusCheckerModalProps> = ({
31
+ onClose,
32
+ onPaymentMadestatusChange,
33
+ paymentMade,
34
+ bill,
35
+ selectedLineItems,
36
+ }) => {
37
+ const { t } = useTranslation();
38
+ const [transactionid, setTransactionid] = useState<string>();
39
+ const { globalActiveSheet } = useClockInStatus();
40
+ const { mobileMoneyPaymentModeUUID } = useConfig<BillingConfig>();
41
+ const { paymentModes, isLoading } = usePaymentModes();
42
+ const [checking, setChecking] = useState(false);
43
+ const mobilemoneypaymentMethod = useMemo(
44
+ () => paymentModes.find((mode) => mode.uuid === mobileMoneyPaymentModeUUID),
45
+ [paymentModes, mobileMoneyPaymentModeUUID],
46
+ );
47
+
48
+ const handleCheckPaymentStatus = useCallback(async () => {
49
+ try {
50
+ setChecking(true);
51
+ const res = await checkPaymentStatus(transactionid);
52
+ if (res?.data?.success) {
53
+ const totalAmountTendered = Number(res.data.data.TransAmount) || 0;
54
+ const amountDue = Number(bill.totalAmount) - (Number(bill.tenderedAmount) + Number(totalAmountTendered));
55
+ const paymentPayload = createPaymentPayload(
56
+ bill,
57
+ bill.patientUuid,
58
+ [{ referenceCode: transactionid, amount: totalAmountTendered, method: mobilemoneypaymentMethod }],
59
+ amountDue,
60
+ selectedLineItems,
61
+ globalActiveSheet,
62
+ );
63
+ await processBillPayment(paymentPayload, bill.uuid);
64
+ showSnackbar({
65
+ title: t('success', 'Success'),
66
+ kind: 'success',
67
+ subtitle: t('paymentReceived', 'Payment received'),
68
+ });
69
+ const url = `/ws/rest/v1/cashier/bill/${bill.uuid}`;
70
+ mutate((key) => typeof key === 'string' && key.startsWith(url), undefined, { revalidate: true });
71
+ onClose?.();
72
+ } else {
73
+ showSnackbar({
74
+ title: t('paymentFailure', 'Payment Failure'),
75
+ kind: 'error',
76
+ subtitle: res?.data?.message || t('paymentNotSettled', 'Payment not settled'),
77
+ });
78
+ }
79
+ } catch (error: any) {
80
+ showSnackbar({ title: t('error', 'Error'), kind: 'error', subtitle: error?.message });
81
+ } finally {
82
+ setChecking(false);
83
+ }
84
+ }, [transactionid, t]);
85
+ return (
86
+ <React.Fragment>
87
+ <ModalHeader className={styles.heading} closeModal={onClose}>
88
+ {t('checkpaymentStatus', 'Check payment status')}
89
+ </ModalHeader>
90
+ <ModalBody>
91
+ <ContentSwitcher
92
+ selectedIndex={paymentMade ? 1 : 0}
93
+ onChange={({ name }) => {
94
+ onPaymentMadestatusChange?.(name === 'paymentMade');
95
+ }}>
96
+ <Switch name="paymentNotMade" text={t('paymentNotMade', 'Payment not made')} />
97
+ <Switch name="paymentMade" text={t('paymentMade', 'Payments already made')} />
98
+ </ContentSwitcher>
99
+ <TextInput
100
+ value={transactionid}
101
+ defaultWidth={300}
102
+ id="text-input-1"
103
+ labelText={t('transactionId', 'Transaction Id')}
104
+ placeholder={t('exampletransactionId', 'e.g TRMEWECEDD')}
105
+ size="md"
106
+ type="text"
107
+ helperText={t('mpesaTransactionId', 'Mpesa transaction Id')}
108
+ onChange={({ target: { value } }) => setTransactionid(value)}
109
+ />
110
+ </ModalBody>
111
+ <ModalFooter>
112
+ <ButtonSet className={styles.buttonSet}>
113
+ <Button kind="secondary" onClick={onClose} className={styles.button}>
114
+ {t('cancel', 'Cancel')}
115
+ </Button>
116
+ <Button
117
+ kind="primary"
118
+ disabled={!transactionid?.length || isLoading || checking}
119
+ onClick={handleCheckPaymentStatus}
120
+ className={styles.button}>
121
+ {isLoading || checking ? (
122
+ <InlineLoading
123
+ description={t('checkingPaymentStatus', 'Check payment status') + '...'}
124
+ iconDescription={t('loading', 'Loading')}
125
+ />
126
+ ) : (
127
+ t('check', 'Check')
128
+ )}
129
+ </Button>
130
+ </ButtonSet>
131
+ </ModalFooter>
132
+ </React.Fragment>
133
+ );
134
+ };
135
+
136
+ export default PaymentStatusCheckerModal;
@@ -1,5 +1,5 @@
1
1
  import useSWR from 'swr';
2
- import { openmrsFetch, useConfig } from '@openmrs/esm-framework';
2
+ import { openmrsFetch, restBaseUrl, useConfig } from '@openmrs/esm-framework';
3
3
  import { BillingConfig } from '../../config-schema';
4
4
 
5
5
  type PaymentMethod = {
@@ -13,6 +13,32 @@ const swrOption = {
13
13
  errorRetryCount: 2,
14
14
  };
15
15
 
16
+ export interface PaymentStatusResponse {
17
+ success: boolean;
18
+ message: string;
19
+ data?: Data;
20
+ }
21
+
22
+ export interface Data {
23
+ id: number;
24
+ TransactionType: string;
25
+ TransID: string;
26
+ TransTime: string;
27
+ TransAmount: string;
28
+ BusinessShortCode: string;
29
+ BillRefNumber: string;
30
+ InvoiceNumber?: string;
31
+ OrgAccountBalance: string;
32
+ ThirdPartyTransID?: string;
33
+ MSISDN: string;
34
+ FirstName: string;
35
+ MiddleName?: string;
36
+ LastName?: string;
37
+ status: string;
38
+ created_at: string;
39
+ updated_at: string;
40
+ }
41
+
16
42
  export const usePaymentModes = () => {
17
43
  const { excludedPaymentMode } = useConfig<BillingConfig>();
18
44
  const url = `/ws/rest/v1/cashier/paymentMode`;
@@ -32,3 +58,8 @@ export const usePaymentModes = () => {
32
58
  error,
33
59
  };
34
60
  };
61
+
62
+ export const checkPaymentStatus = (transactionId: string) => {
63
+ const url = `${restBaseUrl}/rmsdataexchange/api/rmsmpesachecker?transactionId=${transactionId}`;
64
+ return openmrsFetch<PaymentStatusResponse>(url);
65
+ };
@@ -48,3 +48,24 @@
48
48
  .paymentError {
49
49
  margin: layout.$spacing-05 0;
50
50
  }
51
+
52
+ .heading {
53
+ @include type.type-style('heading-02');
54
+ }
55
+
56
+ .button {
57
+ height: layout.$spacing-10;
58
+ display: flex;
59
+ align-content: flex-start;
60
+ align-items: baseline;
61
+ min-width: 20%;
62
+ }
63
+
64
+ .buttonSet {
65
+ padding: 0rem;
66
+ margin-top: layout.$spacing-05;
67
+ display: flex;
68
+ justify-content: space-between;
69
+ width: 100%;
70
+ margin-bottom: layout.$spacing-05;
71
+ }
@@ -173,7 +173,6 @@ export const usePatientBills = (patientUuid: string) => {
173
173
  const patientBills = useMemo(() => {
174
174
  return data?.data?.results?.map(mapBillProperties) ?? [];
175
175
  }, [data?.data?.results]);
176
-
177
176
  return {
178
177
  patientBills: patientBills ?? [],
179
178
  isLoading,
@@ -96,6 +96,8 @@
96
96
  "eligibleBenefits": "Eligible benefits",
97
97
  "emptyClaimsHeader": "No Claims",
98
98
  "emptyClaimsState": "There are no claims to display",
99
+ "emptyPreauthHeader": "No Preauths",
100
+ "emptyPreauthState": "There are no preauth to display",
99
101
  "endDate": "End date",
100
102
  "endVisitAndClaim": "End visit and Process claims",
101
103
  "enterAmount": "Enter amount",
@@ -135,7 +137,7 @@
135
137
  "inactive": "Inactive",
136
138
  "incompletePayment": "Incomplete payment",
137
139
  "incompletePaymentSubtitle": "Please ensure all selected line items are fully paid, Total amount expected is {{selectedLineItemsAmountDue}}",
138
- "initiatePay": "Initiate Payment",
140
+ "initiatePayment": "Initiate Payment",
139
141
  "inputSampleSchema": "Input sample schema",
140
142
  "insuranceScheme": "Insurance scheme",
141
143
  "insurer": "Insurer",
@@ -196,18 +198,21 @@
196
198
  "patientMissingSHANumber": "Patient is missing SHA number, SHA validation cannot be done, Advise patient to visit registration desk",
197
199
  "patientName": "Patient Name",
198
200
  "paymentHistory": "Payment History",
201
+ "paymentMade": "Payments already made",
199
202
  "paymentMethod": "Payment method",
200
203
  "paymentMethodDescription": "Payment method {{methodName}}",
201
204
  "paymentMethodsTitle": "Payment method",
202
205
  "paymentMode": "Payment Mode",
203
206
  "paymentModes": "Payment Modes",
204
207
  "paymentModeSummary": "Payment Mode Summary",
208
+ "paymentNotMade": "Payment not made",
205
209
  "paymentPayment": "Bill Payment",
206
210
  "paymentPoints": "Payment Points",
207
211
  "payments": "Payments",
208
212
  "paymentType": "Payment Type",
209
213
  "Phone Number": "Phone Number",
210
214
  "policyNumber": "Policy number",
215
+ "preathsRequests": "Preauth Requests",
211
216
  "preauthRequest": "Preauth requests",
212
217
  "preAuthRequests": "Pre-auth requests",
213
218
  "preAuthRequets": "Pre-Auth Requests",