Skip to content

Servicio de Depreciación de Activo Fijo

Orchestrator Services

Activo fijoOrchestratorDepreciación

El servicio de depreciación de activo fijo coordina el cálculo mensual de una cuota por activo, la persistencia del resultado y la contabilización posterior del detalle contable asociado. Su función es convertir el estado de cada activo en registros auditables que puedan consultarse, contabilizarse, reversarse o anularse.

Esta página describe el flujo técnico actual de forma pública. No incluye rutas locales, credenciales, nombres de bases de datos de clientes ni detalles operativos que dependan de un entorno privado.

El servicio concentra cuatro responsabilidades:

  • leer el activo, su categoría y cuotas anteriores;
  • validar el periodo, el estado del activo y el tipo de depreciación solicitado;
  • calcular y registrar una cuota mensual del periodo;
  • contabilizar la cuota generando líneas para gasto, depreciación acumulada y corrección monetaria aplicable.

La lógica no recalcula periodos cerrados. Cada cuota persistida conserva el estado usado en el cálculo para mantener trazabilidad y para impedir duplicidad de cuotas no anuladas en el mismo periodo.

  • DirectoryOrchestrator
    • DepreciacionService
    • DepreciacionRepository
    • ActivoFijoRepository
  • DirectoryMother
    • activo_fijo.categorias_activo
    • activo_fijo.activos
    • activo_fijo.depreciacion
    • activo_fijo.depreciacion_detalle
    • parametros.correccion_monetaria

Los fragmentos siguientes muestran el flujo público del servicio por etapa. Los nombres de repositorio y los campos corresponden al dominio actual; los identificadores reales se expresan como parámetros o placeholders.

  1. Recibir el contexto de servicio y el DTO de generación.

    async generar(
    ctx: ServiceContext,
    data: GenerarDepreciacionDTO,
    ): Promise<ServiceResult<DepreciacionEntry>> {
    this.validateRequired(data, [
    'activo_id',
    'periodo_mes',
    'tipo_depreciacion',
    ]);
    }
  2. Validar el tipo de depreciación y que el periodo sea el primer día del mes.

    if (!['NORMAL', 'ACELERADA'].includes(data.tipo_depreciacion)) {
    throw new ValidationError('tipo_depreciacion debe ser NORMAL o ACELERADA');
    }
    if (!/^\d{4}-\d{2}-01$/.test(data.periodo_mes)) {
    throw new ValidationError(
    'periodo_mes debe ser el primer día del mes (formato YYYY-MM-01)',
    );
    }
  3. Abrir una transacción, cargar el activo y exigir que esté en estado ACTIVO.

    return this.withTransaction(ctx, async (client, outbox) => {
    const activo = await ActivoFijoRepository.findActivoById(
    client,
    data.activo_id,
    );
    this.assertExists(activo, 'ActivoFijo', String(data.activo_id));
    if (activo.estado !== 'ACTIVO') {
    throw new ValidationError(
    'Solo se puede depreciar activos en estado ACTIVO',
    );
    }
    });
  4. Verificar que el periodo no sea anterior al inicio de depreciación del activo.

    const periodoDate = this.parsePeriodMonth(data.periodo_mes);
    const fechaBase = activo.fecha_inicio_dep ?? activo.fecha_compra;
    const inicioDepMes = this.toMonthStart(fechaBase);
    if (periodoDate < inicioDepMes) {
    throw new ValidationError(
    'El período es anterior al inicio de depreciación del activo',
    );
    }
  5. Impedir cuotas duplicadas o periodos anteriores al último periodo depreciado.

    const existe = await DepreciacionRepository.existePeriodo(
    client,
    data.activo_id,
    data.periodo_mes,
    );
    if (existe) {
    throw new ValidationError(
    `Ya existe una cuota para el período ${data.periodo_mes}`,
    );
    }
    const ultimaCuota = await DepreciacionRepository.getUltimaCuota(
    client,
    data.activo_id,
    );
  6. Resolver vida útil remanente según el tipo solicitado en el DTO.

    const mesesYaDepreciados = ultimaCuota?.periodo_numero ?? 0;
    const anosVidaUtil =
    data.tipo_depreciacion === 'ACELERADA'
    ? activo.dep_acelerada_anos ?? activo.vida_util_anos
    : activo.vida_util_anos;
    const mesesVidaUtilTotal = anosVidaUtil * 12;
    const mesesVidaUtilRemanente = mesesVidaUtilTotal - mesesYaDepreciados;
    if (mesesVidaUtilRemanente <= 0) {
    throw new ValidationError('El activo ya agotó su vida útil');
    }
  7. Obtener el factor de corrección monetaria y actualizar la base del cálculo.

    const cm = await this.getCorreccionMonetaria(
    anioPeriodo,
    mesOrigenCM,
    mesPeriodo,
    );
    const montoNetoActualizado = Number(
    (montoNetoBase * cm.factor).toFixed(2),
    );
    const depAnterior = Number((depBase * cm.factor).toFixed(2));
    const montoDepreciableRemanente = Number(
    (montoNetoActualizado - depAnterior).toFixed(2),
    );
  8. Calcular una cuota mensual para el periodo.

    const cuotaBaseMensual =
    montoDepreciableRemanente / mesesVidaUtilRemanente;
    let cuota = Number(
    Math.min(cuotaBaseMensual, montoDepreciableRemanente).toFixed(2),
    );
    if (cuota <= 0) {
    cuota = Number(montoDepreciableRemanente.toFixed(2));
    }
  9. Persistir la cuota calculada en activo_fijo.depreciacion.

    const entry = await DepreciacionRepository.create(
    client,
    {
    activo_id: data.activo_id,
    periodo_mes: data.periodo_mes,
    tipo_depreciacion: data.tipo_depreciacion,
    periodo_numero: mesesYaDepreciados + 1,
    meses_aplicados: 1,
    cuota_depreciacion: cuota,
    dep_acumulada_anterior: depAnterior,
    dep_acumulada_posterior: depPosterior,
    valor_libro: valorLibro,
    cm_factor: cm.factor,
    cm_mes_origen: mesOrigenCM,
    notas: data.notas?.trim() || null,
    },
    ctx.userId,
    );
  10. Publicar el evento de dominio y devolver la cuota generada.

    outbox.queue('depreciacion:calculada', {
    activoFijoId: String(data.activo_id),
    periodo: { anio: anioPeriodo, mes: mesPeriodo },
    monto: cuota,
    vidaUtilRestanteMeses: Math.max(mesesVidaUtilRemanente - 1, 0),
    });
    return this.success(entry);

