Vista general
El motor unificado de precios dbo.ArticuloPrecios reemplaza los dos modelos legacy (V1 con DBVenta1..10 en Articulos, y V2 con la tabla ArticuloPrecio) por una sola tabla con:
Tabla y campos
CREATE TABLE dbo.ArticuloPrecios ( Id INT IDENTITY(1,1) NOT NULL UNIQUE, ArticuloCodigo VARCHAR(15) NOT NULL, NumeroPrecio INT NOT NULL, -- 1..n dinámico MonedaCodigo VARCHAR(3) NOT NULL DEFAULT 'CRC', -- por precio Costo DECIMAL(18,4) NOT NULL DEFAULT 0, -- en MonedaCodigo Margen DECIMAL(9,4) NOT NULL DEFAULT 0, -- % ANCLA del usuario Precio DECIMAL(19,5) NOT NULL DEFAULT 0, -- sin IVA, 5 decimales (#2465) MargenSobre VARCHAR(5) NOT NULL DEFAULT 'COSTO', -- 'COSTO'|'VENTA' PrecioAuto BIT NOT NULL DEFAULT 1, -- 1=auto, 0=manual DescuentoFijo DECIMAL(9,4) NOT NULL DEFAULT 0, DescuentoMaximo DECIMAL(9,4) NOT NULL DEFAULT 0, UtilidadMinima DECIMAL(9,4) NOT NULL DEFAULT 0, RedondeoPrecio DECIMAL(10,4) NOT NULL DEFAULT 0.01, -- step redondeo Neto PRIMARY KEY CLUSTERED (ArticuloCodigo, NumeroPrecio) );
Conceptos clave
1. Margen es ANCLA del usuario
Regla de oro: el campo Margen guardado en BD es la "intención" del usuario y solo cambia cuando el usuario lo edita explícitamente. Cualquier otra operación — cambio de Costo, edición de Precio, redondeo, conversión de moneda, etc. — NO toca Margen.
2. Costo en moneda del precio
Articulos.DBCosto vive en la moneda del artículo (Articulos.Cod_TipoMoneda). En cada fila ArticuloPrecios.Costo se guarda el costo convertido a la moneda de esa fila, usando TipoCambioVenta de dbo.TipoCambio.
3. RedondeoPrecio per-fila
Cada fila guarda su propio RedondeoPrecio (step) — distinto para CRC (típico × 100) que para USD (típico 0.01).
4. Tipo de precio: A, M, E o C
Cada nivel tiene su tipo, guardado como letra en PrecioAuto (los niveles viejos pueden tener todavía 1/0, que valen A/M). Decide qué pasa con el precio cuando cambia el costo (ver Tipo de precio):
| Tipo | Columnas | Precio en pantalla | Al cambiar Costo |
|---|---|---|---|
| A Automático | PrecioAuto='A' (o '1') | readonly | El Precio se recalcula desde el Margen (sube o baja) |
| M Manual | PrecioAuto='M' (o '0') | editable | El Precio no se toca; lo que se mueve es el margen real |
| E Especial | PrecioAuto='E' | editable | Costo sube → el Precio sube · costo baja → el Precio no baja |
| C Compuesto | PrecioAuto='C' | editable (recalcula el Margen) | Costo baja → el Precio no baja · costo sube → el Precio solo sube si el margen real cae bajo el Margen, y justo hasta él |
Cambio de Costo — flujo completo
Cuando el usuario edita uno de los 4 costos (Propio / Promedio / Último / Bruto) en el formulario y presiona Enter o el botón ✓:
- JS
guardarCostoArticulo()armaFormDatacon los 4 costos + el origen seleccionado yPOSTaapi_guardarCostoArticulo.php. - Endpoint Fase 1 (granular): compara cada uno de los 5 campos (4 costos + origen) con la BD;
UPDATEsolo los que cambiaron.DBCosto(Propio) solo se actualiza siorigen = 0. - Endpoint deriva
dbCostoFinalsegún el origen activo:0 → costoPropio1 → costoPromedio2 → costoUltimo3 → costoBruto
- Endpoint Fase 2 (propagación): si
DBCostoefectivamente cambió, llama a la funciónarticulo_precios_actualizar_costo($pdo, $cod, $dbCostoFinal, $moneda). - La función itera todas las filas de
ArticuloPreciosdel artículo y aplica las 3 fases internas (ver siguiente sección).
Fases internas de articulo_precios_actualizar_costo
UPDATE Articulos SET DBCosto, Cod_TipoMoneda
↓ por cada fila ArticuloPrecios:
FASE 1 — Convertir Costo a moneda de la fila
Si MonedaCodigo == moneda del artículo: Costo = DBCosto
Si difiere: convertir vía TipoCambioVenta (TC más reciente <= hoy)
Si TC no disponible: aviso, salta esta fila
FASE 2 — Precio según el TIPO del nivel (Margen NO se toca)
A → Precio = articulo_precios_calcular_precio(Costo, Margen, MargenSobre)
M → Precio queda igual (se mueve el margen real)
E → costo bajó: Precio queda igual
costo subió: Precio = max(Precio + (CostoNuevo − CostoAntes),
calcular_precio(CostoNuevo, Margen, MargenSobre))
FASE 3 — Redondear Neto al step de la fila
Neto = Precio × (1 + IVA / 100)
NetoRound = ceil(Neto / RedondeoPrecio) × RedondeoPrecio
Si NetoRound != Neto:
exacto = NetoRound / (1 + IVA / 100)
Precio = floor(exacto × 10000) / 10000 // truncar (no round)
Cambio de Precio — flujo completo
El usuario tiene varias formas equivalentes de modificar el precio de una fila desde el editor de artículos:
| Acción | Trigger | Backend recibe |
|---|---|---|
| Edita Margen | blur / Enter | { Margen: X } |
| Edita Precio (en Manual) | blur / Enter | { Precio: X } |
| Edita Ganancia (en Manual) | blur / Enter | { Precio: Costo + Gan } |
| Edita Precio+IVA (Manual) | blur / Enter | { Precio: Neto / (1+IVA) } |
| Toggle Auto/Manual | change | { PrecioAuto: 0|1 } |
| Doble-clic en Mon | change select | { MonedaCodigo: 'X' } |
| Cambio de Redondeo | change select | { RedondeoPrecio: X } |
| Click en # abre modal MAP-001 | botón Guardar | todos los campos del modal |
Todas terminan llamando a guardarPrecioArticulo(numero, cambios) en JS, que hace POST a api_guardarPrecioArticulo.php y este invoca la función articulo_precios_actualizar_precio.
Fases internas de articulo_precios_actualizar_precio
SELECT fila actual de ArticuloPrecios
↓
Aplicar $cambios sobre copia
↓
Validaciones (descuentos 0..100, fijo<=max, util>=0, margen>=0)
↓
FASE 1 — Convertir Costo según la moneda
Si MonedaCodigo cambió:
Lee Articulos.DBCosto + Cod_TipoMoneda + Param 249 (nacional)
Costo nuevo = convertir_costo(DBCosto, monedaArt → monedaNueva)
Si TC no disponible: throw error
Si no cambió: Costo se mantiene
FASE 2 — Recalcular según QUÉ editó el usuario
Si user mandó 'margen': Precio = f(Costo, Margen, MargenSobre)
Si user mandó 'precio': NO toca Margen (queda como ancla)
Validación: precio < costo → throw
Si user no mandó ninguno: no recalcula nada
FASE 3 — Redondear Neto al step (RedondeoPrecio)
Igual que en actualizar_costo (ceil + floor con truncate)
Margen NUNCA se toca acá
↓
UPDATE ArticuloPrecios + INSERT BitacoraSistema
Redondeo del Neto per-fila
El RedondeoPrecio de cada fila define el "paso" al cual se redondea el Precio + IVA (no el Precio sin IVA). El signo del valor indica el sentido:
- Positivo (5, 10, 100, 1000…) → redondea el Neto hacia ARRIBA (ceil) al múltiplo.
- Negativo (-5, -10, -100, -1000…) → redondea el Neto hacia ABAJO (floor) al múltiplo de
abs(step). - 0 → «Sin redondeo» (primera opción de la lista, #2465): no se aplica ningún redondeo; el precio sin IVA queda tal como se escribió o calculó, con hasta 5 decimales, y el neto es el que sale de él.
- 0.01 (positivo) → «↑ 2 decimales»: el Neto sube al céntimo; el Precio sin IVA se ajusta para que el neto quede en céntimos exactos (caso default de un nivel nuevo).
| Step | Etiqueta UI | Comportamiento | Caso típico |
|---|---|---|---|
| 0 | Sin redondeo | No redondea nada: el precio queda como está (hasta 5 decimales) | Precio sin IVA exacto (#2465) |
| 0.01 / -0.01 | ↑ 2 dec / ↓ 2 dec | Neto al céntimo hacia arriba / hacia abajo | 0.01 = default de un nivel nuevo |
| 0.10 / -0.10 | ↑ 1 dec / ↓ 1 dec | Al 0.10 más cercano hacia arriba / hacia abajo | USD "precio bonito" |
| 1 / -1 | ↑ Ent / ↓ Ent | Al entero hacia arriba / hacia abajo | USD enteros / CRC truncado |
| 5 / -5 | ↑ × 5 / ↓ × 5 | Múltiplos de 5 (arriba / abajo) | CRC menor |
| 10 / -10 | ↑ × 10 / ↓ × 10 | Múltiplos de 10 (arriba / abajo) | CRC retail |
| 25 / -25 | ↑ × 25 / ↓ × 25 | Múltiplos de 25 (arriba / abajo) | CRC retail |
| 50 / -50 | ↑ × 50 / ↓ × 50 | Múltiplos de 50 (arriba / abajo) | CRC retail |
| 100 / -100 | ↑ × 100 / ↓ × 100 | Múltiplos de 100 (arriba / abajo) | CRC mayoreo |
| 500 / -500 | ↑ × 500 / ↓ × 500 | Múltiplos de 500 (arriba / abajo) | CRC mayoreo |
| 1000 / -1000 | ↑ × 1000 / ↓ × 1000 | Múltiplos de 1000 (arriba / abajo) | CRC mayoreo grande |
Editor de artículo — UI
Layout principal
┌─ Costos y Precios (col-middle) ─────────────────────────────┐
│ [Propio ●][Promedio][Último][Bruto] [✓ Guardar] │ ← edita costo
│ 327.33 320.00 327.33 330.00 │
├─────────────────────────────────────────────────────────────┤
│ 📊 Tabla ArticuloPrecios 11 filas ☐MON ☐COSTO [+ AGR] │
├─────────────────────────────────────────────────────────────┤
│ # [Mon] [Costo] PRECIO MARGEN GAN. PRECIO+IVA│
│ 1 411.5044 [25.72][25.00] 84.17 465.00 │
│ ─ │
│ ▓ verde Auto │
│ 2 429.2035 [31.12][30.00] 101.87 485.00 │
│ ─ │
│ ▓ ámbar Manual │
└─────────────────────────────────────────────────────────────┘
Columnas visibles por default
#, Precio, Margen (con Margen Real al lado), Ganancia, Precio+IVA. Adicionalmente con toggle: Mon y Costo.
Columnas siempre ocultas (en modal)
Sobre, Desc.Fijo, Desc.Max, Util.Min. Estos campos se editan vía el modal MAP-001 (lápiz de la fila o doble clic).
Tipo: la columna Tipo tiene un combo por nivel con M-Manual, A-Automático, E-Especial y C-Compuesto; el color dice cuál rige (verde A, gris M, naranja E, celeste C). Al escoger otro se guarda de una vez. Ver Tipo de precio.
Inline editing
- Margen: editable siempre (ancla del usuario)
- Precio: editable en Manual, Especial y Compuesto (readonly solo en Auto). En Compuesto, escribir el precio recalcula el margen del dueño desde ese precio (ya redondeado) y desde ahí rige la subida por costo.
- Ganancia: editable solo en Manual (al editar, recalcula Precio = Costo + Ganancia)
- Precio+IVA: editable solo en Manual (al editar, recalcula Precio = Neto / (1+IVA))
- Mon: doble-clic abre select inline
Indicador visual de modo
Debajo del badge # aparece una barra de 2px:
- verde = AUTO
- ámbar = MANUAL
Alertas visuales
- Margen = 0 → input rojo pulsante
- Margen negativo (no permitido) → bloqueado en backend
- Precio < Costo → input rojo + tooltip
- Precio = 0 en Manual → input rojo
Modal MAP-001 — Edición avanzada
Click en el badge # de cualquier fila abre el modal MAP-001:
┌──────────────────────────────────────────┐ │ ✏ Editar Precio P3 MAP-001 [×] │ ├──────────────────────────────────────────┤ │ ⚙ CONFIGURACIÓN │ │ Moneda: [CRC ▼] Modo: [A = Auto ▼] │ │ Margen sobre: [COSTO] Redondeo: [×100]│ │ │ │ 🛡 DESCUENTOS Y UTILIDAD MÍNIMA │ │ Desc.Fijo Desc.Máximo Util.Mínima │ │ Desc. x caja (solo si Uds x Med > 1) │ │ 📦 Neto caja de 6: 7.936,20 │ │ │ │ ℹ ESTADO ACTUAL │ │ Costo: 326.55 CRC | Precio: ... | Margen │ ├──────────────────────────────────────────┤ │ [🗑 Eliminar] [Cancelar] [✓ Guardar] │ └──────────────────────────────────────────┘
Acciones del modal:
- Eliminar — borra la fila P_n (con confirmación)
- Cancelar / Esc / click overlay — cierra sin guardar
- Guardar — envía todos los campos vía
api_guardarPrecioArticulo(ejecuta Fases 1+2+3)
El botón + Agregar en el header de la tabla crea un nuevo P_(max+1) con defaults (Margen=0, Precio=0, Moneda del artículo, etc.).
Tipo de precio: A, M, E (Especial) y C (Compuesto)
Cada nivel de precio (P1, P2…) tiene un tipo, que decide qué pasa con el precio cuando cambia el costo, ya sea porque se aplicó una compra, porque se cambió el costo en la ficha o desde Cambios globales:
- A = Automático: el costo sube o baja y el precio lo sigue; el margen no se toca.
- M = Manual: el precio no se toca; lo que cambia es el margen real.
- E = Especial: si el costo sube, el precio sube solo; si el costo baja, el precio no baja: la empresa se queda con la diferencia y solo cambia el margen real. El margen que puso el dueño no cambia.
- C = Compuesto (Especial/Auto): si el costo baja, el precio no baja (como en E). Si el costo sube, el precio se queda mientras el margen real siga igual o por encima del margen del dueño; solo cuando el margen real va a quedar por debajo, el precio sube justo lo necesario para volver al margen del dueño. Es más conservador que el E: no mueve el precio al público por subidas pequeñas que el margen real puede absorber.
¿Cuánto sube el precio en el tipo E?
Sube lo mismo que subió el costo base (DBCosto, que sale del origen marcado: Propio, Promedio, Último o Bruto), pero nunca queda por debajo del margen del dueño. Se compara el costo base de antes con el nuevo; si se cambia el origen y el costo base sube, también cuenta.
| Paso | Costo | Precio | Por qué |
|---|---|---|---|
| Inicio (margen del dueño 50 %) | 100 | 150 | |
| Compra más barata | 80 | 150 | el costo bajó: el precio se queda |
| Vuelve a subir | 90 | 160 | subió 10 → el precio sube 10 |
El piso del margen del dueño: si el precio estaba justo en su margen (costo 100, precio 150) y el costo sube a 120, sumar la diferencia daría 170, que es menos del 50 % sobre 120. En ese caso se usa el margen del dueño: 180. Siempre se toma el mayor de los dos.
Si el nivel tiene redondeo, el precio nuevo se redondea igual que siempre.
¿Cuándo y cuánto sube el precio en el tipo C?
Se usa el mismo costo base que en el E (DBCosto del origen marcado, comparando el de antes con el nuevo). Ejemplo con margen del dueño 40 % sobre costo:
| Paso | Costo | Precio | Margen real | Por qué |
|---|---|---|---|---|
| Inicio | 2.000 | 3.000 | 50 % | |
| Compra más barata | 1.600 | 3.000 | 87,5 % | el costo bajó: el precio se queda |
| Sube un poco | 2.100 | 3.000 | 42,9 % | sigue por encima del 40 %: el precio no se mueve |
| Sube mucho | 2.500 | 3.500 | 40 % | con 3.000 quedaría en 20 %: sube justo hasta el 40 % |
| Vuelve a bajar | 2.300 | 3.500 | 52,2 % | el costo bajó: el precio se queda |
Diferencia con el E: en el paso «sube un poco» el E habría subido el precio lo mismo que el costo; el C lo deja quieto porque el margen real todavía alcanza.
Si se reescribe el margen del dueño en un nivel C, el precio se recalcula desde el costo actual con el margen nuevo, igual que en el A: es como empezar de cero con el nuevo límite de margen. Si sube el margen, el precio sube; si lo baja, el precio también baja. Ejemplo: costo 2.600, precio 3.640 y usted escribe 30 % → el precio queda en 3.380 (2.600 × 1,30). Si no toca el margen, nada cambia.
Si se escribe el precio a mano en un nivel C (precio sin IVA o neto), el margen del dueño pasa a ser el de ese precio (calculado sobre el precio ya redondeado) y la dinámica del C sigue igual con ese margen nuevo. Ejemplo: costo 2.000, precio 3.000, margen 40 %; usted escribe 3.200 → el margen queda en 60 %. Si después el costo sube a 2.100, con 3.200 el margen real quedaría en 52,4 % (bajo el 60 %), así que el precio sube a 3.360. Si el costo baja, el precio se queda. Un precio por debajo del costo se rechaza. Los precios que ya existían no se tocaron con este cambio: siguen igual hasta que un cambio de costo, un margen nuevo o un precio escrito los mueva.
Si el nivel tiene redondeo, el precio nuevo se redondea igual que siempre.
Si se revierte la compra
Cuando se revierte una compra que había subido el precio de un nivel E o C, ese precio vuelve al que tenía antes de la compra (esa compra no cuenta). Si después de la compra alguien cambió el precio —a mano u otra compra—, no se toca y la reversa lo avisa en su detalle.
Cómo se escoge
- IN-002 (ficha del artículo): combo de la columna Tipo en la lista de precios. En el celular, el mismo combo en la cabecera de cada nivel.
- MAP-001 / agregar nivel: selector Modo con A, M, E y C.
- Cambios globales (MX-010): tarjetas Modo de venta (todos los niveles) y Modo de venta (Auto / Manual / Especial / Compuesto) (un nivel).
- Nuevo artículo desde la compra (MCO-003): cuadro Tipo de precio por nivel, una casilla por nivel que se recuerda (ver Compras).
- Análisis de precios de la compra (CO-020): se ve como Especial o Compuesto y se cambia en su modal. En un nivel C el precio también se puede escribir ahí: recalcula el margen del dueño.
En el tipo E se puede escribir el precio o el margen a mano igual que en Manual. En el tipo C, como en el A, solo se escribe el margen.
Filtro, Excel y sucursales
- En IN-002, Más filtros → Modo precio tiene las opciones Especial y Compuesto (sobre el nivel elegido en Precio).
- El Excel de IN-002 trae la columna Tipo precio (Pn) con Auto, Manual, Especial o Compuesto.
- El tipo viaja a las sucursales al guardar, desde Cambios globales y en la sincronización masiva (CF-SART). Una sucursal que todavía no tenga esta actualización recibe el nivel E o C como Manual (su precio no baja).
- Si la empresa todavía no tiene la actualización, E-Especial y C-Compuesto aparecen como «no disponible» y no se pueden escoger.
Descuento por caja
Es un porcentaje por nivel de precio que la factura aplica sola a las cajas enteras de un artículo que se vende por empaque. Típico de supermercados y farmacias: la cerveza por unidad a ₡1.300, y si lleva el six-pack completo, 10 % menos.
- Solo aparece en el modal MAP-001 si el artículo tiene Unidades x medida mayor que 1. Con 1, la opción no se ofrece.
- Cada nivel tiene el suyo: P1 puede tener 10 % y P2 0 % (en P2 no hay descuento por caja).
- Debajo de los descuentos se ve el Neto caja: lo que cuesta la caja completa, con IVA, ya con el descuento fijo + el de caja.
- En la factura (FactuDesk) solo cubre cajas enteras: con cajas de 6, si lleva 8 el descuento va sobre 6 y las otras 2 van a precio normal. Detalle en el manual de FactuDesk.
Candado de utilidad mínima
Si el nivel tiene Util. mínima, el modal no deja guardar cuando alguno de los descuentos dejaría el precio por debajo de ella: el descuento fijo, el descuento máximo o el fijo + el de caja (los dos se suman en la factura). Aparece un aviso en rojo que dice cuál es, y el botón Guardar queda gris hasta que se baje ese descuento o la utilidad mínima. Se calcula igual que en la factura: utilidad sobre el costo, (Precio con descuento − Costo) ÷ Costo × 100.
Ejemplo: costo ₡1.000, precio ₡1.300 (30 % de utilidad), utilidad mínima 20 %. Un descuento de 5 % deja 23,5 % → se puede. Uno de 10 % deja 17 % → no se puede guardar.
Para muchos artículos a la vez (MX-010)
En el listado de IN-002 seleccione los artículos (o todo el filtro) → Config → Cambios globales → Precios por nivel → Descuento x caja: elija el nivel, escriba el % y presione Establecer desc. x caja. Solo se aplica a los que se venden por empaque (Uds x Med mayor que 1); a los demás no se les toca nada. Al terminar sale un resumen, por ejemplo:
«De los 100 artículos filtrados, se aplicó a 23 que se venden en cajas y unidades; 4 quedan por debajo de la utilidad mínima. ¿Desea verlos?»
Si responde que sí, el listado se queda con su mismo filtro pero muestra los que hay que revisar (el punto rojo de Margen/Costo, a la par de Buscar, + Se venden x paq. de Más filtros: así no se cuelan artículos por unidad; el rojo trae además los que quedan bajo el costo, así que pueden salir más de los contados), para revisarlos uno por uno o volver a aplicar un % más bajo. En los cambios globales el descuento se aplica igual aunque rompa la utilidad mínima: por eso el resumen lo cuenta; en la factura, esos artículos no reciben la parte de caja mientras la rompan (salvo permiso 006).
Sucursales (empresas sincronizadas)
Si la empresa sincroniza su catálogo con otras sucursales, el descuento por caja viaja igual que los demás descuentos: al guardar en el modal MAP-001 o en IN-002, al aplicarlo desde Cambios globales y en la sincronización masiva (CF-SART), cada sucursal queda con el mismo % en el mismo nivel. La sucursal tiene que estar al día con las actualizaciones del sistema; si todavía no tiene el descuento por caja, el resto de los precios se sincroniza normal y ese dato se omite.
Ejemplos numéricos
Ejemplo 1 — Cambio de costo en CRC
Acción: usuario cambia DbCostoUltimo de 320 a 327.33 (origen = Último).
| P | Margen | Step | Precio recalc | Neto pre-redondeo | Neto redondeado | Precio final |
|---|---|---|---|---|---|---|
| 1 | 25 | 5 | 409.16 | 462.35 | 465.00 | 411.5044 |
| 2 | 30 | 5 | 425.53 | 480.85 | 485.00 | 429.2035 |
| 5 | 35 | 5 | 441.90 | 499.35 | 500.00 | 442.4778 |
| 7 | 30 | 0.01 | 425.53 | 480.85 | 480.86 | 425.5398 |
| 9 | 30 | 1 | 425.53 | 480.85 | 481.00 | 425.6637 |
| 11 (USD) | 50 | 0.10 | 1.07 | 1.21 | 1.30 | 1.1504 |
Ejemplo 2 — Bug histórico del round → ceil
Neto = 450Si usáramosround()half-up: 450 / 1.13 = 398.230088... round(398.230088, 4) = 398.2301 ↑ (5to dec = 8, sube) 398.2301 × 1.13 = 450.000013 ↑ pasa de 450 ceil(450.000013 / 5) × 5 = 455 ❌ Confloor()(truncar): floor(398.230088 × 10000) / 10000 = 398.2300 ↓ 398.2300 × 1.13 = 449.999 ↓ debajo de 450 ceil(449.999 / 5) × 5 = 450 ✓
API / Funciones
Funciones backend (/functions/shared/articulos/articulo_precios.php)
| Función | Propósito |
|---|---|
articulo_precios_actualizar_costo($pdo, $cod, $costo, $moneda) | Cambia DBCosto + propaga a TODAS las filas |
articulo_precios_actualizar_precio($pdo, $cod, $numero, $cambios) | Edita una fila (P_n) con cualquier subset de campos |
articulo_precios_crear_fila($pdo, $cod, $numero?, $cambios) | Crea P_n nuevo (auto-asigna max+1 si null) |
articulo_precios_eliminar_fila($pdo, $cod, $numero) | Borra P_n |
articulo_precios_calcular_precio($costo, $margen, $sobre) | Helper puro (sin DB) — fórmula |
articulo_precios_calcular_margen($costo, $precio, $sobre) | Helper puro (sin DB) — fórmula inversa |
Endpoints HTTP
| Endpoint | POST body |
|---|---|
api_guardarCostoArticulo.php | Cod_Articulo + 4 costos + DbArticuloOrigenCosto |
api_guardarPrecioArticulo.php | Cod_Articulo + NumeroPrecio + cualquier subset |
api_crearPrecioArticulo.php | Cod_Articulo + opciones |
api_eliminarPrecioArticulo.php | Cod_Articulo + NumeroPrecio |
JS (articulos_crear.js)
guardarCostoArticulo()— disparado por click ✓ o Enter en input de costoguardarPrecioArticulo(numero, cambios)— wrapper genéricorenderArticuloPrecios(rows)— re-pinta la tablaarticulo_precios_calc_real_margen(costo, precio, sobre)— helper puro JS para Margen virtual
Bitácora
Cada función registra entradas en dbo.BitacoraSistema:
Articulos.DBCosto | art ABC100 | costo 1000 CRC → 1200 CRC | filas afectadas 7/10 ArticuloPrecios | art ABC100 P3 | costo 1000→1200, precio 1300→1560 Costo Fase 2 | art ABC100 | DBCosto 250→331.85 | origen=Bruto (3) — DBCosto sincroniza ArticuloPrecios CREATE | art ABC100 P11 | Mon=USD, Costo=2.4, Margen=30, Precio=3.12 ArticuloPrecios DELETE | art ABC100 P8 | Mon=CRC, Precio=375