7.2 KiB
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 |