erp-core/docs/03-requerimientos/RF-catalogs/RF-CATALOG-003.md

7.2 KiB

RF-CATALOG-003: Monedas y Tasas de Cambio

Identificacion

Campo Valor
ID RF-CATALOG-003
Modulo MGN-005 Catalogs
Titulo Monedas y Tasas de Cambio
Prioridad Alta
Estado Draft
Fecha 2025-12-05

Descripcion

El sistema debe proporcionar un catalogo de monedas (global) y tasas de cambio (por tenant) para soportar operaciones multi-moneda.

Referencia Odoo:

  • res.currency (odoo/addons/base/models/res_currency.py)
  • res.currency.rate (mismo archivo)

Arquitectura de Monedas

Catalogo Global vs Por Tenant

Tabla Scope Descripcion
core_catalogs.currencies Global Lista ISO 4217 (sin tenant_id)
core_catalogs.currency_rates Por Tenant Tasas historicas (con tenant_id)

Rationale:

  • Las monedas son estandares ISO, compartidas globalmente
  • Las tasas de cambio varian por tenant (cada uno puede usar diferentes fuentes)

Requisitos Funcionales

RF-CATALOG-003.1: Catalogo de Monedas (Global)

Campo Tipo Descripcion Ejemplo
id UUID Identificador unico auto
name char(3) Codigo ISO 4217 "MXN"
full_name string Nombre completo "Mexican Peso"
symbol string Simbolo "$"
iso_numeric int Codigo numerico ISO 484
decimal_places int Decimales 2
rounding decimal Factor de redondeo 0.01
position enum Posicion del simbolo before/after
is_active boolean Disponible para uso true

Datos:

  • ~180 monedas segun ISO 4217
  • Precargadas en migracion inicial

RF-CATALOG-003.2: Tasas de Cambio (Por Tenant)

Cada tenant mantiene su historial de tasas:

Campo Tipo Descripcion
id UUID Identificador unico
tenant_id FK Tenant propietario
currency_id FK Moneda (global)
rate decimal(20,10) Tasa vs moneda base
inverse_rate computed 1/rate
name date Fecha de la tasa
created_at timestamp Fecha creacion
created_by FK Usuario que registro

Reglas:

  • La moneda base del tenant tiene tasa = 1.0
  • Tasas se expresan como: 1 BASE = X MONEDA
  • Se mantiene historial completo

RF-CATALOG-003.3: Moneda Base del Tenant

Cada tenant tiene una moneda base definida en tenant_settings:

{
  "regional": {
    "defaultCurrency": "MXN",
    "currencyRateDate": "current"  // current | invoice_date
  }
}

Reglas:

  • La moneda base NO puede cambiarse una vez hay transacciones
  • Todas las tasas se calculan contra la moneda base

RF-CATALOG-003.4: Conversion de Monedas

El sistema debe proporcionar funciones de conversion:

// Convertir monto de una moneda a otra
convertAmount(
  amount: number,
  fromCurrency: UUID,
  toCurrency: UUID,
  date?: Date  // Default: hoy
): number

// Obtener tasa de cambio
getRate(
  fromCurrency: UUID,
  toCurrency: UUID,
  date?: Date
): number

Logica de conversion:

amount_to = amount_from * (rate_to / rate_from)

RF-CATALOG-003.5: Redondeo de Monedas

Cada moneda define su precision:

// Redondear segun precision de moneda
roundCurrency(amount: number, currency: Currency): number

// Ejemplo MXN (rounding = 0.01):
roundCurrency(123.456, MXN) // => 123.46

RF-CATALOG-003.6: Actualizacion Automatica de Tasas

El sistema puede integrarse con APIs de tasas:

Proveedor Endpoint Frecuencia
Open Exchange Rates api.openexchangerates.org Diaria
Fixer.io data.fixer.io Diaria
Banco de Mexico banxico.org.mx Diaria
ECB ecb.europa.eu Diaria

Configuracion por tenant:

{
  "regional": {
    "currencyRateProvider": "banxico",
    "currencyRateAutoUpdate": true
  }
}

