@openmrs/esm-billing-app 1.0.1-pre.100

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.
Files changed (179) hide show
  1. package/.editorconfig +12 -0
  2. package/.eslintignore +2 -0
  3. package/.eslintrc +57 -0
  4. package/.husky/pre-commit +7 -0
  5. package/.husky/pre-push +6 -0
  6. package/.prettierignore +14 -0
  7. package/.turbo.json +18 -0
  8. package/.yarn/plugins/@yarnpkg/plugin-outdated.cjs +35 -0
  9. package/LICENSE +401 -0
  10. package/README.md +7 -0
  11. package/__mocks__/bills.mock.ts +394 -0
  12. package/__mocks__/delivery-summary.mock.ts +89 -0
  13. package/__mocks__/encounter-observation.mock.ts +10651 -0
  14. package/__mocks__/encounter-observations.mock.ts +6189 -0
  15. package/__mocks__/hiv-summary.mock.ts +22 -0
  16. package/__mocks__/patient-summary.mock.ts +32 -0
  17. package/__mocks__/patient.mock.ts +59 -0
  18. package/__mocks__/program-summary.mock.ts +43 -0
  19. package/__mocks__/react-i18next.js +57 -0
  20. package/dist/146.js +1 -0
  21. package/dist/146.js.map +1 -0
  22. package/dist/294.js +2 -0
  23. package/dist/294.js.LICENSE.txt +9 -0
  24. package/dist/294.js.map +1 -0
  25. package/dist/319.js +1 -0
  26. package/dist/384.js +1 -0
  27. package/dist/384.js.map +1 -0
  28. package/dist/421.js +1 -0
  29. package/dist/421.js.map +1 -0
  30. package/dist/533.js +1 -0
  31. package/dist/533.js.map +1 -0
  32. package/dist/574.js +1 -0
  33. package/dist/591.js +2 -0
  34. package/dist/591.js.LICENSE.txt +9 -0
  35. package/dist/591.js.map +1 -0
  36. package/dist/614.js +2 -0
  37. package/dist/614.js.LICENSE.txt +37 -0
  38. package/dist/614.js.map +1 -0
  39. package/dist/753.js +1 -0
  40. package/dist/753.js.map +1 -0
  41. package/dist/757.js +1 -0
  42. package/dist/770.js +1 -0
  43. package/dist/770.js.map +1 -0
  44. package/dist/783.js +1 -0
  45. package/dist/783.js.map +1 -0
  46. package/dist/788.js +1 -0
  47. package/dist/800.js +2 -0
  48. package/dist/800.js.LICENSE.txt +3 -0
  49. package/dist/800.js.map +1 -0
  50. package/dist/807.js +1 -0
  51. package/dist/833.js +1 -0
  52. package/dist/935.js +2 -0
  53. package/dist/935.js.LICENSE.txt +19 -0
  54. package/dist/935.js.map +1 -0
  55. package/dist/992.js +1 -0
  56. package/dist/992.js.map +1 -0
  57. package/dist/main.js +2 -0
  58. package/dist/main.js.LICENSE.txt +47 -0
  59. package/dist/main.js.map +1 -0
  60. package/dist/openmrs-esm-billing-app.js +1 -0
  61. package/dist/openmrs-esm-billing-app.js.buildmanifest.json +609 -0
  62. package/dist/openmrs-esm-billing-app.js.map +1 -0
  63. package/dist/routes.json +1 -0
  64. package/e2e/README.md +115 -0
  65. package/e2e/core/global-setup.ts +32 -0
  66. package/e2e/core/index.ts +1 -0
  67. package/e2e/core/test.ts +20 -0
  68. package/e2e/fixtures/api.ts +27 -0
  69. package/e2e/fixtures/index.ts +1 -0
  70. package/e2e/pages/home-page.ts +9 -0
  71. package/e2e/pages/index.ts +1 -0
  72. package/e2e/specs/sample-test.spec.ts +11 -0
  73. package/e2e/support/github/Dockerfile +34 -0
  74. package/e2e/support/github/docker-compose.yml +24 -0
  75. package/e2e/support/github/run-e2e-docker-env.sh +49 -0
  76. package/example.env +6 -0
  77. package/i18next-parser.config.js +89 -0
  78. package/jest.config.js +34 -0
  79. package/package.json +124 -0
  80. package/playwright.config.ts +32 -0
  81. package/prettier.config.js +8 -0
  82. package/src/bill-history/bill-history.component.tsx +199 -0
  83. package/src/bill-history/bill-history.scss +151 -0
  84. package/src/bill-history/bill-history.test.tsx +122 -0
  85. package/src/billable-services/bill-waiver/bill-selection.component.tsx +76 -0
  86. package/src/billable-services/bill-waiver/bill-waiver-form.component.tsx +110 -0
  87. package/src/billable-services/bill-waiver/bill-waiver-form.scss +34 -0
  88. package/src/billable-services/bill-waiver/bill-waiver.component.tsx +32 -0
  89. package/src/billable-services/bill-waiver/bill-waiver.scss +10 -0
  90. package/src/billable-services/bill-waiver/patient-bills.component.tsx +137 -0
  91. package/src/billable-services/bill-waiver/utils.ts +41 -0
  92. package/src/billable-services/billable-service.resource.ts +72 -0
  93. package/src/billable-services/billable-services-home.component.tsx +51 -0
  94. package/src/billable-services/billable-services.component.tsx +255 -0
  95. package/src/billable-services/billable-services.scss +218 -0
  96. package/src/billable-services/billable-services.test.tsx +16 -0
  97. package/src/billable-services/create-edit/add-billable-service.component.tsx +322 -0
  98. package/src/billable-services/create-edit/add-billable-service.scss +131 -0
  99. package/src/billable-services/create-edit/add-billable-service.test.tsx +152 -0
  100. package/src/billable-services/dashboard/dashboard.component.tsx +15 -0
  101. package/src/billable-services/dashboard/dashboard.scss +27 -0
  102. package/src/billable-services/dashboard/dashboard.test.tsx +11 -0
  103. package/src/billable-services/dashboard/service-metrics.component.tsx +41 -0
  104. package/src/billable-services-admin-card-link.component.test.tsx +21 -0
  105. package/src/billable-services-admin-card-link.component.tsx +25 -0
  106. package/src/billing-dashboard/billing-dashboard.component.tsx +20 -0
  107. package/src/billing-dashboard/billing-dashboard.scss +27 -0
  108. package/src/billing-dashboard/billing-dashboard.test.tsx +13 -0
  109. package/src/billing-form/billing-checkin-form.component.tsx +127 -0
  110. package/src/billing-form/billing-checkin-form.scss +13 -0
  111. package/src/billing-form/billing-checkin-form.test.tsx +134 -0
  112. package/src/billing-form/billing-form.component.tsx +347 -0
  113. package/src/billing-form/billing-form.resource.ts +32 -0
  114. package/src/billing-form/billing-form.scss +88 -0
  115. package/src/billing-form/visit-attributes/visit-attributes-form.component.tsx +173 -0
  116. package/src/billing-form/visit-attributes/visit-attributes-form.scss +22 -0
  117. package/src/billing-header/billing-header.component.tsx +43 -0
  118. package/src/billing-header/billing-header.scss +83 -0
  119. package/src/billing-header/billing-illustration.component.tsx +30 -0
  120. package/src/billing.resource.ts +148 -0
  121. package/src/bills-table/bills-table.component.tsx +280 -0
  122. package/src/bills-table/bills-table.scss +181 -0
  123. package/src/bills-table/bills-table.test.tsx +154 -0
  124. package/src/config-schema.ts +50 -0
  125. package/src/constants.ts +3 -0
  126. package/src/dashboard.meta.ts +7 -0
  127. package/src/declarations.d.ts +4 -0
  128. package/src/helpers/functions.ts +66 -0
  129. package/src/helpers/index.ts +1 -0
  130. package/src/index.ts +72 -0
  131. package/src/invoice/invoice-table.component.tsx +189 -0
  132. package/src/invoice/invoice-table.scss +91 -0
  133. package/src/invoice/invoice.component.tsx +144 -0
  134. package/src/invoice/invoice.scss +93 -0
  135. package/src/invoice/invoice.test.tsx +242 -0
  136. package/src/invoice/payments/invoice-breakdown/invoice-breakdown.component.tsx +17 -0
  137. package/src/invoice/payments/invoice-breakdown/invoice-breakdown.scss +29 -0
  138. package/src/invoice/payments/payment-form/payment-form.component.tsx +105 -0
  139. package/src/invoice/payments/payment-form/payment-form.scss +54 -0
  140. package/src/invoice/payments/payment-history/payment-history.component.tsx +69 -0
  141. package/src/invoice/payments/payment.resource.ts +44 -0
  142. package/src/invoice/payments/payments.component.tsx +147 -0
  143. package/src/invoice/payments/payments.scss +46 -0
  144. package/src/invoice/payments/utils.ts +68 -0
  145. package/src/invoice/payments/visit-tags/visit-attribute.component.tsx +21 -0
  146. package/src/invoice/printable-invoice/print-receipt.component.tsx +29 -0
  147. package/src/invoice/printable-invoice/print-receipt.scss +14 -0
  148. package/src/invoice/printable-invoice/printable-footer.component.tsx +19 -0
  149. package/src/invoice/printable-invoice/printable-footer.scss +17 -0
  150. package/src/invoice/printable-invoice/printable-footer.test.tsx +30 -0
  151. package/src/invoice/printable-invoice/printable-invoice-header.component.tsx +63 -0
  152. package/src/invoice/printable-invoice/printable-invoice-header.scss +61 -0
  153. package/src/invoice/printable-invoice/printable-invoice-header.test.tsx +58 -0
  154. package/src/invoice/printable-invoice/printable-invoice.component.tsx +146 -0
  155. package/src/invoice/printable-invoice/printable-invoice.scss +50 -0
  156. package/src/left-panel-link.component.tsx +41 -0
  157. package/src/left-panel-link.test.tsx +38 -0
  158. package/src/metrics-cards/card.component.tsx +14 -0
  159. package/src/metrics-cards/card.scss +20 -0
  160. package/src/metrics-cards/metrics-cards.component.tsx +42 -0
  161. package/src/metrics-cards/metrics-cards.scss +12 -0
  162. package/src/metrics-cards/metrics-cards.test.tsx +44 -0
  163. package/src/metrics-cards/metrics.resource.ts +45 -0
  164. package/src/modal/require-payment-modal.component.tsx +85 -0
  165. package/src/modal/require-payment.scss +6 -0
  166. package/src/root.component.tsx +19 -0
  167. package/src/root.scss +30 -0
  168. package/src/routes.json +78 -0
  169. package/src/setup-tests.ts +13 -0
  170. package/src/types/index.ts +181 -0
  171. package/test-helpers.tsx +23 -0
  172. package/translations/am.json +117 -0
  173. package/translations/en.json +117 -0
  174. package/translations/es.json +117 -0
  175. package/translations/fr.json +117 -0
  176. package/translations/he.json +117 -0
  177. package/translations/km.json +117 -0
  178. package/tsconfig.json +16 -0
  179. package/webpack.config.js +1 -0
