From c6be57da06e2cf3d546ebcf8af171713510795aa Mon Sep 17 00:00:00 2001 From: AJAL ODORA JONATHAN <43242517+ODORA0@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:22:23 +0300 Subject: [PATCH 1/5] O3-2879 Add billing form in Patient's billing history to RefApp --- src/bill-history/bill-history.component.tsx | 154 ++++++++-------- src/billing-form/billing-form.component.tsx | 188 +++++++++++++++++++- src/billing-form/billing-form.scss | 20 +++ src/billing.resource.ts | 23 +++ 4 files changed, 307 insertions(+), 78 deletions(-) diff --git a/src/bill-history/bill-history.component.tsx b/src/bill-history/bill-history.component.tsx index 8b44f79..95859c1 100644 --- a/src/bill-history/bill-history.component.tsx +++ b/src/bill-history/bill-history.component.tsx @@ -20,6 +20,7 @@ import { } from '@carbon/react'; import { isDesktop, useLayoutType, usePagination } from '@openmrs/esm-framework'; import { + CardHeader, EmptyDataIllustration, ErrorState, launchPatientWorkspace, @@ -28,6 +29,7 @@ import { import { useBills } from '../billing.resource'; import InvoiceTable from '../invoice/invoice-table.component'; import styles from './bill-history.scss'; +import { Add } from '@carbon/react/icons'; interface BillHistoryProps { patientUuid: string; @@ -108,79 +110,89 @@ const BillHistory: React.FC = ({ patientUuid }) => { } return ( -
- - {({ - rows, - headers, - getExpandHeaderProps, - getTableProps, - getTableContainerProps, - getHeaderProps, - getRowProps, - }) => ( - - - - - - {headers.map((header, i) => ( - - {header.header} - - ))} - - - - {rows.map((row, i) => { - const currentBill = bills?.find((bill) => bill.uuid === row.id); + <> + + + +
+ + {({ + rows, + headers, + getExpandHeaderProps, + getTableProps, + getTableContainerProps, + getHeaderProps, + getRowProps, + }) => ( + +
+ + + + {headers.map((header, i) => ( + + {header.header} + + ))} + + + + {rows.map((row, i) => { + const currentBill = bills?.find((bill) => bill.uuid === row.id); - return ( - - - {row.cells.map((cell) => ( - {cell.value} - ))} - - {row.isExpanded ? ( - -
- -
-
- ) : ( - - )} -
- ); - })} -
-
-
+ return ( + + + {row.cells.map((cell) => ( + {cell.value} + ))} + + {row.isExpanded ? ( + +
+ +
+
+ ) : ( + + )} +
+ ); + })} + + + + )} +
+ {paginated && ( + { + if (newPage !== currentPage) { + goTo(newPage); + } + }} + /> )} - - {paginated && ( - { - if (newPage !== currentPage) { - goTo(newPage); - } - }} - /> - )} -
+ + ); }; diff --git a/src/billing-form/billing-form.component.tsx b/src/billing-form/billing-form.component.tsx index f0ed760..dba2fc4 100644 --- a/src/billing-form/billing-form.component.tsx +++ b/src/billing-form/billing-form.component.tsx @@ -1,23 +1,197 @@ -import React from 'react'; -import { RadioButtonGroup, RadioButton } from '@carbon/react'; -import { useTranslation } from 'react-i18next'; +import React, { useState, useRef } from 'react'; +import { + ButtonSet, + Button, + RadioButtonGroup, + RadioButton, + Search, + Table, + TableHead, + TableBody, + TableHeader, + TableRow, + TableCell, +} from '@carbon/react'; import styles from './billing-form.scss'; +import { useTranslation } from 'react-i18next'; +import { showSnackbar } from '@openmrs/esm-framework'; +import { useFetchSearchResults, processBillItems } from '../billing.resource'; // Corrected the import path +import { mutate } from 'swr'; + +type BillItem = { + uuid: string; + Item: string; + Qnty: number; + Price: number; + Total: number; + category: string; +}; type BillingFormProps = { patientUuid: string; + closeWorkspace: () => void; }; -const BillingForm: React.FC = ({ patientUuid }) => { +const BillingForm: React.FC = ({ patientUuid, closeWorkspace }) => { const { t } = useTranslation(); + const [GrandTotal, setGrandTotal] = useState(0); + const [searchOptions, setSearchOptions] = useState([]); + const [BillItems, setBillItems] = useState([]); + const [searchVal, setSearchVal] = useState(''); + const [category, setCategory] = useState(''); + + const numberRef = useRef(null); + + const toggleSearch = (choiceSelected: string) => { + setSearchVal(''); + setSearchOptions([]); + setCategory(choiceSelected === 'Stock Item' ? 'Stock Item' : 'Service'); + }; + + const calculateTotal = (event: React.ChangeEvent, itemName: string) => { + const Qnty = parseInt(event.target.value); + const price = BillItems.find((item) => item.Item.toLowerCase() === itemName.toLowerCase())?.Price || 0; + const total = price * Qnty; + + const updatedBillItems = BillItems.map((item) => + item.Item.toLowerCase() === itemName.toLowerCase() ? { ...item, Qnty, Total: total } : item, + ); + + setBillItems(updatedBillItems); + + const grandTotal = updatedBillItems.reduce((acc, curr) => acc + curr.Total, 0); + setGrandTotal(grandTotal); + }; + + const addItemToBill = (item: BillItem) => { + const existingItemIndex = BillItems.findIndex((existingItem) => existingItem.Item === item.Item); + + if (existingItemIndex !== -1) { + const updatedBillItems = [...BillItems]; + updatedBillItems[existingItemIndex].Qnty += 1; + updatedBillItems[existingItemIndex].Total = + updatedBillItems[existingItemIndex].Price * updatedBillItems[existingItemIndex].Qnty; + setBillItems(updatedBillItems); + } else { + setBillItems((prevItems) => [...prevItems, { ...item, Qnty: 1, Total: item.Price }]); + } + + setGrandTotal((prevTotal) => prevTotal + item.Price); + setSearchVal(''); + setSearchOptions([]); + }; + + const postBillItems = () => { + const bill = { + cashPoint: '54065383-b4d4-42d2-af4d-d250a1fd2590', + cashier: 'f9badd80-ab76-11e2-9e96-0800200c9a66', + lineItems: BillItems.map((item) => ({ + [item.category === 'StockItem' ? 'item' : 'billableService']: item.uuid, + quantity: item.Qnty, + price: item.Price, + priceName: 'Default', + priceUuid: '7b9171ac-d3c1-49b4-beff-c9902aee5245', + lineItemOrder: 0, + paymentStatus: 'PENDING', + })), + payments: [], + patient: patientUuid, + status: 'PENDING', + }; + + processBillItems(bill).then( + () => { + closeWorkspace(); + showSnackbar({ + title: t('billItems', 'Save Bill'), + subtitle: 'Bill processing has been successful', + kind: 'success', + timeoutInMs: 3000, + }); + }, + (error) => { + showSnackbar({ title: 'Bill processing error', kind: 'error', subtitle: error }); + }, + ); + }; + return (
- - + defaultSelected="radio-1" + className={styles.billingItem} + onChange={({ value }) => toggleSearch(value)}> + + + + setSearchVal(e.target.value)} + className={styles.billingItem} + /> + +
    + {searchOptions.map((row) => ( +
  • + +
  • + ))} +
