erp-construccion-database-v2/docs/JSONB-SCHEMAS.md
Adrian Flores Cortes dcdc15e162 [ST-P2-002,ST-P2-003] feat: Add compound indices and JSONB schema documentation
- 11-compound-indices.sql: 14 compound indices for common queries
- docs/JSONB-SCHEMAS.md: 50+ TypeScript interfaces documenting JSONB fields
- DDL-EXECUTION-ORDER.md: Updated to include compound indices step

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 07:39:14 -06:00

15 KiB

JSONB Schemas Documentation - ERP Construccion

Version: 1.0.0 Fecha: 2026-02-03 Proyecto: erp-construccion


Descripcion

Este documento define los schemas esperados para los campos JSONB en la base de datos de ERP Construccion. Los campos JSONB almacenan datos flexibles/extensibles sin estructura rigida de tablas relacionales.


Convenciones

Notacion

  • {} - Objeto JSON
  • [] - Array JSON
  • ? - Campo opcional
  • type - Tipo esperado (string, number, boolean, date)

Validacion

Los schemas deben validarse en el backend antes de insertar datos. Se recomienda usar JSON Schema o Zod para validacion.


Schema: construction

fraccionamientos.metadata

interface FraccionamientoMetadata {
  // Informacion legal
  escritura_publica?: string;
  notario?: string;
  numero_registro_publico?: string;
  fecha_registro?: string; // YYYY-MM-DD

  // Permisos
  permiso_uso_suelo?: string;
  fecha_permiso_uso_suelo?: string;
  permiso_construccion?: string;
  fecha_permiso_construccion?: string;

  // Urbanizacion
  porcentaje_urbanizacion?: number; // 0-100
  servicios_instalados?: string[]; // ['agua', 'luz', 'drenaje', 'gas']

  // Contacto
  contacto_nombre?: string;
  contacto_telefono?: string;
  contacto_email?: string;

  // Financiamiento
  credito_puente?: boolean;
  banco_financiador?: string;
  monto_credito?: number;

  // Seguros
  poliza_rc?: string;
  vigencia_poliza_rc?: string;

  // Custom fields
  [key: string]: any;
}

prototipos.metadata

interface PrototipoMetadata {
  // Especificaciones tecnicas
  estructura?: string; // 'concreto', 'acero', 'mixta'
  acabados?: {
    pisos?: string;
    muros?: string;
    plafones?: string;
    cocina?: string;
    banos?: string;
  };

  // Equipamiento incluido
  equipamiento?: string[]; // ['calentador', 'tinaco', 'hidroneumatico']

  // Renders y materiales de venta
  fotos_galeria?: string[]; // URLs
  video_recorrido?: string;
  brochure_pdf?: string;

  // Precios por concepto
  desglose_precio?: {
    terreno?: number;
    construccion?: number;
    equipamiento?: number;
    gastos_escrituracion?: number;
  };

  // Custom fields
  [key: string]: any;
}

Schema: finance

bank_accounts.metadata

interface BankAccountMetadata {
  // Acceso banca electronica
  banca_electronica?: {
    url?: string;
    usuario_consulta?: string;
    tiene_transferencias?: boolean;
    limite_diario?: number;
    limite_mensual?: number;
  };

  // Documentos
  contrato_url?: string;
  estado_cuenta_url?: string;

  // Autorizadores
  firmas_autorizadas?: Array<{
    nombre: string;
    cargo: string;
    limite_firma?: number;
  }>;

  // Integraciones
  api_config?: {
    provider?: string;
    api_key_ref?: string; // Referencia a secret manager
    webhook_url?: string;
  };

  // Custom fields
  [key: string]: any;
}

bank_movements.raw_data

interface BankMovementRawData {
  // Datos originales del banco
  referencia_banco?: string;
  concepto_banco?: string;
  tipo_movimiento_banco?: string;
  codigo_movimiento?: string;

  // Datos de importacion
  imported_from?: 'api' | 'csv' | 'pdf' | 'manual';
  import_date?: string;
  original_row?: Record<string, any>;

  // Matching info
  auto_matched?: boolean;
  match_score?: number;
  match_candidates?: string[]; // IDs de posibles matches
}

