Pular para conteúdo

Data Model: Validação e escopo por CRP

ProfessionalProfile (existing, extended)

  • ownerUserId unique; cpf, cnpj normalized and independently nullable, with at least one for completion.
  • profileCompletedAt; existing users may remain grandfathered during rollout.
  • Partial unique indexes for non-null CPF/CNPJ.

ProfessionalRegistration

  • IDs: id, ownerUserId, profileId.
  • CRP: region, number, canonicalKey.
  • Document: linkedDocumentType: cpf|cnpj, linkedDocumentValue snapshot.
  • Validation: validationState, officialStatus, source, stateVersion.
  • Times: graceStartedAt, graceExpiresAt, lastConfirmedAt, lastAttemptAt, nextAttemptAt, timestamps.
  • Error: stable lastErrorCode, never raw provider payload.
  • Unique owner/canonical/document association; cross-owner conflict resolved neutrally.

ValidationState

pending → provisional → validated | invalid_inactive | expired; temporary failures preserve last confirmed state. Success after expiry transitions to validated. Grace dates never renew.

CrpValidationAttempt

  • registrationId, attemptKey, start/completion, outcome, provider status/error code.
  • Unique attemptKey; no CPF/CNPJ or raw response.

Patient (existing, extended)

  • professionalRegistrationId nullable FK during migration.
  • New patients require a registration; legacy null is visible only in “Todos”.
  • Owner and registration owner must match. Changing registration does not affect quota.
  • Index owner/registration/status and owner/registration/name.

UserPlan (existing)

  • Tier planKey: free|standard|future-key.
  • free.activePatientsMax=5; usage always counted by owner without CRP filter.

OperationDecision (read model)

  • action, allowed, blockers[].
  • Blocker: code, source: profile|crp|plan|policy, optional registration scope, safe message, optional resolution.
  • Decisions are calculated; source states are persisted, not a global user status.

Invariants

  • Every query first restricts owner; CRP can only narrow.
  • New patient has one registration; dependents derive via patient.
  • Absence of CRP filter never means absence of owner authorization.
  • Expiry is graceExpiresAt <= now, independent of worker execution.