+ + + + + Item + Quantity + Price + Total + + + + {BillItems.map((row) => ( + + {row.Item} + + calculateTotal(e, row.Item)} + /> + + {row.Price} + {row.Total} + + ))} + + + + Grand Total: + {GrandTotal} + + +
+ + + + +
); }; diff --git a/src/billing-form/billing-form.scss b/src/billing-form/billing-form.scss index 30150b8..ea78569 100644 --- a/src/billing-form/billing-form.scss +++ b/src/billing-form/billing-form.scss @@ -3,3 +3,23 @@ .billingFormContainer { padding: layout.$spacing-05; } + +.billingItem { + margin-top: 30px; +} + +.searchContent { + position: absolute; + background-color: #fff; + min-width: 230px; + overflow: auto; + border: 1px solid #ddd; + z-index: 1; + width: 92%; +} + +.searchItem { + border-bottom: 0.1rem solid; + border-bottom-color: silver; + cursor: pointer; +} \ No newline at end of file diff --git a/src/billing.resource.ts b/src/billing.resource.ts index c1c5032..a3675f4 100644 --- a/src/billing.resource.ts +++ b/src/billing.resource.ts @@ -118,3 +118,26 @@ export const usePatientPaymentInfo = (patientUuid: string) => { return paymentInformation; }; + +export function useFetchSearchResults(searchVal, category) { + let url = ``; + if (category == 'Stock Item') { + url = `/ws/rest/v1/stockmanagement/stockitem?v=default&limit=10&q=${searchVal}`; + } else { + url = `/ws/rest/v1/cashier/billableService?v=custom:(uuid,name,shortName,serviceStatus,serviceType:(display),servicePrices:(uuid,name,price,paymentMode))`; + } + const { data, error, isLoading, isValidating } = useSWR(searchVal ? url : null, openmrsFetch, {}); + + return { data: data?.data, error, isLoading: isLoading, isValidating }; +} + +export const processBillItems = (payload) => { + const url = `/ws/rest/v1/cashier/bill`; + return openmrsFetch(url, { + method: 'POST', + body: payload, + headers: { + 'Content-Type': 'application/json', + }, + }); +}; From 22927c8e242344c0dcb539d2c5476a1aa074aafc Mon Sep 17 00:00:00 2001 From: AJAL ODORA JONATHAN <43242517+ODORA0@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:53:44 +0300 Subject: [PATCH 2/5] refactor billing form and add uat backend --- package.json | 2 +- src/billing-form/billing-form.component.tsx | 249 ++++++++++++-------- 2 files changed, 158 insertions(+), 93 deletions(-) diff --git a/package.json b/package.json index fa1d789..7b22551 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "prettier": "prettier --config prettier.config.js --write \"src/**/*.{ts,tsx,css,scss}\" \"e2e/**/*.ts\"", "release": "lerna version --no-git-tag-version", "serve": "webpack serve --mode=development", - "start": "openmrs develop", + "start": "openmrs develop --backend https://uat.kenyahmis.org/ ", "test-e2e": "playwright test", "test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests", "test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js", diff --git a/src/billing-form/billing-form.component.tsx b/src/billing-form/billing-form.component.tsx index dba2fc4..b178ae7 100644 --- a/src/billing-form/billing-form.component.tsx +++ b/src/billing-form/billing-form.component.tsx @@ -15,18 +15,9 @@ import { import styles from './billing-form.scss'; import { useTranslation } from 'react-i18next'; import { showSnackbar } from '@openmrs/esm-framework'; -import { useFetchSearchResults, processBillItems } from '../billing.resource'; // Corrected the import path +import { useFetchSearchResults, processBillItems } from '../billing.resource'; import { mutate } from 'swr'; -type BillItem = { - uuid: string; - Item: string; - Qnty: number; - Price: number; - Total: number; - category: string; -}; - type BillingFormProps = { patientUuid: string; closeWorkspace: () => void; @@ -34,74 +25,130 @@ type BillingFormProps = { const BillingForm: React.FC = ({ patientUuid, closeWorkspace }) => { const { t } = useTranslation(); - const [GrandTotal, setGrandTotal] = useState(0); - const [searchOptions, setSearchOptions] = useState([]); - const [BillItems, setBillItems] = useState([]); - const [searchVal, setSearchVal] = useState(''); - const [category, setCategory] = useState(''); - const numberRef = useRef(null); + const [grandTotal, setGrandTotal] = useState(0); + const [searchOptions, setSearchOptions] = useState([]); + const [billItems, setBillItems] = useState([]); + const [searchVal, setSearchVal] = useState(''); + const [category, setCategory] = useState(''); - const toggleSearch = (choiceSelected: string) => { - setSearchVal(''); - setSearchOptions([]); + const toggleSearch = (choiceSelected) => { + (document.getElementById('searchField') as HTMLInputElement).disabled = false; setCategory(choiceSelected === 'Stock Item' ? 'Stock Item' : 'Service'); }; - const calculateTotal = (event: React.ChangeEvent, itemName: string) => { - const Qnty = parseInt(event.target.value); - const price = BillItems.find((item) => item.Item.toLowerCase() === itemName.toLowerCase())?.Price || 0; - const total = price * Qnty; + const calculateTotal = (event, itemName) => { + const Qnty = event.target.value; + const price = document.getElementById(`${event.target.id}Price`).innerHTML; + const total = parseInt(price) * Qnty; - const updatedBillItems = BillItems.map((item) => - item.Item.toLowerCase() === itemName.toLowerCase() ? { ...item, Qnty, Total: total } : item, - ); + const updatedItems = billItems.map((item) => { + if (item.Item.toLowerCase().includes(itemName.toLowerCase())) { + item.Qnty = Qnty; + item.Total = total; + } + return item; + }); - setBillItems(updatedBillItems); + setBillItems(updatedItems); - const grandTotal = updatedBillItems.reduce((acc, curr) => acc + curr.Total, 0); - setGrandTotal(grandTotal); + const totals = Array.from(document.querySelectorAll('[id$="Total"]')); + let addUpTotals = 0; + totals.forEach((tot) => { + addUpTotals += parseInt(tot.innerHTML); + }); + setGrandTotal(addUpTotals); }; - const addItemToBill = (item: BillItem) => { - const existingItemIndex = BillItems.findIndex((existingItem) => existingItem.Item === item.Item); - - if (existingItemIndex !== -1) { - const updatedBillItems = [...BillItems]; - updatedBillItems[existingItemIndex].Qnty += 1; - updatedBillItems[existingItemIndex].Total = - updatedBillItems[existingItemIndex].Price * updatedBillItems[existingItemIndex].Qnty; - setBillItems(updatedBillItems); - } else { - setBillItems((prevItems) => [...prevItems, { ...item, Qnty: 1, Total: item.Price }]); - } + const calculateTotalAfterAddBillItem = () => { + const sum = billItems.reduce((acc, item) => acc + item.Price, 0); + setGrandTotal(sum); + }; + + const addItemToBill = (event, itemid, itemname, itemcategory, itemPrice) => { + const newItem = { + uuid: itemid, + Item: itemname, + Qnty: 1, + Price: itemPrice, + Total: itemPrice, + category: itemcategory, + }; - setGrandTotal((prevTotal) => prevTotal + item.Price); - setSearchVal(''); + setBillItems([...billItems, newItem]); setSearchOptions([]); + calculateTotalAfterAddBillItem(); + (document.getElementById('searchField') as HTMLInputElement).value = ''; + }; + + const { data, error, isLoading, isValidating } = useFetchSearchResults(searchVal, category); + + const filterItems = (val) => { + setSearchVal(val); + + if (!isLoading && data) { + const res = data as { results: any[] }; + + const options = res.results.map((o) => { + if (o.commonName && o.commonName !== '') { + return { + uuid: o.uuid || '', + Item: o.commonName, + Qnty: 1, + Price: 10, + Total: 10, + category: 'StockItem', + }; + } else if (o.name.toLowerCase().includes(val.toLowerCase())) { + return { + uuid: o.uuid || '', + Item: o.name, + Qnty: 1, + Price: o.servicePrices[0].price, + Total: o.servicePrices[0].price, + category: 'Service', + }; + } + }); + + setSearchOptions(options.filter((option) => option)); // Filter out undefined/null values + } }; const postBillItems = () => { const bill = { cashPoint: '54065383-b4d4-42d2-af4d-d250a1fd2590', cashier: 'f9badd80-ab76-11e2-9e96-0800200c9a66', - lineItems: BillItems.map((item) => ({ - [item.category === 'StockItem' ? 'item' : 'billableService']: item.uuid, - quantity: item.Qnty, + lineItems: [], + payments: [], + patient: patientUuid, + status: 'PENDING', + }; + + billItems.forEach((item) => { + const lineItem: any = { + quantity: parseInt(item.Qnty), price: item.Price, priceName: 'Default', priceUuid: '7b9171ac-d3c1-49b4-beff-c9902aee5245', lineItemOrder: 0, paymentStatus: 'PENDING', - })), - payments: [], - patient: patientUuid, - status: 'PENDING', - }; + }; + + if (item.category === 'StockItem') { + lineItem.item = item.uuid; + } else { + lineItem.billableService = item.uuid; + } + bill.lineItems.push(lineItem); + }); + + const url = `/ws/rest/v1/cashier/bill`; processBillItems(bill).then( () => { closeWorkspace(); + mutate((key) => typeof key === 'string' && key.startsWith(url), undefined, { revalidate: true }); showSnackbar({ title: t('billItems', 'Save Bill'), subtitle: 'Bill processing has been successful', @@ -122,31 +169,39 @@ const BillingForm: React.FC = ({ patientUuid, closeWorkspace } name="radio-button-group" defaultSelected="radio-1" className={styles.billingItem} - onChange={({ value }) => toggleSearch(value)}> + onChange={toggleSearch}> - setSearchVal(e.target.value)} - className={styles.billingItem} - /> - -
    - {searchOptions.map((row) => ( -
  • - -
  • - ))} -
+
+ {}} + className={styles.billingItem} + onKeyUp={(e) => { + filterItems(e.target.value); + }} + /> + +
    + {searchOptions.map((row) => ( +
  • + +
  • + ))} +
