Skip to main content
FacturaService es la capa central de negocio de LEFA que centraliza todas las operaciones transaccionales sobre facturas. Cada método público es estático y opera sobre la base de datos SQLite a través de SQLAlchemy, garantizando integridad en cada escritura mediante contextos de sesión (session_scope y session_scope_immediate). El servicio también coordina la asignación de numeración correlativa (delegada en NumeracionService), la creación de registros VeriFactu encadenados y el guardado de las preferencias del último cliente utilizado.

DTOs

Los objetos de transferencia de datos se definen como dataclass de Python y se utilizan para comunicar la capa de interfaz de usuario con FacturaService sin exponer directamente los modelos ORM.

LineaFacturaDTO

Representa una línea individual de una factura en tránsito desde la UI.
str
required
Texto descriptivo del concepto o servicio facturado.
float
required
Número de unidades del concepto.
float
required
Precio por unidad en euros (sin impuestos).

TotalesFactura

Resultado inmutable del cálculo de importes, devuelto por calcular_totales.
float
Suma de cantidad × precio_unitario para todas las líneas, redondeada a 2 decimales.
float
Importe de IVA calculado sobre el subtotal.
float
Retención de IRPF calculada sobre el subtotal.
float
Importe neto: subtotal + iva − irpf.

ResumenHoy

Tres cifras para el panel de inicio de LEFA, calculadas en tiempo real sin analítica adicional.
int
Número de facturas con fecha_emision igual a hoy y estado distinto de BORRADOR.
int
Número de facturas en estado EMITIDA (enviadas pero aún no cobradas).
float
Suma de los totales de todas las facturas en estado EMITIDA.

Métodos

calcular_totales

Cálculo puro en memoria, sin acceso a base de datos, diseñado para actualizarse en tiempo real mientras el usuario edita la factura en la interfaz. Aplica la fórmula subtotal × (porcentaje / 100) para IVA e IRPF y redondea cada resultado a dos decimales.
list[LineaFacturaDTO]
required
Lista de líneas de factura con descripción, cantidad y precio unitario.
float
required
Porcentaje de IVA a aplicar (p. ej. 21.0).
float
required
Porcentaje de retención de IRPF a aplicar (p. ej. 15.0).
Retorna: TotalesFactura con los cuatro importes calculados.

listar_todas

Devuelve todas las facturas almacenadas en la base de datos, incluyendo borradores, ordenadas por id descendente. Las relaciones cliente y lineas se cargan con joinedload para evitar consultas N+1 en la UI. Retorna: Lista de objetos Factura desvinculados de la sesión (expunged).

resumen_hoy

Calcula en tiempo real las tres métricas del panel de inicio: facturas emitidas hoy, número de facturas pendientes de cobro e importe total pendiente. Ejecuta dos consultas agregadas sobre SQLite. Retorna: ResumenHoy con los tres campos poblados.

listar_por_cliente

Devuelve las facturas no borrador de un cliente concreto, ordenadas por fecha de emisión descendente y, a igualdad de fecha, por id descendente.
int
required
Identificador del cliente en la base de datos.
Retorna: Lista de Factura en estado EMITIDA o COBRADA.

total_facturado_cliente

Suma los totales de todas las facturas emitidas y cobradas de un cliente usando listar_por_cliente internamente.
int
required
Identificador del cliente.
Retorna: float con el total acumulado en euros, redondeado a 2 decimales.

obtener_por_id

Carga una factura por su clave primaria con sus relaciones cliente, lineas y factura_rectificada precargadas.
int
required
Identificador de la factura.
Retorna: Factura desvinculada de la sesión, o None si no existe.

guardar_borrador