Operaciones

Listar Monedas (Global)

GET /api/v1/catalogs/currencies?active=true

Response:
{
  "data": [
    {
      "id": "uuid",
      "name": "MXN",
      "fullName": "Mexican Peso",
      "symbol": "$",
      "decimalPlaces": 2,
      "position": "before"
    },
    ...
  ]
}

Obtener Tasas de un Tenant

GET /api/v1/currencies/rates?currency=MXN&from=2025-01-01&to=2025-01-31

Response:
{
  "data": [
    { "date": "2025-01-01", "rate": 17.2345 },
    { "date": "2025-01-02", "rate": 17.3021 },
    ...
  ]
}

Registrar Tasa Manual

POST /api/v1/currencies/rates
{
  "currencyId": "uuid-usd",
  "rate": 17.5432,
  "date": "2025-12-05"
}

Convertir Monto

GET /api/v1/currencies/convert?from=USD&to=MXN&amount=100&date=2025-12-05

Response:
{
  "from": { "currency": "USD", "amount": 100 },
  "to": { "currency": "MXN", "amount": 1754.32 },
  "rate": 17.5432,
  "date": "2025-12-05"
}

Reglas de Negocio

ID Regla Severidad
BR-001 Moneda base tasa = 1.0 siempre Error
BR-002 Tasa debe ser > 0 Error
BR-003 Una tasa por moneda por fecha Error
BR-004 No eliminar moneda con transacciones Error
BR-005 Moneda inactiva no seleccionable Warning

Casos de Prueba

ID Escenario Resultado Esperado
TC-001 Listar monedas activas ~30 monedas comunes
TC-002 Registrar tasa USD Tasa guardada
TC-003 Registrar tasa duplicada (misma fecha) Error
TC-004 Convertir 100 USD a MXN Monto convertido
TC-005 Convertir sin tasa del dia Usar ultima tasa
TC-006 Redondear MXN (2 decimales) Precision correcta
TC-007 Actualizar tasas automaticas Tasas actualizadas

Criterios de Aceptacion

  • ~180 monedas ISO 4217 cargadas
  • Registro de tasas por tenant
  • Conversion bidireccional
  • Redondeo segun moneda
  • Historial de tasas
  • Integracion con proveedor externo (opcional)

Notas Tecnicas

Precision de Tasas

-- Alta precision para tasas
rate DECIMAL(20, 10) NOT NULL

Rationale: Algunas monedas tienen tasas muy pequenas (ej: 1 BTC = 0.000012 USD inverso)

Funcion de Conversion SQL

CREATE OR REPLACE FUNCTION core_catalogs.convert_currency(
    p_amount DECIMAL,
    p_from_currency UUID,
    p_to_currency UUID,
    p_tenant_id UUID,
    p_date DATE DEFAULT CURRENT_DATE
) RETURNS DECIMAL AS $$
DECLARE
    v_from_rate DECIMAL;
    v_to_rate DECIMAL;
BEGIN
    -- Obtener tasa de origen
    SELECT rate INTO v_from_rate
    FROM core_catalogs.currency_rates
    WHERE tenant_id = p_tenant_id
      AND currency_id = p_from_currency
      AND name <= p_date
    ORDER BY name DESC LIMIT 1;

    -- Obtener tasa de destino
    SELECT rate INTO v_to_rate
    FROM core_catalogs.currency_rates
    WHERE tenant_id = p_tenant_id
      AND currency_id = p_to_currency
      AND name <= p_date
    ORDER BY name DESC LIMIT 1;

    -- Convertir
    RETURN p_amount * (COALESCE(v_to_rate, 1) / COALESCE(v_from_rate, 1));
END;
$$ LANGUAGE plpgsql STABLE;

Cache de Tasas

  • Tasas del dia: Cache 1 hora
  • Tasas historicas: Cache 24 horas
  • Invalidacion: Al registrar nueva tasa

Historial

Version Fecha Autor Cambios
1.0 2025-12-05 System Creacion inicial