@ampath/esm-dha-workflow-app 4.0.0-claim-workflows.310 → 4.0.0-claim-workflows.312

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 (72) hide show
  1. package/dist/1362.js +1 -1
  2. package/dist/1362.js.map +1 -1
  3. package/dist/1925.js +1 -1
  4. package/dist/1927.js +1 -1
  5. package/dist/2021.js +1 -1
  6. package/dist/2054.js +1 -1
  7. package/dist/2092.js +1 -1
  8. package/dist/2210.js +1 -1
  9. package/dist/2230.js +1 -1
  10. package/dist/2493.js +1 -1
  11. package/dist/262.js +2 -0
  12. package/dist/262.js.map +1 -0
  13. package/dist/3202.js +1 -1
  14. package/dist/347.js +1 -1
  15. package/dist/3771.js +1 -1
  16. package/dist/3771.js.map +1 -1
  17. package/dist/4003.js +1 -1
  18. package/dist/4205.js +1 -1
  19. package/dist/4231.js +1 -1
  20. package/dist/4454.js +1 -1
  21. package/dist/4674.js +1 -1
  22. package/dist/4986.js +1 -1
  23. package/dist/5225.js +1 -1
  24. package/dist/5344.js +1 -1
  25. package/dist/5344.js.map +1 -1
  26. package/dist/5576.js +1 -1
  27. package/dist/5851.js +1 -1
  28. package/dist/6028.js +1 -1
  29. package/dist/6182.js +1 -1
  30. package/dist/6264.js +1 -1
  31. package/dist/6730.js +1 -0
  32. package/dist/6730.js.map +1 -0
  33. package/dist/6824.js +1 -1
  34. package/dist/7521.js +1 -1
  35. package/dist/7549.js +1 -1
  36. package/dist/7557.js +1 -1
  37. package/dist/7830.js +1 -1
  38. package/dist/7941.js +1 -1
  39. package/dist/8558.js +1 -1
  40. package/dist/9043.js +1 -1
  41. package/dist/9091.js +1 -1
  42. package/dist/9093.js +1 -1
  43. package/dist/9338.js +1 -1
  44. package/dist/9428.js +1 -1
  45. package/dist/9428.js.map +1 -1
  46. package/dist/esm-dha-workflow-app.js +1 -1
  47. package/dist/esm-dha-workflow-app.js.buildmanifest.json +102 -102
  48. package/dist/esm-dha-workflow-app.js.map +1 -1
  49. package/dist/main.js +1 -1
  50. package/dist/main.js.map +1 -1
  51. package/dist/routes.json +1 -1
  52. package/package.json +1 -1
  53. package/src/billing/billing-claims.resource.ts +31 -1
  54. package/src/billing/dashboard/v2/patient-bill-details/patient-bill-details.tsx +2 -2
  55. package/src/billing/dashboard/v2/patient-bill-details/payment-mode.ts +10 -3
  56. package/src/billing/dashboard/v3/claim-visits/claim-intervention-details/claim-intervention-details.component.tsx +100 -71
  57. package/src/billing/dashboard/v3/claim-visits/claim-visit-details/claim-visit-details.component.tsx +27 -1
  58. package/src/billing/dashboard/v3/patient-bill-details/bill-details/bill-details.tsx +1 -0
  59. package/src/billing/dashboard/v3/patient-bill-details/claim-details/patient-claim-details.component.tsx +1 -0
  60. package/src/billing/dashboard/v3/patient-bill-details/modals/add-claim-line/add-claim-line.modal.tsx +10 -2
  61. package/src/billing/dashboard/v3/patient-bill-details/patient-bill-details.tsx +2 -1
  62. package/src/billing/dashboard/v3/preauth/preauth-list.component.tsx +8 -3
  63. package/src/billing/dashboard/v3/preauth/preauth.resource.ts +5 -0
  64. package/src/billing/dashboard/v3/preauth/preauth.workspace.tsx +10 -6
  65. package/src/billing/types/index.ts +5 -0
  66. package/src/claims/claims.resource.ts +33 -6
  67. package/dist/1234.js +0 -1
  68. package/dist/1234.js.map +0 -1
  69. package/dist/5555.js +0 -2
  70. package/dist/5555.js.map +0 -1
  71. package/src/billing/dashboard/v3/claim-visits/claim-intervention-details/claim-intervention-details.component.scss +0 -5
  72. /package/dist/{5555.js.LICENSE.txt → 262.js.LICENSE.txt} +0 -0
