Saltar a contenido

PDF-010A - B2B Price Lists and Customer Sync Contract

Fecha: 2026-06-23

Estado: DISENO DOCUMENTAL / SIN WRITES OPERATIVOS / PENDING_VALIDATE

portal_visible = yes

Scope: tenant

tenant_id: alpuntodeventa

Destino funcional: SGC -> PostgreSQL/OpenClaw -> WooCommerce La Directa

Owner: Gabi / Carlos Canu

A. SAFE POINT inicial

Control Resultado
git status -sb ## main...origin/main
git rev-parse HEAD 06979f2687269929146c46a664819b198466372a
git rev-parse origin/main 06979f2687269929146c46a664819b198466372a
git log -3 --oneline 06979f2 docs: add pdf-009m product draft pilot write; f9e54f0 docs: add pdf-009l categories controlled write; e127c0f docs: add pdf-009k marca terms controlled write

Alcance de esta tarea:

  • no ejecutar WooCommerce API;
  • no tocar PostgreSQL;
  • no ejecutar sync;
  • no crear cron, scheduler ni pipelines;
  • no hacer push, deploy ni commit;
  • solo crear y actualizar documentacion versionada.

B. Documentacion leida

Este contrato se basa en:

  • sources/SOURCE-002-SGC-PRODUCTOS.md
  • SOURCE-002-ECONOMIC-LAYER.md
  • SOURCE-002-FUTURE-LAYER-MAPPING.md
  • sources/SOURCE-001-SGC-CLIENTES.md
  • mappings/SOURCE-001-CLIENTES-MAPPING.md
  • BUSINESS-OBSERVER-DATA-CONTRACT-001.md
  • production/PDF-009M-WOOCOMMERCE-PRODUCT-DRAFT-PILOT-WRITE.md
  • DDL candidate infra/business-observer/production/postgres/products/PDF-008D/002-source-002-products-ddl-forward.candidate.sql

C. Inventario de precios disponibles en productos

SOURCE-002 publica 81 campos. El bloque de precios disponible incluye:

Familia Campos observados Lectura
precio base final PrecioListaFinal precio final de referencia calculado desde costo, markup e IVA
precio base neto PrecioListaNeto precio neto antes de IVA
oferta PrecioOfertaFinal promocion vigente, no equivalente a lista base
precio con descuento permitido PrecioCDescPermitidoFinal derivado desde Price_L1 y Descuento Permitido
listas finales PrecioL1 a PrecioL9 precios finales por lista
listas netas PrecioNetoL1 a PrecioNetoL9 netos por lista
IVA por lista IVAPrecioL1 a IVAPrecioL9 componente fiscal por lista
margen por lista MargenL1 a MargenL9 margen vigente por lista
origen SQL preservado Price_L1..Price_L9, Margen_L1..Margen_L9 nombres fuente del patron repetible
payload raw/candidate price_lists_payload cache o auditoria secundaria, no fuente principal

No se encontro evidencia de campos destino llamados literalmente priceL1..priceLn. La evidencia vigente usa PrecioL1..PrecioL9 en documentacion y Price_L1..Price_L9 en autoridad SQL origen.

Decision vigente heredada:

  • no modelar PrecioL1..PrecioL9 como columnas fijas en la capa destino principal;
  • normalizar listas como filas por tenant_id + sku + lista_codigo;
  • permitir futuras L10, L11 u otras listas sin cambio estructural.

D. L1 vs precio publico default

Veredicto: PENDING_VALIDATE.

Evidencia encontrada:

  • PDF-009M uso PrecioListaFinal como regular_price en los 10 productos draft creados.
  • PDF-009E dejo PrecioListaFinal como regular_price candidate.
  • SOURCE-002-FUTURE-LAYER-MAPPING define PrecioCDescPermitidoFinal como calculado desde Price_L1, pero no declara que L1 sea el precio publico default.
  • SOURCE-002-ECONOMIC-LAYER confirma que L1..L9 son listas normalizables, pero no cierra semantica comercial de L1 como lista publica.

Regla documental hasta validar con negocio:

  • visitante no logueado usa regular_price vigente en WooCommerce;
  • para carga actual, regular_price sigue basado en PrecioListaFinal;
  • no migrar regular_price a L1 sin validacion humana o evidencia de SGC;
  • si negocio confirma que L1 es precio publico, abrir gate separado para actualizar contrato, mapping y payloads.

E. Tabla conceptual de precios por SKU/lista

Tabla conceptual recomendada:

b2b_product_price_lists

Grano:

  • una fila por tenant_id;
  • una fila por sku;
  • una fila por price_list_code;
  • una fila por vigencia observada.

Campos minimos:

Campo Tipo sugerido Regla
tenant_id text obligatorio; esperado alpuntodeventa
sku text obligatorio; ancla con SOURCE-002
price_list_code text obligatorio; L1, L2, ..., futuras listas
price_total numeric(18,3) precio final con impuestos, derivado de PrecioLn
currency text default candidate ARS, pendiente de validacion final
valid_from timestamptz inicio de vigencia observada
valid_to timestamptz nullable; cierre de vigencia
source_hash text hash de la fila/valor fuente para detectar cambios

Campos compatibles con DDL candidate existente:

  • price_list_code equivale funcionalmente a list_code;
  • price_total equivale funcionalmente a price_final;
  • currency equivale funcionalmente a currency_code;
  • source_hash equivale funcionalmente a source_row_hash o hash especifico de valor.