Crea una factura nueva en estado BORRADOR o actualiza un borrador existente. En ningún caso se asigna número oficial (numero_factura permanece NULL hasta la emisión). Si se proporciona factura_rectificada_id, la serie se fuerza automáticamente a "RECT".
int
required
Cliente al que se emitirá la factura.
list[LineaFacturaDTO]
required
Líneas de la factura. Debe contener al menos un elemento.
float
required
Porcentaje de IVA (p. ej. 21.0).
float
required
Porcentaje de retención de IRPF (p. ej. 15.0).
int | None
Si se indica, actualiza el borrador existente con ese ID en lugar de crear uno nuevo.
str | None
Prefijo de serie (p. ej. "FACT", "CLI"). Si es None, se lee de las preferencias del emisor.
date | None
Fecha límite de pago. Si es None, se calculará automáticamente en la emisión según dias_vencimiento de las preferencias.
int | None
ID de la factura original que rectifica este borrador. Activa la serie "RECT" automáticamente.
int | None
ID del presupuesto del que proviene esta factura (trazabilidad bidireccional).
Retorna: Factura en estado BORRADOR desvinculada de la sesión. Lanza ValueError si:
  • lineas está vacía.
  • Se proporciona factura_id pero la factura no existe.
  • La factura encontrada con factura_id no está en estado BORRADOR.

emitir_factura

Transacción atómica de emisión usando BEGIN IMMEDIATE (SQLite). El flujo interno es:
  1. Valida que la factura sea un borrador con al menos una línea.
  2. Obtiene el siguiente correlativo con NumeracionService.siguiente_secuencia.
  3. Formatea el número oficial (p. ej. FACT-2026-0044).
  4. Fija fecha_emision a hoy y calcula fecha_vencimiento si no fue establecida.
  5. Genera el registro VeriFactu encadenado (hash SHA-256) dentro de la misma transacción.
  6. Hace commit y, tras él, persiste el JSON VeriFactu en disco.
int
required
ID de la factura borrador a emitir.
Retorna: Factura en estado EMITIDA con número oficial asignado. Lanza ValueError si:
  • La factura no existe.
  • No está en estado BORRADOR.
  • No tiene líneas.

marcar_enviada

Registra el envío exitoso de una factura por correo electrónico: activa el flag enviada, guarda la fecha actual en fecha_envio y almacena la dirección del destinatario.
int
required
ID de la factura enviada.
str
required
Dirección de correo electrónico del destinatario (se recortan espacios en blanco).
Retorna: Factura actualizada. Lanza ValueError si: la factura no existe.

duplicar_factura

Crea un borrador nuevo copiando el cliente, las líneas, la serie y los porcentajes de impuestos de una factura ya emitida o cobrada. El nuevo borrador no tiene número ni historial de envío; la fecha de vencimiento se recalcula a partir de las preferencias actuales.
int
required
ID de la factura a duplicar. Debe estar en estado EMITIDA o COBRADA.
Retorna: Nuevo Factura en estado BORRADOR. Lanza ValueError si:
  • La factura no existe.
  • La factura está en estado BORRADOR (solo se duplican facturas ya emitidas).

rectificar_factura

Crea un borrador rectificativo (serie RECT) vinculado a la factura original mediante factura_rectificada_id. Copia las líneas tal cual; el usuario puede modificar importes (p. ej. negativos para devoluciones) antes de emitir.
int
required
ID de la factura a rectificar. Debe estar en estado EMITIDA o COBRADA y no ser ella misma rectificativa.
Retorna: Nuevo Factura en estado BORRADOR con serie RECT. Lanza ValueError si:
  • La factura no existe.
  • No está en estado EMITIDA ni COBRADA.
  • La factura ya es rectificativa (es_rectificativa == True).

marcar_cobrada

Cambia el estado de una factura de EMITIDA a COBRADA. No admite transiciones desde otros estados.
int
required
ID de la factura a marcar como cobrada.
Retorna: Factura en estado COBRADA. Lanza ValueError si:
  • La factura no existe.
  • No está en estado EMITIDA.

Ejemplo de uso

El siguiente fragmento muestra el flujo completo desde la creación de un borrador hasta su cobro:
emitir_factura utiliza BEGIN IMMEDIATE para garantizar que dos emisiones concurrentes nunca compartan el mismo número correlativo, incluso en entornos con varios procesos que accedan a la misma base de datos SQLite.