fix(web): el precio de una línea nueva queda vacío para que SAP aplique la lista de precios

Probado contra SBODemoCL: crear y actualizar órdenes de venta y de compra, reemplazo de líneas y 412 por conflicto.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
GeorgeandClaude Sonnet 5.5 committed 2026-10-14 11:06:58 -05:00
1 parent 6978fed6fb
commit 4b35aa6bfd
4 files changed
+14 -5

No files matched your search

+2 -2
View File
@@ -48,7 +48,7 @@ Cada menú abre primero una **lista de los últimos documentos** (barra de filtr
- Probar contra el SAP real con la extensión de Chrome (`list_connected_browsers` debe mostrar un navegador). Si no conecta, usar Chrome headless por CDP. - Probar contra el SAP real con la extensión de Chrome (`list_connected_browsers` debe mostrar un navegador). Si no conecta, usar Chrome headless por CDP.
- **No escribir contraseñas en formularios**: obtener el token con `curl` a `/api/auth/login` (la clave de prueba se lee de `EmployeesInfo` solo para eso, nunca se imprime ni se guarda) e inyectar solo el token en `localStorage` (`erp_session`). - **No escribir contraseñas en formularios**: obtener el token con `curl` a `/api/auth/login` (la clave de prueba se lee de `EmployeesInfo` solo para eso, nunca se imprime ni se guarda) e inyectar solo el token en `localStorage` (`erp_session`).
- Al terminar: cerrar la sesión de prueba (`POST /api/auth/logout`), borrar `erp_session`/`erp_theme` del navegador, cerrar la pestaña, apagar los servidores y borrar archivos temporales (token, `apps/api/data/`). No dejar páginas de demostración fuera de `/dev/componentes`. - Al terminar: cerrar la sesión de prueba (`POST /api/auth/logout`), borrar `erp_session`/`erp_theme` del navegador, cerrar la pestaña, apagar los servidores y borrar archivos temporales (token, `apps/api/data/`). No dejar páginas de demostración fuera de `/dev/componentes`.
- **Escrituras a SAP (POST/PATCH):** hasta ahora solo se probaron contra un SAP simulado. Pedir autorización al usuario antes de crear o modificar datos reales, aunque sea la base demo. - **Escrituras a SAP (POST/PATCH):** el usuario autorizó (2026-10-14) escribir en `SBODemoCL`, su ambiente propio de pruebas; ya se probaron crear/actualizar órdenes de venta y de compra, reemplazo de líneas y el 412 por conflicto. Documentos de prueba creados: orden de venta 315 y orden de compra 2883 (comentario "PRUEBA basic-erp"). Aun así, avisar antes de escribir en cualquier otra base.
- Los tests no detectan todo: el foco tras crear una línea (se pedía antes de que existiera la fila) solo falló en el navegador real. - Los tests no detectan todo: el foco tras crear una línea (se pedía antes de que existiera la fila) solo falló en el navegador real.
## Estado y próximos pasos ## Estado y próximos pasos
@@ -56,7 +56,7 @@ Cada menú abre primero una **lista de los últimos documentos** (barra de filtr
Hecho (commits en `main`): monorepo · API (config, capa SAP con sesión compartida y cola, login, permisos SQLite, proxy `/api/sl`) · web (temas, login, layout con menú por permisos, `sbo-value-help`, campos y filas compuestas, listas con filtros, ventana/toolbar/pestañas/menús, grilla de líneas, `/dev/componentes`). Hecho (commits en `main`): monorepo · API (config, capa SAP con sesión compartida y cola, login, permisos SQLite, proxy `/api/sl`) · web (temas, login, layout con menú por permisos, `sbo-value-help`, campos y filas compuestas, listas con filtros, ventana/toolbar/pestañas/menús, grilla de líneas, `/dev/componentes`).
Pendiente, en orden: Pendiente, en orden:
1. **Paso 9 — formulario de documentos:** hecho para ventas y compras (`document-form.page`, `document-model.ts`: cargar, crear, actualizar con `If-Match`/412, navegación, totales con `SalesTaxCodes` (también en compras), solo lectura), probado solo con SAP simulado y **sin verlo aún en el navegador**. Falta: probar escrituras reales (pedir autorización), "Copiar de/a", botón PDF, fila del flete, toolbar de ancho completo, formularios de inventario y solicitudes de compra. 1. **Paso 9 — formulario de documentos:** hecho para ventas y compras (`document-form.page`, `document-model.ts`: cargar, crear, actualizar con `If-Match`/412, navegación, totales con `SalesTaxCodes` (también en compras), solo lectura), probado con SAP simulado y contra `SBODemoCL` (leer, crear, actualizar, líneas, 412). Falta: resolver al salir del campo el nombre del socio y la descripción del artículo cuando se escriben a mano (hoy SAP los completa al guardar), "Copiar de/a", botón PDF, fila del flete, toolbar de ancho completo, formularios de inventario y solicitudes de compra.
2. Pantallas de maestros (`Items`, `BusinessPartners`: Clientes/Proveedores, Artículos), hoy "no disponible". 2. Pantallas de maestros (`Items`, `BusinessPartners`: Clientes/Proveedores, Artículos), hoy "no disponible".
3. ~~Pantalla web de permisos~~ — hecha (`/admin/permisos`). 3. ~~Pantalla web de permisos~~ — hecha (`/admin/permisos`).
4. Auditoría (`audit_log` en SQLite; hoy solo líneas `AUDIT` en el log) y `nestjs-cls` para el usuario en los logs. 4. Auditoría (`audit_log` en SQLite; hoy solo líneas `AUDIT` en el log) y `nestjs-cls` para el usuario en los logs.
@@ -1,4 +1,5 @@
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
import { newDocumentLine } from './line-columns';
import { import {
computeTotals, computeTotals,
createPayload, createPayload,
@@ -85,6 +86,11 @@ describe('createPayload', () => {
}); });
}); });
it('un precio sin escribir no se envía: SAP aplica la lista de precios', () => {
const body = createPayload({ CardCode: 'C1' }, [{ ...newDocumentLine(), ItemCode: 'A1' }]);
expect(body['DocumentLines']).toEqual([{ ItemCode: 'A1', Quantity: 1, DiscountPercent: 0 }]);
});
it('isBlankLine', () => { it('isBlankLine', () => {
expect(isBlankLine({ ItemCode: '' })).toBe(true); expect(isBlankLine({ ItemCode: '' })).toBe(true);
expect(isBlankLine({ ItemCode: 'A1' })).toBe(false); expect(isBlankLine({ ItemCode: 'A1' })).toBe(false);
@@ -52,7 +52,7 @@ describe('columnas estándar de línea', () => {
describe('línea nueva y "catálogo completo"', () => { describe('línea nueva y "catálogo completo"', () => {
it('la línea nueva trae cantidad 1 y total 0', () => { it('la línea nueva trae cantidad 1 y total 0', () => {
expect(newDocumentLine()).toMatchObject({ Quantity: 1, Price: 0, LineTotal: 0, ItemCode: '' }); expect(newDocumentLine()).toMatchObject({ Quantity: 1, Price: null, LineTotal: 0, ItemCode: '' });
}); });
it('cada artículo elegido se convierte en una línea nueva', () => { it('cada artículo elegido se convierte en una línea nueva', () => {
@@ -12,13 +12,16 @@ export function recalcLine(line: GridLine): GridLine {
return { ...line, LineTotal: round2(total) }; return { ...line, LineTotal: round2(total) };
} }
/** Línea nueva con los valores por defecto de un documento. */ /**
* Línea nueva con los valores por defecto de un documento. El precio queda vacío (no 0): si el
* usuario no lo escribe no se envía y SAP aplica la lista de precios del socio de negocio.
*/
export const newDocumentLine = (): GridLine => ({ export const newDocumentLine = (): GridLine => ({
ItemCode: '', ItemCode: '',
ItemDescription: '', ItemDescription: '',
Quantity: 1, Quantity: 1,
WarehouseCode: '', WarehouseCode: '',
Price: 0, Price: null,
DiscountPercent: 0, DiscountPercent: 0,
TaxCode: '', TaxCode: '',
LineTotal: 0, LineTotal: 0,