bank_reconciliation.reconciliation_items

interface ReconciliationItems {
  matched: Array<{
    bank_movement_id: string;
    erp_movement_id: string;
    match_date: string;
    match_type: 'auto' | 'manual';
    amount: number;
  }>;

  unmatched_bank: Array<{
    bank_movement_id: string;
    amount: number;
    date: string;
    description: string;
  }>;

  unmatched_erp: Array<{
    erp_movement_id: string;
    amount: number;
    date: string;
    description: string;
  }>;

  adjustments: Array<{
    type: 'charge' | 'commission' | 'interest' | 'other';
    amount: number;
    description: string;
    date: string;
  }>;
}

cash_flow_projection.income_breakdown / expense_breakdown

interface CashFlowBreakdown {
  categories: Array<{
    category: string;
    subcategories: Array<{
      name: string;
      projected: number;
      actual?: number;
      variance?: number;
      notes?: string;
    }>;
    total_projected: number;
    total_actual?: number;
  }>;

  total: number;
  currency: string;
}

Schema: hse

programa_seguridad.metas

interface ProgramaSeguridad_Metas {
  indicadores: Array<{
    indicador_id: string;
    meta_anual: number;
    metas_mensuales?: number[]; // 12 valores
    tolerancia_inferior?: number;
    tolerancia_superior?: number;
  }>;

  objetivos: Array<{
    descripcion: string;
    fecha_limite: string;
    responsable_id?: string;
    estado?: 'pendiente' | 'en_progreso' | 'completado';
    evidencia_url?: string;
  }>;
}

documento_stps.datos_documento

interface DocumentoSTPS_DatosDocumento {
  // Para DC-3 (Constancias)
  dc3?: {
    folio_constancia: string;
    nombre_curso: string;
    duracion_horas: number;
    fecha_capacitacion: string;
    nombre_instructor: string;
    registro_stps_instructor: string;
    agente_capacitador: string;
    registro_agente: string;
  };

  // Para ST-7 (Dictamen)
  st7?: {
    numero_dictamen: string;
    unidad_verificacion: string;
    fecha_verificacion: string;
    resultado: 'favorable' | 'condicionado' | 'negativo';
    vigencia_hasta: string;
    observaciones?: string[];
  };

  // Campos genericos
  [key: string]: any;
}

tipo_permiso_trabajo.documentos_requeridos

interface PermisoTrabajo_DocumentosRequeridos {
  documentos: Array<{
    nombre: string;
    descripcion?: string;
    obligatorio: boolean;
    formato_aceptado?: string[]; // ['pdf', 'jpg', 'png']
    plantilla_url?: string;
  }>;
}

tipo_permiso_trabajo.requisitos_personal

interface PermisoTrabajo_RequisitosPersonal {
  roles: Array<{
    rol: 'ejecutor' | 'supervisor' | 'vigia' | 'operador';
    cantidad_minima: number;
    capacitaciones_requeridas?: string[]; // IDs de capacitaciones
    certificaciones_requeridas?: string[];
  }>;

  epp_obligatorio?: string[]; // IDs de EPP del catalogo
  examen_medico_requerido?: boolean;
}

tipo_permiso_trabajo.equipos_requeridos

interface PermisoTrabajo_EquiposRequeridos {
  equipos: Array<{
    descripcion: string;
    cantidad: number;
    especificaciones?: string;
    certificacion_requerida?: boolean;
  }>;

  herramientas: Array<{
    descripcion: string;
    cantidad: number;
  }>;
}

Schema: assets

equipment.specifications

interface EquipmentSpecifications {
  // Motor
  motor?: {
    tipo?: string;
    marca?: string;
    modelo?: string;
    potencia_hp?: number;
    combustible?: string;
    capacidad_tanque_lt?: number;
  };

  // Dimensiones
  dimensiones?: {
    largo_m?: number;
    ancho_m?: number;
    alto_m?: number;
    peso_kg?: number;
  };

  // Capacidades
  capacidades?: {
    carga_max_kg?: number;
    volumen_m3?: number;
    alcance_m?: number;
  };