@@ -1,34 +1,52 @@
1
1
  import React from 'react';
2
2
  import { type PatientFacilityBillDetails, type VisitIntervention } from '../../types';
3
- import { Button, ButtonSet, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tag } from '@carbon/react';
3
+ import {
4
+ MenuButton,
5
+ MenuItem,
6
+ Table,
7
+ TableBody,
8
+ TableCell,
9
+ TableHead,
10
+ TableHeader,
11
+ TableRow,
12
+ Tag,
13
+ } from '@carbon/react';
4
14
  import { launchWorkspace, showSnackbar, useSession } from '@openmrs/esm-framework';
5
15
 
6
- import styles from './claim-intervention-details.component.scss';
7
16
  import { invalidatePreauthPreview, parseDocTypes, readSpecialtyFlags } from '../../../v2/preauth/preauth.resource';
17
+ import {
18
+ interventionHasBlockingPreauth,
19
+ interventionHasFailedPreauth,
20
+ usePreauthPreview,
21
+ } from '../../../../../claims/claims.resource';
8
22
 
9
23
  interface claimInterventionDetailsProps {
10
24
  claimInterventions: VisitIntervention[];
11
25
  patientBillDetails: PatientFacilityBillDetails;
12
26
  consentToken: string;
13
27
  visitUuid: string;
28
+ canSwitchIntervention?: boolean;
29
+ onSwitchSuccess?: () => void;
14
30
  }
