Hallazgos verificados: Sales BOM vía Service Layer¶
Fecha de pruebas: 2026-07-13
Ambiente: Service Layer https://sapsl.compulandia.com.py/b1s/v2, base dev ZZ_COMPU_3
(lecturas de contraste contra prod COMPU).
Herramienta: sl-helper.js (en _borradores/, no publicado) (ver §9).
Todo lo descrito abajo fue probado contra el servidor, no es teoría de manual.
1. Estructura del producto compuesto¶
- Ítem padre: ítem normal de
ItemsconInventoryItem = tNO,SalesItem = tYES. No mueve stock nunca. - BOM: entidad
ProductTrees,TreeCode= ItemCode del padre,TreeType = iSalesTree,ProductTreeLines= componentes con cantidad y almacén. - Al crear el
ProductTree, SAP estampa automáticamenteTreeType = iSalesTreeen el ítem padre.
// POST ProductTrees
{
"TreeCode": "TESTBOM-PC", // = ItemCode del padre
"TreeType": "iSalesTree",
"Quantity": 1,
"Warehouse": "CEN",
"PriceList": 1,
"ProductTreeLines": [
{ "ItemCode": "TESTBOM-RAM", "Quantity": 2, "Warehouse": "CEN" },
{ "ItemCode": "TESTBOM-CPU", "Quantity": 1, "Warehouse": "CEN" },
{ "ItemCode": "TESTBOM-MB", "Quantity": 1, "Warehouse": "CEN" },
{ "ItemCode": "TESTBOM-PSU", "Quantity": 1, "Warehouse": "CEN" }
]
}
Campo relevante del árbol: HideBOMComponentsInPrintout (tYES/tNO) — controla si el
layout de impresión muestra los componentes. Es por BOM. El combo de prod 09379 lo
tiene en tYES (cliente ve solo el combo); el requerimiento pide componentes visibles ⇒ tNO.
2. Crear pedido: explosión automática¶
POST Orders enviando una sola línea con el ItemCode del padre. SAP explota los
componentes solo — pedido de prueba 33246: 1 línea enviada → 5 líneas devueltas.
// POST Orders (payload mínimo verificado)
{
"CardCode": "C32857",
"DocDueDate": "2026-07-13",
"DocumentLines": [
{
"ItemCode": "TESTBOM-PC",
"Quantity": 1,
"WarehouseCode": "CEN",
"TaxCode": "IVA_10", // ⚠ OBLIGATORIO acá — ver §5
"PriceAfterVAT": 4950000 // opcional: precio manual del combo — ver §4
}
]
}
Respuesta (líneas):
| LineNum | ItemCode | TreeType | Significado |
|---|---|---|---|
| 0 | TESTBOM-PC | iSalesTree |
Línea padre — lleva precio e impuesto |
| 1..4 | componentes | iIngredient |
Explotadas por SAP — precio 0 |
| — | cualquier otra | iNotATree |
Línea normal, ajena al kit |
⚠ NUNCA enviar las líneas de componentes explícitas en el POST: SAP explota el kit
igual y además agrega las líneas enviadas como independientes (iNotATree) ⇒ productos
duplicados en el pedido (verificado, pedido 33243, cancelado).
Las cantidades escalan solas: PATCH de Quantity del padre 1→2 duplicó las cantidades
de los 4 hijos y el total (verificado, pedido 33241).
3. Modelo de precios: depende de una configuración de EMPRESA¶
Hallazgo central de la investigación. No es una regla del Service Layer — es el checkbox:
Cliente B1 → Gestión → Inicialización del sistema → Parametrizaciones de documentos → pestaña General → "Visualizar precios de artículos componentes de lista de materiales"
| Checkbox | Comportamiento |
|---|---|
| ☑ Marcado (estaba así en dev hasta 2026-07-13) | Precios en las líneas de componentes (auto-valorizados desde lista de precios); línea padre siempre 0 y su precio no editable (error ODBC -1029) |
| ☐ Desmarcado (prod, y dev desde 2026-07-13) | Precio en la línea padre (de su precio de lista); componentes explotan en 0 |
- No se puede leer ni cambiar por Service Layer (verificado: no existe objeto
DocumentSettingsen$metadata;AdminInfoyCompanyInfono lo incluyen — se diffearon completos entre dev y prod). Tampoco lo expone la DI API. Solo cliente B1. - Afecta solo documentos nuevos.
- Ambas bases quedaron alineadas: precio en el padre, que es lo que pide el requerimiento.
4. Precio del combo (config actual: precio en padre) — verificado¶
- Precio de lista: si el ítem padre tiene precio en la lista del cliente, la línea padre
lo toma sola (pedido 33250: lista Gs. 5.500.000 IVA inc →
UnitPrice5.000.000 neto). - Precio manual: enviar
PriceAfterVATen la línea padre del POST. SAP calcula el neto y marcaPriceSource: "dpsManual"(pedido 33252). Es el mismo campo que el OMS ya usa en líneas normales. - Edición posterior:
PriceAfterVATyDiscountPercentde la línea padre son editables por PATCH. ⚠ Si se cambian precio y descuento, mandarlos juntos en un solo PATCH: en llamadas separadas SAP recalcula el descuento sobre el precio unitario original, no sobre el último parcheado (verificado). - En prod se ve el patrón del cliente B1: precio manual tipeado ⇒
UnitPricede lista +DiscountPercentnegativo (ej. pedido prod 72649:-143.58%de "descuento" = recargo). Por SL no hace falta imitar eso;PriceAfterVATdirecto funciona.
5. Impuesto (IVA) — trampa crítica¶
- El
TaxCode(ej.IVA_10) debe ir en la línea del padre en el POST inicial. Se propaga automáticamente a los componentes explotados. - Si se omite: después no se puede agregar a la línea padre por PATCH (
ODBC -1029) y la factura falla con1250000103 - Row without tax was found. El pedido queda facturable solo recreándolo (verificado, pedido 33244, cancelado). - Los ítems creados por SL sin grupo impositivo quedan sin IVA por defecto — el alta de ítems del OMS debe asignar el grupo de IVA.
6. Edición del pedido ("predefinido + editable") — verificado¶
| Operación | Resultado |
|---|---|
PATCH Quantity del padre |
✅ Escala todos los hijos proporcionalmente |
PATCH Quantity de un componente |
✅ Se edita libremente, independiente del resto |
PATCH ItemCode de un componente |
⚠ Funciona, pero la línea se desprende del kit: pasa a iNotATree y toma su propio precio de lista |
| Agregar línea nueva al pedido | ✅ Entra como iNotATree (independiente del kit) |
| PATCH precio/descuento del padre | ✅ (config actual; ver §4) |
Implicancia para el OMS: el "upgrade de componente" (ej. cambiar la RAM) es posible pero la línea deja de pertenecer estructuralmente al combo — la UI debe decidir cómo representarlo (p. ej. poner la línea desprendida en precio 0 si el combo mantiene su precio, o recalcular).
7. Facturación — verificado¶
POST Invoices con referencias al documento base, una por cada línea del pedido
(padre + componentes). Referenciar solo la línea padre falla.
// POST Invoices (pedido 33252 → factura 724250)
{
"CardCode": "C32857",
"DocumentLines": [
{ "BaseType": 17, "BaseEntry": 33252, "BaseLine": 0 },
{ "BaseType": 17, "BaseEntry": 33252, "BaseLine": 1 },
{ "BaseType": 17, "BaseEntry": 33252, "BaseLine": 2 },
{ "BaseType": 17, "BaseEntry": 33252, "BaseLine": 3 },
{ "BaseType": 17, "BaseEntry": 33252, "BaseLine": 4 }
]
}
- La factura conserva la estructura (
iSalesTree/iIngredient), el precio y descuento del padre, y cierra el pedido (bost_Close). - Stock: al facturar se descuenta el stock de los componentes (verificado:
RAM 18→16 por un combo con RAM ×2). El padre no mueve inventario. Mientras el pedido
está abierto, el compromiso (
Committed) también vive en los componentes.
8. Datos de prueba que quedaron en dev (ZZ_COMPU_3)¶
Reutilizables para las pruebas del OMS. Cliente de prueba: C32857 ("TEST").
| Objeto | Detalle |
|---|---|
| Ítems | TESTBOM-PC (padre, lista 1 = 5.500.000), TESTBOM-RAM, TESTBOM-CPU, TESTBOM-MB, TESTBOM-PSU (con precios en lista 1 y stock en CEN) |
| BOM | ProductTrees('TESTBOM-PC'), RAM ×2 + CPU + MB + PSU |
| Pedidos cerrados por factura | 33246, 33252 |
| Facturas | 724248 (precios en componentes, config vieja), 724250 (precio en padre, config actual) |
| Pedidos cancelados | 33241, 33243, 33244, 33248, 33250 |
Nota: dev no es copia reciente de prod (el ítem 09379 de prod no existe en dev;
feriados cargados hasta 2023).
9. Herramienta de pruebas¶
sl-helper.js (en _borradores/, no publicado) — CLI mínimo para el Service Layer usado en esta investigación.
node sl-helper.js GET "ProductTrees('TESTBOM-PC')"
node sl-helper.js POST "Orders" body.json
PROD=1 node sl-helper.js GET "Orders(72649)" # producción: SOLO lectura (bloquea escrituras)
- Lee credenciales de
backend/.env(dev) obackend/.env.save(prod, conPROD=1). - Maneja login y sesión (
B1SESSION), reintento en 401. - En modo prod rechaza todo método que no sea GET (excepto getters
CompanyService_Get*).
10. Pendientes para la implementación en el OMS¶
- Diseño backend: detección de
TreeType = iSalesTreeenprocessOrderLines(orders.service.ts) — enviar solo línea padre conTaxCode+PriceAfterVAT. - UI: representación padre/componentes (
TreeTypepor línea), edición de cantidades, política para el "cambio de componente" (línea desprendida, ver §6). - Facturación del OMS: incluir todas las líneas base del kit al copiar pedido → factura.
- Alta de combos desde el OMS (opcional):
POST ProductTreesya verificado (§1). - Definir
HideBOMComponentsInPrintoutestándar para combos nuevos (requerimiento:tNO).