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.mdSOURCE-002-ECONOMIC-LAYER.mdSOURCE-002-FUTURE-LAYER-MAPPING.mdsources/SOURCE-001-SGC-CLIENTES.mdmappings/SOURCE-001-CLIENTES-MAPPING.mdBUSINESS-OBSERVER-DATA-CONTRACT-001.mdproduction/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..PrecioL9como columnas fijas en la capa destino principal; - normalizar listas como filas por
tenant_id + sku + lista_codigo; - permitir futuras
L10,L11u otras listas sin cambio estructural.
D. L1 vs precio publico default¶
Veredicto: PENDING_VALIDATE.
Evidencia encontrada:
PDF-009MusoPrecioListaFinalcomoregular_priceen los10productos draft creados.PDF-009EdejoPrecioListaFinalcomoregular_pricecandidate.SOURCE-002-FUTURE-LAYER-MAPPINGdefinePrecioCDescPermitidoFinalcomo calculado desdePrice_L1, pero no declara queL1sea el precio publico default.SOURCE-002-ECONOMIC-LAYERconfirma queL1..L9son listas normalizables, pero no cierra semantica comercial deL1como lista publica.
Regla documental hasta validar con negocio:
- visitante no logueado usa
regular_pricevigente en WooCommerce; - para carga actual,
regular_pricesigue basado enPrecioListaFinal; - no migrar
regular_priceaL1sin validacion humana o evidencia de SGC; - si negocio confirma que
L1es 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_codeequivale funcionalmente alist_code;price_totalequivale funcionalmente aprice_final;currencyequivale funcionalmente acurrency_code;source_hashequivale funcionalmente asource_row_hasho hash especifico de valor.
Reglas:
price_list_codeno debe depender de que existan exactamente9listas.price_totaldebe ser el precio final aplicable al cliente.valid_to IS NULLsignifica vigencia abierta.- no usar
JSONcomo 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_clientees la clave logica fuerte.Lista_Precioexiste como campo fuente, pero su confiabilidad comercial sigue pendiente de validacion final.WhatsAppyemailson 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_pricese carga desdePrecioListaFinal; - si se valida
L1como 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_totalde 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_pricepublico/default;- precio efectivo aplicado a carrito/pedido;
- metadata minima no sensible para trazabilidad operativa;
wc_customer_idcomo 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¶
- Validar humanamente si
L1es precio publico default. - Validar catalogo real de
Lista_PrecioenSOURCE-001. - Definir matching entre
sgc_customer_idywc_customer_id. - Definir plugin/hook WooCommerce o capa headless para aplicar precios por cliente en render, carrito y checkout.
- Definir fallback comercial permitido para clientes sin lista o SKU sin precio de lista.
- 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.