Reglas:

  • price_list_code no debe depender de que existan exactamente 9 listas.
  • price_total debe ser el precio final aplicable al cliente.
  • valid_to IS NULL significa vigencia abierta.
  • no usar JSON como fuente primaria de precios B2B; puede existir como cache o evidencia secundaria.

F. Tabla conceptual de customer mapping

Tabla conceptual recomendada:

b2b_customer_price_list_mapping

Grano:

  • una fila vigente por tenant_id + sgc_customer_id;
  • historizacion futura por cambio de lista si negocio necesita auditoria fina.

Campos minimos:

Campo Tipo sugerido Origen / regla
tenant_id text obligatorio; esperado alpuntodeventa
sgc_customer_id text SOURCE-001.Codigo canonicalizado
name text cliente_nombre
CUIT text cuit / tax_id_raw; sensible
email text email_raw / normalizado si aplica
WhatsApp text whatsapp_raw o normalizado; no es clave unica
price_list_code text Lista_Precio / price_list_code_raw
wc_customer_id bigint nullable; se completa al matchear o crear cliente Woo
status text derivado: active, suspended, inactive, unknown

Reglas heredadas de SOURCE-001:

  • tenant_id + codigo_cliente es la clave logica fuerte.
  • Lista_Precio existe como campo fuente, pero su confiabilidad comercial sigue pendiente de validacion final.
  • WhatsApp y email son datos sensibles y no deben usarse como unica clave de cliente.
  • clientes suspendidos o de baja se preservan; no hay hard delete por estado comercial.

G. Comportamiento B2B esperado

Visitante no logueado

  • ve precio publico default desde WooCommerce regular_price;
  • en el estado actual documentado, regular_price se carga desde PrecioListaFinal;
  • si se valida L1 como precio publico, el cambio debe pasar por gate separado.

Cliente logueado

  • debe resolverse wc_customer_id -> sgc_customer_id -> price_list_code;
  • si el cliente tiene lista vigente, cada SKU debe mostrar price_total de esa lista;
  • si el cliente no tiene lista o la lista no existe, aplicar fallback documentado y auditable, no silencioso.

Carrito y checkout

  • el precio del carrito debe recalcularse con la lista asignada al cliente;
  • el checkout debe persistir evidencia minima de lista aplicada por linea: sku, price_list_code, price_total, timestamp y hash/fuente;
  • no confiar solo en el precio renderizado en frontend;
  • los cambios de cliente, login/logout o cambio de lista deben invalidar y recalcular precios de carrito.

H. Fuente maestra

Regla obligatoria:

WooCommerce no debe ser la fuente maestra de listas multiples.

WooCommerce puede conservar:

  • regular_price publico/default;
  • precio efectivo aplicado a carrito/pedido;
  • metadata minima no sensible para trazabilidad operativa;
  • wc_customer_id como identificador externo.

PostgreSQL/OpenClaw debe conservar:

  • listas multiples por SKU;
  • cliente SGC y lista asignada;
  • hash, vigencia, auditoria y fuente;
  • historicos economicos sensibles;
  • reglas de fallback y calidad.

I. Fallbacks y bloqueos

Caso Comportamiento
visitante usar regular_price
cliente logueado sin mapping SGC PENDING_CUSTOMER_MAPPING; no inventar lista
cliente sin price_list_code PENDING_PRICE_LIST; fallback solo si negocio lo aprueba
lista asignada sin precio para SKU BLOCKED_PRICE_MISSING; no reemplazar silenciosamente
SKU sin precio publico BLOCKED_PUBLIC_PRICE_MISSING
price_total <= 0 bloquear salvo regla comercial explicita
valid_to vencida no aplicar; buscar vigencia abierta o bloquear

J. Riesgos

Riesgo Impacto Control recomendado
cache de precios mostrar precio viejo por cliente invalidar cache por customer_id, price_list_code, SKU y hash
performance consultas por linea degradan catalogo/carrito cache precomputada por lista y SKU, indices por sku + price_list_code
carrito/checkout precio display distinto al precio cobrado recalculo server-side en carrito y checkout
clientes sin lista venta con precio incorrecto estado PENDING_PRICE_LIST y reporte operativo
SKU sin precio para lista conversion bloqueada o margen incorrecto BLOCKED_PRICE_MISSING con fallback solo aprobado
seguridad comercial exposicion de listas, margenes o clientes no exponer endpoints publicos de listas; no guardar costos/margenes en Woo
datos fiscales/contacto CUIT, email y WhatsApp sensibles minimizacion, logs sin datos completos, permisos por rol
drift SGC/Woo cliente o lista cambiada no aplicada hash, last_seen_at, auditoria y reconciliacion

K. Gates futuros requeridos

  1. Validar humanamente si L1 es precio publico default.
  2. Validar catalogo real de Lista_Precio en SOURCE-001.
  3. Definir matching entre sgc_customer_id y wc_customer_id.
  4. Definir plugin/hook WooCommerce o capa headless para aplicar precios por cliente en render, carrito y checkout.
  5. Definir fallback comercial permitido para clientes sin lista o SKU sin precio de lista.
  6. Ejecutar dry-run sin writes antes de cualquier cambio operativo.

L. Restricciones preservadas

Durante este gate:

  • no se ejecuto WooCommerce API;
  • no se toco PostgreSQL;
  • no se ejecuto SQL;
  • no se leyo ni imprimio .env;
  • no se hizo sync;
  • no se creo cron, scheduler ni pipeline;
  • no se hizo push;
  • no se hizo deploy;
  • no se hizo commit.