La generación de la cuota no inserta líneas en activo_fijo.depreciacion_detalle. El detalle contable se crea cuando una cuota PENDIENTE se contabiliza.

  1. Cargar la cuota pendiente y el activo asociado.

    const dep = await DepreciacionRepository.findById(client, id);
    if (!dep || dep.estado !== 'PENDIENTE') {
    throw new ValidationError(
    'Cuota no encontrada o no está en estado PENDIENTE',
    );
    }
    const activo = await ActivoFijoRepository.findActivoById(client, dep.activo_id);
    if (!activo) {
    throw new ValidationError('Activo no encontrado');
    }
  2. Cargar la categoría para obtener las cuentas contables.

    const categoria = await ActivoFijoRepository.findCategoriaById(
    client,
    activo.categoria_id,
    );
    if (!categoria) {
    throw new ValidationError('Categoría del activo no encontrada');
    }
  3. Crear las dos líneas base de depreciación.

    const cuota = Number(dep.cuota_depreciacion);
    const detalleRows = [];
    detalleRows.push(
    {
    depreciacion_id: id,
    tipo_linea: 'CUOTA_DEP',
    cuenta_codigo: categoria.cuenta_gasto_dep_codigo,
    monto: cuota,
    es_debito: true,
    },
    {
    depreciacion_id: id,
    tipo_linea: 'CUOTA_DEP',
    cuenta_codigo: categoria.cuenta_dep_acumulada_codigo,
    monto: cuota,
    es_debito: false,
    },
    );
  4. Agregar líneas de corrección monetaria si el factor aplicado es distinto de 1.

    if (Number(dep.cm_factor) !== 1) {
    detalleRows.push(
    {
    depreciacion_id: id,
    tipo_linea: 'CM_ACTIVO',
    cuenta_codigo: categoria.cuenta_activo_codigo,
    monto: ajusteCMActivo,
    es_debito: true,
    },
    {
    depreciacion_id: id,
    tipo_linea: 'CM_ACTIVO',
    cuenta_codigo: '4201002',
    monto: ajusteCMActivo,
    es_debito: false,
    },
    );
    }
  5. Insertar el detalle y cambiar la cuota a estado CONTABILIZADO.

    await DepreciacionRepository.insertDetalle(client, detalleRows, ctx.userId);
    const entry = await DepreciacionRepository.contabilizar(
    client,
    id,
    ctx.userId,
    );
    return this.success(entry);