+
@@ -158,28 +213,38 @@ const BillingForm: React.FC = ({ patientUuid, closeWorkspace } - {BillItems.map((row) => ( - - {row.Item} - - calculateTotal(e, row.Item)} - /> - - {row.Price} - {row.Total} - - ))} + {billItems && Array.isArray(billItems) ? ( + billItems.map((row) => ( + + {row.Item} + + { + calculateTotal(e, row.Item); + row.Qnty = e.target.value; + }} + /> + + {row.Price} + + {row.Total} + + + )) + ) : ( +

Loading...

+ )} - - + + Grand Total: - {GrandTotal} + {grandTotal}
From 4f6fd770a5221667e988d99be01d4015048a647b Mon Sep 17 00:00:00 2001 From: AJAL ODORA JONATHAN <43242517+ODORA0@users.noreply.github.com> Date: Thu, 22 Feb 2024 12:29:25 +0300 Subject: [PATCH 3/5] Remove backend, set currency, add translations --- package.json | 2 +- src/bill-history/bill-history.component.tsx | 17 +++++++++++++++-- translations/am.json | 6 ++++-- translations/en.json | 6 ++++-- translations/es.json | 6 ++++-- translations/fr.json | 6 ++++-- translations/he.json | 6 ++++-- translations/km.json | 6 ++++-- 8 files changed, 40 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 7b22551..fa1d789 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "prettier": "prettier --config prettier.config.js --write \"src/**/*.{ts,tsx,css,scss}\" \"e2e/**/*.ts\"", "release": "lerna version --no-git-tag-version", "serve": "webpack serve --mode=development", - "start": "openmrs develop --backend https://uat.kenyahmis.org/ ", + "start": "openmrs develop", "test-e2e": "playwright test", "test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests", "test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js", diff --git a/src/bill-history/bill-history.component.tsx b/src/bill-history/bill-history.component.tsx index 95859c1..d20ad12 100644 --- a/src/bill-history/bill-history.component.tsx +++ b/src/bill-history/bill-history.component.tsx @@ -18,7 +18,7 @@ import { TableRow, Tile, } from '@carbon/react'; -import { isDesktop, useLayoutType, usePagination } from '@openmrs/esm-framework'; +import { isDesktop, useConfig, useLayoutType, usePagination } from '@openmrs/esm-framework'; import { CardHeader, EmptyDataIllustration, @@ -44,6 +44,8 @@ const BillHistory: React.FC = ({ patientUuid }) => { const { paginated, goTo, results, currentPage } = usePagination(bills, PAGE_SIZE); const { pageSizes } = usePaginationInfo(PAGE_SIZE, bills?.length, currentPage, results?.length); + const { defaultCurrency } = useConfig(); + const headerData = [ { header: t('visitTime', 'Visit time'), @@ -66,10 +68,21 @@ const BillHistory: React.FC = ({ patientUuid }) => { const setBilledItems = (bill) => bill.lineItems.reduce((acc, item) => acc + (acc ? ' & ' : '') + (item.billableService || item.item || ''), ''); + const formatCurrency = (amount) => { + const currencyCode = defaultCurrency._default || 'UGX'; + + return new Intl.NumberFormat(undefined, { + style: 'currency', + currency: currencyCode, + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }).format(amount); + }; + const rowData = results?.map((bill) => ({ id: bill.uuid, uuid: bill.uuid, - billTotal: bill.totalAmount, + billTotal: formatCurrency(bill.totalAmount), visitTime: bill.dateCreated, identifier: bill.identifier, billedItems: setBilledItems(bill), diff --git a/translations/am.json b/translations/am.json index 4954da7..65a5eb6 100644 --- a/translations/am.json +++ b/translations/am.json @@ -1,5 +1,6 @@ { "actions": "Actions", + "addBill": "Add bill item(s)", "addBillableServices": "Add Billable Services", "addNewBillableService": "Add new billable service", "addNewService": "Add new service", @@ -14,6 +15,7 @@ "billedTo": "Billed to", "billErrorService": "Bill service error", "billing": "Billing", + "billingHistory": "Billing History", "billItem": "Bill item", "billItems": "Bill Items", "billList": "Bill list", @@ -31,7 +33,6 @@ "checkFilters": "Check the filters above", "discard": "Discard", "discount": "Discount", - "drug": "Drug", "enterAmount": "Enter amount", "enterConcept": "Associated concept", "enterReferenceNumber": "Enter ref. number", @@ -60,7 +61,6 @@ "noMatchingBillsToDisplay": "No matching bills to display", "noMatchingItemsToDisplay": "No matching items to display", "noMatchingServicesToDisplay": "No matching services to display", - "nonDrug": "Non drug", "noResultsFor": "No results for", "noServicesToDisplay": "There are no services to display", "ok": "OK", @@ -91,6 +91,7 @@ "selectPaymentMethod": "Select payment method", "sellingAmount": "Enter selling price", "sellingPrice": "Selling Price", + "service": "Service", "serviceMetrics": "Service Metrics", "serviceName": "Service Name", "serviceShortName": "Short Name", @@ -98,6 +99,7 @@ "serviceType": "Service Type", "shortName": "Short Name", "status": "Service Status", + "stockItem": "Stock Item", "total": "Total", "totalAmount": "Total Amount", "totalTendered": "Total Tendered", diff --git a/translations/en.json b/translations/en.json index 4954da7..65a5eb6 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1,5 +1,6 @@ { "actions": "Actions", + "addBill": "Add bill item(s)", "addBillableServices": "Add Billable Services", "addNewBillableService": "Add new billable service", "addNewService": "Add new service", @@ -14,6 +15,7 @@ "billedTo": "Billed to", "billErrorService": "Bill service error", "billing": "Billing", + "billingHistory": "Billing History", "billItem": "Bill item", "billItems": "Bill Items", "billList": "Bill list", @@ -31,7 +33,6 @@ "checkFilters": "Check the filters above", "discard": "Discard", "discount": "Discount", - "drug": "Drug", "enterAmount": "Enter amount", "enterConcept": "Associated concept", "enterReferenceNumber": "Enter ref. number", @@ -60,7 +61,6 @@ "noMatchingBillsToDisplay": "No matching bills to display", "noMatchingItemsToDisplay": "No matching items to display", "noMatchingServicesToDisplay": "No matching services to display", - "nonDrug": "Non drug", "noResultsFor": "No results for", "noServicesToDisplay": "There are no services to display", "ok": "OK", @@ -91,6 +91,7 @@ "selectPaymentMethod": "Select payment method", "sellingAmount": "Enter selling price", "sellingPrice": "Selling Price", + "service": "Service", "serviceMetrics": "Service Metrics", "serviceName": "Service Name", "serviceShortName": "Short Name", @@ -98,6 +99,7 @@ "serviceType": "Service Type", "shortName": "Short Name", "status": "Service Status", + "stockItem": "Stock Item", "total": "Total", "totalAmount": "Total Amount", "totalTendered": "Total Tendered", diff --git a/translations/es.json b/translations/es.json index 4954da7..65a5eb6 100644 --- a/translations/es.json +++ b/translations/es.json @@ -1,5 +1,6 @@ { "actions": "Actions", + "addBill": "Add bill item(s)", "addBillableServices": "Add Billable Services", "addNewBillableService": "Add new billable service", "addNewService": "Add new service", @@ -14,6 +15,7 @@ "billedTo": "Billed to", "billErrorService": "Bill service error", "billing": "Billing", + "billingHistory": "Billing History", "billItem": "Bill item", "billItems": "Bill Items", "billList": "Bill list", @@ -31,7 +33,6 @@ "checkFilters": "Check the filters above", "discard": "Discard", "discount": "Discount", - "drug": "Drug", "enterAmount": "Enter amount", "enterConcept": "Associated concept", "enterReferenceNumber": "Enter ref. number", @@ -60,7 +61,6 @@ "noMatchingBillsToDisplay": "No matching bills to display", "noMatchingItemsToDisplay": "No matching items to display", "noMatchingServicesToDisplay": "No matching services to display", - "nonDrug": "Non drug", "noResultsFor": "No results for", "noServicesToDisplay": "There are no services to display", "ok": "OK", @@ -91,6 +91,7 @@ "selectPaymentMethod": "Select payment method", "sellingAmount": "Enter selling price", "sellingPrice": "Selling Price", + "service": "Service", "serviceMetrics": "Service Metrics", "serviceName": "Service Name", "serviceShortName": "Short Name", @@ -98,6 +99,7 @@ "serviceType": "Service Type", "shortName": "Short Name", "status": "Service Status", + "stockItem": "Stock Item", "total": "Total", "totalAmount": "Total Amount", "totalTendered": "Total Tendered", diff --git a/translations/fr.json b/translations/fr.json index 4954da7..65a5eb6 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -1,5 +1,6 @@ { "actions": "Actions", + "addBill": "Add bill item(s)", "addBillableServices": "Add Billable Services", "addNewBillableService": "Add new billable service", "addNewService": "Add new service", @@ -14,6 +15,7 @@ "billedTo": "Billed to", "billErrorService": "Bill service error", "billing": "Billing", + "billingHistory": "Billing History", "billItem": "Bill item", "billItems": "Bill Items", "billList": "Bill list", @@ -31,7 +33,6 @@ "checkFilters": "Check the filters above", "discard": "Discard", "discount": "Discount", - "drug": "Drug", "enterAmount": "Enter amount", "enterConcept": "Associated concept", "enterReferenceNumber": "Enter ref. number", @@ -60,7 +61,6 @@ "noMatchingBillsToDisplay": "No matching bills to display", "noMatchingItemsToDisplay": "No matching items to display", "noMatchingServicesToDisplay": "No matching services to display", - "nonDrug": "Non drug", "noResultsFor": "No results for", "noServicesToDisplay": "There are no services to display", "ok": "OK", @@ -91,6 +91,7 @@ "selectPaymentMethod": "Select payment method", "sellingAmount": "Enter selling price", "sellingPrice": "Selling Price", + "service": "Service", "serviceMetrics": "Service Metrics", "serviceName": "Service Name", "serviceShortName": "Short Name", @@ -98,6 +99,7 @@ "serviceType": "Service Type", "shortName": "Short Name", "status": "Service Status", + "stockItem": "Stock Item", "total": "Total", "totalAmount": "Total Amount", "totalTendered": "Total Tendered", diff --git a/translations/he.json b/translations/he.json index 4954da7..65a5eb6 100644 --- a/translations/he.json +++ b/translations/he.json @@ -1,5 +1,6 @@ { "actions": "Actions", + "addBill": "Add bill item(s)", "addBillableServices": "Add Billable Services", "addNewBillableService": "Add new billable service", "addNewService": "Add new service", @@ -14,6 +15,7 @@ "billedTo": "Billed to", "billErrorService": "Bill service error", "billing": "Billing", + "billingHistory": "Billing History", "billItem": "Bill item", "billItems": "Bill Items", "billList": "Bill list", @@ -31,7 +33,6 @@ "checkFilters": "Check the filters above", "discard": "Discard", "discount": "Discount", - "drug": "Drug", "enterAmount": "Enter amount", "enterConcept": "Associated concept", "enterReferenceNumber": "Enter ref. number", @@ -60,7 +61,6 @@ "noMatchingBillsToDisplay": "No matching bills to display", "noMatchingItemsToDisplay": "No matching items to display", "noMatchingServicesToDisplay": "No matching services to display", - "nonDrug": "Non drug", "noResultsFor": "No results for", "noServicesToDisplay": "There are no services to display", "ok": "OK", @@ -91,6 +91,7 @@ "selectPaymentMethod": "Select payment method", "sellingAmount": "Enter selling price", "sellingPrice": "Selling Price", + "service": "Service", "serviceMetrics": "Service Metrics", "serviceName": "Service Name", "serviceShortName": "Short Name", @@ -98,6 +99,7 @@ "serviceType": "Service Type", "shortName": "Short Name", "status": "Service Status", + "stockItem": "Stock Item", "total": "Total", "totalAmount": "Total Amount", "totalTendered": "Total Tendered", diff --git a/translations/km.json b/translations/km.json index 4954da7..65a5eb6 100644 --- a/translations/km.json +++ b/translations/km.json @@ -1,5 +1,6 @@ { "actions": "Actions", + "addBill": "Add bill item(s)", "addBillableServices": "Add Billable Services", "addNewBillableService": "Add new billable service", "addNewService": "Add new service", @@ -14,6 +15,7 @@ "billedTo": "Billed to", "billErrorService": "Bill service error", "billing": "Billing", + "billingHistory": "Billing History", "billItem": "Bill item", "billItems": "Bill Items", "billList": "Bill list", @@ -31,7 +33,6 @@ "checkFilters": "Check the filters above", "discard": "Discard", "discount": "Discount", - "drug": "Drug", "enterAmount": "Enter amount", "enterConcept": "Associated concept", "enterReferenceNumber": "Enter ref. number", @@ -60,7 +61,6 @@ "noMatchingBillsToDisplay": "No matching bills to display", "noMatchingItemsToDisplay": "No matching items to display", "noMatchingServicesToDisplay": "No matching services to display", - "nonDrug": "Non drug", "noResultsFor": "No results for", "noServicesToDisplay": "There are no services to display", "ok": "OK", @@ -91,6 +91,7 @@ "selectPaymentMethod": "Select payment method", "sellingAmount": "Enter selling price", "sellingPrice": "Selling Price", + "service": "Service", "serviceMetrics": "Service Metrics", "serviceName": "Service Name", "serviceShortName": "Short Name", @@ -98,6 +99,7 @@ "serviceType": "Service Type", "shortName": "Short Name", "status": "Service Status", + "stockItem": "Stock Item", "total": "Total", "totalAmount": "Total Amount", "totalTendered": "Total Tendered", From f2c811138979a33bdfb8e6f60cccd487dc95ef56 Mon Sep 17 00:00:00 2001 From: AJAL ODORA JONATHAN <43242517+ODORA0@users.noreply.github.com> Date: Thu, 22 Feb 2024 21:36:17 +0300 Subject: [PATCH 4/5] use existing currency Convert function --- src/bill-history/bill-history.component.tsx | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/bill-history/bill-history.component.tsx b/src/bill-history/bill-history.component.tsx index d20ad12..cd6ec94 100644 --- a/src/bill-history/bill-history.component.tsx +++ b/src/bill-history/bill-history.component.tsx @@ -30,6 +30,7 @@ import { useBills } from '../billing.resource'; import InvoiceTable from '../invoice/invoice-table.component'; import styles from './bill-history.scss'; import { Add } from '@carbon/react/icons'; +import { convertToCurrency } from '../helpers'; interface BillHistoryProps { patientUuid: string; @@ -44,8 +45,6 @@ const BillHistory: React.FC = ({ patientUuid }) => { const { paginated, goTo, results, currentPage } = usePagination(bills, PAGE_SIZE); const { pageSizes } = usePaginationInfo(PAGE_SIZE, bills?.length, currentPage, results?.length); - const { defaultCurrency } = useConfig(); - const headerData = [ { header: t('visitTime', 'Visit time'), @@ -68,21 +67,10 @@ const BillHistory: React.FC = ({ patientUuid }) => { const setBilledItems = (bill) => bill.lineItems.reduce((acc, item) => acc + (acc ? ' & ' : '') + (item.billableService || item.item || ''), ''); - const formatCurrency = (amount) => { - const currencyCode = defaultCurrency._default || 'UGX'; - - return new Intl.NumberFormat(undefined, { - style: 'currency', - currency: currencyCode, - minimumFractionDigits: 0, - maximumFractionDigits: 0, - }).format(amount); - }; - const rowData = results?.map((bill) => ({ id: bill.uuid, uuid: bill.uuid, - billTotal: formatCurrency(bill.totalAmount), + billTotal: convertToCurrency(bill.totalAmount), visitTime: bill.dateCreated, identifier: bill.identifier, billedItems: setBilledItems(bill), From b3d1c5aa821d1d92cf6f4504cff51864261e4ffc Mon Sep 17 00:00:00 2001 From: AJAL ODORA JONATHAN <43242517+ODORA0@users.noreply.github.com> Date: Thu, 22 Feb 2024 22:40:14 +0300 Subject: [PATCH 5/5] PR comments --- src/bill-history/bill-history.component.tsx | 8 +++---- src/billing-form/billing-form.component.tsx | 23 ++++++++------------- src/config-schema.ts | 8 +++++++ 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/bill-history/bill-history.component.tsx b/src/bill-history/bill-history.component.tsx index cd6ec94..02b66ec 100644 --- a/src/bill-history/bill-history.component.tsx +++ b/src/bill-history/bill-history.component.tsx @@ -37,13 +37,12 @@ interface BillHistoryProps { } const BillHistory: React.FC = ({ patientUuid }) => { - const PAGE_SIZE = 10; const { t } = useTranslation(); const { bills, isLoading, error } = useBills(patientUuid); const layout = useLayoutType(); const responsiveSize = isDesktop(layout) ? 'sm' : 'lg'; - const { paginated, goTo, results, currentPage } = usePagination(bills, PAGE_SIZE); - const { pageSizes } = usePaginationInfo(PAGE_SIZE, bills?.length, currentPage, results?.length); + const { paginated, goTo, results, currentPage } = usePagination(bills); + const { pageSize } = useConfig(); const headerData = [ { @@ -180,8 +179,7 @@ const BillHistory: React.FC = ({ patientUuid }) => { forwardText={t('nextPage', 'Next page')} backwardText={t('previousPage', 'Previous page')} page={currentPage} - pageSize={PAGE_SIZE} - pageSizes={pageSizes} + pageSize={pageSize._default} totalItems={bills.length} className={styles.pagination} size={responsiveSize} diff --git a/src/billing-form/billing-form.component.tsx b/src/billing-form/billing-form.component.tsx index b178ae7..319cd63 100644 --- a/src/billing-form/billing-form.component.tsx +++ b/src/billing-form/billing-form.component.tsx @@ -14,9 +14,10 @@ import { } from '@carbon/react'; import styles from './billing-form.scss'; import { useTranslation } from 'react-i18next'; -import { showSnackbar } from '@openmrs/esm-framework'; +import { showSnackbar, useConfig } from '@openmrs/esm-framework'; import { useFetchSearchResults, processBillItems } from '../billing.resource'; import { mutate } from 'swr'; +import { convertToCurrency } from '../helpers'; type BillingFormProps = { patientUuid: string; @@ -38,26 +39,20 @@ const BillingForm: React.FC = ({ patientUuid, closeWorkspace } }; const calculateTotal = (event, itemName) => { - const Qnty = event.target.value; - const price = document.getElementById(`${event.target.id}Price`).innerHTML; - const total = parseInt(price) * Qnty; - + const quantity = parseInt(event.target.value); const updatedItems = billItems.map((item) => { if (item.Item.toLowerCase().includes(itemName.toLowerCase())) { - item.Qnty = Qnty; - item.Total = total; + const price = item.Price; + const total = price * quantity; + return { ...item, Qnty: quantity, Total: total }; } return item; }); setBillItems(updatedItems); - const totals = Array.from(document.querySelectorAll('[id$="Total"]')); - let addUpTotals = 0; - totals.forEach((tot) => { - addUpTotals += parseInt(tot.innerHTML); - }); - setGrandTotal(addUpTotals); + const updatedGrandTotal = updatedItems.reduce((acc, item) => acc + item.Total, 0); + setGrandTotal(updatedGrandTotal); }; const calculateTotalAfterAddBillItem = () => { @@ -244,7 +239,7 @@ const BillingForm: React.FC = ({ patientUuid, closeWorkspace } Grand Total: - {grandTotal} + {convertToCurrency(grandTotal)} diff --git a/src/config-schema.ts b/src/config-schema.ts index 2d7cb38..f47328a 100644 --- a/src/config-schema.ts +++ b/src/config-schema.ts @@ -33,10 +33,18 @@ export const configSchema = { _description: 'The default currency for the application. Specify the currency code (e.g., KES, UGX, GBP).', _default: 'KES', }, + + pageSize: { + _type: Type.String, + _description: 'The default page size', + _default: 10, + }, }; export interface ConfigObject { patientCatergory: Object; defaultCurrency: string; catergoryConcepts: Object; + pageSize; + object; }