  // Llantas/Rodamiento
  rodamiento?: {
    tipo?: string; // 'neumatico', 'orugas', 'mixto'
    cantidad?: number;
    medida?: string;
  };

  // Custom fields
  [key: string]: any;
}

maintenance_plan.activities

interface MaintenancePlanActivities {
  activities: Array<{
    sequence: number;
    name: string;
    description?: string;
    duration_minutes?: number;
    skill_required?: string;

    checklist: Array<{
      item: string;
      type: 'check' | 'measurement' | 'photo';
      required: boolean;
      expected_value?: string | number;
      tolerance?: number;
    }>;

    safety_notes?: string[];
  }>;
}

maintenance_plan.required_parts / required_tools

interface MaintenancePlanParts {
  parts: Array<{
    part_number?: string;
    description: string;
    quantity: number;
    unit: string;
    estimated_cost?: number;
    supplier_id?: string;
  }>;
}

interface MaintenancePlanTools {
  tools: Array<{
    name: string;
    specification?: string;
    quantity: number;
  }>;
}

maintenance_order.activities_checklist

interface MaintenanceOrderChecklist {
  completed_activities: Array<{
    activity_index: number;
    completed_at: string;
    completed_by: string;

    checklist_results: Array<{
      item_index: number;
      passed: boolean;
      value?: string | number;
      photo_url?: string;
      notes?: string;
    }>;
  }>;

  overall_result: 'pass' | 'fail' | 'partial';
  technician_notes?: string;
}

maintenance_order.photos_before / photos_after / documents

interface MaintenanceOrderPhotos {
  photos: Array<{
    url: string;
    description?: string;
    taken_at?: string;
    taken_by?: string;
    location?: string;
  }>;
}

interface MaintenanceOrderDocuments {
  documents: Array<{
    url: string;
    name: string;
    type: 'report' | 'invoice' | 'warranty' | 'other';
    uploaded_at?: string;
    uploaded_by?: string;
  }>;
}

equipment_telemetry.raw_data

interface EquipmentTelemetryRawData {
  // Datos crudos del dispositivo
  device_id: string;
  timestamp: string;
  signal_strength?: number;
  battery_level?: number;

  // Payload del sensor
  payload: Record<string, any>;

  // Procesamiento
  processed?: boolean;
  processing_errors?: string[];
}

Schema: documents

document_types.metadata

interface DocumentTypeMetadata {
  // Reglas de nomenclatura
  naming_convention?: {
    prefix?: string;
    separator?: string;
    include_date?: boolean;
    date_format?: string;
    sequence_length?: number;
  };

  // Restricciones
  max_file_size_mb?: number;
  allowed_extensions?: string[];
  max_versions?: number;

  // Workflow por defecto
  default_workflow_id?: string;
  auto_start_workflow?: boolean;

  // Retencion
  retention_years?: number;
  auto_archive?: boolean;
}

documents.custom_fields

interface DocumentCustomFields {
  // Campos dinamicos segun tipo de documento
  [field_name: string]: {
    value: any;
    label?: string;
    type?: 'text' | 'number' | 'date' | 'select' | 'boolean';
  };
}

document_versions.extracted_metadata

interface DocumentExtractedMetadata {
  // Metadatos extraidos del archivo
  author?: string;
  title?: string;
  subject?: string;
  keywords?: string[];
  creation_date?: string;
  modification_date?: string;

  // Para PDFs
  pdf_version?: string;
  page_count?: number;
  is_encrypted?: boolean;
  has_forms?: boolean;

  // Para imagenes
  width?: number;
  height?: number;
  color_space?: string;
  dpi?: number;

  // GPS si disponible
  gps_coordinates?: {
    latitude: number;
    longitude: number;
  };

  // OCR
  ocr_text?: string;
  ocr_confidence?: number;
}

approval_workflows.steps

interface ApprovalWorkflowSteps {
  steps: Array<{
    step_number: number;
    name: string;
    type: 'single' | 'any' | 'all' | 'percentage';

    // Aprobadores
    approvers: Array<{
      type: 'user' | 'role' | 'department';
      id: string;
    }>;

    // Para type='percentage'
    required_percentage?: number;
    // Para type='any' o 'all'
    required_count?: number;

    // Tiempo limite
    deadline_hours?: number;
    reminder_hours?: number;
    escalation_user_id?: string;

    // Acciones permitidas
    allow_comments?: boolean;
    allow_attachments?: boolean;
    require_signature?: boolean;
  }>;
}

