@openmrs/esm-billing-app 1.1.2-pre.8 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/cache/31f1dfc7f71601df-meta.json +1 -0
- package/.turbo/cache/31f1dfc7f71601df.tar.zst +0 -0
- package/.turbo/turbo-build.log +13 -42
- package/__mocks__/bills.mock.ts +3 -2
- package/dist/1480.js +1 -0
- package/dist/1480.js.map +1 -0
- package/dist/1564.js +1 -0
- package/dist/1564.js.map +1 -0
- package/dist/1578.js +1 -0
- package/dist/1578.js.map +1 -0
- package/dist/1646.js +1 -0
- package/dist/1646.js.map +1 -0
- package/dist/1869.js +1 -0
- package/dist/1869.js.map +1 -0
- package/dist/1877.js +1 -0
- package/dist/1877.js.map +1 -0
- package/dist/1899.js +1 -0
- package/dist/1899.js.map +1 -0
- package/dist/196.js +2 -0
- package/dist/196.js.map +1 -0
- package/dist/2250.js +43 -0
- package/dist/2250.js.map +1 -0
- package/dist/2269.js +1 -0
- package/dist/2269.js.map +1 -0
- package/dist/2317.js +1 -0
- package/dist/2317.js.map +1 -0
- package/dist/2416.js +1 -0
- package/dist/2416.js.map +1 -0
- package/dist/2489.js +1 -0
- package/dist/2489.js.map +1 -0
- package/dist/282.js +1 -0
- package/dist/282.js.map +1 -0
- package/dist/2881.js +1 -0
- package/dist/2881.js.map +1 -0
- package/dist/2997.js +1 -0
- package/dist/2997.js.map +1 -0
- package/dist/3378.js +1 -0
- package/dist/3378.js.map +1 -0
- package/dist/3379.js +1 -0
- package/dist/3379.js.map +1 -0
- package/dist/3784.js +1 -0
- package/dist/3784.js.map +1 -0
- package/dist/3963.js +1 -0
- package/dist/3963.js.map +1 -0
- package/dist/4106.js +1 -0
- package/dist/4106.js.map +1 -0
- package/dist/4111.js +1 -0
- package/dist/4111.js.map +1 -0
- package/dist/434.js +1 -0
- package/dist/434.js.map +1 -0
- package/dist/4348.js +1 -0
- package/dist/4348.js.map +1 -0
- package/dist/4383.js +1 -0
- package/dist/4383.js.map +1 -0
- package/dist/4658.js +1 -0
- package/dist/4658.js.map +1 -0
- package/dist/4870.js +1 -0
- package/dist/4870.js.map +1 -0
- package/dist/4928.js +1 -0
- package/dist/4928.js.map +1 -0
- package/dist/5098.js +1 -0
- package/dist/5098.js.map +1 -0
- package/dist/5117.js +1 -0
- package/dist/5117.js.map +1 -0
- package/dist/5132.js +1 -0
- package/dist/5132.js.map +1 -0
- package/dist/5145.js +1 -0
- package/dist/5145.js.map +1 -0
- package/dist/5390.js +1 -0
- package/dist/5390.js.map +1 -0
- package/dist/5503.js +1 -0
- package/dist/5503.js.map +1 -0
- package/dist/556.js +1 -0
- package/dist/556.js.map +1 -0
- package/dist/5644.js +1 -0
- package/dist/5644.js.map +1 -0
- package/dist/5898.js +1 -0
- package/dist/5898.js.map +1 -0
- package/dist/5940.js +1 -0
- package/dist/5940.js.map +1 -0
- package/dist/6047.js +1 -0
- package/dist/6047.js.map +1 -0
- package/dist/6237.js +1 -0
- package/dist/6237.js.map +1 -0
- package/dist/6362.js +1 -0
- package/dist/6362.js.map +1 -0
- package/dist/6371.js +1 -0
- package/dist/6371.js.map +1 -0
- package/dist/6377.js +1 -0
- package/dist/6377.js.map +1 -0
- package/dist/6444.js +1 -0
- package/dist/6444.js.map +1 -0
- package/dist/6508.js +1 -0
- package/dist/6508.js.map +1 -0
- package/dist/6594.js +1 -0
- package/dist/6594.js.map +1 -0
- package/dist/6724.js +1 -0
- package/dist/6724.js.map +1 -0
- package/dist/6904.js +1 -0
- package/dist/6904.js.map +1 -0
- package/dist/7045.js +1 -0
- package/dist/7045.js.map +1 -0
- package/dist/7175.js +1 -0
- package/dist/7175.js.map +1 -0
- package/dist/7182.js +1 -0
- package/dist/7182.js.map +1 -0
- package/dist/7247.js +1 -0
- package/dist/7247.js.map +1 -0
- package/dist/7742.js +1 -0
- package/dist/7742.js.map +1 -0
- package/dist/7912.js +1 -0
- package/dist/7912.js.map +1 -0
- package/dist/8358.js +1 -0
- package/dist/8358.js.map +1 -0
- package/dist/8359.js +1 -0
- package/dist/8359.js.map +1 -0
- package/dist/8695.js +1 -0
- package/dist/8695.js.map +1 -0
- package/dist/903.js +1 -0
- package/dist/903.js.map +1 -0
- package/dist/9072.js +1 -0
- package/dist/9072.js.map +1 -0
- package/dist/9414.js +1 -0
- package/dist/9414.js.map +1 -0
- package/dist/9655.js +11 -0
- package/dist/9655.js.map +1 -0
- package/dist/9806.js +1 -0
- package/dist/9806.js.map +1 -0
- package/dist/990.js +1 -0
- package/dist/990.js.map +1 -0
- package/dist/main.js +17 -2
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-billing-app.js +6 -1
- package/dist/openmrs-esm-billing-app.js.buildmanifest.json +643 -436
- package/dist/openmrs-esm-billing-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/e2e/commands/billing-operations.ts +21 -0
- package/e2e/commands/types.ts +9 -1
- package/e2e/pages/discounts-page.ts +75 -0
- package/e2e/pages/index.ts +1 -0
- package/e2e/pages/invoice-page.ts +7 -7
- package/e2e/specs/bill-discounts.spec.ts +255 -0
- package/e2e/specs/billing-dashboard.spec.ts +3 -3
- package/e2e/specs/billing-patient-chart.spec.ts +2 -2
- package/package.json +13 -22
- package/rspack.config.js +1 -0
- package/src/bill-history/bill-action-menu.component.tsx +20 -2
- package/src/bill-history/bill-history.test.tsx +23 -22
- package/src/bill-item-actions/edit-bill-item.modal.tsx +1 -1
- package/src/bill-item-actions/edit-bill-item.test.tsx +29 -27
- package/src/billable-services/billable-service-form/billable-service-form.test.tsx +74 -73
- package/src/billable-services/billable-services-home.component.tsx +4 -2
- package/src/billable-services/billable-services.test.tsx +8 -7
- package/src/billable-services/dashboard/dashboard.test.tsx +3 -2
- package/src/billable-services-admin-card-link.test.tsx +2 -1
- package/src/billing-dashboard/billing-dashboard.test.tsx +19 -3
- package/src/billing-form/billing-checkin-form.component.tsx +7 -3
- package/src/billing-form/billing-checkin-form.test.tsx +22 -21
- package/src/billing-form/billing-form.resource.test.ts +7 -6
- package/src/billing-form/billing-form.test.tsx +77 -40
- package/src/billing-form/billing-form.workspace.tsx +25 -6
- package/src/billing-form/visit-attributes/visit-attributes-form.component.tsx +6 -2
- package/src/billing.resource.test.ts +43 -41
- package/src/billing.resource.ts +65 -16
- package/src/bills-table/bills-table.component.tsx +15 -4
- package/src/bills-table/bills-table.test.tsx +72 -71
- package/src/config-schema.ts +0 -7
- package/src/discounts/admin/discount-requests-left-panel-link.component.tsx +43 -0
- package/src/discounts/admin/discount-requests.component.tsx +316 -0
- package/src/discounts/admin/discount-requests.scss +133 -0
- package/src/discounts/admin/discount-requests.test.tsx +104 -0
- package/src/discounts/admin/review-bill-discounts/bill-line-items-table/bill-line-items-table.component.tsx +42 -0
- package/src/discounts/admin/review-bill-discounts/bill-line-items-table/bill-line-items-table.scss +76 -0
- package/src/discounts/admin/review-bill-discounts/bill-payments-table/bill-payments-table.component.tsx +50 -0
- package/src/discounts/admin/review-bill-discounts/bill-payments-table/bill-payments-table.scss +63 -0
- package/src/discounts/admin/review-bill-discounts/bill-receipt-rail/bill-receipt-rail.component.tsx +73 -0
- package/src/discounts/admin/review-bill-discounts/bill-receipt-rail/bill-receipt-rail.scss +54 -0
- package/src/discounts/admin/review-bill-discounts/bill-totals-summary/bill-totals-summary.component.tsx +95 -0
- package/src/discounts/admin/review-bill-discounts/bill-totals-summary/bill-totals-summary.scss +128 -0
- package/src/discounts/admin/review-bill-discounts/discount-card/discount-card.component.tsx +158 -0
- package/src/discounts/admin/review-bill-discounts/discount-card/discount-card.scss +164 -0
- package/src/discounts/admin/review-bill-discounts/discount-review-stack/discount-review-stack.component.tsx +86 -0
- package/src/discounts/admin/review-bill-discounts/discount-review-stack/discount-review-stack.scss +40 -0
- package/src/discounts/admin/review-bill-discounts/review-bill-discounts.modal.scss +14 -0
- package/src/discounts/admin/review-bill-discounts/review-bill-discounts.modal.test.tsx +153 -0
- package/src/discounts/admin/review-bill-discounts/review-bill-discounts.modal.tsx +167 -0
- package/src/discounts/admin/review-bill-discounts/review-bill-discounts.utils.ts +42 -0
- package/src/discounts/discounts-table.component.tsx +109 -0
- package/src/discounts/discounts-table.scss +37 -0
- package/src/discounts/discounts-table.test.tsx +67 -0
- package/src/discounts/discounts.resource.ts +71 -0
- package/src/discounts/request-discount.modal.scss +88 -0
- package/src/discounts/request-discount.modal.test.tsx +161 -0
- package/src/discounts/request-discount.modal.tsx +253 -0
- package/src/index.ts +52 -21
- package/src/invoice/invoice-table.component.tsx +116 -18
- package/src/invoice/invoice-table.test.tsx +165 -13
- package/src/invoice/invoice.component.tsx +111 -7
- package/src/invoice/invoice.test.tsx +366 -66
- package/src/invoice/line-item-action-menu.component.tsx +31 -1
- package/src/invoice/payments/payment-form/payment-form.test.tsx +20 -19
- package/src/invoice/payments/payment-history/payment-history.test.tsx +13 -10
- package/src/invoice/payments/payments.component.tsx +20 -6
- package/src/invoice/payments/payments.test.tsx +88 -23
- package/src/invoice/printable-invoice/print-receipt.test.tsx +10 -28
- package/src/invoice/printable-invoice/printable-footer.test.tsx +5 -4
- package/src/invoice/printable-invoice/printable-invoice-header.component.tsx +3 -3
- package/src/invoice/printable-invoice/printable-invoice-header.test.tsx +26 -11
- package/src/invoice/printable-invoice/printable-invoice.component.tsx +38 -15
- package/src/left-panel-link.test.tsx +3 -3
- package/src/metrics-cards/metrics-cards.test.tsx +11 -10
- package/src/modal/delete-bill-confirmation.modal.test.tsx +134 -0
- package/src/modal/delete-bill-confirmation.modal.tsx +98 -0
- package/src/modal/delete-line-item-confirmation.modal.test.tsx +11 -9
- package/src/modal/finalize-bill-confirmation.modal.test.tsx +10 -8
- package/src/modal/require-payment-modal.test.tsx +12 -11
- package/src/payment-status-tag/payment-status-tag.component.tsx +50 -0
- package/src/payment-status-tag/payment-status-tag.scss +6 -0
- package/src/payment-status-tag/payment-status-tag.test.tsx +113 -0
- package/src/refunds/admin/refund-requests-left-panel-link.component.tsx +43 -0
- package/src/refunds/admin/refund-requests.component.tsx +324 -0
- package/src/refunds/admin/refund-requests.scss +133 -0
- package/src/refunds/admin/refund-requests.test.tsx +99 -0
- package/src/refunds/admin/review-bill-refunds/bill-line-items-table/bill-line-items-table.component.tsx +42 -0
- package/src/refunds/admin/review-bill-refunds/bill-line-items-table/bill-line-items-table.scss +76 -0
- package/src/refunds/admin/review-bill-refunds/bill-payments-table/bill-payments-table.component.tsx +50 -0
- package/src/refunds/admin/review-bill-refunds/bill-payments-table/bill-payments-table.scss +63 -0
- package/src/refunds/admin/review-bill-refunds/bill-receipt-rail/bill-receipt-rail.component.tsx +84 -0
- package/src/refunds/admin/review-bill-refunds/bill-receipt-rail/bill-receipt-rail.scss +54 -0
- package/src/refunds/admin/review-bill-refunds/bill-totals-summary/bill-totals-summary.component.tsx +83 -0
- package/src/refunds/admin/review-bill-refunds/bill-totals-summary/bill-totals-summary.scss +65 -0
- package/src/refunds/admin/review-bill-refunds/refund-card/refund-card.component.tsx +170 -0
- package/src/refunds/admin/review-bill-refunds/refund-card/refund-card.scss +155 -0
- package/src/refunds/admin/review-bill-refunds/refund-review-stack/refund-review-stack.component.tsx +86 -0
- package/src/refunds/admin/review-bill-refunds/refund-review-stack/refund-review-stack.scss +40 -0
- package/src/refunds/admin/review-bill-refunds/review-bill-refunds.modal.scss +14 -0
- package/src/refunds/admin/review-bill-refunds/review-bill-refunds.modal.test.tsx +313 -0
- package/src/refunds/admin/review-bill-refunds/review-bill-refunds.modal.tsx +188 -0
- package/src/refunds/admin/review-bill-refunds/review-bill-refunds.utils.ts +66 -0
- package/src/refunds/refunds-table.component.tsx +137 -0
- package/src/refunds/refunds-table.scss +37 -0
- package/src/refunds/refunds-table.test.tsx +105 -0
- package/src/refunds/refunds.resource.test.ts +44 -0
- package/src/refunds/refunds.resource.ts +42 -0
- package/src/refunds/refunds.types.test.ts +15 -0
- package/src/refunds/request-refund.modal.scss +84 -0
- package/src/refunds/request-refund.modal.test.tsx +204 -0
- package/src/refunds/request-refund.modal.tsx +218 -0
- package/src/routes.json +36 -2
- package/src/types/index.ts +116 -1
- package/src/visit-bills/visit-bills-panel.component.tsx +151 -0
- package/src/visit-bills/visit-bills-panel.scss +31 -0
- package/src/visit-bills/visit-bills-panel.test.tsx +113 -0
- package/tools/empty-module.ts +1 -0
- package/tools/setup-tests.ts +9 -9
- package/translations/am.json +154 -16
- package/translations/ar.json +154 -16
- package/translations/ar_SY.json +154 -16
- package/translations/bn.json +154 -16
- package/translations/cs.json +154 -16
- package/translations/de.json +154 -16
- package/translations/en.json +154 -16
- package/translations/en_US.json +154 -16
- package/translations/es.json +154 -16
- package/translations/es_MX.json +154 -16
- package/translations/fr.json +154 -16
- package/translations/he.json +154 -16
- package/translations/hi.json +154 -16
- package/translations/hi_IN.json +154 -16
- package/translations/id.json +154 -16
- package/translations/it.json +154 -16
- package/translations/ka.json +154 -16
- package/translations/km.json +154 -16
- package/translations/ku.json +154 -16
- package/translations/ky.json +154 -16
- package/translations/lg.json +154 -16
- package/translations/ne.json +154 -16
- package/translations/pl.json +154 -16
- package/translations/pt.json +154 -16
- package/translations/pt_BR.json +154 -16
- package/translations/qu.json +154 -16
- package/translations/ro_RO.json +154 -16
- package/translations/ru_RU.json +154 -16
- package/translations/si.json +154 -16
- package/translations/sq.json +154 -16
- package/translations/sw.json +154 -16
- package/translations/sw_KE.json +154 -16
- package/translations/tr.json +154 -16
- package/translations/tr_TR.json +154 -16
- package/translations/uk.json +154 -16
- package/translations/uz.json +154 -16
- package/translations/uz@Latn.json +154 -16
- package/translations/uz_UZ.json +154 -16
- package/translations/vi.json +154 -16
- package/translations/zh.json +154 -16
- package/translations/zh_CN.json +179 -41
- package/translations/zh_TW.json +154 -16
- package/tsconfig.json +3 -3
- package/vitest.config.js +28 -0
- package/.turbo/cache/6c998b0f30a031ab-meta.json +0 -1
- package/.turbo/cache/6c998b0f30a031ab.tar.zst +0 -0
- package/dist/1119.js +0 -1
- package/dist/1197.js +0 -1
- package/dist/1435.js +0 -1
- package/dist/1435.js.map +0 -1
- package/dist/1807.js +0 -1
- package/dist/1807.js.map +0 -1
- package/dist/2146.js +0 -1
- package/dist/2177.js +0 -2
- package/dist/2177.js.LICENSE.txt +0 -9
- package/dist/2177.js.map +0 -1
- package/dist/2690.js +0 -1
- package/dist/2704.js +0 -1
- package/dist/2704.js.map +0 -1
- package/dist/3002.js +0 -1
- package/dist/3002.js.map +0 -1
- package/dist/3041.js +0 -1
- package/dist/3041.js.map +0 -1
- package/dist/3099.js +0 -1
- package/dist/3184.js +0 -2
- package/dist/3184.js.LICENSE.txt +0 -14
- package/dist/3184.js.map +0 -1
- package/dist/3584.js +0 -1
- package/dist/4055.js +0 -1
- package/dist/4132.js +0 -1
- package/dist/4225.js +0 -1
- package/dist/4225.js.map +0 -1
- package/dist/4300.js +0 -1
- package/dist/4335.js +0 -1
- package/dist/439.js +0 -1
- package/dist/4618.js +0 -1
- package/dist/4652.js +0 -1
- package/dist/4944.js +0 -1
- package/dist/5173.js +0 -1
- package/dist/5241.js +0 -1
- package/dist/5422.js +0 -1
- package/dist/5422.js.map +0 -1
- package/dist/5442.js +0 -1
- package/dist/5661.js +0 -1
- package/dist/6022.js +0 -1
- package/dist/6404.js +0 -1
- package/dist/6404.js.map +0 -1
- package/dist/6468.js +0 -1
- package/dist/6540.js +0 -2
- package/dist/6540.js.LICENSE.txt +0 -9
- package/dist/6540.js.map +0 -1
- package/dist/6589.js +0 -1
- package/dist/6606.js +0 -1
- package/dist/6606.js.map +0 -1
- package/dist/6679.js +0 -1
- package/dist/6792.js +0 -1
- package/dist/6792.js.map +0 -1
- package/dist/6840.js +0 -1
- package/dist/6859.js +0 -1
- package/dist/7097.js +0 -1
- package/dist/7159.js +0 -1
- package/dist/723.js +0 -1
- package/dist/7255.js +0 -1
- package/dist/7255.js.map +0 -1
- package/dist/7617.js +0 -1
- package/dist/795.js +0 -1
- package/dist/8163.js +0 -1
- package/dist/8341.js +0 -2
- package/dist/8341.js.LICENSE.txt +0 -52
- package/dist/8341.js.map +0 -1
- package/dist/8349.js +0 -1
- package/dist/8371.js +0 -1
- package/dist/8421.js +0 -1
- package/dist/8421.js.map +0 -1
- package/dist/8618.js +0 -1
- package/dist/890.js +0 -1
- package/dist/9214.js +0 -1
- package/dist/9538.js +0 -1
- package/dist/9569.js +0 -1
- package/dist/961.js +0 -2
- package/dist/961.js.LICENSE.txt +0 -19
- package/dist/961.js.map +0 -1
- package/dist/986.js +0 -1
- package/dist/9879.js +0 -1
- package/dist/9895.js +0 -1
- package/dist/9900.js +0 -1
- package/dist/9913.js +0 -1
- package/dist/main.js.LICENSE.txt +0 -62
- package/src/billable-services/bill-waiver/bill-selection.component.tsx +0 -76
- package/src/billable-services/bill-waiver/bill-waiver-form.component.tsx +0 -107
- package/src/billable-services/bill-waiver/bill-waiver-form.scss +0 -34
- package/src/billable-services/bill-waiver/bill-waiver.component.tsx +0 -34
- package/src/billable-services/bill-waiver/bill-waiver.scss +0 -10
- package/src/billable-services/bill-waiver/patient-bills.component.tsx +0 -134
- package/webpack.config.js +0 -1
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Button,
|
|
4
|
+
FormGroup,
|
|
5
|
+
InlineLoading,
|
|
6
|
+
ModalBody,
|
|
7
|
+
ModalFooter,
|
|
8
|
+
ModalHeader,
|
|
9
|
+
NumberInput,
|
|
10
|
+
RadioButton,
|
|
11
|
+
RadioButtonGroup,
|
|
12
|
+
Stack,
|
|
13
|
+
TextArea,
|
|
14
|
+
} from '@carbon/react';
|
|
15
|
+
import { useTranslation } from 'react-i18next';
|
|
16
|
+
import { getCoreTranslation, showSnackbar, useConfig } from '@openmrs/esm-framework';
|
|
17
|
+
import type { BillingConfig } from '../config-schema';
|
|
18
|
+
import { convertToCurrency } from '../helpers';
|
|
19
|
+
import { BillDiscountType } from '../types';
|
|
20
|
+
import { requestDiscount } from './discounts.resource';
|
|
21
|
+
import styles from './request-discount.modal.scss';
|
|
22
|
+
|
|
23
|
+
interface Props {
|
|
24
|
+
closeModal: () => void;
|
|
25
|
+
bill: {
|
|
26
|
+
uuid: string;
|
|
27
|
+
total: number;
|
|
28
|
+
amountDue: number;
|
|
29
|
+
receiptNumber?: string;
|
|
30
|
+
lineItemCount?: number;
|
|
31
|
+
};
|
|
32
|
+
lineItem?: {
|
|
33
|
+
uuid: string;
|
|
34
|
+
display: string;
|
|
35
|
+
total: number;
|
|
36
|
+
quantity?: number;
|
|
37
|
+
price?: number;
|
|
38
|
+
};
|
|
39
|
+
onMutate?: () => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const RequestDiscountModal: React.FC<Props> = ({ closeModal, bill, lineItem, onMutate }) => {
|
|
43
|
+
const { t } = useTranslation();
|
|
44
|
+
const { defaultCurrency } = useConfig<BillingConfig>();
|
|
45
|
+
const [discountType, setDiscountType] = useState<BillDiscountType>(BillDiscountType.PERCENTAGE);
|
|
46
|
+
const [value, setValue] = useState<number | null>(null);
|
|
47
|
+
const [justification, setJustification] = useState('');
|
|
48
|
+
const [submitting, setSubmitting] = useState(false);
|
|
49
|
+
|
|
50
|
+
const isLineItem = !!lineItem;
|
|
51
|
+
const scopeTotal = lineItem?.total ?? bill.total;
|
|
52
|
+
|
|
53
|
+
const trimmedJustification = justification.trim();
|
|
54
|
+
|
|
55
|
+
const discountAmount =
|
|
56
|
+
value != null && !Number.isNaN(value) && value > 0
|
|
57
|
+
? discountType === BillDiscountType.PERCENTAGE
|
|
58
|
+
? (scopeTotal * value) / 100
|
|
59
|
+
: value
|
|
60
|
+
: null;
|
|
61
|
+
|
|
62
|
+
const justificationPlaceholder = isLineItem
|
|
63
|
+
? t('lineItemJustificationPlaceholder', 'e.g., damaged item, wrong quantity billed, expired stock')
|
|
64
|
+
: t('billJustificationPlaceholder', 'e.g., hardship assistance, charity case, write-off');
|
|
65
|
+
|
|
66
|
+
const justificationError =
|
|
67
|
+
justification.length > 1000 ? t('justificationTooLong', 'Justification cannot exceed 1000 characters') : null;
|
|
68
|
+
|
|
69
|
+
const valueError = (() => {
|
|
70
|
+
if (value == null || Number.isNaN(value)) return null;
|
|
71
|
+
if (value <= 0) return t('valueMustBePositive', 'Value must be greater than 0');
|
|
72
|
+
if (discountType === BillDiscountType.PERCENTAGE && value > 100)
|
|
73
|
+
return t('percentageTooHigh', 'Percentage cannot exceed 100');
|
|
74
|
+
if (discountAmount == null) return null;
|
|
75
|
+
if (discountAmount > bill.amountDue) return t('discountExceedsAmountDue', 'Discount cannot exceed the amount due');
|
|
76
|
+
if (lineItem && discountAmount > lineItem.total)
|
|
77
|
+
return t('discountExceedsLineTotal', 'Discount cannot exceed the line item total');
|
|
78
|
+
return null;
|
|
79
|
+
})();
|
|
80
|
+
|
|
81
|
+
const canSubmit =
|
|
82
|
+
!submitting && value != null && trimmedJustification.length > 0 && !justificationError && !valueError;
|
|
83
|
+
|
|
84
|
+
const handleSubmit = async () => {
|
|
85
|
+
if (!canSubmit || value == null) return;
|
|
86
|
+
setSubmitting(true);
|
|
87
|
+
try {
|
|
88
|
+
await requestDiscount({
|
|
89
|
+
bill: bill.uuid,
|
|
90
|
+
lineItem: lineItem?.uuid,
|
|
91
|
+
discountType,
|
|
92
|
+
discountValue: value,
|
|
93
|
+
justification: trimmedJustification,
|
|
94
|
+
});
|
|
95
|
+
onMutate?.();
|
|
96
|
+
showSnackbar({
|
|
97
|
+
title: t('discountRequested', 'Discount request submitted'),
|
|
98
|
+
subtitle: t('discountPendingReview', 'An admin will review your request'),
|
|
99
|
+
kind: 'success',
|
|
100
|
+
});
|
|
101
|
+
closeModal();
|
|
102
|
+
} catch (err: unknown) {
|
|
103
|
+
const subtitle = (err as any)?.responseBody?.error?.message ?? (err instanceof Error ? err.message : undefined);
|
|
104
|
+
showSnackbar({
|
|
105
|
+
title: t('discountRequestFailed', 'Could not submit discount request'),
|
|
106
|
+
subtitle,
|
|
107
|
+
kind: 'error',
|
|
108
|
+
});
|
|
109
|
+
} finally {
|
|
110
|
+
setSubmitting(false);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<>
|
|
116
|
+
<ModalHeader closeModal={closeModal} title={t('requestDiscount', 'Request discount')} />
|
|
117
|
+
<ModalBody className={styles.modalBody}>
|
|
118
|
+
<Stack gap={5}>
|
|
119
|
+
<section className={styles.scopePanel} aria-label={t('discountScope', 'Discount scope')}>
|
|
120
|
+
<div className={styles.scopePrimary}>
|
|
121
|
+
{isLineItem ? lineItem.display : t('wholeBillScope', 'Whole bill')}
|
|
122
|
+
</div>
|
|
123
|
+
<ul className={styles.scopeMeta}>
|
|
124
|
+
{bill.receiptNumber && <li>{t('invoiceLabel', 'Invoice {{number}}', { number: bill.receiptNumber })}</li>}
|
|
125
|
+
{isLineItem && lineItem.quantity != null && lineItem.price != null && (
|
|
126
|
+
<li>
|
|
127
|
+
{t('qtyTimesPrice', 'Qty {{quantity}} × {{price}}', {
|
|
128
|
+
quantity: lineItem.quantity,
|
|
129
|
+
price: convertToCurrency(lineItem.price, defaultCurrency),
|
|
130
|
+
})}
|
|
131
|
+
</li>
|
|
132
|
+
)}
|
|
133
|
+
{!isLineItem && bill.lineItemCount != null && (
|
|
134
|
+
<li>{t('lineItemCount', '{{count}} line items', { count: bill.lineItemCount })}</li>
|
|
135
|
+
)}
|
|
136
|
+
<li className={styles.scopeTotal}>
|
|
137
|
+
{isLineItem
|
|
138
|
+
? t('lineTotalLabel', 'Line total {{total}}', {
|
|
139
|
+
total: convertToCurrency(scopeTotal, defaultCurrency),
|
|
140
|
+
})
|
|
141
|
+
: t('billTotalLabel', 'Bill total {{total}}', {
|
|
142
|
+
total: convertToCurrency(scopeTotal, defaultCurrency),
|
|
143
|
+
})}
|
|
144
|
+
</li>
|
|
145
|
+
</ul>
|
|
146
|
+
</section>
|
|
147
|
+
|
|
148
|
+
<FormGroup legendText={t('discount', 'Discount')} className={styles.discountGroup}>
|
|
149
|
+
<Stack gap={4}>
|
|
150
|
+
<RadioButtonGroup
|
|
151
|
+
legendText=""
|
|
152
|
+
name="discount-type"
|
|
153
|
+
valueSelected={discountType}
|
|
154
|
+
onChange={(v) => setDiscountType(v as BillDiscountType)}>
|
|
155
|
+
<RadioButton labelText={t('percentage', 'Percentage')} value={BillDiscountType.PERCENTAGE} />
|
|
156
|
+
<RadioButton labelText={t('fixedAmount', 'Fixed amount')} value={BillDiscountType.FIXED_AMOUNT} />
|
|
157
|
+
</RadioButtonGroup>
|
|
158
|
+
|
|
159
|
+
<NumberInput
|
|
160
|
+
id="discount-value"
|
|
161
|
+
label={
|
|
162
|
+
discountType === BillDiscountType.PERCENTAGE
|
|
163
|
+
? t('valuePercentLabel', 'Value (%)')
|
|
164
|
+
: t('valueAmountLabel', 'Value ({{currency}})', { currency: defaultCurrency })
|
|
165
|
+
}
|
|
166
|
+
allowEmpty
|
|
167
|
+
min={0}
|
|
168
|
+
value={value ?? ''}
|
|
169
|
+
invalid={!!valueError}
|
|
170
|
+
invalidText={valueError ?? ''}
|
|
171
|
+
onChange={(_e, { value: v }) => {
|
|
172
|
+
if (v === '' || v == null) {
|
|
173
|
+
setValue(null);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const n = typeof v === 'string' ? parseFloat(v) : v;
|
|
177
|
+
setValue(Number.isNaN(n) ? null : n);
|
|
178
|
+
}}
|
|
179
|
+
/>
|
|
180
|
+
</Stack>
|
|
181
|
+
</FormGroup>
|
|
182
|
+
|
|
183
|
+
{discountAmount != null && (
|
|
184
|
+
<section className={styles.summaryCard} aria-label={t('discountSummary', 'Discount summary')}>
|
|
185
|
+
<span className={styles.summaryLabel}>
|
|
186
|
+
{isLineItem ? t('lineTotal', 'Line total') : t('billTotal', 'Bill total')}
|
|
187
|
+
</span>
|
|
188
|
+
<span className={styles.summaryValue}>{convertToCurrency(scopeTotal, defaultCurrency)}</span>
|
|
189
|
+
|
|
190
|
+
<span className={styles.summaryLabel}>
|
|
191
|
+
{discountType === BillDiscountType.PERCENTAGE && value != null
|
|
192
|
+
? t('discountWithPercent', 'Discount ({{percent}}%)', { percent: value })
|
|
193
|
+
: t('discountLabel', 'Discount')}
|
|
194
|
+
</span>
|
|
195
|
+
<span className={`${styles.summaryValue} ${styles.summaryDiscount}`}>
|
|
196
|
+
−{convertToCurrency(discountAmount, defaultCurrency)}
|
|
197
|
+
</span>
|
|
198
|
+
|
|
199
|
+
{isLineItem && (
|
|
200
|
+
<>
|
|
201
|
+
<span className={styles.summaryLabel}>{t('lineAfterDiscount', 'Line after discount')}</span>
|
|
202
|
+
<span className={styles.summaryValue}>
|
|
203
|
+
{convertToCurrency(scopeTotal - discountAmount, defaultCurrency)}
|
|
204
|
+
</span>
|
|
205
|
+
</>
|
|
206
|
+
)}
|
|
207
|
+
|
|
208
|
+
<span className={styles.summaryDivider} aria-hidden="true" />
|
|
209
|
+
|
|
210
|
+
<span className={styles.summaryLabel}>{t('outstanding', 'Outstanding')}</span>
|
|
211
|
+
<span className={styles.summaryValue}>{convertToCurrency(bill.amountDue, defaultCurrency)}</span>
|
|
212
|
+
|
|
213
|
+
<span className={styles.summaryTotalLabel}>
|
|
214
|
+
{t('amountDueOnBillAfterDiscount', 'Amount due on bill after discount')}
|
|
215
|
+
</span>
|
|
216
|
+
<span className={styles.summaryTotalValue}>
|
|
217
|
+
{convertToCurrency(bill.amountDue - discountAmount, defaultCurrency)}
|
|
218
|
+
</span>
|
|
219
|
+
</section>
|
|
220
|
+
)}
|
|
221
|
+
|
|
222
|
+
<TextArea
|
|
223
|
+
id="discount-justification"
|
|
224
|
+
labelText={t('justification', 'Justification')}
|
|
225
|
+
placeholder={justificationPlaceholder}
|
|
226
|
+
maxCount={1000}
|
|
227
|
+
enableCounter
|
|
228
|
+
required
|
|
229
|
+
rows={3}
|
|
230
|
+
value={justification}
|
|
231
|
+
invalid={!!justificationError}
|
|
232
|
+
invalidText={justificationError ?? ''}
|
|
233
|
+
onChange={(e) => setJustification(e.target.value)}
|
|
234
|
+
/>
|
|
235
|
+
</Stack>
|
|
236
|
+
</ModalBody>
|
|
237
|
+
<ModalFooter>
|
|
238
|
+
<Button kind="secondary" onClick={closeModal}>
|
|
239
|
+
{getCoreTranslation('cancel')}
|
|
240
|
+
</Button>
|
|
241
|
+
<Button kind="primary" onClick={handleSubmit} disabled={!canSubmit}>
|
|
242
|
+
{submitting ? (
|
|
243
|
+
<InlineLoading description={t('submitting', 'Submitting') + '...'} />
|
|
244
|
+
) : (
|
|
245
|
+
<span>{t('submitRequest', 'Submit request')}</span>
|
|
246
|
+
)}
|
|
247
|
+
</Button>
|
|
248
|
+
</ModalFooter>
|
|
249
|
+
</>
|
|
250
|
+
);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export default RequestDiscountModal;
|
package/src/index.ts
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FinancialAssets, Settings, TagGroup, Wallet } from '@carbon/react/icons';
|
|
2
2
|
import { createDashboard, defineConfigSchema, getAsyncLifecycle, getSyncLifecycle } from '@openmrs/esm-framework';
|
|
3
3
|
import { createLeftPanelLink } from './left-panel-link.component';
|
|
4
|
-
import { dashboardMeta } from './dashboard.meta';
|
|
5
|
-
import { Settings, Wallet } from '@carbon/react/icons';
|
|
6
4
|
import { createBillableServicesLeftPanelLink } from './billable-services/billable-services-left-panel-link.component';
|
|
7
5
|
import { createBillableServicesLeftPanelMenu } from './billable-services/billable-services-left-panel-menu.component';
|
|
8
|
-
import
|
|
6
|
+
import { createDiscountRequestsLeftPanelLink } from './discounts/admin/discount-requests-left-panel-link.component';
|
|
7
|
+
import { createRefundRequestsLeftPanelLink } from './refunds/admin/refund-requests-left-panel-link.component';
|
|
9
8
|
import appMenu from './billable-services/billable-services-menu-item/item.component';
|
|
10
9
|
import BillableServiceHome from './billable-services/billable-services-home.component';
|
|
11
10
|
import BillableServicesCardLink from './billable-services-admin-card-link.component';
|
|
12
11
|
import BillHistory from './bill-history/bill-history.component';
|
|
13
12
|
import BillingCheckInForm from './billing-form/billing-checkin-form.component';
|
|
13
|
+
import VisitAttributeTags from './invoice/payments/visit-tags/visit-attribute.component';
|
|
14
14
|
import DeletePaymentModeModal from './billable-services/payment-modes/delete-payment-mode.modal';
|
|
15
15
|
import EditBillLineItemModal from './bill-item-actions/edit-bill-item.modal';
|
|
16
16
|
import PaymentModeFormModal from './billable-services/payment-modes/payment-mode-form.modal';
|
|
17
17
|
import RequirePaymentModal from './modal/require-payment.modal';
|
|
18
|
+
import AddCashPointModal from './billable-services/cash-point/add-cash-point.modal';
|
|
19
|
+
import RequestDiscountModal from './discounts/request-discount.modal';
|
|
20
|
+
import ReviewBillDiscountsModal from './discounts/admin/review-bill-discounts/review-bill-discounts.modal';
|
|
18
21
|
import RootComponent from './root.component';
|
|
19
|
-
import
|
|
22
|
+
import PaymentStatusTag from './payment-status-tag/payment-status-tag.component';
|
|
23
|
+
import { configSchema } from './config-schema';
|
|
24
|
+
import { dashboardMeta } from './dashboard.meta';
|
|
20
25
|
|
|
21
26
|
const moduleName = '@openmrs/esm-billing-app';
|
|
22
27
|
|
|
@@ -54,6 +59,8 @@ export const billingCheckInForm = getSyncLifecycle(BillingCheckInForm, options);
|
|
|
54
59
|
|
|
55
60
|
export const billingPatientSummary = getSyncLifecycle(BillHistory, options);
|
|
56
61
|
|
|
62
|
+
export const visitBillsPanel = getAsyncLifecycle(() => import('./visit-bills/visit-bills-panel.component'), options);
|
|
63
|
+
|
|
57
64
|
export const requirePaymentModal = getSyncLifecycle(RequirePaymentModal, options);
|
|
58
65
|
|
|
59
66
|
export const paymentModeFormModal = getSyncLifecycle(PaymentModeFormModal, options);
|
|
@@ -68,6 +75,8 @@ export const root = getSyncLifecycle(RootComponent, options);
|
|
|
68
75
|
|
|
69
76
|
export const visitAttributeTags = getSyncLifecycle(VisitAttributeTags, options);
|
|
70
77
|
|
|
78
|
+
export const patientPaymentStatusTag = getSyncLifecycle(PaymentStatusTag, options);
|
|
79
|
+
|
|
71
80
|
export const billingFormWorkspace = getAsyncLifecycle(() => import('./billing-form/billing-form.workspace'), options);
|
|
72
81
|
|
|
73
82
|
export const billableServiceFormWorkspace = getAsyncLifecycle(
|
|
@@ -86,22 +95,6 @@ export const billableServicesLeftPanelLink = getSyncLifecycle(
|
|
|
86
95
|
options,
|
|
87
96
|
);
|
|
88
97
|
|
|
89
|
-
// t('billWaiver', 'Bill waiver')
|
|
90
|
-
// Bill waiver feature disabled - O3-5057
|
|
91
|
-
// The following export is commented out along with:
|
|
92
|
-
// - BillWaiver component import and route in billable-services-home.component.tsx
|
|
93
|
-
// - bill-waiver-left-panel-link extension removed from routes.json
|
|
94
|
-
// export const billWaiverLeftPanelLink = getSyncLifecycle(
|
|
95
|
-
// createBillableServicesLeftPanelLink({
|
|
96
|
-
// name: 'bill-waiver',
|
|
97
|
-
// title: 'billWaiver',
|
|
98
|
-
// path: 'waive-bill',
|
|
99
|
-
// icon: Money,
|
|
100
|
-
// privilege: 'coreapps.systemAdministration',
|
|
101
|
-
// }),
|
|
102
|
-
// options,
|
|
103
|
-
// );
|
|
104
|
-
|
|
105
98
|
// t('billingSettings', 'Billing settings')
|
|
106
99
|
// t('cashPointConfig', 'Cash point configuration')
|
|
107
100
|
// t('paymentModesConfig', 'Payment modes configuration')
|
|
@@ -135,3 +128,41 @@ export const finalizeBillConfirmationModal = getAsyncLifecycle(
|
|
|
135
128
|
() => import('./modal/finalize-bill-confirmation.modal'),
|
|
136
129
|
options,
|
|
137
130
|
);
|
|
131
|
+
|
|
132
|
+
export const deleteBillConfirmationModal = getAsyncLifecycle(
|
|
133
|
+
() => import('./modal/delete-bill-confirmation.modal'),
|
|
134
|
+
options,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
export const requestDiscountModal = getSyncLifecycle(RequestDiscountModal, options);
|
|
138
|
+
|
|
139
|
+
export const reviewBillDiscountsModal = getSyncLifecycle(ReviewBillDiscountsModal, options);
|
|
140
|
+
|
|
141
|
+
// t('discountRequests', 'Discount requests')
|
|
142
|
+
export const discountRequestsLeftPanelLink = getSyncLifecycle(
|
|
143
|
+
createDiscountRequestsLeftPanelLink({
|
|
144
|
+
name: 'discount-requests',
|
|
145
|
+
title: 'discountRequests',
|
|
146
|
+
path: 'discount-requests',
|
|
147
|
+
icon: TagGroup,
|
|
148
|
+
}),
|
|
149
|
+
options,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
export const requestRefundModal = getAsyncLifecycle(() => import('./refunds/request-refund.modal'), options);
|
|
153
|
+
|
|
154
|
+
export const reviewBillRefundsModal = getAsyncLifecycle(
|
|
155
|
+
() => import('./refunds/admin/review-bill-refunds/review-bill-refunds.modal'),
|
|
156
|
+
options,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// t('refundRequests', 'Refund requests')
|
|
160
|
+
export const refundRequestsLeftPanelLink = getSyncLifecycle(
|
|
161
|
+
createRefundRequestsLeftPanelLink({
|
|
162
|
+
name: 'refund-requests',
|
|
163
|
+
title: 'refundRequests',
|
|
164
|
+
path: 'refund-requests',
|
|
165
|
+
icon: FinancialAssets,
|
|
166
|
+
}),
|
|
167
|
+
options,
|
|
168
|
+
);
|
|
@@ -16,20 +16,30 @@ import {
|
|
|
16
16
|
TableToolbarSearch,
|
|
17
17
|
Tile,
|
|
18
18
|
} from '@carbon/react';
|
|
19
|
-
import {
|
|
20
|
-
|
|
19
|
+
import {
|
|
20
|
+
getCoreTranslation,
|
|
21
|
+
isDesktop,
|
|
22
|
+
showModal,
|
|
23
|
+
useConfig,
|
|
24
|
+
useDebounce,
|
|
25
|
+
useLayoutType,
|
|
26
|
+
} from '@openmrs/esm-framework';
|
|
27
|
+
import LineItemActionMenu from './line-item-action-menu.component';
|
|
21
28
|
import { convertToCurrency } from '../helpers';
|
|
22
29
|
import type { BillingConfig } from '../config-schema';
|
|
23
|
-
import
|
|
30
|
+
import { BillStatus, RefundStatus, type LineItem, type MappedBill } from '../types';
|
|
24
31
|
import styles from './invoice-table.scss';
|
|
25
32
|
|
|
33
|
+
const getLineItemTotal = (item: LineItem) => (item.price ?? 0) * (item.quantity ?? 0);
|
|
34
|
+
|
|
26
35
|
type InvoiceTableProps = {
|
|
27
36
|
bill: MappedBill;
|
|
28
37
|
isLoadingBill?: boolean;
|
|
29
38
|
onMutate?: () => void;
|
|
39
|
+
viewOnly?: boolean;
|
|
30
40
|
};
|
|
31
41
|
|
|
32
|
-
const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isLoadingBill, onMutate }) => {
|
|
42
|
+
const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isLoadingBill, onMutate, viewOnly }) => {
|
|
33
43
|
const { t } = useTranslation();
|
|
34
44
|
const { defaultCurrency } = useConfig<BillingConfig>();
|
|
35
45
|
const layout = useLayoutType();
|
|
@@ -38,6 +48,78 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isLoadingBill, onMuta
|
|
|
38
48
|
const [searchTerm, setSearchTerm] = useState('');
|
|
39
49
|
const debouncedSearchTerm = useDebounce(searchTerm);
|
|
40
50
|
|
|
51
|
+
const discounts = useMemo(() => (bill?.discounts ?? []).filter((d) => !d.voided), [bill?.discounts]);
|
|
52
|
+
const billStatusEligible = bill?.status === BillStatus.PENDING || bill?.status === BillStatus.POSTED;
|
|
53
|
+
const hasBillLevelDiscount = discounts.some((d) => !d.lineItemUuid);
|
|
54
|
+
|
|
55
|
+
const billRefunds = useMemo(() => (bill?.refunds ?? []).filter((r) => !r.voided), [bill?.refunds]);
|
|
56
|
+
const billStatusRefundEligible =
|
|
57
|
+
bill?.status === BillStatus.PAID ||
|
|
58
|
+
bill?.status === BillStatus.PARTIALLY_REFUNDED ||
|
|
59
|
+
bill?.status === BillStatus.REFUND_REQUESTED;
|
|
60
|
+
const activeRefunds = billRefunds.filter(
|
|
61
|
+
(r) => r.status === RefundStatus.REQUESTED || r.status === RefundStatus.APPROVED,
|
|
62
|
+
);
|
|
63
|
+
const activeBillLevelRefund = activeRefunds.some((r) => !r.lineItemUuid);
|
|
64
|
+
|
|
65
|
+
const lineHasActiveRefund = (lineItemUuid: string) => activeRefunds.some((r) => r.lineItemUuid === lineItemUuid);
|
|
66
|
+
|
|
67
|
+
const showLineItemRefundRequest = (lineItem: LineItem) =>
|
|
68
|
+
billStatusRefundEligible && !activeBillLevelRefund && !lineHasActiveRefund(lineItem.uuid ?? '') && !!lineItem.uuid;
|
|
69
|
+
|
|
70
|
+
const lineHasActiveDiscount = (lineItemUuid: string) => discounts.some((d) => d.lineItemUuid === lineItemUuid);
|
|
71
|
+
|
|
72
|
+
const showLineItemRequest = (lineItem: LineItem) =>
|
|
73
|
+
billStatusEligible && !hasBillLevelDiscount && !lineHasActiveDiscount(lineItem.uuid);
|
|
74
|
+
|
|
75
|
+
const handleLineItemRefundRequest = (item: LineItem) => {
|
|
76
|
+
if (!item.uuid) return;
|
|
77
|
+
const lineTotal = getLineItemTotal(item);
|
|
78
|
+
const lineCommittedRefunds = billRefunds.filter(
|
|
79
|
+
(r) =>
|
|
80
|
+
r.lineItemUuid === item.uuid && (r.status === RefundStatus.APPROVED || r.status === RefundStatus.COMPLETED),
|
|
81
|
+
);
|
|
82
|
+
const remaining = lineTotal - lineCommittedRefunds.reduce((s, r) => s + r.refundAmount, 0);
|
|
83
|
+
const dispose = showModal('request-refund-modal', {
|
|
84
|
+
bill: {
|
|
85
|
+
uuid: bill.uuid,
|
|
86
|
+
total: bill.totalAmount ?? 0,
|
|
87
|
+
amountAfterDiscount: bill.netAmount ?? bill.totalAmount ?? 0,
|
|
88
|
+
},
|
|
89
|
+
lineItem: {
|
|
90
|
+
uuid: item.uuid,
|
|
91
|
+
display: item.item || item.billableService || '--',
|
|
92
|
+
total: lineTotal,
|
|
93
|
+
quantity: item.quantity,
|
|
94
|
+
price: item.price,
|
|
95
|
+
},
|
|
96
|
+
remainingRefundable: Math.max(0, remaining),
|
|
97
|
+
onMutate: () => onMutate?.(),
|
|
98
|
+
closeModal: () => dispose(),
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const handleLineItemRequest = (lineItem: LineItem) => {
|
|
103
|
+
const dispose = showModal('request-discount-modal', {
|
|
104
|
+
bill: {
|
|
105
|
+
uuid: bill.uuid,
|
|
106
|
+
total: bill.totalAmount ?? 0,
|
|
107
|
+
amountDue: Math.max(0, (bill.netAmount ?? bill.totalAmount ?? 0) - (bill.tenderedAmount ?? 0)),
|
|
108
|
+
receiptNumber: bill.receiptNumber,
|
|
109
|
+
lineItemCount: lineItems.length,
|
|
110
|
+
},
|
|
111
|
+
lineItem: {
|
|
112
|
+
uuid: lineItem.uuid,
|
|
113
|
+
display: lineItem.item || lineItem.billableService || '--',
|
|
114
|
+
total: getLineItemTotal(lineItem),
|
|
115
|
+
quantity: lineItem.quantity,
|
|
116
|
+
price: lineItem.price,
|
|
117
|
+
},
|
|
118
|
+
onMutate: () => onMutate?.(),
|
|
119
|
+
closeModal: () => dispose(),
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
|
|
41
123
|
const filteredLineItems = useMemo(() => {
|
|
42
124
|
if (!debouncedSearchTerm) {
|
|
43
125
|
return lineItems;
|
|
@@ -68,10 +150,10 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isLoadingBill, onMuta
|
|
|
68
150
|
no: `${index + 1}`,
|
|
69
151
|
id: `${item.uuid}`,
|
|
70
152
|
billItem: item.billableService ? item.billableService : item?.item,
|
|
71
|
-
status: item.
|
|
153
|
+
status: item.status,
|
|
72
154
|
quantity: item.quantity,
|
|
73
155
|
price: convertToCurrency(item.price, defaultCurrency),
|
|
74
|
-
total: convertToCurrency(item
|
|
156
|
+
total: convertToCurrency(getLineItemTotal(item), defaultCurrency),
|
|
75
157
|
})) ?? [],
|
|
76
158
|
[filteredLineItems, defaultCurrency],
|
|
77
159
|
);
|
|
@@ -104,13 +186,15 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isLoadingBill, onMuta
|
|
|
104
186
|
</span>
|
|
105
187
|
}
|
|
106
188
|
title={t('lineItems', 'Line items')}>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
189
|
+
{!viewOnly && (
|
|
190
|
+
<TableToolbarSearch
|
|
191
|
+
className={styles.searchbox}
|
|
192
|
+
expanded
|
|
193
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearchTerm(e.target.value)}
|
|
194
|
+
placeholder={t('searchThisTable', 'Search this table')}
|
|
195
|
+
size={responsiveSize}
|
|
196
|
+
/>
|
|
197
|
+
)}
|
|
114
198
|
<Table
|
|
115
199
|
{...getTableProps()}
|
|
116
200
|
aria-label={t('invoiceLineItems', 'Invoice line items')}
|
|
@@ -120,7 +204,7 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isLoadingBill, onMuta
|
|
|
120
204
|
{headers.map((header) => (
|
|
121
205
|
<TableHeader key={header.key}>{header.header}</TableHeader>
|
|
122
206
|
))}
|
|
123
|
-
<TableHeader aria-label={getCoreTranslation('actions')} />
|
|
207
|
+
{!viewOnly && <TableHeader aria-label={getCoreTranslation('actions')} />}
|
|
124
208
|
</TableRow>
|
|
125
209
|
</TableHead>
|
|
126
210
|
<TableBody>
|
|
@@ -131,9 +215,21 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isLoadingBill, onMuta
|
|
|
131
215
|
{row.cells.map((cell) => (
|
|
132
216
|
<TableCell key={cell.id}>{cell.value}</TableCell>
|
|
133
217
|
))}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
218
|
+
{!viewOnly && (
|
|
219
|
+
<TableCell className="cds--table-column-menu">
|
|
220
|
+
{item && (
|
|
221
|
+
<LineItemActionMenu
|
|
222
|
+
bill={bill}
|
|
223
|
+
item={item}
|
|
224
|
+
onMutate={onMutate}
|
|
225
|
+
showDiscountRequest={showLineItemRequest(item)}
|
|
226
|
+
onDiscountRequest={() => handleLineItemRequest(item)}
|
|
227
|
+
showRefundRequest={showLineItemRefundRequest(item)}
|
|
228
|
+
onRefundRequest={() => handleLineItemRefundRequest(item)}
|
|
229
|
+
/>
|
|
230
|
+
)}
|
|
231
|
+
</TableCell>
|
|
232
|
+
)}
|
|
137
233
|
</TableRow>
|
|
138
234
|
);
|
|
139
235
|
})}
|
|
@@ -149,7 +245,9 @@ const InvoiceTable: React.FC<InvoiceTableProps> = ({ bill, isLoadingBill, onMuta
|
|
|
149
245
|
<p className={styles.filterEmptyStateContent}>
|
|
150
246
|
{t('noMatchingItemsToDisplay', 'No matching items to display')}
|
|
151
247
|
</p>
|
|
152
|
-
|
|
248
|
+
{!viewOnly && (
|
|
249
|
+
<p className={styles.filterEmptyStateHelper}>{t('checkFilters', 'Check the filters above')}</p>
|
|
250
|
+
)}
|
|
153
251
|
</Tile>
|
|
154
252
|
</Layer>
|
|
155
253
|
</div>
|