EntradaDescripción
ctxContexto de servicio con base tenant y usuario ejecutor.
activo_idActivo específico sobre el cual se genera la cuota.
periodo_mesMes contable en formato YYYY-MM-01.
tipo_depreciacionValor explícito NORMAL o ACELERADA.
notasTexto opcional asociado a la cuota.

Los identificadores reales deben tratarse como datos de entorno. En documentación pública, usar placeholders como $TENANT_ID, $PERIODO o $ACTIVO_ID.

La cuota periódica se expresa conceptualmente así:

Cperiodo=AactualizadoDacum. actualizadaVuˊtil remanenteC_{\text{periodo}} = \frac{A_{\text{actualizado}} - D_{\text{acum. actualizada}}}{V_{\text{útil remanente}}}

Donde:

  • A_actualizado es el monto neto base actualizado por corrección monetaria;
  • D_acum. actualizada es la depreciación acumulada base actualizada por corrección monetaria;
  • V_útil remanente corresponde a los meses de vida útil total menos los meses ya depreciados.

El servicio registra una cuota mensual única para el periodo, con meses_aplicados igual a 1.

Registra una cuota por activo y periodo. Conserva el tipo de depreciación aplicado, la cuota, el valor libro, el factor de corrección monetaria y los acumulados antes y después del cálculo.

Campo conceptualUso
periodo_mesMes contable al que pertenece la cuota.
tipo_depreciacionNORMAL o ACELERADA.
periodo_numeroNúmero secuencial de cuota del activo.
meses_aplicadosMeses depreciados por la cuota; actualmente 1.
cuota_depreciacionMonto calculado.
dep_acumulada_anteriorEstado previo actualizado.
dep_acumulada_posteriorEstado posterior actualizado.
valor_libroValor contable restante.
cm_factorFactor de corrección monetaria aplicado.
cm_mes_origenMes origen usado para consultar corrección monetaria.
estadoControla si la cuota está pendiente, contabilizada o anulada.

Registra las líneas contables generadas al contabilizar una cuota.

Tipo de líneaDebeHaber
CUOTA_DEPgasto por depreciación de la categoríadepreciación acumulada de la categoría
CM_ACTIVOcuenta de activo de la categoría4201002
CM_DEP_ACUM3401004depreciación acumulada de la categoría

Separar la cuota base de los ajustes permite reportar depreciación y corrección monetaria sin mezclar sus efectos.

El servicio no decide el tipo de depreciación desde configuración de empresa. El llamador envía tipo_depreciacion en el DTO y el servicio valida que sea NORMAL o ACELERADA.

Cuando el tipo es NORMAL, usa vida_util_anos. Cuando el tipo es ACELERADA, usa dep_acelerada_anos si existe; si no existe, usa vida_util_anos como respaldo. La cuota persistida conserva el tipo aplicado.

DepreciacionRepository.findByActivo consulta las cuotas de un activo y agrega nombre de activo y categoría.

DepreciacionRepository.findByPeriodo consulta las cuotas de un mes contable y permite filtrar por estado.

DepreciacionRepository.findById consulta una cuota específica y se usa como entrada de contabilización, reversa y validaciones de estado.

OperaciónEstado esperadoResultado
generarSin cuota no anulada para el periodoCrea una cuota nueva.
contabilizarPENDIENTEInserta detalle y cambia a CONTABILIZADO.
reversarContabilizacionCONTABILIZADOBorra detalle y vuelve a PENDIENTE.
anularDistinto de ANULADOCambia a ANULADO con motivo.
eliminarPENDIENTEElimina la cuota.
ReglaMotivo
Validar periodo_mes como YYYY-MM-01Alinea el cálculo al cierre mensual.
Impedir duplicidad por activo y periodoEvita cuotas repetidas mientras no estén anuladas.
Persistir cuota antes del detalleSepara cálculo de contabilización.
Generar detalle solo al contabilizarMantiene la cuota calculada en estado PENDIENTE hasta su aprobación.
Usar estadosDiferencia cuotas pendientes, contabilizadas y anuladas.

Esta documentación nombra servicios, schemas, tablas y conceptos de dominio porque forman parte del contrato técnico del sistema. No publica rutas locales de desarrollo, credenciales, endpoints internos protegidos ni datos reales de empresas.

Cuando se requiera un ejemplo operativo, usar placeholders:

{
"tenantId": "$TENANT_ID",
"periodo": "$PERIODO",
"activoId": "$ACTIVO_ID"
}