Skip to main content

Visión General

El sistema de facturación electrónica permite gestionar múltiples empresas desde una única instalación. Cada empresa puede tener sus propios usuarios, configuraciones, series de comprobantes y datos fiscales independientes.

Modelo de Datos

Empresa

El modelo Empresa representa una empresa registrada en el sistema:
App\Models\Empresa
Ubicación: app/Models/Empresa.php Campos principales:
CampoTipoDescripción
id_empresaintegerIdentificador único de la empresa (PK)
rucstringRUC de la empresa
razon_socialstringRazón social completa
comercialstringNombre comercial
direccionstringDirección fiscal
emailstringCorreo electrónico
telefonostringTeléfono principal
user_solstringUsuario SOL para SUNAT
clave_solstringClave SOL para SUNAT
logostringRuta del logo de la empresa
ubigeostringCódigo de ubigeo
distritostringDistrito
provinciastringProvincia
departamentostringDepartamento
igvdecimalTasa de IGV (por defecto 18.00)
estadostringEstado de la empresa (1=activo, 0=inactivo)
modostringModo de operación (beta/producción)

Relación con Usuarios

Cada usuario pertenece a una empresa específica a través del campo id_empresa en el modelo User:
// En app/Models/User.php
public function empresa()
{
    return $this->belongsTo(\App\Models\Empresa::class, 'id_empresa', 'id_empresa');
}

Funcionamiento Multi-Empresa

Usuarios Administradores

Los usuarios con rol_id = 1 (Administradores) tienen privilegios especiales:
  • Acceso a todas las empresas: Pueden ver y gestionar datos de cualquier empresa activa
  • Cambio de empresa: Pueden cambiar de contexto entre empresas mediante el endpoint switchEmpresa
  • Sin restricciones de permisos: Tienen acceso automático a todos los módulos

Usuarios Normales

Los usuarios con roles diferentes al administrador (rol_id != 1):
  • Empresa fija: Solo pueden acceder a los datos de su empresa asignada (id_empresa)
  • Permisos limitados: Sus acciones están controladas por el sistema de permisos
  • Sin cambio de empresa: No pueden cambiar de contexto empresarial

Autenticación y Empresas

Login

Al iniciar sesión, el sistema retorna información sobre las empresas disponibles:
{
  "success": true,
  "token": "...",
  "user": {
    "id": 1,
    "name": "Juan Pérez",
    "email": "[email protected]",
    "rol_id": 1,
    "id_empresa": 3
  },
  "empresas": [
    {
      "id_empresa": 1,
      "comercial": "Mi Empresa SAC",
      "ruc": "20612706702",
      "razon_social": "MI EMPRESA SOCIEDAD ANONIMA CERRADA",
      "logo": "logos/empresa1.png",
      "direccion": "Av. Principal 123"
    }
  ],
  "permissions": ["ventas.view", "ventas.create", ...]
}
Comportamiento por rol:
  • Admin (rol_id = 1): Retorna todas las empresas activas (estado = '1')
  • Usuario normal: Retorna solo la empresa asignada al usuario
Implementación: app/Http/Controllers/Api/AuthController.php:52-66

Cambiar de Empresa

Endpoint: POST /api/auth/switch-empresa Parámetros:
{
  "id_empresa": 2
}
Respuesta exitosa:
{
  "success": true,
  "message": "Empresa cambiada exitosamente",
  "id_empresa": 2
}
Restricciones:
  • Solo disponible para usuarios administradores (rol_id = 1)
  • La empresa debe existir y estar activa (estado = '1')
  • Usuarios normales reciben error 403
Implementación: app/Http/Controllers/Api/AuthController.php:169-204

Alcance de Datos por Empresa

Todas las tablas principales del sistema incluyen el campo id_empresa para filtrar datos por empresa:
  • Ventas (ventas.id_empresa)
  • Compras (compras.id_empresa)
  • Productos (producto.id_empresa)
  • Clientes (cliente.id_empresa)
  • Proveedores (proveedores.id_empresa)
  • Cotizaciones (cotizaciones.id_empresa)
  • Guías de Remisión (guias_remision.id_empresa)
  • Notas de Crédito/Débito (notas_credito.id_empresa, notas_debito.id_empresa)

