@mindbill/react 0.53.1 → 0.54.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/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { CSSProperties, ReactNode, ReactElement } from 'react';
3
- import { BillCourtesyCopyInput, BillCourtesyCopyPreview, BillCourtesyCopyResult, SubmitBillInput, BillDeliveryOptions as BillDeliveryOptions$1, BillReviewPayerOption, BrowserBillSubmissionResult, BillLifecycleSessionProvider, BillReviewPayerListInput, BillReviewPayerPage, BillReviewPayer as BillReviewPayer$1, BillClaimsAdministratorDirectory, BillClaimsAdministratorSource, BrowserBillSubmissionDocument, OrganizationProfileData, HistoricalBillReviewData, BillLifecycleDelivery, BillReviewAttachment as BillReviewAttachment$1, BillTasksDashboardData, BillTasksAgingBucket, BillHistoryEntry, BillAttemptSummary, ReportBillStatusInput, BillRemittanceSummary, BillEorDocument, BillPaymentRecord, BillLifecycleAction, BillRejection, BillLifecycleData, BillLifecycleClientOptions, BillLifecycleClient, SubmitSecondReviewInput } from '@mindbill/browser';
3
+ import { BillCourtesyCopyInput, BillCourtesyCopyPreview, BillCourtesyCopyResult, SubmitBillInput, BillDeliveryOptions as BillDeliveryOptions$1, BillFeeQuoteInput, BillReviewPayerOption, BrowserBillSubmissionResult, BillLifecycleSessionProvider, BillReviewPayerListInput, BillReviewPayerPage, BillReviewPayer as BillReviewPayer$1, BillClaimsAdministratorDirectory, BillClaimsAdministratorSource, BillProcedureCodeSearchInput, BillProcedureCodePage, BillFeeQuote, BrowserBillSubmissionDocument, OrganizationProfileData, HistoricalBillReviewData, BillLifecycleDelivery, BillReviewAttachment as BillReviewAttachment$1, BillTasksDashboardData, BillTasksAgingBucket, BillHistoryEntry, BillAttemptSummary, ReportBillStatusInput, BillRemittanceSummary, BillEorDocument, BillPaymentRecord, BillLifecycleAction, BillRejection, BillLifecycleData, BillLifecycleClientOptions, BillLifecycleClient, SubmitSecondReviewInput } from '@mindbill/browser';
4
4
  export { BILL_TASKS_AGING_BUCKETS, BillActivityRecord, BillClaimsAdministratorContact, BillClaimsAdministratorDirectory, BillClaimsAdministratorMailingAddress, BillClaimsAdministratorPattern, BillClaimsAdministratorPayer, BillClaimsAdministratorSource, BillCourtesyCopyInput, BillCourtesyCopyPreview, BillCourtesyCopyResult, BillEorDocument, BillLifecycleAction, BillLifecycleActionId, BillLifecycleClient, BillLifecycleClientOptions, BillLifecycleData, BillLifecycleDelivery, BillLifecycleSession, BillLifecycleSessionProvider, BillLifecycleSessionRequest, BillPaymentRecord, BillRejection, BillRemittanceSummary, BillReviewPayerOption, BillReviewPayerSuggestion, BillSubmissionDetail, BillTasksAgingBucket, BillTasksDashboardData, BillTasksDashboardItem, BillTasksDashboardRow, BillTasksDashboardSection, BillTasksDashboardSectionInput, BillTasksDashboardTone, BillTeamNote, BrowserBillSubmissionResult, CloseBillInput, HistoricalBillReviewData, PostBillPaymentInput, REPORT_BILL_STATUS_OPTIONS, ReopenBillInput, ReportBillStatusContacts, ReportBillStatusId, ReportBillStatusInput, SECOND_REVIEW_REASON_TEMPLATE, SECOND_REVIEW_WINDOW_DAYS, SecondReviewDeadline, SendDuplicateBillInput, SubmitSecondReviewInput, billTasksAgingBucketIndex, buildBillTasksDashboard, createBillLifecycleClient, reportBillStatusContacts, secondReviewDeadline } from '@mindbill/browser';
5
5
  import { MindBillAppearance, MindBillEventDetail, MindBillErrorDetail } from '@mindbill/embed';
6
6
  export { MindBillAppearance, MindBillErrorDetail, MindBillEventDetail } from '@mindbill/embed';
@@ -291,6 +291,8 @@ type BillSubmissionInput$1 = {
291
291
  serviceDateEnd?: string | null;
292
292
  charge?: number;
293
293
  diagnosisPointers?: number[];
294
+ /** Authorized RFA item associated with this procedure. Cleared when its code changes. */
295
+ rfaItemId?: string;
294
296
  }>;
295
297
  };
296
298
  /** A validated, submission-ready bill. The form accepts partial provider data while editing,
@@ -359,6 +361,7 @@ type BillSubmissionFormValue = {
359
361
  sourceAttachmentReportTypes: Record<string, string>;
360
362
  uploads: BillSubmissionUpload[];
361
363
  };
364
+ type BillSubmissionFeeContext = Omit<BillFeeQuoteInput, "code" | "dateOfService" | "units" | "modifiers" | "serviceZip" | "chargeCents">;
362
365
  type BillSubmissionFormProps = {
363
366
  initialBill: BillSubmissionInput$1;
364
367
  /** Host-owned choices or organizationProfileOptions(profile). Selection copies a snapshot into this bill only. */
