Ir al contenido

Partidas y capítulos (BudgetItem)

Fichero: backend/app/models/budget_item.py.

BudgetItem representa cualquier línea del presupuesto: capítulos, partidas, subtotales (Ʃ) y porcentajes (%). Se organiza como un árbol mediante parent_id.

Campo Tipo Descripción
id UUID PK
budget_id UUID FK a budgets.
parent_id UUID FK a sí mismo; raíz si es null.
codigo string(20) Código del concepto.
codigo_origen string(40) Código original (BC3).
descripcion string(500) Descripción corta.
descripcion_larga text Descripción larga.
unidad string(20) Unidad de medida.
cantidad Numeric(18,4) Cantidad presupuestada.
precio_unitario Numeric(18,4) Precio unitario.
importe Numeric(18,4) Importe calculado.
precio_venta Numeric(18,4) Precio de venta final.
beneficio_industrial_pct Numeric(5,2) BI propio.
gastos_generales_pct Numeric(5,2) GG propio.
iva_pct Numeric(5,2) IVA propio.
tipo string(20) partida, capitulo, gg, bi, etc.
tipo_concepto string(20) partida, subtotal, porcentaje.
orden int Orden dentro del padre.
porcentaje Numeric(5,2) Valor del porcentaje (%).
es_ajuste bool Indica si es un ajuste.
cantidad_inicial / importe_inicial Numeric Valores previos a cambios (liquidación).
precio_ajustado_manual bool Precio venta editado manualmente.
precio_calculado Numeric Precio calculado por el motor.
formula_cantidad / formula_precio string Fórmulas con variables.
grupo string(100) Lote / familia de compras.
supplier_id UUID Proveedor asignado.

El campo tipo_concepto distingue tres semánticas:

  • partida: concepto normal. Importe = cantidad × precio unitario, o Σ recursos.
  • subtotal: código que empieza por Ʃ. Suma importes anteriores del mismo nivel.
  • porcentaje: código con %. Importe = base × pct / 100.

La detección automática se hace en BudgetItem.detectar_tipo_concepto (backend/app/models/budget_item.py:115):

@staticmethod
def detectar_tipo_concepto(codigo: str, unidad: str | None = None) -> str:
if codigo.startswith("Ʃ"):
return "subtotal"
if "%" in codigo or unidad == "%":
return "porcentaje"
return "partida"
budget: Mapped["Budget"]
supplier: Mapped["Supplier | None"]
parent: Mapped["BudgetItem | None"]
children: Mapped[list["BudgetItem"]]
breakdowns: Mapped[list["BudgetBreakdown"]]
measurements: Mapped[list["BudgetMeasurement"]]
scenarios: Mapped[list["BudgetItemScenario"]]
  • uq_budget_item_codigo: código único dentro de un presupuesto.
  • ondelete="CASCADE": al borrar un padre se borran sus hijos.
C-01 Capítulo 1
P-01 Partida A
P-02 Partida B
Ʃ-01 Subtotal capítulo
C-02 Capítulo 2
Ʃ-02 Subtotal obra

Véase también Motor de cálculo para entender cómo se calculan los importes.