annotations.style

interface AnnotationStyle {
  // Para todas las anotaciones
  color?: string; // Hex color
  opacity?: number; // 0-1

  // Para lineas
  stroke_width?: number;
  stroke_style?: 'solid' | 'dashed' | 'dotted';
  arrow_start?: boolean;
  arrow_end?: boolean;

  // Para formas
  fill_color?: string;
  fill_opacity?: number;
  border_radius?: number;

  // Para texto
  font_family?: string;
  font_size?: number;
  font_weight?: 'normal' | 'bold';
  text_align?: 'left' | 'center' | 'right';
  text_decoration?: 'none' | 'underline' | 'strikethrough';

  // Para stamps
  stamp_text?: string;
  stamp_rotation?: number;
}

document_audit.action_details

interface DocumentAuditActionDetails {
  // Contexto de la accion
  ip_address?: string;
  user_agent?: string;
  session_id?: string;

  // Datos especificos por tipo de accion
  view?: {
    page_viewed?: number;
    duration_seconds?: number;
  };

  download?: {
    format?: string;
  };

  edit?: {
    fields_changed?: string[];
    previous_values?: Record<string, any>;
    new_values?: Record<string, any>;
  };

  share?: {
    shared_with?: string[];
    permissions?: string[];
    expires_at?: string;
  };

  workflow?: {
    action?: 'approve' | 'reject' | 'request_changes';
    step_number?: number;
    comments?: string;
  };
}

Schema: infonavit

derechohabiente.metadata

interface DerechohabienteMetadata {
  // Datos adicionales INFONAVIT
  puntos_infonavit?: number;
  monto_credito_aprobado?: number;
  subsidio_aprobado?: number;

  // Documentos
  identificacion_url?: string;
  constancia_situacion_fiscal_url?: string;
  comprobante_domicilio_url?: string;
  acta_nacimiento_url?: string;

  // Historial crediticio
  score_buro?: number;
  fecha_consulta_buro?: string;

  // Referencias
  referencias_personales?: Array<{
    nombre: string;
    telefono: string;
    parentesco: string;
  }>;

  // Custom fields
  [key: string]: any;
}

lote_infonavit.metadata

interface LoteInfonavitMetadata {
  // Especificaciones vivienda
  tipo_vivienda?: string;
  valor_vivienda?: number;
  ecotecnologias?: string[];

  // Avaluo
  numero_avaluo?: string;
  fecha_avaluo?: string;
  valor_avaluo?: number;
  valuador?: string;

  // ROC
  roc_asignado?: string;
  fecha_asignacion_roc?: string;

  // Custom fields
  [key: string]: any;
}

Validacion con Zod (Ejemplo)

import { z } from 'zod';

// Schema para bank_accounts.metadata
export const BankAccountMetadataSchema = z.object({
  banca_electronica: z.object({
    url: z.string().url().optional(),
    usuario_consulta: z.string().optional(),
    tiene_transferencias: z.boolean().optional(),
    limite_diario: z.number().positive().optional(),
    limite_mensual: z.number().positive().optional(),
  }).optional(),

  contrato_url: z.string().url().optional(),
  estado_cuenta_url: z.string().url().optional(),

  firmas_autorizadas: z.array(z.object({
    nombre: z.string(),
    cargo: z.string(),
    limite_firma: z.number().optional(),
  })).optional(),

  api_config: z.object({
    provider: z.string().optional(),
    api_key_ref: z.string().optional(),
    webhook_url: z.string().url().optional(),
  }).optional(),
}).passthrough(); // Allow additional fields

// Uso
const validateMetadata = (data: unknown) => {
  return BankAccountMetadataSchema.parse(data);
};

Referencias

  • DDL-EXECUTION-ORDER.md - Orden de ejecucion DDL
  • @ESTANDAR-DATABASE - Estandares de base de datos
  • TypeScript interfaces en backend

Documentacion creada: 2026-02-03 - ST-P2-003