@openmrs/esm-billing-app 1.4.1-pre.2132 → 1.5.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/0b83e033a7f526de-meta.json +1 -0
- package/.turbo/cache/0b83e033a7f526de.tar.zst +0 -0
- package/.turbo/cache/4d87f683ec2d0aaf-meta.json +1 -0
- package/.turbo/cache/4d87f683ec2d0aaf.tar.zst +0 -0
- package/.turbo/cache/be36f8f2ffb033b0-meta.json +1 -0
- package/.turbo/cache/be36f8f2ffb033b0.tar.zst +0 -0
- package/.turbo/cache/fc440559fa397add-meta.json +1 -0
- package/.turbo/cache/fc440559fa397add.tar.zst +0 -0
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +0 -0
- package/.turbo/turbo-test.log +262 -0
- package/.turbo/turbo-typescript.log +0 -0
- package/dist/1899.css +1 -1
- package/dist/1899.css.map +1 -1
- package/dist/1899.js +1 -1
- package/dist/1899.js.map +1 -1
- package/dist/openmrs-esm-billing-app.js +1 -1
- package/dist/openmrs-esm-billing-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +2 -3
- package/src/billing-form/billing-form.scss +5 -0
- package/src/billing-form/billing-form.test.tsx +58 -1
- package/src/billing-form/billing-form.workspace.tsx +141 -133
- package/.turbo/cache/8872ba2be2ec04be-meta.json +0 -1
- package/.turbo/cache/8872ba2be2ec04be.tar.zst +0 -0
|
@@ -12,8 +12,10 @@ import {
|
|
|
12
12
|
NumberInput,
|
|
13
13
|
} from '@carbon/react';
|
|
14
14
|
import {
|
|
15
|
+
ExtensionSlot,
|
|
15
16
|
useConfig,
|
|
16
17
|
useLayoutType,
|
|
18
|
+
usePatient,
|
|
17
19
|
useVisit,
|
|
18
20
|
showSnackbar,
|
|
19
21
|
getCoreTranslation,
|
|
@@ -60,6 +62,7 @@ const BillingForm: React.FC<Workspace2DefinitionProps<BillingFormProps>> = ({
|
|
|
60
62
|
}) => {
|
|
61
63
|
const isTablet = useLayoutType() === 'tablet';
|
|
62
64
|
const { t } = useTranslation();
|
|
65
|
+
const { patient } = usePatient(patientUuid);
|
|
63
66
|
const { mutate: swrMutate } = useSWRConfig();
|
|
64
67
|
const { defaultCurrency, postBilledItems } = useConfig<BillingConfig>();
|
|
65
68
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
@@ -272,152 +275,157 @@ const BillingForm: React.FC<Workspace2DefinitionProps<BillingFormProps>> = ({
|
|
|
272
275
|
title={isEditMode ? t('addItemsToBill', 'Add items to bill') : t('addBillItems', 'Add bill items')}
|
|
273
276
|
hasUnsavedChanges={selectedItems.length > 0}>
|
|
274
277
|
<Form className={styles.form} onSubmit={handleSubmit}>
|
|
275
|
-
<div className={styles.
|
|
276
|
-
{
|
|
277
|
-
<
|
|
278
|
-
) : billUuid && billError ? (
|
|
279
|
-
<InlineNotification
|
|
280
|
-
kind="error"
|
|
281
|
-
lowContrast
|
|
282
|
-
title={t('errorLoadingBill', 'Error loading bill')}
|
|
283
|
-
subtitle={billError?.message}
|
|
284
|
-
/>
|
|
285
|
-
) : isEditMode && billableServicesError ? (
|
|
286
|
-
<InlineNotification
|
|
287
|
-
kind="error"
|
|
288
|
-
lowContrast
|
|
289
|
-
title={t('errorLoadingBillableServices', 'Error loading billable services')}
|
|
290
|
-
subtitle={billableServicesError?.message}
|
|
291
|
-
/>
|
|
292
|
-
) : (
|
|
293
|
-
<>
|
|
294
|
-
{isEditMode && (
|
|
295
|
-
<div className={styles.existingItemsContainer}>
|
|
296
|
-
<h4 className={styles.sectionHeading}>{t('existingItems', 'Existing items')}</h4>
|
|
297
|
-
{bill.lineItems.map((item) => (
|
|
298
|
-
<div key={item.uuid} className={styles.existingItemRow}>
|
|
299
|
-
<span className={styles.existingItemName}>
|
|
300
|
-
{item.billableService || item.item || item.display}
|
|
301
|
-
</span>
|
|
302
|
-
<span className={styles.existingItemDetail}>
|
|
303
|
-
{item.quantity} x {convertToCurrency(item.price, defaultCurrency)}
|
|
304
|
-
</span>
|
|
305
|
-
<span className={styles.existingItemTotal}>
|
|
306
|
-
{convertToCurrency(item.price * item.quantity, defaultCurrency)}
|
|
307
|
-
</span>
|
|
308
|
-
</div>
|
|
309
|
-
))}
|
|
310
|
-
<div className={styles.existingItemsSubtotal}>
|
|
311
|
-
<strong>
|
|
312
|
-
{t('subtotal', 'Subtotal')}: {convertToCurrency(existingItemsTotal, defaultCurrency)}
|
|
313
|
-
</strong>
|
|
314
|
-
</div>
|
|
315
|
-
</div>
|
|
316
|
-
)}
|
|
317
|
-
{isEditMode && <h4 className={styles.sectionHeading}>{t('newItems', 'New items')}</h4>}
|
|
318
|
-
{isLoading ? (
|
|
319
|
-
<InlineLoading description={getCoreTranslation('loading') + '...'} />
|
|
320
|
-
) : error ? (
|
|
321
|
-
<InlineNotification
|
|
322
|
-
kind="error"
|
|
323
|
-
lowContrast
|
|
324
|
-
title={t('errorLoadingBillableServices', 'Error loading billable services')}
|
|
325
|
-
subtitle={error?.message}
|
|
326
|
-
/>
|
|
327
|
-
) : (
|
|
328
|
-
<ComboBox
|
|
329
|
-
id="searchItems"
|
|
330
|
-
onChange={({ selectedItem: item }: { selectedItem: BillableItem }) => selectBillableItem(item)}
|
|
331
|
-
itemToString={(item: BillableItem) => item?.name || ''}
|
|
332
|
-
items={availableBillableItems}
|
|
333
|
-
titleText={t('searchItems', 'Search items and services')}
|
|
334
|
-
/>
|
|
335
|
-
)}
|
|
336
|
-
</>
|
|
278
|
+
<div className={styles.formContent}>
|
|
279
|
+
{isTablet && patient && patientUuid && (
|
|
280
|
+
<ExtensionSlot name="patient-header-slot" state={{ patient, patientUuid, hideActionsOverflow: true }} />
|
|
337
281
|
)}
|
|
338
|
-
{
|
|
339
|
-
|
|
340
|
-
<
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
282
|
+
<div className={styles.grid}>
|
|
283
|
+
{billUuid && isLoadingBill ? (
|
|
284
|
+
<InlineLoading description={getCoreTranslation('loading') + '...'} />
|
|
285
|
+
) : billUuid && billError ? (
|
|
286
|
+
<InlineNotification
|
|
287
|
+
kind="error"
|
|
288
|
+
lowContrast
|
|
289
|
+
title={t('errorLoadingBill', 'Error loading bill')}
|
|
290
|
+
subtitle={billError?.message}
|
|
291
|
+
/>
|
|
292
|
+
) : isEditMode && billableServicesError ? (
|
|
293
|
+
<InlineNotification
|
|
294
|
+
kind="error"
|
|
295
|
+
lowContrast
|
|
296
|
+
title={t('errorLoadingBillableServices', 'Error loading billable services')}
|
|
297
|
+
subtitle={billableServicesError?.message}
|
|
298
|
+
/>
|
|
299
|
+
) : (
|
|
300
|
+
<>
|
|
301
|
+
{isEditMode && (
|
|
302
|
+
<div className={styles.existingItemsContainer}>
|
|
303
|
+
<h4 className={styles.sectionHeading}>{t('existingItems', 'Existing items')}</h4>
|
|
304
|
+
{bill.lineItems.map((item) => (
|
|
305
|
+
<div key={item.uuid} className={styles.existingItemRow}>
|
|
306
|
+
<span className={styles.existingItemName}>
|
|
307
|
+
{item.billableService || item.item || item.display}
|
|
308
|
+
</span>
|
|
309
|
+
<span className={styles.existingItemDetail}>
|
|
310
|
+
{item.quantity} x {convertToCurrency(item.price, defaultCurrency)}
|
|
311
|
+
</span>
|
|
312
|
+
<span className={styles.existingItemTotal}>
|
|
313
|
+
{convertToCurrency(item.price * item.quantity, defaultCurrency)}
|
|
314
|
+
</span>
|
|
315
|
+
</div>
|
|
316
|
+
))}
|
|
317
|
+
<div className={styles.existingItemsSubtotal}>
|
|
318
|
+
<strong>
|
|
319
|
+
{t('subtotal', 'Subtotal')}: {convertToCurrency(existingItemsTotal, defaultCurrency)}
|
|
320
|
+
</strong>
|
|
321
|
+
</div>
|
|
352
322
|
</div>
|
|
323
|
+
)}
|
|
324
|
+
{isEditMode && <h4 className={styles.sectionHeading}>{t('newItems', 'New items')}</h4>}
|
|
325
|
+
{isLoading ? (
|
|
326
|
+
<InlineLoading description={getCoreTranslation('loading') + '...'} />
|
|
327
|
+
) : error ? (
|
|
328
|
+
<InlineNotification
|
|
329
|
+
kind="error"
|
|
330
|
+
lowContrast
|
|
331
|
+
title={t('errorLoadingBillableServices', 'Error loading billable services')}
|
|
332
|
+
subtitle={error?.message}
|
|
333
|
+
/>
|
|
334
|
+
) : (
|
|
335
|
+
<ComboBox
|
|
336
|
+
id="searchItems"
|
|
337
|
+
onChange={({ selectedItem: item }: { selectedItem: BillableItem }) => selectBillableItem(item)}
|
|
338
|
+
itemToString={(item: BillableItem) => item?.name || ''}
|
|
339
|
+
items={availableBillableItems}
|
|
340
|
+
titleText={t('searchItems', 'Search items and services')}
|
|
341
|
+
/>
|
|
342
|
+
)}
|
|
343
|
+
</>
|
|
344
|
+
)}
|
|
345
|
+
{selectedItems && selectedItems.length > 0 && (
|
|
346
|
+
<div className={styles.selectedItemsContainer}>
|
|
347
|
+
<h4 className={styles.sectionHeading}>{t('selectedItems', 'Selected items')}</h4>
|
|
348
|
+
{selectedItems.map((item) => (
|
|
349
|
+
<div key={item.uuid} className={styles.itemCard}>
|
|
350
|
+
<div className={styles.itemHeader}>
|
|
351
|
+
<span className={styles.itemName}>{item.display}</span>
|
|
352
|
+
<IconButton
|
|
353
|
+
align="top-end"
|
|
354
|
+
kind="ghost"
|
|
355
|
+
label={t('remove', 'Remove')}
|
|
356
|
+
onClick={() => removeSelectedBillableItem(item.uuid)}>
|
|
357
|
+
<TrashCanIcon size={16} />
|
|
358
|
+
</IconButton>
|
|
359
|
+
</div>
|
|
353
360
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
}
|
|
369
|
-
selectedItem={item.selectedPaymentMethod}
|
|
370
|
-
onChange={({ selectedItem }) => {
|
|
371
|
-
if (selectedItem) {
|
|
372
|
-
updatePaymentMethod(item.uuid, selectedItem);
|
|
361
|
+
<div className={styles.itemControls}>
|
|
362
|
+
{item.availablePaymentMethods && item.availablePaymentMethods.length > 1 ? (
|
|
363
|
+
<div className={styles.controlSection}>
|
|
364
|
+
<label>{t('selectPaymentMethod', 'Select payment method')}</label>
|
|
365
|
+
<ComboBox
|
|
366
|
+
id={`payment-method-${item.uuid}`}
|
|
367
|
+
items={item.availablePaymentMethods}
|
|
368
|
+
itemToString={(method: ServicePrice) =>
|
|
369
|
+
method
|
|
370
|
+
? `${method.name} - ${convertToCurrency(
|
|
371
|
+
typeof method.price === 'number' ? method.price : parseFloat(method.price),
|
|
372
|
+
defaultCurrency,
|
|
373
|
+
)}`
|
|
374
|
+
: ''
|
|
373
375
|
}
|
|
376
|
+
selectedItem={item.selectedPaymentMethod}
|
|
377
|
+
onChange={({ selectedItem }) => {
|
|
378
|
+
if (selectedItem) {
|
|
379
|
+
updatePaymentMethod(item.uuid, selectedItem);
|
|
380
|
+
}
|
|
381
|
+
}}
|
|
382
|
+
placeholder={t('selectPaymentMethod', 'Select payment method')}
|
|
383
|
+
titleText=""
|
|
384
|
+
/>
|
|
385
|
+
</div>
|
|
386
|
+
) : (
|
|
387
|
+
<div className={styles.controlSection}>
|
|
388
|
+
<label>{t('unitPrice', 'Unit price')}</label>
|
|
389
|
+
<span className={styles.priceDisplay}>{convertToCurrency(item.price, defaultCurrency)}</span>
|
|
390
|
+
</div>
|
|
391
|
+
)}
|
|
392
|
+
|
|
393
|
+
<div className={styles.controlSection}>
|
|
394
|
+
<label>{t('quantity', 'Quantity')}</label>
|
|
395
|
+
<NumberInput
|
|
396
|
+
allowEmpty
|
|
397
|
+
disableWheel
|
|
398
|
+
hideSteppers
|
|
399
|
+
id={`quantity-${item.uuid}`}
|
|
400
|
+
invalid={!item.quantity || item.quantity < 1}
|
|
401
|
+
invalidText={t('quantityMustBeAtLeastOne', 'Quantity must be at least 1')}
|
|
402
|
+
onChange={(_, { value }) => {
|
|
403
|
+
const number = parseFloat(String(value));
|
|
404
|
+
updateQuantity(item.uuid, isNaN(number) ? 0 : number);
|
|
374
405
|
}}
|
|
375
|
-
|
|
376
|
-
titleText=""
|
|
406
|
+
value={item.quantity || ''}
|
|
377
407
|
/>
|
|
378
408
|
</div>
|
|
379
|
-
|
|
409
|
+
|
|
380
410
|
<div className={styles.controlSection}>
|
|
381
|
-
<label>{t('
|
|
382
|
-
<span className={styles.
|
|
411
|
+
<label>{t('total', 'Total')}</label>
|
|
412
|
+
<span className={styles.totalDisplay}>
|
|
413
|
+
{convertToCurrency(item.price * item.quantity, defaultCurrency)}
|
|
414
|
+
</span>
|
|
383
415
|
</div>
|
|
384
|
-
)}
|
|
385
|
-
|
|
386
|
-
<div className={styles.controlSection}>
|
|
387
|
-
<label>{t('quantity', 'Quantity')}</label>
|
|
388
|
-
<NumberInput
|
|
389
|
-
allowEmpty
|
|
390
|
-
disableWheel
|
|
391
|
-
hideSteppers
|
|
392
|
-
id={`quantity-${item.uuid}`}
|
|
393
|
-
invalid={!item.quantity || item.quantity < 1}
|
|
394
|
-
invalidText={t('quantityMustBeAtLeastOne', 'Quantity must be at least 1')}
|
|
395
|
-
onChange={(_, { value }) => {
|
|
396
|
-
const number = parseFloat(String(value));
|
|
397
|
-
updateQuantity(item.uuid, isNaN(number) ? 0 : number);
|
|
398
|
-
}}
|
|
399
|
-
value={item.quantity || ''}
|
|
400
|
-
/>
|
|
401
|
-
</div>
|
|
402
|
-
|
|
403
|
-
<div className={styles.controlSection}>
|
|
404
|
-
<label>{t('total', 'Total')}</label>
|
|
405
|
-
<span className={styles.totalDisplay}>
|
|
406
|
-
{convertToCurrency(item.price * item.quantity, defaultCurrency)}
|
|
407
|
-
</span>
|
|
408
416
|
</div>
|
|
409
417
|
</div>
|
|
410
|
-
|
|
411
|
-
))}
|
|
418
|
+
))}
|
|
412
419
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
420
|
+
<div className={styles.grandTotal}>
|
|
421
|
+
<strong>
|
|
422
|
+
{t('grandTotal', 'Grand total')}:{' '}
|
|
423
|
+
{convertToCurrency(existingItemsTotal + calculateTotalAmount(selectedItems), defaultCurrency)}
|
|
424
|
+
</strong>
|
|
425
|
+
</div>
|
|
418
426
|
</div>
|
|
419
|
-
|
|
420
|
-
|
|
427
|
+
)}
|
|
428
|
+
</div>
|
|
421
429
|
</div>
|
|
422
430
|
|
|
423
431
|
<ButtonSet className={isTablet ? styles.tablet : styles.desktop}>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"hash":"8872ba2be2ec04be","duration":9885}
|
|
Binary file
|