Ejemplo de Consulta

// Obtener todas las ventas de la empresa actual del usuario
$ventas = Venta::where('id_empresa', $user->id_empresa)->get();

// Los administradores deben especificar la empresa activa
if ($user->rol_id == 1) {
    $ventas = Venta::where('id_empresa', $empresaActivaId)->get();
}

Archivos SUNAT por Empresa

Los archivos XML, CDR y certificados se almacenan organizados por RUC de empresa:
storage/app/sunat/
├── xml/
│   ├── 20612706702/          # XML por RUC
│   │   ├── 20612706702-01-F001-00000001.xml
│   │   └── 20612706702-03-B001-00000023.xml
│   └── 20000000001/
│       └── 20000000001-09-T001-00000001.xml
├── cdr/
│   ├── 20612706702/          # CDR por RUC
│   │   ├── R-20612706702-01-F001-00000001.zip
│   │   └── R-20612706702-03-B001-00000023.zip
│   └── 20000000001/
└── certificados/             # Certificados digitales
    ├── 20612706702.pem
    └── 20000000001.pem
Configuración: config/sunat.php

Configuración de Empresa

Cada empresa puede tener configuraciones independientes:

Credenciales SUNAT

  • Usuario SOL: user_sol - Usuario para webservices SUNAT
  • Clave SOL: clave_sol - Contraseña para webservices SUNAT

Modo de Operación

  • Beta: Para pruebas (usa RUC 20000000001)
  • Producción: Para emisión real de comprobantes

Personalización

  • Logo: Imagen que aparece en los comprobantes PDF
  • Propaganda: Texto adicional para pie de comprobante
  • Tipo de impresión: Configuración de formato de impresión

Endpoints de Empresas

Listar Empresas

Endpoint: GET /api/empresas Respuesta:
{
  "success": true,
  "data": [
    {
      "id_empresa": 1,
      "ruc": "20612706702",
      "razon_social": "MI EMPRESA SAC",
      "comercial": "Mi Empresa",
      "direccion": "Av. Principal 123",
      "email": "[email protected]",
      "estado": "1"
    }
  ]
}

Obtener Empresa

Endpoint: GET /api/empresas/{id}

Actualizar Empresa

Endpoint: POST /api/empresas/{id} Nota: Usa POST en lugar de PUT/PATCH para soportar FormData con carga de logo. Endpoint: DELETE /api/empresas/{id}/logo

Mejores Prácticas

En Controladores

public function index(Request $request)
{
    $user = $request->user();
    
    // Siempre filtrar por empresa del usuario
    $query = Venta::where('id_empresa', $user->id_empresa);
    
    return response()->json([
        'success' => true,
        'data' => $query->get()
    ]);
}

En Servicios

public function generarXML($venta)
{
    // Obtener la empresa de la venta
    $empresa = Empresa::findOrFail($venta->id_empresa);
    
    // Usar credenciales de la empresa
    $config = [
        'user_sol' => $empresa->user_sol,
        'clave_sol' => $empresa->clave_sol,
        'ruc' => $empresa->ruc
    ];
    
    // ...
}

Validaciones

// Al crear un registro, asignar la empresa del usuario
$venta = new Venta();
$venta->id_empresa = $request->user()->id_empresa;
$venta->save();

// Al actualizar, verificar que pertenece a la empresa del usuario
$venta = Venta::where('id', $id)
    ->where('id_empresa', $request->user()->id_empresa)
    ->firstOrFail();

Consideraciones

Seguridad

  • Aislamiento de datos: Cada empresa solo ve sus propios datos
  • Validación estricta: Siempre verificar id_empresa en consultas
  • Credenciales separadas: Cada empresa tiene sus propias credenciales SUNAT

Rendimiento

  • Índices: Todas las tablas principales tienen índice en id_empresa
  • Caché: Considerar cachear datos de empresa activa en sesión

Migración

  • Si viene de un sistema mono-empresa, asegúrese de:
    • Asignar id_empresa a todos los registros existentes
    • Crear al menos una empresa en la tabla empresas
    • Actualizar id_empresa en usuarios existentes

Build docs developers (and LLMs) love