@@ -0,0 +1,181 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @import '~@openmrs/esm-styleguide/src/vars';
4
+
5
+ .container {
6
+ margin: 2rem 0;
7
+ }
8
+
9
+ .emptyStateContainer,
10
+ .loaderContainer {
11
+ @extend .container;
12
+ }
13
+
14
+ .billListContainer {
15
+ background-color: $ui-02;
16
+ border: 1px solid $ui-03;
17
+ width: 100%;
18
+ margin: 0 auto;
19
+ max-width: 95vw;
20
+ padding-bottom: 0;
21
+
22
+ :has(.filterEmptyState) {
23
+ border-bottom: none;
24
+ }
25
+ }
26
+
27
+ .filterContainer {
28
+ flex: 1;
29
+
30
+ :global(.cds--dropdown__wrapper--inline) {
31
+ gap: 0;
32
+ }
33
+
34
+ :global(.cds--list-box__menu-icon) {
35
+ height: 1rem;
36
+ }
37
+
38
+ :global(.cds--list-box__menu) {
39
+ min-width: max-content;
40
+ }
41
+
42
+ :global(.cds--list-box) {
43
+ margin-left: layout.$spacing-03;
44
+ }
45
+ }
46
+
47
+ .menu {
48
+ margin-left: layout.$spacing-03;
49
+ }
50
+
51
+ .headerContainer {
52
+ display: flex;
53
+ justify-content: space-between;
54
+ align-items: center;
55
+ padding: layout.$spacing-04 layout.$spacing-05;
56
+ background-color: $ui-02;
57
+ }
58
+
59
+ .backgroundDataFetchingIndicator {
60
+ align-items: center;
61
+ display: flex;
62
+ flex: 1;
63
+ justify-content: space-between;
64
+
65
+ &:global(.cds--inline-loading) {
66
+ max-height: 1rem;
67
+ }
68
+ }
69
+
70
+ .tableContainer section {
71
+ position: relative;
72
+ }
73
+
74
+ .tableContainer a {
75
+ text-decoration: none;
76
+ }
77
+
78
+ .pagination {
79
+ overflow: hidden;
80
+
81
+ &:global(.cds--pagination) {
82
+ border-top: none;
83
+ }
84
+ }
85
+
86
+ .hiddenRow {
87
+ display: none;
88
+ }
89
+
90
+ .emptyRow {
91
+ padding: 0 1rem;
92
+ display: flex;
93
+ align-items: center;
94
+ }
95
+
96
+ .visitSummaryContainer {
97
+ width: 100%;
98
+ max-width: 768px;
99
+ margin: 1rem auto;
100
+ }
101
+
102
+ .expandedActiveVisitRow > td > div {
103
+ max-height: max-content !important;
104
+ }
105
+
106
+ .expandedActiveVisitRow td {
107
+ padding: 0 2rem;
108
+ }
109
+
110
+ .expandedActiveVisitRow th[colspan] td[colspan] > div:first-child {
111
+ padding: 0 1rem;
112
+ }
113
+
114
+ .action {
115
+ margin-bottom: layout.$spacing-03;
116
+ }
117
+
118
+ .illo {
119
+ margin-top: layout.$spacing-05;
120
+ }
121
+
122
+ .content {
123
+ @include type.type-style('heading-compact-01');
124
+ color: $text-02;
125
+ margin-top: layout.$spacing-05;
126
+ margin-bottom: layout.$spacing-03;
127
+ }
128
+
129
+ .desktopHeading,
130
+ .tabletHeading {
131
+ text-align: left;
132
+ text-transform: capitalize;
133
+ flex: 1;
134
+
135
+ h4 {
136
+ @include type.type-style('heading-compact-02');
137
+ color: $text-02;
138
+
139
+ &:after {
140
+ content: '';
141
+ display: block;
142
+ width: 2rem;
143
+ padding-top: 3px;
144
+ border-bottom: 0.375rem solid;
145
+ @include brand-03(border-bottom-color);
146
+ }
147
+ }
148
+ }
149
+
150
+ .tile {
151
+ text-align: center;
152
+ border: 1px solid $ui-03;
153
+ }
154
+
155
+ .menuitem {
156
+ max-width: none;
157
+ }
158
+
159
+ .filterEmptyState {
160
+ display: flex;
161
+ justify-content: center;
162
+ align-items: center;
163
+ padding: layout.$spacing-05;
164
+ margin: layout.$spacing-09;
165
+ text-align: center;
166
+ }
167
+
168
+ .filterEmptyStateTile {
169
+ margin: auto;
170
+ }
171
+
172
+ .filterEmptyStateContent {
173
+ @include type.type-style('heading-compact-02');
174
+ color: $text-02;
175
+ margin-bottom: 0.5rem;
176
+ }
177
+
178
+ .filterEmptyStateHelper {
179
+ @include type.type-style('body-compact-01');
180
+ color: $text-02;
181
+ }
@@ -0,0 +1,154 @@
1
+ import React from 'react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { render, screen } from '@testing-library/react';
4
+ import { useBills } from '../billing.resource';
5
+ import BillsTable from './bills-table.component';
6
+
7
+ const mockbills = useBills as jest.Mock;
8
+
9
+ const mockBillsData = [
10
+ { uuid: '1', patientName: 'John Doe', identifier: '12345678', visitType: 'Checkup', patientUuid: 'uuid1' },
11
+ { uuid: '2', patientName: 'Mary Smith', identifier: '98765432', visitType: 'Wake up', patientUuid: 'uuid2' },
12
+ ];
13
+
14
+ jest.mock('../billing.resource', () => ({
15
+ ...jest.requireActual('../billing.resource'),
16
+ useBills: jest.fn(() => ({
17
+ bills: mockBillsData,
18
+ isLoading: false,
19
+ isValidating: false,
20
+ error: null,
21
+ })),
22
+ }));
23
+
24
+ jest.mock('@openmrs/esm-framework', () => ({
25
+ ...jest.requireActual('@openmrs/esm-framework'),
26
+ ErrorState: jest.fn(() => (
27
+ <div>
28
+ Sorry, there was a problem displaying this information. You can try to reload this page, or contact the site
29
+ administrator and quote the error code above.
30
+ </div>
31
+ )),
32
+ useConfig: jest.fn(() => ({ bills: { pageSizes: [10, 20, 30, 40, 50], pageSize: 10 } })),
33
+ usePagination: jest.fn().mockImplementation((data) => ({
34
+ currentPage: 1,
35
+ goTo: () => {},
36
+ results: data,
37
+ paginated: false,
38
+ })),
39
+ }));
40
+
41
+ describe('BillsTable', () => {
42
+ let user;
43
+
44
+ beforeEach(() => {
45
+ user = userEvent.setup();
46
+ });
47
+
48
+ xit('renders data table with pending bills', () => {
49
+ render(<BillsTable />);
50
+
51
+ expect(screen.getByText('Visit time')).toBeInTheDocument();
52
+ expect(screen.getByText('Identifier')).toBeInTheDocument();
53
+ const expectedColumnHeaders = [/Visit time/, /Identifier/, /Name/, /Billing service/];
54
+ expectedColumnHeaders.forEach((header) => {
55
+ expect(screen.getByRole('columnheader', { name: new RegExp(header, 'i') })).toBeInTheDocument();
56
+ });
57
+
58
+ const patientNameLink = screen.getByText('John Doe');
59
+ expect(patientNameLink).toBeInTheDocument();
60
+ expect(patientNameLink.tagName).toBe('A');
61
+ });
62
+
63
+ it('displays empty state when there are no bills', () => {
64
+ mockbills.mockImplementationOnce(() => ({
65
+ bills: [],
66
+ isLoading: false,
67
+ isValidating: false,
68
+ error: null,
69
+ }));
70
+
71
+ render(<BillsTable />);
72
+
73
+ expect(screen.getByText(/there are no bills to display/i)).toBeInTheDocument();
74
+ });
75
+
76
+ it('should not display the table when the data is loading', () => {
77
+ mockbills.mockImplementationOnce(() => ({
78
+ bills: undefined,
79
+ isLoading: true,
80
+ isValidating: false,
81
+ error: null,
82
+ }));
83
+
84
+ render(<BillsTable />);
85
+
86
+ const expectedColumnHeaders = [/Visit time/, /Identifier/, /Name/, /Billing service/, /Department/];
87
+ expectedColumnHeaders.forEach((header) => {
88
+ expect(screen.queryByRole('columnheader', { name: new RegExp(header, 'i') })).not.toBeInTheDocument();
89
+ });
90
+ });
91
+
92
+ it('should display the error state when there is error', () => {
93
+ mockbills.mockImplementationOnce(() => ({
94
+ activeVisits: undefined,
95
+ isLoading: false,
96
+ isValidating: false,
97
+ error: 'Error in fetching data',
98
+ }));
99
+
100
+ render(<BillsTable />);
101
+
102
+ expect(screen.getByText(/sorry, there was a problem displaying this information/i)).toBeInTheDocument();
103
+ expect(screen.queryByRole('table')).not.toBeInTheDocument();
104
+ });
105
+
106
+ test('should filter bills by search term and bill payment status', async () => {
107
+ render(<BillsTable />);
108
+
109
+ const searchInput = screen.getByRole('searchbox');
110
+ await user.type(searchInput, 'John Doe');
111
+
112
+ expect(screen.getByText('John Doe')).toBeInTheDocument();
113
+ expect(screen.queryByText('Mary Smith')).not.toBeInTheDocument();
114
+
115
+ await user.clear(searchInput);
116
+ await user.type(searchInput, 'Mary Smith');
117
+
118
+ expect(screen.getByText('Mary Smith')).toBeInTheDocument();
119
+ expect(screen.queryByText('John Doe')).not.toBeInTheDocument();
120
+
121
+ // Should filter the table when bill payment status combobox is changed
122
+ const billCategorySelect = screen.getByRole('combobox');
123
+ expect(billCategorySelect).toBeInTheDocument();
124
+ await user.click(billCategorySelect, { name: 'All bills' });
125
+ expect(mockbills).toHaveBeenCalledWith('', '');
126
+
127
+ await user.click(screen.getByText('Pending bills'));
128
+ expect(screen.getByText('Pending bills')).toBeInTheDocument();
129
+ expect(mockbills).toHaveBeenCalledWith('', 'PENDING');
130
+ });
131
+
132
+ test('should show the loading spinner while retrieving data', () => {
133
+ mockbills.mockImplementationOnce(() => ({
134
+ bills: undefined,
135
+ isLoading: true,
136
+ isValidating: false,
137
+ error: null,
138
+ }));
139
+
140
+ render(<BillsTable />);
141
+
142
+ const dataTableSkeleton = screen.getByRole('table');
143
+ expect(dataTableSkeleton).toBeInTheDocument();
144
+ expect(dataTableSkeleton).toHaveClass('cds--skeleton cds--data-table cds--data-table--zebra');
145
+ });
146
+
147
+ test('should render patient name as a link', async () => {
148
+ render(<BillsTable />);
149
+
150
+ const patientNameLink = screen.getByRole('link', { name: 'John Doe' });
151
+ expect(patientNameLink).toBeInTheDocument();
152
+ expect(patientNameLink).toHaveAttribute('href', '/openmrs/spa/home/billing/patient/uuid1/1');
153
+ });
154
+ });
@@ -0,0 +1,50 @@
1
+ import { Type } from '@openmrs/esm-framework';
2
+
3
+ export interface BillingConfig {}
4
+
5
+ export const configSchema = {
6
+ patientCatergory: {
7
+ _type: Type.Object,
8
+ _description: 'Patient Category Custom UUIDs',
9
+ _default: {
10
+ paymentDetails: 'fbc0702d-b4c9-4968-be63-af8ad3ad6239',
11
+ paymentMethods: '8553afa0-bdb9-4d3c-8a98-05fa9350aa85',
12
+ policyNumber: '3a988e33-a6c0-4b76-b924-01abb998944b',
13
+ insuranceScheme: 'aac48226-d143-4274-80e0-264db4e368ee',
14
+ patientCategory: '3b9dfac8-9e4d-11ee-8c90-0242ac120002',
15
+ formPayloadPending: '919b51c9-8e2e-468f-8354-181bf3e55786',
16
+ },
17
+ },
18
+
19
+ catergoryConcepts: {
20
+ _type: Type.Object,
21
+ _description: 'Patient Category Concept UUIDs',
22
+ _default: {
23
+ payingDetails: '44b34972-6630-4e5a-a9f6-a6eb0f109650',
24
+ nonPayingDetails: 'f3fb2d88-cccd-422c-8766-be101ba7bd2e',
25
+ insuranceDetails: 'beac329b-f1dc-4a33-9e7c-d95821a137a6',
26
+ childUnder5: '1528AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
27
+ student: '159465AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
28
+ },
29
+ },
30
+
31
+ defaultCurrency: {
32
+ _type: Type.String,
33
+ _description: 'The default currency for the application. Specify the currency code (e.g., KES, UGX, GBP).',
34
+ _default: 'KES',
35
+ },
36
+
37
+ pageSize: {
38
+ _type: Type.String,
39
+ _description: 'The default page size',
40
+ _default: 10,
41
+ },
42
+ };
43
+
44
+ export interface ConfigObject {
45
+ patientCatergory: Object;
46
+ defaultCurrency: string;
47
+ catergoryConcepts: Object;
48
+ pageSize;
49
+ object;
50
+ }
@@ -0,0 +1,3 @@
1
+ import { restBaseUrl } from '@openmrs/esm-framework';
2
+
3
+ export const apiBasePath = `${restBaseUrl}/billing/`;
@@ -0,0 +1,7 @@
1
+ export const dashboardMeta = {
2
+ slot: 'patient-chart-billing-dashboard-slot',
3
+ columns: 1,
4
+ title: 'Billing history',
5
+ path: 'Billing history',
6
+ hideDashboardTitle: true,
7
+ };
@@ -0,0 +1,4 @@
1
+ declare module '*.css';
2
+ declare module '*.scss';
3
+ declare module '@carbon/react';
4
+ declare type SideNavProps = object;
@@ -0,0 +1,66 @@
1
+ import { type Payment, type LineItem } from '../types';
2
+ import { type ConfigObject } from '../config-schema';
3
+ import { useConfig } from '@openmrs/esm-framework';
4
+
5
+ // amount already paid
6
+ export function calculateTotalAmountTendered(payments: Array<Payment>) {
7
+ return Array.isArray(payments)
8
+ ? payments.reduce((totalAmount, item) => {
9
+ // Ensure that "amount" property is present and numeric
10
+ if (typeof item.amount === 'number' && item.voided !== true) {
11
+ return totalAmount + item.amount;
12
+ }
13
+ return totalAmount;
14
+ }, 0)
15
+ : 0;
16
+ }
17
+
18
+ // balance
19
+ export function calculateTotalBalance(lineItems: Array<LineItem>, payments: Array<Payment>) {
20
+ return Math.min(this.calculateTotalAmount(lineItems) - this.calculateTotalAmountTendered(payments));
21
+ }
22
+
23
+ // total bill
24
+ export function calculateTotalAmount(lineItems: Array<LineItem>) {
25
+ return Array.isArray(lineItems)
26
+ ? lineItems.reduce((totalAmount, item) => {
27
+ // Ensure that "price" and "quantity" properties are present and numeric
28
+ if (typeof item.price === 'number' && typeof item.quantity === 'number' && item.voided !== true) {
29
+ return totalAmount + item.price * item.quantity;
30
+ }
31
+ return totalAmount;
32
+ }, 0)
33
+ : 0;
34
+ }
35
+
36
+ export const convertToCurrency = (amountToConvert: number, currencyType?: string) => {
37
+ const locale = window.i18next?.language?.substring(0, 2) ?? '';
38
+ const formatter = new Intl.NumberFormat(locale, {
39
+ style: 'currency',
40
+ currency: currencyType,
41
+ minimumFractionDigits: 2,
42
+ });
43
+
44
+ let formattedAmount = formatter.format(Math.abs(amountToConvert));
45
+
46
+ if (amountToConvert < 0) {
47
+ formattedAmount = `(${formattedAmount})`;
48
+ }
49
+
50
+ return formattedAmount;
51
+ };
52
+
53
+ export const getGender = (gender: string, t) => {
54
+ switch (gender) {
55
+ case 'male':
56
+ return t('male', 'Male');
57
+ case 'female':
58
+ return t('female', 'Female');
59
+ case 'other':
60
+ return t('other', 'Other');
61
+ case 'unknown':
62
+ return t('unknown', 'Unknown');
63
+ default:
64
+ return gender;
65
+ }
66
+ };
@@ -0,0 +1 @@
1
+ export * from './functions';
package/src/index.ts ADDED
@@ -0,0 +1,72 @@
1
+ import { configSchema } from './config-schema';
2
+ import { createDashboardLink, registerWorkspace } from '@openmrs/esm-patient-common-lib';
3
+ import { createLeftPanelLink } from './left-panel-link.component';
4
+ import { dashboardMeta } from './dashboard.meta';
5
+ import {
6
+ defineConfigSchema,
7
+ getAsyncLifecycle,
8
+ getSyncLifecycle,
9
+ registerFeatureFlag,
10
+ translateFrom,
11
+ } from '@openmrs/esm-framework';
12
+ import BillableServiceHome from './billable-services/billable-services-home.component';
13
+ import BillableServicesCardLink from './billable-services-admin-card-link.component';
14
+ import BillHistory from './bill-history/bill-history.component';
15
+ import BillingCheckInForm from './billing-form/billing-checkin-form.component';
16
+ import RequirePaymentModal from './modal/require-payment-modal.component';
17
+ import RootComponent from './root.component';
18
+ import VisitAttributeTags from './invoice/payments/visit-tags/visit-attribute.component';
19
+ import BillableServicesDashboard from './billable-services/dashboard/dashboard.component';
20
+ import ServiceMetrics from './billable-services/dashboard/service-metrics.component';
21
+
22
+ const moduleName = '@openmrs/esm-billing-app';
23
+
24
+ const options = {
25
+ featureName: 'billing',
26
+ moduleName,
27
+ };
28
+
29
+ registerFeatureFlag(
30
+ 'billing',
31
+ 'Billing module',
32
+ 'This feature introduces navigation links on the patient chart and home page to allow accessing the billing module features',
33
+ );
34
+
35
+ // t('billing', 'Billing')
36
+ export const billingDashboardLink = getSyncLifecycle(
37
+ createLeftPanelLink({
38
+ name: 'billing',
39
+ title: 'Billing',
40
+ }),
41
+ options,
42
+ );
43
+
44
+ export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
45
+
46
+ export function startupApp() {
47
+ defineConfigSchema(moduleName, configSchema);
48
+
49
+ // t('billingForm', 'Billing form')
50
+ registerWorkspace({
51
+ name: 'billing-form-workspace',
52
+ title: translateFrom(moduleName, 'billingForm', 'Billing form'),
53
+ load: getAsyncLifecycle(() => import('./billing-form/billing-form.component'), options),
54
+ type: 'billing',
55
+ canHide: false,
56
+ width: 'wider',
57
+ });
58
+ }
59
+
60
+ export const billingSummaryDashboardLink = getSyncLifecycle(
61
+ createDashboardLink({ ...dashboardMeta, moduleName }),
62
+ options,
63
+ );
64
+
65
+ export const billableServicesCardLink = getSyncLifecycle(BillableServicesCardLink, options);
66
+ export const billableServicesHome = getSyncLifecycle(BillableServiceHome, options);
67
+ export const billingCheckInForm = getSyncLifecycle(BillingCheckInForm, options);
68
+ export const serviceMetrics = getSyncLifecycle(ServiceMetrics, options);
69
+ export const billingPatientSummary = getSyncLifecycle(BillHistory, options);
70
+ export const requirePaymentModal = getSyncLifecycle(RequirePaymentModal, options);
71
+ export const root = getSyncLifecycle(RootComponent, options);
72
+ export const visitAttributeTags = getSyncLifecycle(VisitAttributeTags, options);