31
+
32
+ const isActiveIntervention = (iv: VisitIntervention) => (iv.workflow_state ?? '').toUpperCase() === 'ACTIVE';
33
+
15
34
  const ClaimInterventionDetails: React.FC<claimInterventionDetailsProps> = ({
16
35
  claimInterventions,
17
36
  patientBillDetails,
18
37
  consentToken,
19
38
  visitUuid,
39
+ canSwitchIntervention = false,
40
+ onSwitchSuccess,
20
41
  }) => {
42
+ const session = useSession();
43
+ const locationUuid = session?.sessionLocation?.uuid;
44
+ const { preview: preauthPreview } = usePreauthPreview(consentToken, locationUuid);
45
+
21
46
  if (!claimInterventions || claimInterventions.length === 0) {
22
47
  return <>No Intervention data</>;
23
48
  }
24
- function formatPreAuthText(preAuth: boolean) {
25
- if (preAuth) {
26
- return 'YES';
27
- }
28
- return 'NO';
29
- }
30
- const session = useSession();
31
- const locationUuid = session?.sessionLocation?.uuid;
49
+
32
50
  const handleAddAttachment = (ci: any) => {
33
51
  launchWorkspace('upload-intervention-attachments-workspace', {
34
52
  consentToken: consentToken,
@@ -44,10 +62,31 @@ const ClaimInterventionDetails: React.FC<claimInterventionDetailsProps> = ({
44
62
  bill: patientBillDetails,
45
63
  });
46
64
  };
65
+
66
+ const canActOnIntervention = (intervention: VisitIntervention) =>
67
+ canSwitchIntervention && isActiveIntervention(intervention);
68
+
69
+ const hasBlockingPreauth = (intervention: VisitIntervention) =>
70
+ interventionHasBlockingPreauth(preauthPreview, intervention.intervention_code);
71
+
72
+ /** Switch stays locked while a non-resubmittable preauth is in flight / finalised. */
73
+ const canSwitchFor = (intervention: VisitIntervention) =>
74
+ canActOnIntervention(intervention) && !hasBlockingPreauth(intervention);
75
+
76
+ const canRaisePreauthFor = (intervention: VisitIntervention) => {
77
+ return canActOnIntervention(intervention) && Boolean(intervention.needs_preauth) && !hasBlockingPreauth(intervention);
78
+ };
79
+
80
+ const isResubmitPreauth = (intervention: VisitIntervention) =>
81
+ interventionHasFailedPreauth(preauthPreview, intervention.intervention_code);
82
+
83
+ const raisePreauthLabel = (intervention: VisitIntervention) =>
84
+ isResubmitPreauth(intervention) ? 'Resubmit preauth' : 'Raise preauth';
85
+
47
86
  const handleSwitchIntervention = (intervention: VisitIntervention) => {
48
- // if (!canSwitchIntervention) {
49
- // return;
50
- // }
87
+ if (!canSwitchFor(intervention)) {
88
+ return;
89
+ }
51
90
  launchWorkspace('switch-intervention-workspace', {
52
91
  consentToken: consentToken,
53
92
  currentInterventions: [intervention],
@@ -56,16 +95,14 @@ const ClaimInterventionDetails: React.FC<claimInterventionDetailsProps> = ({
56
95
  visitUuid: visitUuid,
57
96
  billDate: patientBillDetails?.bill_date,
58
97
  onSwitchSuccess: () => {
59
- // invalidateProviderClaimPreview();
98
+ onSwitchSuccess?.();
60
99
  },
61
100
  });
62
101
  };
63
-
64
102
  const handleRaisePreauth = (intervention: VisitIntervention) => {
65
- // if (!canSwitchIntervention) {
66
- // return;
67
- // }
68
- // const consentToken = claimsVisit.authorization_code;
103
+ if (!canSwitchIntervention) {
104
+ return;
105
+ }
69
106
  if (!consentToken) {
70
107
  showSnackbar({
71
108
  kind: 'error',
@@ -74,9 +111,9 @@ const ClaimInterventionDetails: React.FC<claimInterventionDetailsProps> = ({
74
111
  });
75
112
  return;
76
113
  }
77
- // if (!intervention.needs_preauth) {
78
- // return;
79
- // }
114
+ if (!canRaisePreauthFor(intervention)) {
115
+ return;
116
+ }
80
117
 
81
118
  const requiredDocs = parseDocTypes(
82
119
  Array.isArray(intervention.required_preauth_document_types)
@@ -87,6 +124,7 @@ const ClaimInterventionDetails: React.FC<claimInterventionDetailsProps> = ({
87
124
  ? intervention.applicable_document_types.map(String)
88
125
  : parseDocTypes(intervention.applicable_document_types as string | null | undefined);
89
126
 
127
+ // Fresh raise and failure-state resubmit both reopen the same preauth form.
90
128
  launchWorkspace('preauth-form-workspace', {
91
129
  consentToken,
92
130
  patientUuid: patientBillDetails?.patient_uuid,
@@ -110,10 +148,11 @@ const ClaimInterventionDetails: React.FC<claimInterventionDetailsProps> = ({
110
148
  },
111
149
  onSuccess: async () => {
112
150
  await invalidatePreauthPreview(consentToken, locationUuid!);
113
- // invalidateProviderClaimPreview();
151
+ onSwitchSuccess?.();
114
152
  },
115
153
  });
116
154
  };
155
+
117
156
  return (
118
157
  <>
119
158
  <Table size="sm">
@@ -133,55 +172,45 @@ const ClaimInterventionDetails: React.FC<claimInterventionDetailsProps> = ({
133
172
  </TableHead>
134
173
  <TableBody>
135
174
  {claimInterventions &&
136
- claimInterventions.map((ci, index) => {
175
+ claimInterventions.map((ci) => {
176
+ const canSwitch = canSwitchFor(ci);
177
+ const canRaise = canRaisePreauthFor(ci);
178
+ const hasAttachments = (ci.applicable_document_types ?? []).length > 0;
137
179
  return (
138
- <>
139
- <TableRow key={ci.id}>
140
- <TableCell>{ci.intervention_code}</TableCell>
141
- <TableCell>{ci.intervention_payment_mechanism}</TableCell>
142
- <TableCell>{ci.intervention_name}</TableCell>
143
- <TableCell>{ci.accrued_per_diem_amount}</TableCell>
144
- <TableCell>{ci.accrued_per_diem_days}</TableCell>
145
- <TableCell>{ci.workflow_state}</TableCell>
146
- <TableCell>{ci.sub_benefit_code}</TableCell>
147
- <TableCell>{ci.intervention_fund}</TableCell>
148
- <TableCell>
149
- {ci.applicable_document_types.length > 0 ? (
150
- <div className={styles.attachmentButtons}>
151
- <Button kind="primary" size="sm" onClick={() => handleGenerateAttachment(ci)}>
152
- Generate
153
- </Button>
154
-
155
- <Button kind="primary" size="sm" onClick={() => handleAddAttachment(ci)}>
156
- Upload
157
- </Button>
158
- </div>
159
- ) : (
160
- <Tag type="gray">Not Required</Tag>
161
- )}
162
- </TableCell>
163
- <TableCell>
164
- <div className={styles.attachmentButtons}>
165
- <Button
166
- size="sm"
167
- onClick={() => {
168
- handleRaisePreauth(ci);
169
- }}
170
- >
171
- Raise preauth
172
- </Button>
173
- <Button
174
- size="sm"
175
- onClick={() => {
176
- handleSwitchIntervention(ci);
177
- }}
178
- >
179
- Switch intervention
180
- </Button>
181
- </div>
182
- </TableCell>
183
- </TableRow>
184
- </>
180
+ <TableRow key={ci.id}>
181
+ <TableCell>{ci.intervention_code}</TableCell>
182
+ <TableCell>{ci.intervention_payment_mechanism}</TableCell>
183
+ <TableCell>{ci.intervention_name}</TableCell>
184
+ <TableCell>{ci.accrued_per_diem_amount}</TableCell>
185
+ <TableCell>{ci.accrued_per_diem_days}</TableCell>
186
+ <TableCell>{ci.workflow_state}</TableCell>
187
+ <TableCell>{ci.sub_benefit_code}</TableCell>
188
+ <TableCell>{ci.intervention_fund}</TableCell>
189
+ <TableCell>
190
+ {hasAttachments ? (
191
+ <MenuButton label="Attachments" kind="ghost" size="sm">
192
+ <MenuItem label="Generate" onClick={() => handleGenerateAttachment(ci)} />
193
+ <MenuItem label="Upload" onClick={() => handleAddAttachment(ci)} />
194
+ </MenuButton>
195
+ ) : (
196
+ <Tag type="gray">Not Required</Tag>
197
+ )}
198
+ </TableCell>
199
+ <TableCell>
200
+ <MenuButton label="Actions" kind="ghost" size="sm">
201
+ <MenuItem
202
+ label={raisePreauthLabel(ci)}
203
+ onClick={() => handleRaisePreauth(ci)}
204
+ disabled={!canRaise}
205
+ />
206
+ <MenuItem
207
+ label="Switch intervention"
208
+ onClick={() => handleSwitchIntervention(ci)}
209
+ disabled={!canSwitch}
210
+ />
211
+ </MenuButton>
212
+ </TableCell>
213
+ </TableRow>
185
214
  );
186
215
  })}
187
216
  </TableBody>
@@ -14,17 +14,23 @@ import ClaimDoctors from '../claim-doctors/claim-doctors';
14
14
  import AddClaimDoctorModal from '../modal/claim-doctors/add-claim-doctor/add-claim-doctor.modal';
15
15
  import { VisitTypeUuids } from '../../../../../shared/constants/visit-types';
16
16
  import { VisitType } from '../../../../../claims';
17
+ import { canEditClaimContent } from '../../../v2/claim-statuses';
18
+ import { interventionHasBlockingPreauth, usePreauthPreview } from '../../../../../claims/claims.resource';
19
+
17
20
  interface claimVisitDetailsProps {
18
21
  claimsVisit: ClaimsVisit;
19
22
  locationUuid: string;
20
23
  patientBillDetails?: PatientFacilityBillDetails;
21
24
  onBillDetailsChange?: () => void;
25
+ /** True while claim preview is revalidating — stand down content edits. */
26
+ claimRefreshing?: boolean;
22
27
  }
23
28
  const ClaimVisitDetails: React.FC<claimVisitDetailsProps> = ({
24
29
  claimsVisit,
25
30
  locationUuid,
26
31
  patientBillDetails,
27
32
  onBillDetailsChange,
33
+ claimRefreshing = false,
28
34
  }) => {
29
35
  const [showCloseClaimModal, setShowCloseClaimModal] = useState<boolean>();
30
36
  const [showSubmitClaimModal, setSubmitCloseClaimModal] = useState<boolean>(false);
@@ -76,6 +82,7 @@ const ClaimVisitDetails: React.FC<claimVisitDetailsProps> = ({
76
82
  }, [activeVisit, VisitTypeUuids]);
77
83
 
78
84
  const invalidateProviderClaimPreview = useInvalidateProviderClaimPreview();
85
+ const { preview: preauthPreview } = usePreauthPreview(claimsVisit?.authorization_code, locationUuid);
79
86
 
80
87
  if (!claimsVisit) {
81
88
  return <>No Data</>;
@@ -127,7 +134,21 @@ const ClaimVisitDetails: React.FC<claimVisitDetailsProps> = ({
127
134
  });
128
135
  };
129
136
 
137
+ // Same gates as v2: content edits only while claim is DRAFT / DRAFT_RESUBMIT and not refreshing.
138
+ const canSwitchIntervention = canEditClaimContent(claimsVisit.workflow_state) && !claimRefreshing;
139
+ const hasSwitchableIntervention = Boolean(
140
+ claimsVisit.interventions?.some((iv) => {
141
+ const active = (iv.workflow_state ?? '').toUpperCase() === 'ACTIVE';
142
+ if (!active) return false;
143
+ // Match row Actions: blocking preauth locks Switch together with Raise/Resubmit.
144
+ return !interventionHasBlockingPreauth(preauthPreview, iv.intervention_code);
145
+ }),
146
+ );
147
+
130
148
  const handleSwitchIntervention = () => {
149
+ if (!canSwitchIntervention || !hasSwitchableIntervention) {
150
+ return;
151
+ }
131
152
  launchWorkspace('switch-intervention-workspace', {
132
153
  consentToken: claimsVisit.authorization_code,
133
154
  currentInterventions: claimsVisit.interventions,
@@ -158,7 +179,7 @@ const ClaimVisitDetails: React.FC<claimVisitDetailsProps> = ({
158
179
  <Button
159
180
  kind="tertiary"
160
181
  onClick={handleSwitchIntervention}
161
- disabled={!claimsVisit.interventions?.some((iv) => (iv.workflow_state ?? '').toUpperCase() === 'ACTIVE')}
182
+ disabled={!canSwitchIntervention || !hasSwitchableIntervention}
162
183
  >
163
184
  Switch Intervention
164
185
  </Button>
@@ -231,6 +252,11 @@ const ClaimVisitDetails: React.FC<claimVisitDetailsProps> = ({
231
252
  claimInterventions={claimsVisit.interventions}
232
253
  consentToken={claimsVisit.authorization_code}
233
254
  visitUuid={activeVisit?.uuid}
255
+ canSwitchIntervention={canSwitchIntervention}
256
+ onSwitchSuccess={() => {
257
+ invalidateProviderClaimPreview();
258
+ onBillDetailsChange?.();
259
+ }}
234
260
  />
235
261
  )}
236
262
  </div>
@@ -195,6 +195,7 @@ const BillDetails: React.FC<billDetailsProps> = ({ patientBillDetails, patientPa
195
195
  onClose={handleCloseAddClaimItemModal}
196
196
  onSuccess={onSuccess}
197
197
  locationUuid={locationUuid}
198
+ consentToken={consentToken}
198
199
  />
199
200
  }
200
201
  </div>
@@ -50,6 +50,7 @@ const PatientClaimDetails: React.FC<patientClaimDetailsProps> = ({
50
50
  claimsVisit={claimVisit}
51
51
  locationUuid={locationUuid}
52
52
  onBillDetailsChange={onBillDetailsChange}
53
+ claimRefreshing={isValidating}
53
54
  />
54
55
  )
55
56
  }
@@ -11,8 +11,16 @@ interface addClaimLineModalProps {
11
11
  onSuccess: () => void;
12
12
  billItem: PatientFacilityBillDetails;
13
13
  locationUuid: string;
14
+ consentToken?: string;
14
15
  }
15
- const AddClaimLineModal: React.FC<addClaimLineModalProps> = ({ open, onClose, onSuccess, billItem, locationUuid }) => {
16
+ const AddClaimLineModal: React.FC<addClaimLineModalProps> = ({
17
+ open,
18
+ onClose,
19
+ onSuccess,
20
+ billItem,
21
+ locationUuid,
22
+ consentToken: consentTokenProp,
23
+ }) => {
16
24
  const [loading,setLoading] = useState<boolean>(false);
17
25
  async function handleAddClaimLineItem() {
18
26
  setLoading(true);
@@ -46,7 +54,7 @@ const AddClaimLineModal: React.FC<addClaimLineModalProps> = ({ open, onClose, on
46
54
  }
47
55
  function getClaimLineDto(): AddClaimLineDto {
48
56
  return {
49
- consentToken: billItem.consent_token,
57
+ consentToken: (consentTokenProp || billItem.consent_token || '').trim(),
50
58
  interventionCode: billItem.intervention_code,
51
59
  unitPrice: String(billItem.item_price),
52
60
  quantity: String(billItem.item_quantity),
@@ -18,6 +18,7 @@ import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@carbon/react';
18
18
  import BillDetails from './bill-details/bill-details';
19
19
  import PatientClaimDetails from './claim-details/patient-claim-details.component';
20
20
  import { type AmrsVisitDiagnosisDto, type AmrsVisitDiagnosis,type AmrsMaternityDiagnosisDto } from '../../../types';
21
+ import { resolveConsentTokenFromBillLines } from '../../v2/patient-bill-details/payment-mode';
21
22
  interface patientBillDetailsProps {
22
23
  patientUuid: string;
23
24
  locationUuid: string;
@@ -60,7 +61,7 @@ const PatientBillDetails: React.FC<patientBillDetailsProps> = ({ patientUuid, lo
60
61
  const data = await fetchPatientFacilityBillDetails(patientBillPayload);
61
62
  if (data) {
62
63
  setPatientBillDetails(data);
63
- setConsentToken(data[0].consent_token);
64
+ setConsentToken(resolveConsentTokenFromBillLines(data));
64
65
  }
65
66
  } catch (error) {
66
67
  showSnackbar({
@@ -15,6 +15,7 @@ import { launchWorkspace, showSnackbar } from '@openmrs/esm-framework';
15
15
  import {
16
16
  checkPreauthStatus,
17
17
  invalidatePreauthPreview,
18
+ isPreauthNeedsClarification,
18
19
  type PreauthCheckKind,
19
20
  } from '../../../../claims/claims.resource';
20
21
  import EmptyState from '../shared/empty-state.component';
@@ -222,12 +223,14 @@ const PreauthList: React.FC<PreauthListProps> = ({ locationUuid, billingDate, on
222
223
  const flags = interventionFlagsFromBillItem(item);
223
224
  const meta = rowMeta[key];
224
225
  const elective = needsElectivePreauth(item);
225
- // Already-raised (pending / clarification / finalised) must not show Raise again.
226
+ // Not raised, terminal failure, or clarification → Raise / Resubmit.
227
+ // Other pending / finalised states stay blocked.
226
228
  const canRaise =
227
229
  meta?.kind === 'not_raised' ||
228
230
  meta?.kind === 'failed' ||
231
+ isPreauthNeedsClarification(meta?.status ?? '') ||
229
232
  (elective && meta?.kind === 'no_token');
230
- const showNotes = Boolean(meta?.notes?.trim()) && !canRaise;
233
+ const showNotes = Boolean(meta?.notes?.trim()) && meta?.kind !== 'not_raised';
231
234
  return (
232
235
  <TableRow key={key}>
233
236
  <TableCell>
@@ -257,7 +260,9 @@ const PreauthList: React.FC<PreauthListProps> = ({ locationUuid, billingDate, on
257
260
  <TableCell>
258
261
  {canRaise ? (
259
262
  <Button kind="ghost" size="sm" onClick={() => handleRaise(item)}>
260
- Raise preauth
263
+ {meta?.kind === 'failed' || isPreauthNeedsClarification(meta?.status ?? '')
264
+ ? 'Resubmit preauth'
265
+ : 'Raise preauth'}
261
266
  </Button>
262
267
  ) : (
263
268
  '—'
@@ -1060,6 +1060,11 @@ export {
1060
1060
  usePreauthPreview,
1061
1061
  isPreauthFinalised,
1062
1062
  isPreauthTerminalFailure,
1063
+ isPreauthNeedsClarification,
1064
+ isPreauthResubmittable,
1065
+ interventionHasBlockingPreauth,
1066
+ interventionHasFailedPreauth,
1067
+ interventionAllowsPreauthResubmit,
1063
1068
  invalidatePreauthPreview,
1064
1069
  preauthPreviewSwrKey,
1065
1070
  type PreauthCheckKind,
@@ -27,7 +27,7 @@ import {
27
27
  type PreauthFormPayload,
28
28
  } from '../../../../claims/claims.resource';
29
29
  import { cancelAllPendingAuthorizations, sendClaimsOTP, authorizeClaimsWithOtp } from '../../../../registry/hie.resource';
30
- import { addClaimDiagnosis, fetchPatientDiagnosesForBilling } from '../../../billing-claims.resource';
30
+ import { addClaimDiagnosis, fetchPatientDiagnosesForBilling, preferredDiagnosisForPreauth } from '../../../billing-claims.resource';
31
31
  import { ensureInterventionOnVisit } from '../../../../claims/interventions.resource';
32
32
  import { type AmrsVisitDiagnosis } from '../../../types';
33
33
  import { type PatientFacilityBillDetails } from '../types';
@@ -75,7 +75,9 @@ const diagnosisPickLabel = (item: DiagnosisPick | null) => {
75
75
  if (!item) return '';
76
76
  if (item.kind === 'visit') {
77
77
  const d = item.dx;
78
- return `${d.icd11_code ?? '—'} · ${d.concept_source_name || d.encounter_type || 'Visit diagnosis'}`;
78
+ const rank =
79
+ d.dx_rank != null && Number.isFinite(Number(d.dx_rank)) ? ` · Rank ${Number(d.dx_rank)}` : '';
80
+ return `${d.icd11_code ?? '—'} · ${d.concept_source_name || d.encounter_type || 'Visit diagnosis'}${rank}`;
79
81
  }
80
82
  return `${item.hit.icd11Code} · ${item.hit.display}`;
81
83
  };
@@ -428,9 +430,11 @@ const PreauthForm: React.FC<PreauthWorkspaceProps> = ({
428
430
  });
429
431
  if (!cancelled) {
430
432
  setVisitDiagnoses(results ?? []);
431
- const withIcd = (results ?? []).filter((d) => d.icd11_code);
432
- if (withIcd.length === 1) {
433
- applyDiagnosisPick(visitDxPick(withIcd[0]), { fromUser: false });
433
+ // Prefer dx_rank === 1; otherwise best-ranked ICD-coded row. Multiple options
434
+ // stay available in the ComboBox (already ordered by rank from the fetch).
435
+ const preferred = preferredDiagnosisForPreauth(results ?? []);
436
+ if (preferred) {
437
+ applyDiagnosisPick(visitDxPick(preferred), { fromUser: false });
434
438
  }
435
439
  }
436
440
  } catch {
@@ -1231,7 +1235,7 @@ const PreauthForm: React.FC<PreauthWorkspaceProps> = ({
1231
1235
 
1232
1236
  <div className={styles.searchBlock}>
1233
1237
  <p className={styles.fieldHint}>
1234
- Diagnosis (ICD-11)
1238
+ Diagnosis (ICD-11). Prefills rank 1 when available; choose another if needed.
1235
1239
  </p>
1236
1240
  {loadingDx ? (
1237
1241
  <InlineLoading description="Loading patient diagnoses…" />
@@ -93,6 +93,11 @@ export type AmrsVisitDiagnosis = {
93
93
  value_coded?: number | null;
94
94
  /** Encounter diagnosis coded concept (/patient/encounter-diagnosis). */
95
95
  diagnosis_coded?: number | null;
96
+ /**
97
+ * Diagnosis rank from encounter-diagnosis ETL (1 = primary / preferred for preauth).
98
+ * Lower ranks are preferred when multiple diagnoses are present.
99
+ */
100
+ dx_rank?: number | null;
96
101
  concept_source_name: string;
97
102
  hl7_code: string;
98
103
  icd11_code: string;
@@ -876,7 +876,7 @@ export async function fetchPreauthPreviewRowsForTokens(
876
876
  return rows;
877
877
  }
878
878
 
879
- const PREAUTH_TERMINAL_FAILURE = new Set(['REJECTED', 'CANCELLED', 'FAILED', 'DECLINED']);
879
+ const PREAUTH_TERMINAL_FAILURE = new Set(['REJECTED', 'CANCELLED', 'CANCELED', 'FAILED', 'DECLINED']);
880
880
 
881
881
  const PREAUTH_SUBMITTED = new Set([
882
882
  'ACTIVE',
@@ -960,15 +960,33 @@ export function extractPreauthCodeForIntervention(
960
960
  }
961
961
 
962
962
  /**
963
- * True when preview already has a non-failed preauth for this intervention —
964
- * Raise should be hidden (re-raise allowed after terminal failure).
963
+ * True when preview already has a non-failed, non-clarification preauth for this
964
+ * intervention — Raise should be hidden. Terminal failure and clarification allow
965
+ * reopening the form to resubmit.
965
966
  */
966
967
  export function interventionHasBlockingPreauth(preview: unknown, interventionCode: string): boolean {
967
968
  const item = findPreauthPreviewForIntervention(preview, interventionCode);
968
969
  if (!item) return false;
969
970
  const status = statusFromPreviewItem(item);
970
971
  if (!status) return true; // row exists but status blank — treat as already raised
971
- return !isPreauthTerminalFailure(status);
972
+ return !isPreauthResubmittable(status);
973
+ }
974
+
975
+ /**
976
+ * True when this intervention's preauth failed or needs clarification —
977
+ * UI can offer Resubmit and reopen the preauth form.
978
+ */
979
+ export function interventionHasFailedPreauth(preview: unknown, interventionCode: string): boolean {
980
+ const status = extractPreauthStatusForIntervention(preview, interventionCode);
981
+ return Boolean(status) && isPreauthResubmittable(status);
982
+ }
983
+
984
+ /** Alias for clarity at call sites that mean "resubmit", not only hard failure. */
985
+ export function interventionAllowsPreauthResubmit(
986
+ preview: unknown,
987
+ interventionCode: string,
988
+ ): boolean {
989
+ return interventionHasFailedPreauth(preview, interventionCode);
972
990
  }
973
991
 
974
992
  export function extractPreauthStatus(preview: unknown): string {
@@ -1014,7 +1032,15 @@ export function isPreauthFinalised(status: string): boolean {
1014
1032
  }
1015
1033
 
1016
1034
  export function isPreauthTerminalFailure(status: string): boolean {
1017
- return PREAUTH_TERMINAL_FAILURE.has((status || '').toUpperCase());
1035
+ const s = (status || '').toUpperCase();
1036
+ if (PREAUTH_TERMINAL_FAILURE.has(s)) return true;
1037
+ // HIE sometimes returns prefixed / alternate spellings (e.g. PREAUTH_CANCELLED).
1038
+ return s.includes('CANCEL');
1039
+ }
1040
+
1041
+ /** Failure, cancellation, or clarification — provider may reopen the preauth form and resubmit. */
1042
+ export function isPreauthResubmittable(status: string): boolean {
1043
+ return isPreauthTerminalFailure(status) || isPreauthNeedsClarification(status);
1018
1044
  }
1019
1045
 
1020
1046
  /** Statuses that mean the preauth was accepted by HIE — form can close. */
@@ -1085,7 +1111,8 @@ export async function checkPreauthStatus(
1085
1111
  if (isPreauthTerminalFailure(status)) {
1086
1112
  return { status, preview, kind: 'failed', preauthCode, notes };
1087
1113
  }
1088
- // Includes CLARIFICATION_AFTER_AUTOMATIC_CHECKS and other in-flight payer states.
1114
+ // Clarification stays `pending` for status-tag colouring ("Needs clarification"),
1115
+ // but is not a blocking preauth — callers use isPreauthNeedsClarification / resubmit helpers.
1089
1116
  return { status, preview, kind: 'pending', preauthCode, notes };
1090
1117
  } catch (e: unknown) {
1091
1118
  const message = e instanceof Error ? e.message : String(e ?? 'Failed to get preauth preview');