@@ -391,6 +394,10 @@ type BillSubmissionFormProps = {
391
394
  onSearchDiagnoses?: (query: string, limit?: number, offset?: number) => Promise<BillSubmissionDiagnosisOption[]>;
392
395
  onLookupPostalCode?: (postalCode: string) => Promise<BillSubmissionPostalPlace | null>;
393
396
  procedureOptions?: BillSubmissionProcedureOption[];
397
+ /** Extend the existing procedure picker with treatment codes and authoritative fee quotes. */
398
+ treatmentBilling?: boolean;
399
+ onSearchProcedureCodes?: (input?: BillProcedureCodeSearchInput) => Promise<BillProcedureCodePage>;
400
+ onQuoteFee?: (input: BillFeeQuoteInput) => Promise<BillFeeQuote>;
394
401
  modifierOptions?: BillSubmissionModifierOption[];
395
402
  /** Common NUCC provider taxonomies are bundled; supplied values extend or replace matching codes. */
396
403
  taxonomyOptions?: BillSubmissionTaxonomyOption[];
@@ -472,7 +479,7 @@ declare function BillSubmissionProvidersSection(): ReactElement;
472
479
  declare function BillSubmissionServiceLinesSection(): ReactElement;
473
480
  declare function BillSubmissionAttachmentsSection(): ReactElement;
474
481
  declare function BillSubmissionActions(): ReactElement;
475
- declare function BillSubmissionForm({ initialBill, attachments, onSubmit, onSubmitted, getSession, sessionEndpoint, apiBaseUrl, profileOptions, profileDisplay, fetch: fetchOverride, onListClaimsAdministrators, onSearchClaimsAdministrators, onGetClaimsAdministratorDirectory, claimsAdministratorSources, claimsAdministratorHint, diagnosisOptions, onSearchDiagnoses, onLookupPostalCode, procedureOptions, modifierOptions, taxonomyOptions, deliveryRoutePicker, deliveryRouteDialogTitle, attachmentReportTypeMode, attachmentReportTypes, defaultAttachmentReportType, appearance, className, style, disabled, submitLabel, heading, description, attentionFields, attentionMessage, children, }: BillSubmissionFormProps): ReactElement;
482
+ declare function BillSubmissionForm({ initialBill, attachments, onSubmit, onSubmitted, getSession, sessionEndpoint, apiBaseUrl, profileOptions, profileDisplay, fetch: fetchOverride, onListClaimsAdministrators, onSearchClaimsAdministrators, onGetClaimsAdministratorDirectory, claimsAdministratorSources, claimsAdministratorHint, diagnosisOptions, onSearchDiagnoses, onLookupPostalCode, procedureOptions, treatmentBilling, onSearchProcedureCodes, onQuoteFee, modifierOptions, taxonomyOptions, deliveryRoutePicker, deliveryRouteDialogTitle, attachmentReportTypeMode, attachmentReportTypes, defaultAttachmentReportType, appearance, className, style, disabled, submitLabel, heading, description, attentionFields, attentionMessage, children, }: BillSubmissionFormProps): ReactElement;
476
483
 
477
484
  type BillSubmissionProfileOption<K extends "billingProvider" | "renderingProvider" | "serviceLocation"> = {
478
485
  id: string;
@@ -1734,4 +1741,4 @@ type MindBillWidgetProps = {
1734
1741
  declare function MindBillBillTimeline(props: MindBillWidgetProps): ReactElement;
1735
1742
  declare function MindBillBillReview(props: MindBillWidgetProps): ReactElement;
1736
1743
 
1737
- export { BILL_STATUS_AGING_BUCKETS, BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS, BILL_SUBMISSION_DOCUMENT_TYPES, BILL_SUBMISSION_REPORT_TYPES, BILL_SUBMISSION_REQUIRED_FIELDS, type BillActivityEvent, BillActivityTimeline, type BillActivityTimelineProps, type BillAgingBucket, type BillAgingBucketId, BillAgingSummary, type BillAgingSummaryProps, BillCourtesyCopyForm, type BillCourtesyCopyFormProps, BillExplanationOfReview, type BillExplanationOfReviewProps, BillHistoryTable, type BillHistoryTableProps, BillLifecycleActions, type BillLifecycleActionsProps, BillLifecycleProgress, type BillLifecycleProgressProps, type BillLifecycleProgressStepsOptions, type BillLifecycleStage, BillList, type BillListProps, BillPayerContactCard, type BillPayerContactCardProps, BillPaymentLedger, type BillPaymentLedgerProps, BillReadOnlyForm, type BillReadOnlyFormProps, type BillRegistryAge, type BillRegistryItem, type BillRegistryQuery, type BillRegistryResult, type BillRegistrySort, type BillRegistryStatus, BillRejectionNotice, type BillRejectionNoticeProps, BillRemittanceCard, type BillRemittanceCardProps, type BillReviewAttachment, type BillReviewBillingProvider, type BillReviewClinician, type BillReviewData, type BillReviewDocumentType, type BillReviewDraft, type BillReviewFeatures, BillReviewForm, type BillReviewFormProps, type BillReviewLineItem, type BillReviewLocation, type BillReviewPayer, type BillReviewSaveInput, BillSnapshotSummary, type BillSnapshotSummaryProps, type BillStatusAction, type BillStatusAgingCell, BillStatusAgingMatrix, type BillStatusAgingMatrixData, type BillStatusAgingMatrixProps, type BillStatusAgingRow, type BillStatusClient, type BillStatusClientOptions, type BillStatusData, type BillStatusSession, type BillStatusSessionProvider, type BillStatusSessionRequest, BillStatusSummary, type BillStatusSummaryProps, BillSubmissionActions, type BillSubmissionAddress, type BillSubmissionAttachmentReportTypeMode, BillSubmissionAttachmentsSection, BillSubmissionClaimSection, type BillSubmissionDiagnosisOption, type BillSubmissionDocumentType, type BillSubmissionEvaluationType, BillSubmissionForm, type BillSubmissionFormProps, type BillSubmissionFormValue, BillSubmissionHeader, type BillSubmissionInput$1 as BillSubmissionInput, type BillSubmissionModifierOption, BillSubmissionPatientSection, type BillSubmissionPostalPlace, type BillSubmissionProcedureOption, type BillSubmissionProfileOption, type BillSubmissionProfileOptions, BillSubmissionProvidersSection, type BillSubmissionReportTypeOption, type BillSubmissionRibbonItem, type BillSubmissionRoute, type BillSubmissionSectionId, BillSubmissionServiceLinesSection, type BillSubmissionSourceAttachment, type BillSubmissionTaxonomyOption, type BillSubmissionUpload, type BillSubmissionValidation, BillSubmissionsRibbon, type BillSubmissionsRibbonProps, BillTasksDashboard, type BillTasksDashboardCell, type BillTasksDashboardProps, type BillTasksResult, type BillingComponentProps, BillingDashboard, type BillingDashboardBill, type BillingDashboardProps, type BillingDashboardSummary, type BillingOperationsClient, type BillingOperationsClientOptions, BillingReport, type BillingReportDimension, type BillingReportProps, type BillingReportRow, BillingSettings, ClaimsAdministratorDirectoryDialog, type ClaimsAdministratorDirectoryDialogProps, ConnectedBillLifecycle, type ConnectedBillLifecycleProps, ConnectedBillSearch, type ConnectedBillSearchProps, ConnectedBillStatus, type ConnectedBillStatusProps, ConnectedBillTasksDashboard, type ConnectedBillTasksDashboardProps, ConnectedBillingWorkspace, type ConnectedBillingWorkspaceProps, ConnectedNotificationRecipientsSettings, ConnectedNotificationSettings, ConnectedPaymentReview, type ConnectedPaymentReviewProps, ConnectedProductivityReport, ConnectedServiceLineItemsReport, type CourtesyCopyRecipientOption, DEFAULT_BILL_SUBMISSION_MODIFIERS, DEFAULT_BILL_SUBMISSION_PROCEDURES, DEFAULT_BILL_SUBMISSION_TAXONOMIES, type DentalDraftContentInput, DentalDraftEditor, type DentalDraftEditorProps, type DentalDraftLineInput, MED_LEGAL_REPORT_TYPE_CODE, MindBillBillReview, MindBillBillTimeline, type MindBillReactAppearance, type MindBillThemePreset, type MindBillWidgetProps, type NotificationAgingDay, type NotificationPreferences, type NotificationRecipient, type NotificationRecipientInvitation, type NotificationRecipientsAdapter, NotificationRecipientsSettings, type NotificationRecipientsSettingsProps, type NotificationRecipientsSnapshot, type NotificationReportDigest, NotificationSettings, type NotificationSettingsAdapter, type NotificationSettingsProps, type NotificationSettingsSnapshot, type NotificationSettingsUpdate, OrganizationOnboarding, type OrganizationOnboardingProps, PSYCH_QME_DEFAULT_DIAGNOSIS, type PaymentReviewItem, type PaymentReviewQuery, type PaymentReviewRange, type PaymentReviewResult, type ProductivityReport, ReportBillStatusDialog, type ReportBillStatusDialogProps, RfaDraftForm, type RfaDraftFormProps, type RfaDraftInput, type RfaDraftItemInput, SECOND_REVIEW_REASON_TEMPLATES, SecondReviewForm, type SecondReviewFormProps, SendRouteDialog, type SendRouteDialogProps, type SendRouteSubmission, type ServiceLineItemsReport, type UseBillLifecycleOptions, type UseBillLifecycleResult, type UseBillStatusOptions, type UseBillStatusResult, type W9ExtractionStatus, W9Upload, type W9UploadProps, applyBillSubmissionEvaluationDiagnoses, applyBillSubmissionEvaluationModifiers, billActivityEventLabel, billAgingBucket, billAgingDays, billLifecycleDisplayLabel, billLifecycleStage, billSubmissionsRibbonDeliveryLabel, billSubmissionsRibbonFromHistory, buildBillReviewSaveInput, buildBillStatusAgingCsv, buildBillStatusAgingMatrix, buildBillingReportCsv, buildBillingReportRows, calculateBillSubmissionAllowedAmount, chooseClaimsAdministrator, claimsAdministratorRecommendations, createBillStatusClient, createBillingOperationsClient, dentalDraftChargeSummary, ensureTrailingBillSubmissionLine, ensureTrailingProcedureLine, exactClaimsAdministratorMatch, extractSendRouteEmail, formatBillSubmissionDate, formatSendRouteFax, isBillLifecycleDraft, mindBillAppearanceStyle, mindBillThemePresets, normalizeClaimsAdministratorName, normalizeRfaDraft, organizationProfileOptions, parseBillSubmissionDate, parseDentalCharge, prepareBillSubmissionDocuments, resolveMindBillAppearance, submittedClaimsAdministrator, summarizeBillingDashboard, useBillLifecycle, useBillStatus, validateBillSubmission, validateDentalDraftContent, validateRfaDraft, visibleBillLifecycleActions };
1744
+ export { BILL_STATUS_AGING_BUCKETS, BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS, BILL_SUBMISSION_DOCUMENT_TYPES, BILL_SUBMISSION_REPORT_TYPES, BILL_SUBMISSION_REQUIRED_FIELDS, type BillActivityEvent, BillActivityTimeline, type BillActivityTimelineProps, type BillAgingBucket, type BillAgingBucketId, BillAgingSummary, type BillAgingSummaryProps, BillCourtesyCopyForm, type BillCourtesyCopyFormProps, BillExplanationOfReview, type BillExplanationOfReviewProps, BillHistoryTable, type BillHistoryTableProps, BillLifecycleActions, type BillLifecycleActionsProps, BillLifecycleProgress, type BillLifecycleProgressProps, type BillLifecycleProgressStepsOptions, type BillLifecycleStage, BillList, type BillListProps, BillPayerContactCard, type BillPayerContactCardProps, BillPaymentLedger, type BillPaymentLedgerProps, BillReadOnlyForm, type BillReadOnlyFormProps, type BillRegistryAge, type BillRegistryItem, type BillRegistryQuery, type BillRegistryResult, type BillRegistrySort, type BillRegistryStatus, BillRejectionNotice, type BillRejectionNoticeProps, BillRemittanceCard, type BillRemittanceCardProps, type BillReviewAttachment, type BillReviewBillingProvider, type BillReviewClinician, type BillReviewData, type BillReviewDocumentType, type BillReviewDraft, type BillReviewFeatures, BillReviewForm, type BillReviewFormProps, type BillReviewLineItem, type BillReviewLocation, type BillReviewPayer, type BillReviewSaveInput, BillSnapshotSummary, type BillSnapshotSummaryProps, type BillStatusAction, type BillStatusAgingCell, BillStatusAgingMatrix, type BillStatusAgingMatrixData, type BillStatusAgingMatrixProps, type BillStatusAgingRow, type BillStatusClient, type BillStatusClientOptions, type BillStatusData, type BillStatusSession, type BillStatusSessionProvider, type BillStatusSessionRequest, BillStatusSummary, type BillStatusSummaryProps, BillSubmissionActions, type BillSubmissionAddress, type BillSubmissionAttachmentReportTypeMode, BillSubmissionAttachmentsSection, BillSubmissionClaimSection, type BillSubmissionDiagnosisOption, type BillSubmissionDocumentType, type BillSubmissionEvaluationType, type BillSubmissionFeeContext, BillSubmissionForm, type BillSubmissionFormProps, type BillSubmissionFormValue, BillSubmissionHeader, type BillSubmissionInput$1 as BillSubmissionInput, type BillSubmissionModifierOption, BillSubmissionPatientSection, type BillSubmissionPostalPlace, type BillSubmissionProcedureOption, type BillSubmissionProfileOption, type BillSubmissionProfileOptions, BillSubmissionProvidersSection, type BillSubmissionReportTypeOption, type BillSubmissionRibbonItem, type BillSubmissionRoute, type BillSubmissionSectionId, BillSubmissionServiceLinesSection, type BillSubmissionSourceAttachment, type BillSubmissionTaxonomyOption, type BillSubmissionUpload, type BillSubmissionValidation, BillSubmissionsRibbon, type BillSubmissionsRibbonProps, BillTasksDashboard, type BillTasksDashboardCell, type BillTasksDashboardProps, type BillTasksResult, type BillingComponentProps, BillingDashboard, type BillingDashboardBill, type BillingDashboardProps, type BillingDashboardSummary, type BillingOperationsClient, type BillingOperationsClientOptions, BillingReport, type BillingReportDimension, type BillingReportProps, type BillingReportRow, BillingSettings, ClaimsAdministratorDirectoryDialog, type ClaimsAdministratorDirectoryDialogProps, ConnectedBillLifecycle, type ConnectedBillLifecycleProps, ConnectedBillSearch, type ConnectedBillSearchProps, ConnectedBillStatus, type ConnectedBillStatusProps, ConnectedBillTasksDashboard, type ConnectedBillTasksDashboardProps, ConnectedBillingWorkspace, type ConnectedBillingWorkspaceProps, ConnectedNotificationRecipientsSettings, ConnectedNotificationSettings, ConnectedPaymentReview, type ConnectedPaymentReviewProps, ConnectedProductivityReport, ConnectedServiceLineItemsReport, type CourtesyCopyRecipientOption, DEFAULT_BILL_SUBMISSION_MODIFIERS, DEFAULT_BILL_SUBMISSION_PROCEDURES, DEFAULT_BILL_SUBMISSION_TAXONOMIES, type DentalDraftContentInput, DentalDraftEditor, type DentalDraftEditorProps, type DentalDraftLineInput, MED_LEGAL_REPORT_TYPE_CODE, MindBillBillReview, MindBillBillTimeline, type MindBillReactAppearance, type MindBillThemePreset, type MindBillWidgetProps, type NotificationAgingDay, type NotificationPreferences, type NotificationRecipient, type NotificationRecipientInvitation, type NotificationRecipientsAdapter, NotificationRecipientsSettings, type NotificationRecipientsSettingsProps, type NotificationRecipientsSnapshot, type NotificationReportDigest, NotificationSettings, type NotificationSettingsAdapter, type NotificationSettingsProps, type NotificationSettingsSnapshot, type NotificationSettingsUpdate, OrganizationOnboarding, type OrganizationOnboardingProps, PSYCH_QME_DEFAULT_DIAGNOSIS, type PaymentReviewItem, type PaymentReviewQuery, type PaymentReviewRange, type PaymentReviewResult, type ProductivityReport, ReportBillStatusDialog, type ReportBillStatusDialogProps, RfaDraftForm, type RfaDraftFormProps, type RfaDraftInput, type RfaDraftItemInput, SECOND_REVIEW_REASON_TEMPLATES, SecondReviewForm, type SecondReviewFormProps, SendRouteDialog, type SendRouteDialogProps, type SendRouteSubmission, type ServiceLineItemsReport, type UseBillLifecycleOptions, type UseBillLifecycleResult, type UseBillStatusOptions, type UseBillStatusResult, type W9ExtractionStatus, W9Upload, type W9UploadProps, applyBillSubmissionEvaluationDiagnoses, applyBillSubmissionEvaluationModifiers, billActivityEventLabel, billAgingBucket, billAgingDays, billLifecycleDisplayLabel, billLifecycleStage, billSubmissionsRibbonDeliveryLabel, billSubmissionsRibbonFromHistory, buildBillReviewSaveInput, buildBillStatusAgingCsv, buildBillStatusAgingMatrix, buildBillingReportCsv, buildBillingReportRows, calculateBillSubmissionAllowedAmount, chooseClaimsAdministrator, claimsAdministratorRecommendations, createBillStatusClient, createBillingOperationsClient, dentalDraftChargeSummary, ensureTrailingBillSubmissionLine, ensureTrailingProcedureLine, exactClaimsAdministratorMatch, extractSendRouteEmail, formatBillSubmissionDate, formatSendRouteFax, isBillLifecycleDraft, mindBillAppearanceStyle, mindBillThemePresets, normalizeClaimsAdministratorName, normalizeRfaDraft, organizationProfileOptions, parseBillSubmissionDate, parseDentalCharge, prepareBillSubmissionDocuments, resolveMindBillAppearance, submittedClaimsAdministrator, summarizeBillingDashboard, useBillLifecycle, useBillStatus, validateBillSubmission, validateDentalDraftContent, validateRfaDraft, visibleBillLifecycleActions };
package/dist/index.js CHANGED
@@ -2336,6 +2336,21 @@ var BILL_SUBMISSION_REPORT_TYPES = [
2336
2336
  { code: "V5", label: "Death Notification" },
2337
2337
  { code: "XP", label: "Photographs" }
2338
2338
  ];
2339
+ var isMedicalLegalCode = (code) => /^ML/i.test(code.trim());
2340
+ function billSubmissionFeeRequest(bill, line, context = {}) {
2341
+ const dateOfService = parseBillSubmissionDate(line.serviceDate ?? bill.service.date) ?? "";
2342
+ const otherSameDayServices = Boolean(dateOfService) && bill.serviceLines.filter((candidate) => candidate.code.trim() && parseBillSubmissionDate(candidate.serviceDate ?? bill.service.date) === dateOfService).length > 1;
2343
+ return {
2344
+ ...context,
2345
+ ...otherSameDayServices && context.physicianContext ? { physicianContext: { ...context.physicianContext, standaloneService: false } } : {},
2346
+ ...otherSameDayServices && context.therapyContext ? { therapyContext: { ...context.therapyContext, otherSameDayServices: true } } : {},
2347
+ code: line.code.trim().toUpperCase(),
2348
+ dateOfService,
2349
+ units: line.units ?? 1,
2350
+ modifiers: line.modifiers ?? [],
2351
+ serviceZip: bill.serviceLocation?.address?.postalCode ?? ""
2352
+ };
2353
+ }
2339
2354
  var BILL_SUBMISSION_REQUIRED_FIELDS = [
2340
2355
  "patient.firstName",
2341
2356
  "patient.lastName",
@@ -2454,6 +2469,12 @@ function ensureTrailingBillSubmissionLine(lines) {
2454
2469
  if (!normalized.length || lineHasContent(normalized.at(-1))) normalized.push(blankLine());
2455
2470
  return normalized;
2456
2471
  }
2472
+ function replaceBillSubmissionServiceLines(bill, serviceLines) {
2473
+ const lostLastRfaLink = bill.serviceLines.some((line) => line.rfaItemId) && !serviceLines.some((line) => line.rfaItemId);
2474
+ const service = lostLastRfaLink ? { ...bill.service } : bill.service;
2475
+ if (lostLastRfaLink) delete service.authorizationNumber;
2476
+ return { ...bill, service, serviceLines };
2477
+ }
2457
2478
  function parseBillSubmissionDate(value) {
2458
2479
  const trimmed = value.trim();
2459
2480
  const iso = /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed);
@@ -2485,6 +2506,7 @@ function evaluationModifiers(type, code) {
2485
2506
  }
2486
2507
  function applyBillSubmissionEvaluationModifiers(lines, type) {
2487
2508
  return lines.map((line) => {
2509
+ if (!isMedicalLegalCode(line.code)) return line;
2488
2510
  const auto = evaluationModifiers(type, line.code);
2489
2511
  const modifiers = (line.modifiers ?? []).filter((item) => !["94", "95", "96"].includes(item.replace(/^-/, "")));
2490
2512
  return { ...line, modifiers: [...auto, ...modifiers] };
@@ -2500,9 +2522,37 @@ function initialEvaluationType(bill) {
2500
2522
  if (bill.renderingProvider?.isAme) return isPsych ? "psych_ame" : "ame";
2501
2523
  return isPsych ? "psych_qme" : "qme";
2502
2524
  }
2503
- function cloneInitialBill(bill) {
2525
+ function remapBillSubmissionDiagnoses(bill, nextCodes) {
2526
+ const diagnoses = [...new Set(nextCodes.map((code) => code.trim().toUpperCase()).filter(Boolean))].slice(0, 12);
2527
+ return { ...bill, diagnoses, serviceLines: bill.serviceLines.map((line) => ({
2528
+ ...line,
2529
+ ...line.diagnosisPointers ? { diagnosisPointers: line.diagnosisPointers.flatMap((pointer) => {
2530
+ const code = bill.diagnoses?.[pointer - 1];
2531
+ const index = code ? diagnoses.indexOf(code.trim().toUpperCase()) : -1;
2532
+ return index < 0 ? [] : [index + 1];
2533
+ }) } : {}
2534
+ })) };
2535
+ }
2536
+ function setBillSubmissionLineDiagnosis(bill, lineIndex, slot, code) {
2537
+ if (!bill.serviceLines[lineIndex] || slot < 0 || slot > 3) return bill;
2538
+ const diagnoses = [...bill.diagnoses ?? []];
2539
+ const normalized = code.trim().toUpperCase();
2540
+ if (normalized && !diagnoses.includes(normalized)) {
2541
+ if (diagnoses.length >= 12) return bill;
2542
+ diagnoses.push(normalized);
2543
+ }
2544
+ return { ...bill, diagnoses, serviceLines: bill.serviceLines.map((line, index) => {
2545
+ if (index !== lineIndex) return line;
2546
+ const pointers = [...line.diagnosisPointers ?? []];
2547
+ if (normalized) pointers[slot] = diagnoses.indexOf(normalized) + 1;
2548
+ else pointers.splice(slot, 1);
2549
+ return { ...line, diagnosisPointers: [...new Set(pointers.filter((pointer) => Number.isInteger(pointer) && pointer > 0))].slice(0, 4) };
2550
+ }) };
2551
+ }
2552
+ function cloneInitialBill(bill, treatmentBilling = false) {
2504
2553
  const cloned = cloneBill(bill);
2505
- cloned.diagnoses = applyBillSubmissionEvaluationDiagnoses(cloned.diagnoses, initialEvaluationType(bill));
2554
+ if (treatmentBilling && cloned.serviceLines.some((line) => line.code.trim() && !isMedicalLegalCode(line.code))) cloned.billingMode = "professional";
2555
+ if (cloned.billingMode !== "professional") cloned.diagnoses = applyBillSubmissionEvaluationDiagnoses(cloned.diagnoses, initialEvaluationType(bill));
2506
2556
  return cloned;
2507
2557
  }
2508
2558
  function stableInitializationValue(value) {
@@ -2568,6 +2618,7 @@ function validateBillSubmission(bill) {
2568
2618
  required("serviceLocation.address.city", bill.serviceLocation?.address?.city, "Enter the service facility city.");
2569
2619
  required("serviceLocation.address.state", bill.serviceLocation?.address?.state, "Enter the service facility 2-letter state code.");
2570
2620
  required("serviceLocation.address.postalCode", bill.serviceLocation?.address?.postalCode, "Enter the service facility ZIP code.");
2621
+ if ((bill.diagnoses?.length ?? 0) > 12) errors.diagnoses = "Select no more than 12 claim diagnoses.";
2571
2622
  if (!(bill.diagnoses ?? []).some((code) => code.trim())) {
2572
2623
  errors.diagnoses = "Select at least one ICD-10 diagnosis code.";
2573
2624
  }
@@ -2589,11 +2640,14 @@ function validateBillSubmission(bill) {
2589
2640
  if (bill.renderingProvider?.taxonomy && !/^[A-Za-z0-9]{10}$/.test(bill.renderingProvider.taxonomy.trim())) errors["renderingProvider.taxonomy"] = "Enter a valid 10-character taxonomy code.";
2590
2641
  if (bill.serviceLocation?.placeOfServiceCode && !/^\d{2}$/.test(bill.serviceLocation.placeOfServiceCode.trim())) errors["serviceLocation.placeOfServiceCode"] = "Enter a valid 2-digit place of service code.";
2591
2642
  const lines = submittedLines(bill.serviceLines);
2643
+ if (lines.some((line) => isMedicalLegalCode(line.code)) && lines.some((line) => line.code && !isMedicalLegalCode(line.code))) errors.serviceLines = "Submit medical-legal and treatment services on separate bills.";
2592
2644
  if (!lines.length) errors.serviceLines = "Add at least one service line";
2593
2645
  lines.forEach((line, index) => {
2594
2646
  required(`serviceLines.${index}.code`, line.code, "Select a procedure code.");
2647
+ const pointers = line.diagnosisPointers ?? [];
2648
+ if (pointers.length > 4 || new Set(pointers).size !== pointers.length || pointers.some((pointer) => !Number.isInteger(pointer) || pointer < 1 || pointer > (bill.diagnoses?.length ?? 0)) || bill.billingMode === "professional" && !pointers.length) errors[`serviceLines.${index}.diagnosisPointers`] = "Select up to four diagnoses for this service line.";
2595
2649
  if (!Number.isInteger(line.units) || (line.units ?? 0) < 1) errors[`serviceLines.${index}.units`] = "Enter at least 1 unit";
2596
- if (bill.billingMode === "professional" && (!Number.isFinite(line.charge) || (line.charge ?? 0) < 0)) errors[`serviceLines.${index}.charge`] = "Enter the billed charge";
2650
+ if (bill.billingMode === "professional" && (!Number.isFinite(line.charge) || (line.charge ?? 0) <= 0)) errors[`serviceLines.${index}.charge`] = "Enter the billed charge";
2597
2651
  });
2598
2652
  return { valid: Object.keys(errors).length === 0, fieldErrors: errors };
2599
2653
  }
@@ -2607,9 +2661,9 @@ var css3 = `
2607
2661
  .mbsf-source-evidence{display:grid;gap:4px;color:var(--mb-muted);font-size:13px}.mbsf-source-evidence a{color:var(--mb-accent);font-weight:700}.mbsf-source-evidence q{color:var(--mb-text)}.mbsf-suggestions{display:flex;align-items:center;flex-wrap:wrap;gap:7px;color:var(--mb-muted);font-size:13px}.mbsf-suggestion{padding:5px 10px;border:1px solid color-mix(in srgb,var(--mb-accent) 55%,var(--mb-border));border-radius:999px;background:color-mix(in srgb,var(--mb-accent) 7%,var(--mb-surface));color:var(--mb-accent);font:inherit;font-weight:700;cursor:pointer}.mbsf-suggestion:hover,.mbsf-suggestion:focus{outline:2px solid color-mix(in srgb,var(--mb-accent) 25%,transparent)}.mbsf-claim-patterns{display:grid;gap:4px;margin-top:2px;font-size:13px}.mbsf-claim-patterns[data-state=match]{color:#087f5b}.mbsf-claim-patterns[data-state=warning]{color:#9a5200}.mbsf-claim-pattern-list{color:var(--mb-muted)}
2608
2662
  .mbsf-chips,.mbsf-quick-picks{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:8px}.mbsf-chip,.mbsf-quick-pick{display:inline-flex;align-items:center;gap:7px;padding:5px 9px;border:1px solid var(--mb-border);border-radius:999px;background:var(--mb-input)}.mbsf-chip button{border:0;background:transparent;color:var(--mb-muted);cursor:pointer;font:inherit}.mbsf-quick-pick{color:var(--mb-text);font:inherit;cursor:pointer}.mbsf-quick-pick[data-selected=true]{border-color:var(--mb-accent);color:var(--mb-accent)}
2609
2663
  .mbsf-segments{display:grid;grid-template-columns:repeat(3,1fr);border:1px solid var(--mb-border);border-radius:var(--mb-control-radius);overflow:hidden}.mbsf-segment{min-height:44px;border:0;border-right:1px solid var(--mb-border);background:var(--mb-input);color:var(--mb-text);font:inherit;font-weight:700;cursor:pointer}.mbsf-segment:last-child{border-right:0}.mbsf-segment[aria-pressed=true]{background:var(--mb-accent);color:var(--mb-accent-contrast)}
2610
- .mbsf-lines{margin-top:18px;border:1px solid var(--mb-border);border-radius:var(--mb-control-radius);overflow:visible}.mbsf-lines[data-invalid=true]{border-color:var(--mb-danger)}.mbsf-line-head,.mbsf-line{display:grid;grid-template-columns:minmax(190px,1.05fr) minmax(190px,1.3fr) minmax(112px,.7fr) 100px 120px 42px;gap:12px;align-items:start;padding:12px}.mbsf-line-head{color:var(--mb-muted);font-size:13px;font-weight:700;border-bottom:1px solid var(--mb-border)}.mbsf-line{border-bottom:1px solid var(--mb-border)}.mbsf-line:last-child{border-bottom:0}.mbsf-line [data-invalid=true] .mbsf-input{border-color:var(--mb-danger);background:color-mix(in srgb,var(--mb-danger) 4%,var(--mb-input))}.mbsf-money{padding-top:12px;text-align:right;font-variant-numeric:tabular-nums}.mbsf-dx{display:flex;flex-wrap:wrap;gap:5px;padding-top:6px}.mbsf-dx-chip{width:30px;height:30px;border:1px solid var(--mb-border);border-radius:8px;background:var(--mb-surface);color:var(--mb-muted);font:inherit;font-size:13px;font-weight:750;cursor:pointer}.mbsf-dx-chip[data-active=true]{border-color:var(--mb-accent);background:color-mix(in srgb,var(--mb-accent) 10%,var(--mb-surface));color:var(--mb-accent)}.mbsf-total{display:flex;justify-content:flex-end;gap:45px;padding:16px 56px 16px 16px;font-size:17px;font-weight:760}
2664
+ .mbsf-lines{margin-top:18px;border:1px solid var(--mb-border);border-radius:var(--mb-control-radius);overflow:visible}.mbsf-lines[data-invalid=true]{border-color:var(--mb-danger)}.mbsf-line-head,.mbsf-line{display:grid;grid-template-columns:minmax(190px,1.05fr) minmax(190px,1.3fr) minmax(180px,1fr) 100px 120px 42px;gap:12px;align-items:start;padding:12px}.mbsf-line-head{color:var(--mb-muted);font-size:13px;font-weight:700;border-bottom:1px solid var(--mb-border)}.mbsf-line{border-bottom:1px solid var(--mb-border)}.mbsf-line:last-child{border-bottom:0}.mbsf-line [data-invalid=true] .mbsf-input{border-color:var(--mb-danger);background:color-mix(in srgb,var(--mb-danger) 4%,var(--mb-input))}.mbsf-money{padding-top:12px;text-align:right;font-variant-numeric:tabular-nums}.mbsf-line-diagnoses{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;position:relative}.mbsf-line-diagnoses .mbsf-combo{position:static;min-width:0}.mbsf-line-diagnoses .mbsf-menu{left:0;right:0;min-width:100%}.mbsf-line-diagnoses .mbsf-input{padding:10px 6px;font-size:16px}.mbsf-fee-details{grid-column:1/-1;font-size:14px;color:var(--mb-muted);padding:8px 0}.mbsf-fee-details summary{cursor:pointer;font-weight:600}.mbsf-fee-details[open]{display:grid;gap:12px}.mbsf-fee-confirm{display:flex;gap:10px;align-items:flex-start;line-height:1.5}.mbsf-fee-confirm input{margin-top:4px}.mbsf-dx{display:flex;flex-wrap:wrap;gap:5px;padding-top:6px}.mbsf-dx-chip{width:30px;height:30px;border:1px solid var(--mb-border);border-radius:8px;background:var(--mb-surface);color:var(--mb-muted);font:inherit;font-size:13px;font-weight:750;cursor:pointer}.mbsf-dx-chip[data-active=true]{border-color:var(--mb-accent);background:color-mix(in srgb,var(--mb-accent) 10%,var(--mb-surface));color:var(--mb-accent)}.mbsf-total{display:flex;justify-content:flex-end;gap:45px;padding:16px 56px 16px 16px;font-size:17px;font-weight:760}
2611
2665
  .mbsf-icon-btn{width:40px;height:42px;border:0;background:transparent;color:var(--mb-text);font-size:22px;cursor:pointer}.mbsf-secondary{min-height:40px;padding:8px 14px;border:1px solid var(--mb-border);border-radius:var(--mb-control-radius);background:var(--mb-surface);color:var(--mb-text);font:inherit;font-weight:680;cursor:pointer}.mbsf-attach-list{display:grid;gap:10px;margin-bottom:18px}.mbsf-attach-row{padding:14px;border:1px solid var(--mb-border);border-radius:var(--mb-control-radius)}.mbsf-attach-row[data-auto=true]{border-color:color-mix(in srgb,#159447 45%,var(--mb-border));background:color-mix(in srgb,#159447 5%,var(--mb-surface))}.mbsf-attach-main{display:flex;align-items:center;gap:12px;min-width:0;flex:1}.mbsf-attach-type{width:min(360px,32vw);flex:0 1 360px}.mbsf-attach-type .mbsf-label{display:block;margin-bottom:6px;font-size:12px}.mbsf-attach-actions{display:flex;align-items:center;gap:6px;flex:0 0 auto}.mbsf-file{min-width:0}.mbsf-file strong{overflow-wrap:anywhere}.mbsf-badge{display:inline-block;margin-left:8px;padding:2px 7px;border:1px solid var(--mb-border);border-radius:7px;color:var(--mb-muted);font-size:12px;font-weight:600}.mbsf-drop{display:grid;width:100%;place-items:center;min-height:210px;padding:30px;border:2px dashed color-mix(in srgb,var(--mb-muted) 55%,transparent);border-radius:var(--mb-control-radius);background:color-mix(in srgb,var(--mb-accent) 3%,var(--mb-surface));color:var(--mb-text);font:inherit;text-align:center;cursor:pointer}.mbsf-drop[data-active=true]{border-color:var(--mb-accent);background:color-mix(in srgb,var(--mb-accent) 10%,var(--mb-surface))}.mbsf-alert{padding:12px 14px;border-radius:var(--mb-control-radius);background:color-mix(in srgb,var(--mb-danger) 10%,transparent);color:var(--mb-danger)}.mbsf-actions{justify-content:flex-end}.mbsf-submit{min-width:180px;min-height:48px;padding:11px 24px;border:0;border-radius:var(--mb-control-radius);background:var(--mb-accent);color:var(--mb-accent-contrast);font:inherit;font-weight:780;cursor:pointer}
2612
- @media(max-width:820px){.mbsf{gap:16px}.mbsf-grid{grid-template-columns:1fr}.mbsf-span{grid-column:auto}.mbsf-card{padding:18px 16px}.mbsf-line-head{display:none}.mbsf-line{position:relative;display:grid;grid-template-columns:minmax(0,1fr) 86px;gap:14px;padding:18px 16px}.mbsf-line>div:before{display:block;margin-bottom:6px;color:var(--mb-muted);font-size:12px;font-weight:700;content:attr(data-label)}.mbsf-line>div:nth-child(1),.mbsf-line>div:nth-child(2),.mbsf-line>div:nth-child(3){grid-column:1/-1}.mbsf-money{align-self:end;padding:0 0 12px;text-align:right}.mbsf-line .mbsf-icon-btn{position:absolute;right:8px;bottom:3px}.mbsf-total{padding:16px 18px;gap:24px}.mbsf-head{align-items:flex-start}.mbsf-segments{grid-template-columns:repeat(3,minmax(0,1fr))}.mbsf-segment{min-width:0;padding:8px 4px;border-right:1px solid var(--mb-border);border-bottom:0;font-size:13px}.mbsf-segment:last-child{border-right:0}.mbsf-payer-option{align-items:flex-start}.mbsf-attach-row{align-items:flex-start;flex-wrap:wrap}.mbsf-attach-main{align-items:flex-start;flex-basis:calc(100% - 150px)}.mbsf-attach-type{width:100%;flex-basis:100%;order:3}.mbsf-attach-actions{margin-left:auto}.mbsf-drop{min-height:190px;padding:24px 18px}.mbsf-actions{position:sticky;bottom:86px;z-index:10}.mbsf.mbsf-lifecycle-correction .mbsf-actions{position:static;bottom:auto}.mbsf-submit{width:100%}}
2666
+ @media(max-width:820px){.mbsf-line-diagnoses{grid-template-columns:repeat(4,minmax(0,1fr))}.mbsf{gap:16px}.mbsf-grid{grid-template-columns:1fr}.mbsf-span{grid-column:auto}.mbsf-card{padding:18px 16px}.mbsf-line-head{display:none}.mbsf-line{position:relative;display:grid;grid-template-columns:minmax(0,1fr) 86px;gap:14px;padding:18px 16px}.mbsf-line>div:before{display:block;margin-bottom:6px;color:var(--mb-muted);font-size:12px;font-weight:700;content:attr(data-label)}.mbsf-line>div:nth-child(1),.mbsf-line>div:nth-child(2),.mbsf-line>div:nth-child(3){grid-column:1/-1}.mbsf-money{align-self:end;padding:0 0 12px;text-align:right}.mbsf-line .mbsf-icon-btn{position:absolute;right:8px;bottom:3px}.mbsf-total{padding:16px 18px;gap:24px}.mbsf-head{align-items:flex-start}.mbsf-segments{grid-template-columns:repeat(3,minmax(0,1fr))}.mbsf-segment{min-width:0;padding:8px 4px;border-right:1px solid var(--mb-border);border-bottom:0;font-size:13px}.mbsf-segment:last-child{border-right:0}.mbsf-payer-option{align-items:flex-start}.mbsf-attach-row{align-items:flex-start;flex-wrap:wrap}.mbsf-attach-main{align-items:flex-start;flex-basis:calc(100% - 150px)}.mbsf-attach-type{width:100%;flex-basis:100%;order:3}.mbsf-attach-actions{margin-left:auto}.mbsf-drop{min-height:190px;padding:24px 18px}.mbsf-actions{position:sticky;bottom:86px;z-index:10}.mbsf.mbsf-lifecycle-correction .mbsf-actions{position:static;bottom:auto}.mbsf-submit{width:100%}}
2613
2667
  `;
2614
2668
  function RequiredMark() {
2615
2669
  return /* @__PURE__ */ jsx6("span", { className: "mbsf-star", children: " *" });
@@ -2627,7 +2681,13 @@ function Field2({ label, required, error, invalid, path, span, children }) {
2627
2681
  function TextDateInput({ value, onChange, disabled, required, ariaLabel }) {
2628
2682
  const [display, setDisplay] = useState6(() => formatBillSubmissionDate(value));
2629
2683
  const [invalid, setInvalid] = useState6(false);
2684
+ const emittedValue = useRef3(void 0);
2630
2685
  useEffect6(() => {
2686
+ if (emittedValue.current !== void 0 && value === emittedValue.current) {
2687
+ emittedValue.current = void 0;
2688
+ return;
2689
+ }
2690
+ emittedValue.current = void 0;
2631
2691
  setDisplay(formatBillSubmissionDate(value));
2632
2692
  setInvalid(false);
2633
2693
  }, [value]);
@@ -2637,8 +2697,8 @@ function TextDateInput({ value, onChange, disabled, required, ariaLabel }) {
2637
2697
  const parsed = parseBillSubmissionDate(next);
2638
2698
  setDisplay(next);
2639
2699
  setInvalid(Boolean(next && !parsed));
2640
- if (!next.trim()) onChange("");
2641
- else if (parsed) onChange(parsed);
2700
+ emittedValue.current = parsed ?? "";
2701
+ onChange(emittedValue.current);
2642
2702
  }, onBlur: () => setInvalid(Boolean(display && !parseBillSubmissionDate(display))) }),
2643
2703
  invalid ? /* @__PURE__ */ jsx6("small", { className: "mbsf-error", children: "Use MM/DD/YYYY" }) : null
2644
2704
  ] });
@@ -2711,7 +2771,7 @@ function focusFirstInvalid(form) {
2711
2771
  }
2712
2772
  function customProcedureOption(query) {
2713
2773
  const code = query.trim().toUpperCase().replace(/\s+/g, "");
2714
- if (!/^(?:\d{5}|[A-Z]\d{4}|ML(?:20[0-5]|PRR))$/.test(code)) return null;
2774
+ if (!/^(?:\d{5}|[A-Z]\d{4}|WC\d{3}|ML(?:20[0-5]|PRR))$/.test(code)) return null;
2715
2775
  return { id: code, label: code, detail: "Use this CPT, HCPCS, or medical-legal code" };
2716
2776
  }
2717
2777
  function mergeOptions(defaults, supplied) {
@@ -2806,6 +2866,9 @@ function BillSubmissionForm({
2806
2866
  onSearchDiagnoses,
2807
2867
  onLookupPostalCode,
2808
2868
  procedureOptions,
2869
+ treatmentBilling = false,
2870
+ onSearchProcedureCodes,
2871
+ onQuoteFee,
2809
2872
  modifierOptions,
2810
2873
  taxonomyOptions,
2811
2874
  deliveryRoutePicker = "auto",
@@ -2824,7 +2887,7 @@ function BillSubmissionForm({
2824
2887
  attentionMessage,
2825
2888
  children
2826
2889
  }) {
2827
- const [bill, setBill] = useState6(() => cloneInitialBill(initialBill));
2890
+ const [bill, setBill] = useState6(() => cloneInitialBill(initialBill, treatmentBilling));
2828
2891
  const [providerEditing, setProviderEditing] = useState6(false);
2829
2892
  const [selectedIds, setSelectedIds] = useState6(() => attachments.map((item) => item.id));
2830
2893
  const [removedSourceIds, setRemovedSourceIds] = useState6([]);
@@ -2852,6 +2915,12 @@ function BillSubmissionForm({
2852
2915
  const [diagnosisLoadingMore, setDiagnosisLoadingMore] = useState6(false);
2853
2916
  const [diagnosisQuery, setDiagnosisQuery] = useState6(null);
2854
2917
  const [diagnosisHasMore, setDiagnosisHasMore] = useState6(true);
2918
+ const [procedureResults, setProcedureResults] = useState6([]);
2919
+ const [procedureLoading, setProcedureLoading] = useState6(false);
2920
+ const [procedureError, setProcedureError] = useState6(null);
2921
+ const procedureRequest = useRef3(0);
2922
+ const [feeDetails, setFeeDetails] = useState6({});
2923
+ const [feeQuotes, setFeeQuotes] = useState6({});
2855
2924
  const [postalStatus, setPostalStatus] = useState6(null);
2856
2925
  const [dragActive, setDragActive] = useState6(false);
2857
2926
  const formRef = useRef3(null);
@@ -2860,12 +2929,12 @@ function BillSubmissionForm({
2860
2929
  const diagnosisAppendPending = useRef3(false);
2861
2930
  const payerRequest = useRef3(0);
2862
2931
  const payerAppendPending = useRef3(false);
2863
- const procedures = useMemo3(() => mergeOptions(DEFAULT_BILL_SUBMISSION_PROCEDURES, procedureOptions), [procedureOptions]);
2932
+ const procedures = useMemo3(() => mergeOptions(DEFAULT_BILL_SUBMISSION_PROCEDURES, [...procedureOptions ?? [], ...procedureResults, ...treatmentBilling ? [{ code: "WC002", description: "Progress report" }, { code: "WC003", description: "Permanent and stationary report (PR-3)" }, { code: "WC004", description: "Permanent and stationary report (PR-4)" }] : []]), [procedureOptions, procedureResults, treatmentBilling]);
2864
2933
  const orderedClaimsAdministratorSources = useMemo3(() => [...claimsAdministratorSources ?? []].filter((source) => source.name.trim()).sort((left, right) => Number(right.source.toLowerCase() === "eams") - Number(left.source.toLowerCase() === "eams")), [claimsAdministratorSources]);
2865
2934
  const sourceClaimsAdministratorName = orderedClaimsAdministratorSources[0]?.name.trim() ?? "";
2866
2935
  const initializationKey = useMemo3(
2867
- () => billSubmissionInitializationKey(initialBill, attachments, sourceClaimsAdministratorName),
2868
- [initialBill, attachments, sourceClaimsAdministratorName]
2936
+ () => `${treatmentBilling}:${billSubmissionInitializationKey(initialBill, attachments, sourceClaimsAdministratorName)}`,
2937
+ [initialBill, attachments, sourceClaimsAdministratorName, treatmentBilling]
2869
2938
  );
2870
2939
  const previousInitializationKey = useRef3(initializationKey);
2871
2940
  const modifiers = useMemo3(() => mergeOptions(DEFAULT_BILL_SUBMISSION_MODIFIERS, modifierOptions), [modifierOptions]);
@@ -2882,7 +2951,9 @@ function BillSubmissionForm({
2882
2951
  useEffect6(() => {
2883
2952
  if (previousInitializationKey.current === initializationKey) return;
2884
2953
  previousInitializationKey.current = initializationKey;
2885
- setBill(cloneInitialBill(initialBill));
2954
+ setFeeDetails({});
2955
+ setFeeQuotes({});
2956
+ setBill(cloneInitialBill(initialBill, treatmentBilling));
2886
2957
  setEvaluationType(initialEvaluationType(initialBill));
2887
2958
  setSelectedIds(attachments.map((item) => item.id));
2888
2959
  setRemovedSourceIds([]);
@@ -2903,12 +2974,7 @@ function BillSubmissionForm({
2903
2974
  setPayerHasMore(true);
2904
2975
  payerRequest.current += 1;
2905
2976
  payerAppendPending.current = false;
2906
- }, [initialBill, attachments, initializationKey]);
2907
- useEffect6(() => {
2908
- if (!validationActive) return;
2909
- const next = validateBillSubmission(bill).fieldErrors;
2910
- setErrors(missingAttachmentReportType ? { ...next, attachments: "Select a report type for every attachment." } : next);
2911
- }, [bill, missingAttachmentReportType, validationActive]);
2977
+ }, [initialBill, attachments, initializationKey, treatmentBilling]);
2912
2978
  useEffect6(() => {
2913
2979
  const root = formRef.current;
2914
2980
  if (!root) return;
@@ -2964,18 +3030,26 @@ function BillSubmissionForm({
2964
3030
  };
2965
3031
  }, [addFiles]);
2966
3032
  const setAddress = (patch) => setBill((current) => ({ ...current, patient: { ...current.patient, address: { ...current.patient.address, ...patch } } }));
2967
- const setLine = (index, patch) => setBill((current) => ({ ...current, serviceLines: ensureTrailingBillSubmissionLine(current.serviceLines.map((line, lineIndex) => {
2968
- if (lineIndex !== index) return line;
2969
- const next = { ...line, ...patch };
2970
- const charge = calculateBillSubmissionAllowedAmount(next, procedures);
2971
- if (charge != null) return { ...next, charge };
2972
- if (Object.hasOwn(patch, "code")) {
2973
- const withoutCharge = { ...next };
2974
- delete withoutCharge.charge;
2975
- return withoutCharge;
2976
- }
2977
- return next;
2978
- })) }));
3033
+ const setLine = (index, patch) => {
3034
+ if (Object.hasOwn(patch, "code")) setFeeDetails((current) => ({ ...current, [index]: {} }));
3035
+ setBill((current) => {
3036
+ const serviceLines = ensureTrailingBillSubmissionLine(current.serviceLines.map((line, lineIndex) => {
3037
+ if (lineIndex !== index) return line;
3038
+ const next = { ...line, ...patch };
3039
+ if (patch.code != null && patch.code !== line.code) delete next.rfaItemId;
3040
+ if (treatmentBilling && next.code && !isMedicalLegalCode(next.code)) {
3041
+ delete next.charge;
3042
+ return next;
3043
+ }
3044
+ const charge = calculateBillSubmissionAllowedAmount(next, procedures);
3045
+ if (charge != null) return { ...next, charge };
3046
+ if (Object.hasOwn(patch, "code")) delete next.charge;
3047
+ return next;
3048
+ }));
3049
+ const populated = serviceLines.filter((line) => line.code.trim());
3050
+ return { ...replaceBillSubmissionServiceLines(current, serviceLines), ...treatmentBilling && populated.length ? { billingMode: populated.every((line) => isMedicalLegalCode(line.code)) ? "med_legal" : "professional" } : {} };
3051
+ });
3052
+ };
2979
3053
  const text = (value, onChange, options = {}) => /* @__PURE__ */ jsx6("input", { className: "mbsf-input", disabled: locked, value: value ?? "", onChange: (event) => onChange(event.target.value), ...options });
2980
3054
  const loadPayers = useCallback((query, append = false) => {
2981
3055
  const trimmed = query.trim();
@@ -3057,6 +3131,30 @@ function BillSubmissionForm({
3057
3131
  setDiagnosisLoadingMore(false);
3058
3132
  });
3059
3133
  };
3134
+ const loadProcedures = (query) => {
3135
+ if (!treatmentBilling) return;
3136
+ const search = onSearchProcedureCodes ?? referenceClient?.searchProcedureCodes;
3137
+ if (!search) return;
3138
+ const normalized = query.trim().toUpperCase();
3139
+ const request = ++procedureRequest.current;
3140
+ if (!/^[A-Z0-9]{0,5}$/.test(normalized) || /^ML/.test(normalized)) {
3141
+ setProcedureResults([]);
3142
+ setProcedureLoading(false);
3143
+ return;
3144
+ }
3145
+ setProcedureLoading(true);
3146
+ setProcedureError(null);
3147
+ void search({ query: normalized, limit: 30, jurisdiction: bill.claim.injuryState === "NY" ? "NY" : "CA" }).then((page) => {
3148
+ if (request === procedureRequest.current) setProcedureResults(page.results.map((item) => ({ code: item.code, description: "Treatment procedure \xB7 fee checked for service date" })));
3149
+ }).catch(() => {
3150
+ if (request === procedureRequest.current) {
3151
+ setProcedureResults([]);
3152
+ setProcedureError("Treatment code search is unavailable. Try again.");
3153
+ }
3154
+ }).finally(() => {
3155
+ if (request === procedureRequest.current) setProcedureLoading(false);
3156
+ });
3157
+ };
3060
3158
  const searchDiagnoses = (query) => loadDiagnoses(query);
3061
3159
  const diagnosisChoices = useMemo3(() => mergeDiagnosisOptions([...diagnosisOptions, ...diagnosisResults]), [diagnosisOptions, diagnosisResults]);
3062
3160
  const updatePostalCode = (postalCode) => {
@@ -3073,15 +3171,113 @@ function BillSubmissionForm({
3073
3171
  };
3074
3172
  const changeEvaluation = (type) => {
3075
3173
  setEvaluationType(type);
3076
- setBill((current) => ({ ...current, diagnoses: applyBillSubmissionEvaluationDiagnoses(current.diagnoses, type), renderingProvider: { ...current.renderingProvider, isAme: type === "ame" || type === "psych_ame", isQme: type === "qme" || type === "psych_qme", ...(type === "psych_qme" || type === "psych_ame") && !current.renderingProvider?.specialty ? { specialty: "Psychiatry" } : {} }, serviceLines: applyBillSubmissionEvaluationModifiers(current.serviceLines, type) }));
3174
+ setBill((current) => {
3175
+ const remapped = remapBillSubmissionDiagnoses(current, current.billingMode === "professional" ? current.diagnoses ?? [] : applyBillSubmissionEvaluationDiagnoses(current.diagnoses, type));
3176
+ return { ...remapped, renderingProvider: { ...current.renderingProvider, isAme: type === "ame" || type === "psych_ame", isQme: type === "qme" || type === "psych_qme", ...(type === "psych_qme" || type === "psych_ame") && !current.renderingProvider?.specialty ? { specialty: "Psychiatry" } : {} }, serviceLines: applyBillSubmissionEvaluationModifiers(remapped.serviceLines, type) };
3177
+ });
3077
3178
  };
3078
- const lineCharge = (line) => calculateBillSubmissionAllowedAmount(line, procedures) ?? (Number.isFinite(line.charge) ? Number(line.charge) : void 0);
3079
- const total = submittedLines(bill.serviceLines).reduce((sum, line) => sum + (lineCharge(line) ?? 0), 0);
3179
+ const supportedFeeJurisdiction = (bill.claim.injuryState ?? "CA").trim().toUpperCase() === "CA";
3180
+ const feeConfirmationKey = (line) => JSON.stringify({ date: parseBillSubmissionDate(line.serviceDate ?? bill.service.date) ?? "", location: bill.serviceLocation, provider: bill.renderingProvider });
3181
+ const quoteInputs = bill.serviceLines.map((line, index) => {
3182
+ if (!treatmentBilling || !line.code.trim() || isMedicalLegalCode(line.code)) return null;
3183
+ const details = feeDetails[index];
3184
+ let context = {};
3185
+ if (details?.confirmed && details.confirmationKey === feeConfirmationKey(line)) {
3186
+ const placeOfService = bill.serviceLocation?.placeOfServiceCode ?? "";
3187
+ if (/^992(?:0[2-5]|1[2-5])$/.test(line.code) && details.providerKind) context = { ...context, hasFeeAgreement: false, physicianContext: { providerKind: details.providerKind, placeOfService, incidentToPhysicianService: false, standaloneService: true, globalPeriodApplies: false, hpsaBonusEligible: false } };
3188
+ if (line.code === "97110" && details.minutes && details.totalMinutes) context = { ...context, hasFeeAgreement: false, therapyContext: { providerKind: "physical_therapist", personallyPerformed: true, hospitalPatient: false, incidentToPhysicianService: false, assistantInvolved: false, placeOfService, directOneOnOneMinutes: details.minutes, totalVisitMinutes: details.totalMinutes, visitsOnDate: 1, completeSameDayServices: true, otherSameDayServices: false, globalPeriodApplies: false, hpsaBonusEligible: false } };
3189
+ }
3190
+ return billSubmissionFeeRequest(bill, line, context);
3191
+ });
3192
+ const quoteKeys = quoteInputs.map((input) => input ? JSON.stringify(input) : null);
3193
+ const quoteBatch = JSON.stringify(quoteInputs);
3194
+ const quoteFee = onQuoteFee ?? referenceClient?.quoteFee;
3195
+ const [feeRetry, setFeeRetry] = useState6(0);
3196
+ useEffect6(() => {
3197
+ let active = true;
3198
+ const inputs = JSON.parse(quoteBatch);
3199
+ const unique = new Map(inputs.filter((input) => Boolean(input)).map((input) => [JSON.stringify(input), input]));
3200
+ const timer = setTimeout(() => {
3201
+ for (const [key, input] of unique) {
3202
+ if (!quoteFee || !input.dateOfService || !supportedFeeJurisdiction) continue;
3203
+ void quoteFee(input).then((result) => {
3204
+ if (active) setFeeQuotes((current) => ({ ...current, [key]: result }));
3205
+ }).catch(() => {
3206
+ if (active) setFeeQuotes((current) => ({ ...current, [key]: { status: "error", reason: "Fee lookup is unavailable. Retry the fee check." } }));
3207
+ });
3208
+ }
3209
+ }, 200);
3210
+ return () => {
3211
+ active = false;
3212
+ clearTimeout(timer);
3213
+ };
3214
+ }, [quoteBatch, quoteFee, feeRetry, supportedFeeJurisdiction]);
3215
+ const lineCharge = (line, index) => {
3216
+ if (treatmentBilling && line.code && !isMedicalLegalCode(line.code)) {
3217
+ const key = quoteKeys[index];
3218
+ const quote = key ? feeQuotes[key] : void 0;
3219
+ return supportedFeeJurisdiction && quote?.status === "priced" ? quote.amountCents / 100 : void 0;
3220
+ }
3221
+ return calculateBillSubmissionAllowedAmount(line, procedures) ?? (Number.isFinite(line.charge) ? Number(line.charge) : void 0);
3222
+ };
3223
+ const feeDetailsFor = (line, index) => {
3224
+ const key = quoteKeys[index];
3225
+ if (!key) return null;
3226
+ const quote = supportedFeeJurisdiction ? feeQuotes[key] : void 0;
3227
+ const details = feeDetails[index] ?? {};
3228
+ const update = (patch) => setFeeDetails((current) => ({ ...current, [index]: { ...current[index], ...patch } }));
3229
+ const office = /^992(?:0[2-5]|1[2-5])$/.test(line.code);
3230
+ const therapy = line.code === "97110";
3231
+ return /* @__PURE__ */ jsxs6("details", { className: "mbsf-fee-details", children: [
3232
+ /* @__PURE__ */ jsxs6("summary", { children: [
3233
+ "Fee schedule details",
3234
+ quote?.status === "priced" ? " \xB7 Verified" : ""
3235
+ ] }),
3236
+ office ? /* @__PURE__ */ jsxs6("label", { className: "mbsf-field", children: [
3237
+ /* @__PURE__ */ jsx6("span", { children: "Service performed by" }),
3238
+ /* @__PURE__ */ jsxs6("select", { className: "mbsf-input", "aria-label": `Service performed by for line ${index + 1}`, value: details.providerKind ?? "", onChange: (event) => update({ providerKind: event.target.value, confirmed: false }), children: [
3239
+ /* @__PURE__ */ jsx6("option", { value: "", children: "Select provider type\u2026" }),
3240
+ /* @__PURE__ */ jsx6("option", { value: "physician", children: "Physician" }),
3241
+ /* @__PURE__ */ jsx6("option", { value: "physician_assistant", children: "Physician assistant" }),
3242
+ /* @__PURE__ */ jsx6("option", { value: "nurse_practitioner", children: "Nurse practitioner" }),
3243
+ /* @__PURE__ */ jsx6("option", { value: "clinical_nurse_specialist", children: "Clinical nurse specialist" }),
3244
+ /* @__PURE__ */ jsx6("option", { value: "other", children: "Other" })
3245
+ ] })
3246
+ ] }) : null,
3247
+ therapy ? /* @__PURE__ */ jsxs6("div", { className: "mbsf-grid", children: [
3248
+ /* @__PURE__ */ jsxs6("label", { className: "mbsf-field", children: [
3249
+ /* @__PURE__ */ jsx6("span", { children: "Direct one-on-one minutes" }),
3250
+ /* @__PURE__ */ jsx6("input", { className: "mbsf-input", "aria-label": `Direct one-on-one minutes for line ${index + 1}`, type: "number", min: "1", value: details.minutes ?? "", onChange: (event) => update({ minutes: Number(event.target.value), confirmed: false }) })
3251
+ ] }),
3252
+ /* @__PURE__ */ jsxs6("label", { className: "mbsf-field", children: [
3253
+ /* @__PURE__ */ jsx6("span", { children: "Total visit minutes" }),
3254
+ /* @__PURE__ */ jsx6("input", { className: "mbsf-input", "aria-label": `Total visit minutes for line ${index + 1}`, type: "number", min: "1", value: details.totalMinutes ?? "", onChange: (event) => update({ totalMinutes: Number(event.target.value), confirmed: false }) })
3255
+ ] })
3256
+ ] }) : null,
3257
+ office || therapy ? /* @__PURE__ */ jsxs6("label", { className: "mbsf-fee-confirm", children: [
3258
+ /* @__PURE__ */ jsx6("input", { type: "checkbox", checked: Boolean(details.confirmed && details.confirmationKey === feeConfirmationKey(line)), onChange: (event) => update({ confirmed: event.target.checked, confirmationKey: feeConfirmationKey(line) }) }),
3259
+ /* @__PURE__ */ jsx6("span", { children: office ? "I confirm this is a standalone visit, not incident to a physician service, with no global-period adjustment, HPSA bonus, or fee agreement." : "I confirm a physical therapist personally performed this service in one visit. All services for this date have been reviewed; there were no other services, assistant, hospital care, incident-to services, global-period adjustment, HPSA bonus, or fee agreement." })
3260
+ ] }) : null,
3261
+ /* @__PURE__ */ jsx6("p", { className: "mbsf-help", role: "status", children: !supportedFeeJurisdiction ? "This jurisdiction requires fee review before submission." : !quoteFee ? "Connect fee lookup to check this service." : !quoteInputs[index]?.dateOfService ? "Enter a valid service date to check the fee." : quote?.status === "priced" ? "Allowed amount verified for this service date and the details above." : quote?.reason ?? "Checking the fee schedule\u2026" }),
3262
+ quote?.status === "error" ? /* @__PURE__ */ jsx6("button", { type: "button", className: "mbsf-secondary", onClick: () => setFeeRetry((value) => value + 1), children: "Retry fee check" }) : null
3263
+ ] });
3264
+ };
3265
+ const total = bill.serviceLines.reduce((sum, line, index) => sum + (lineHasContent(line) ? lineCharge(line, index) ?? 0 : 0), 0);
3266
+ const pricedLines = bill.serviceLines.map((line, index) => {
3267
+ const charge = lineCharge(line, index);
3268
+ const next = { ...line };
3269
+ if (charge == null && treatmentBilling && !isMedicalLegalCode(line.code)) delete next.charge;
3270
+ else if (charge != null) next.charge = charge;
3271
+ return next;
3272
+ }).filter(lineHasContent);
3273
+ const clean = { ...cloneBill(bill), serviceLines: pricedLines, ...treatmentBilling && pricedLines.length ? { billingMode: pricedLines.every((line) => isMedicalLegalCode(line.code)) ? "med_legal" : "professional" } : {} };
3274
+ const liveErrors = JSON.stringify(validateBillSubmission(clean).fieldErrors);
3275
+ useEffect6(() => {
3276
+ if (!validationActive) return;
3277
+ const next = JSON.parse(liveErrors);
3278
+ setErrors(missingAttachmentReportType ? { ...next, attachments: "Select a report type for every attachment." } : next);
3279
+ }, [liveErrors, missingAttachmentReportType, validationActive]);
3080
3280
  async function submit() {
3081
- const clean = { ...cloneBill(bill), serviceLines: submittedLines(bill.serviceLines).map((line) => {
3082
- const charge = lineCharge(line);
3083
- return charge == null ? line : { ...line, charge };
3084
- }) };
3085
3281
  const validation = validateBillSubmission(clean);
3086
3282
  const fieldErrors = missingAttachmentReportType ? { ...validation.fieldErrors, attachments: "Select a report type for every attachment." } : validation.fieldErrors;
3087
3283
  setValidationActive(true);
@@ -3299,7 +3495,7 @@ function BillSubmissionForm({
3299
3495
  /* @__PURE__ */ jsxs6(Field2, { path: "diagnoses", label: "Diagnosis codes (ICD-10)", required: true, span: true, error: errors.diagnoses, children: [
3300
3496
  /* @__PURE__ */ jsx6("div", { className: "mbsf-quick-picks", "aria-label": "Common diagnosis codes", children: BILL_SUBMISSION_DIAGNOSIS_QUICK_PICKS.map((option) => {
3301
3497
  const selected = (bill.diagnoses ?? []).includes(option.code);
3302
- return /* @__PURE__ */ jsxs6("button", { className: "mbsf-quick-pick", "data-selected": selected, type: "button", "aria-pressed": selected, title: `${option.code} \u2014 ${option.description}`, onClick: () => setBill((current) => ({ ...current, diagnoses: selected ? (current.diagnoses ?? []).filter((code) => code !== option.code) : [.../* @__PURE__ */ new Set([...current.diagnoses ?? [], option.code])] })), children: [
3498
+ return /* @__PURE__ */ jsxs6("button", { className: "mbsf-quick-pick", "data-selected": selected, type: "button", "aria-pressed": selected, title: `${option.code} \u2014 ${option.description}`, onClick: () => setBill((current) => remapBillSubmissionDiagnoses(current, selected ? (current.diagnoses ?? []).filter((code) => code !== option.code) : [...current.diagnoses ?? [], option.code])), children: [
3303
3499
  selected ? "\u2713" : "+",
3304
3500
  " ",
3305
3501
  option.label
@@ -3310,12 +3506,12 @@ function BillSubmissionForm({
3310
3506
  return /* @__PURE__ */ jsxs6("span", { className: "mbsf-chip", children: [
3311
3507
  /* @__PURE__ */ jsx6("strong", { children: code }),
3312
3508
  option?.description ? ` ${option.description}` : "",
3313
- /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": `Remove ${code}`, onClick: () => setBill((c) => ({ ...c, diagnoses: (c.diagnoses ?? []).filter((item) => item !== code) })), children: "\xD7" })
3509
+ /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": `Remove ${code}`, onClick: () => setBill((c) => remapBillSubmissionDiagnoses(c, (c.diagnoses ?? []).filter((item) => item !== code))), children: "\xD7" })
3314
3510
  ] }, code);
3315
3511
  }) }),
3316
3512
  /* @__PURE__ */ jsx6(ComboBox, { ariaLabel: "Add diagnosis code", invalid: Boolean(errors.diagnoses), disabled: locked, loading: diagnosisLoading, loadingMore: diagnosisLoadingMore, value: "", placeholder: bill.diagnoses?.length ?? 0 ? `${bill.diagnoses.length} selected \u2014 add more\u2026` : "Search ICD-10 codes\u2026", options: diagnosisChoices.filter((item) => !(bill.diagnoses ?? []).includes(item.code)).map((item) => ({ id: item.code, label: item.code, detail: item.description })), onOpen: () => {
3317
3513
  if (diagnosisQuery !== "") loadDiagnoses("");
3318
- }, onQuery: searchDiagnoses, onEndReached: () => loadDiagnoses(diagnosisQuery ?? "", true), onSelect: (option) => setBill((c) => ({ ...c, diagnoses: [.../* @__PURE__ */ new Set([...c.diagnoses ?? [], option.id])] })) })
3514
+ }, onQuery: searchDiagnoses, onEndReached: () => loadDiagnoses(diagnosisQuery ?? "", true), onSelect: (option) => setBill((c) => remapBillSubmissionDiagnoses(c, [...c.diagnoses ?? [], option.id])) })
3319
3515
  ] })
3320
3516
  ] })
3321
3517
  ] });
@@ -3399,6 +3595,7 @@ function BillSubmissionForm({
3399
3595
  /* @__PURE__ */ jsx6("p", { className: "mbsf-help", children: "Sets the evaluator/specialty modifier on medical-legal evaluation lines." }),
3400
3596
  /* @__PURE__ */ jsx6("div", { className: "mbsf-segments", role: "group", "aria-label": "Evaluation type", children: [["qme", "QME (default)"], ["ame", "AME"], ["psych_qme", "Psych QME"], ["psych_ame", "Psych AME"]].map(([type, label]) => /* @__PURE__ */ jsx6("button", { className: "mbsf-segment", type: "button", "aria-pressed": evaluationType === type, onClick: () => changeEvaluation(type), children: label }, type)) }),
3401
3597
  /* @__PURE__ */ jsx6("p", { className: "mbsf-help", children: evaluationType === "psych_ame" ? "Psychiatric or psychological AME \u2014 eligible ML evaluation codes default to modifiers -94 and -96." : evaluationType === "ame" ? "Agreed Medical Evaluator \u2014 eligible ML evaluation codes default to modifier -94." : evaluationType === "psych_qme" ? "Psychiatric or psychological QME \u2014 eligible ML evaluation codes default to modifiers -95 and -96 (-95 only for ML200 and MLPRR)." : "Qualified Medical Evaluator \u2014 eligible ML evaluation codes default to modifier -95." }),
3598
+ procedureError ? /* @__PURE__ */ jsx6("p", { className: "mbsf-error", role: "alert", children: procedureError }) : null,
3402
3599
  /* @__PURE__ */ jsxs6("div", { className: "mbsf-lines", "data-field-path": "serviceLines", "data-invalid": Boolean(errors.serviceLines), children: [
3403
3600
  /* @__PURE__ */ jsxs6("div", { className: "mbsf-line-head", children: [
3404
3601
  /* @__PURE__ */ jsxs6("span", { children: [
@@ -3417,8 +3614,9 @@ function BillSubmissionForm({
3417
3614
  ] }),
3418
3615
  bill.serviceLines.map((line, index) => /* @__PURE__ */ jsxs6("div", { className: "mbsf-line", children: [
3419
3616
  /* @__PURE__ */ jsxs6("div", { "data-label": "Procedure code", "data-field-path": `serviceLines.${index}.code`, "data-invalid": Boolean(errors[`serviceLines.${index}.code`]), children: [
3420
- /* @__PURE__ */ jsx6(ComboBox, { ariaLabel: `Procedure code ${index + 1}`, invalid: Boolean(errors[`serviceLines.${index}.code`]), disabled: locked, value: line.code, placeholder: "Search or enter code\u2026", options: procedures.map((item) => ({ id: item.code, label: item.code, detail: item.description })), createOption: customProcedureOption, onSelect: (option) => {
3421
- const [updated] = applyBillSubmissionEvaluationModifiers([{ ...line, code: option.id }], evaluationType);
3617
+ /* @__PURE__ */ jsx6(ComboBox, { ariaLabel: `Procedure code ${index + 1}`, invalid: Boolean(errors[`serviceLines.${index}.code`]), disabled: locked, value: line.code, placeholder: "Search or enter code\u2026", loading: procedureLoading, onOpen: () => loadProcedures(""), onQuery: loadProcedures, options: procedures.map((item) => ({ id: item.code, label: item.code, detail: item.description })), createOption: customProcedureOption, onSelect: (option) => {
3618
+ const switched = isMedicalLegalCode(line.code) && !isMedicalLegalCode(option.id);
3619
+ const [updated] = applyBillSubmissionEvaluationModifiers([{ ...line, code: option.id, ...switched ? { modifiers: (line.modifiers ?? []).filter((value) => !["94", "95", "96"].includes(value.replace(/^-/, ""))) } : {} }], evaluationType);
3422
3620
  setLine(index, updated);
3423
3621
  } }),
3424
3622
  line.code ? /* @__PURE__ */ jsx6("small", { className: "mbsf-help", children: procedures.find((item) => item.code === line.code)?.description ?? "Custom CPT, HCPCS, or medical-legal code" }) : null,
@@ -3432,17 +3630,30 @@ function BillSubmissionForm({
3432
3630
  ] }, modifier)) }),
3433
3631
  /* @__PURE__ */ jsx6(ComboBox, { ariaLabel: `Modifiers ${index + 1}`, disabled: locked, value: "", placeholder: line.modifiers?.length ?? 0 ? `${line.modifiers.length} modifier${line.modifiers.length === 1 ? "" : "s"}` : "Add modifiers\u2026", options: modifiers.filter((item) => !(line.modifiers ?? []).includes(item.code)).map((item) => ({ id: item.code, label: `\u2212${item.code}`, detail: item.description })), onSelect: (option) => setLine(index, { modifiers: [.../* @__PURE__ */ new Set([...line.modifiers ?? [], option.id])] }) })
3434
3632
  ] }),
3435
- /* @__PURE__ */ jsx6("div", { "data-label": "Dx", children: /* @__PURE__ */ jsx6("div", { className: "mbsf-dx", children: (bill.diagnoses ?? []).length ? (bill.diagnoses ?? []).map((code, dxIndex) => {
3436
- const pointer = dxIndex + 1;
3437
- const active = (line.diagnosisPointers ?? []).includes(pointer);
3438
- return /* @__PURE__ */ jsx6("button", { type: "button", className: "mbsf-dx-chip", "data-active": active, title: `${String.fromCharCode(64 + pointer)} \u2014 ${code}`, "aria-pressed": active, "aria-label": `Point line ${index + 1} at diagnosis ${code}`, onClick: () => setLine(index, { diagnosisPointers: active ? (line.diagnosisPointers ?? []).filter((item) => item !== pointer) : [...line.diagnosisPointers ?? [], pointer].sort((left, right) => left - right) }), children: String.fromCharCode(64 + pointer) }, code);
3439
- }) : /* @__PURE__ */ jsx6("small", { className: "mbsf-help", children: "Add diagnoses above" }) }) }),
3633
+ /* @__PURE__ */ jsxs6("div", { "data-label": "Dx", "data-field-path": `serviceLines.${index}.diagnosisPointers`, "data-invalid": Boolean(errors[`serviceLines.${index}.diagnosisPointers`]), children: [
3634
+ /* @__PURE__ */ jsx6("div", { className: "mbsf-line-diagnoses", children: [0, 1, 2, 3].map((slot) => {
3635
+ const pointer = line.diagnosisPointers?.[slot];
3636
+ const selectedCode = pointer ? bill.diagnoses?.[pointer - 1] ?? "" : "";
3637
+ const selectedCodes = (line.diagnosisPointers ?? []).map((item) => bill.diagnoses?.[item - 1]);
3638
+ const options = mergeDiagnosisOptions([...(bill.diagnoses ?? []).map((code) => ({ code, description: diagnosisChoices.find((item) => item.code === code)?.description ?? "" })), ...diagnosisChoices]).filter((item) => (!selectedCodes.includes(item.code) || item.code === selectedCode) && ((bill.diagnoses?.length ?? 0) < 12 || bill.diagnoses?.includes(item.code)));
3639
+ return /* @__PURE__ */ jsx6(ComboBox, { ariaLabel: `Diagnosis ${slot + 1} for service line ${index + 1}`, disabled: locked, invalid: Boolean(errors[`serviceLines.${index}.diagnosisPointers`]), value: selectedCode, placeholder: `Dx ${slot + 1}`, loading: diagnosisLoading, loadingMore: diagnosisLoadingMore, options: [...selectedCode ? [{ id: "", label: "Clear diagnosis" }] : [], ...options.map((item) => ({ id: item.code, label: item.code, detail: item.description }))], onOpen: () => loadDiagnoses(""), onQuery: searchDiagnoses, onEndReached: () => loadDiagnoses(diagnosisQuery ?? "", true), onSelect: (option) => setBill((current) => setBillSubmissionLineDiagnosis(current, index, slot, option.id)) }, slot);
3640
+ }) }),
3641
+ errors[`serviceLines.${index}.diagnosisPointers`] ? /* @__PURE__ */ jsx6("small", { className: "mbsf-error", role: "alert", children: errors[`serviceLines.${index}.diagnosisPointers`] }) : null
3642
+ ] }),
3440
3643
  /* @__PURE__ */ jsxs6("div", { "data-label": "Units", "data-field-path": `serviceLines.${index}.units`, "data-invalid": Boolean(errors[`serviceLines.${index}.units`]), children: [
3441
3644
  /* @__PURE__ */ jsx6("input", { className: "mbsf-input", "aria-label": `Units ${index + 1}`, "aria-invalid": Boolean(errors[`serviceLines.${index}.units`]), type: "number", min: 1, value: line.units ?? 1, onChange: (event) => setLine(index, { units: Number(event.target.value) }) }),
3442
3645
  errors[`serviceLines.${index}.units`] ? /* @__PURE__ */ jsx6("small", { className: "mbsf-error", role: "alert", children: errors[`serviceLines.${index}.units`] }) : null
3443
3646
  ] }),
3444
- /* @__PURE__ */ jsx6("div", { className: "mbsf-money", "data-label": "Allowed", children: lineCharge(line) == null ? "\u2014" : lineCharge(line).toLocaleString(void 0, { style: "currency", currency: "USD" }) }),
3445
- /* @__PURE__ */ jsx6("button", { className: "mbsf-icon-btn", type: "button", "aria-label": `Remove service line ${index + 1}`, disabled: locked || !lineHasContent(line) && index === bill.serviceLines.length - 1, onClick: () => setBill((c) => ({ ...c, serviceLines: ensureTrailingBillSubmissionLine(c.serviceLines.filter((_, itemIndex) => itemIndex !== index)) })), children: "\xD7" })
3647
+ /* @__PURE__ */ jsx6("div", { className: "mbsf-money", "data-label": "Allowed", "data-field-path": `serviceLines.${index}.charge`, "data-invalid": Boolean(errors[`serviceLines.${index}.charge`]), children: lineCharge(line, index) == null ? quoteKeys[index] ? !supportedFeeJurisdiction || !quoteFee || !quoteInputs[index]?.dateOfService || feeQuotes[quoteKeys[index]] ? "Needs review" : "Checking\u2026" : "\u2014" : lineCharge(line, index).toLocaleString(void 0, { style: "currency", currency: "USD" }) }),
3648
+ /* @__PURE__ */ jsx6("button", { className: "mbsf-icon-btn", type: "button", "aria-label": `Remove service line ${index + 1}`, disabled: locked || !lineHasContent(line) && index === bill.serviceLines.length - 1, onClick: () => {
3649
+ setFeeDetails({});
3650
+ setBill((c) => {
3651
+ const lines = c.serviceLines.filter((_, itemIndex) => itemIndex !== index);
3652
+ const populated = lines.filter((item) => item.code.trim());
3653
+ return { ...replaceBillSubmissionServiceLines(c, ensureTrailingBillSubmissionLine(lines)), ...treatmentBilling && populated.length ? { billingMode: populated.every((item) => isMedicalLegalCode(item.code)) ? "med_legal" : "professional" } : {} };
3654
+ });
3655
+ }, children: "\xD7" }),
3656
+ feeDetailsFor(line, index)
3446
3657
  ] }, index)),
3447
3658
  /* @__PURE__ */ jsxs6("div", { className: "mbsf-total", children: [
3448
3659
  /* @__PURE__ */ jsx6("span", { children: "Total" }),