25 KiB
CLAUDE.md
Aplicación web Devolución a Proveedores para SAP Business One. Reemplaza al addon
.NET Framework (../SolucionLibros/), que se abandona: todo pasa a Angular + NestJS.
../SolucionLibros/ queda solo como referencia de la lógica original — no modificarlo.
Qué hace el proceso
Genera Devoluciones de mercancías (ORPD, ObjType 21) enlazadas a sus Entradas de
mercancías por compra (OPDN, ObjType 20), a partir de un listado de artículos y
cantidades pegado desde Excel. Cada ejecución se guarda en el UDO LIB_ODEV
(cabecera @LIB_ODEV, líneas @LIB_DEV1).
Pasos de la UI (decididos):
- Pegar: ItemCode + cantidad desde Excel (con el botón Pegar desde el portapapeles o
Ctrl/Cmd+Ven el cuadro). Se valida contra la Consignación del almacén elegido (ver Decisiones). No guarda. - Procesar: reemplaza a "Selección" del addon. Asignación FIFO contra líneas abiertas
de Entradas de mercancías del almacén elegido, ordenadas por
DocDate. Genera filas Padre (F, artículo) y Hijo (S, una por Entrada consumida). El usuario marca o desmarca qué líneas Hijo devolver. - Crear devoluciones: agrupa las líneas marcadas sin
TargetEntrypor proveedor y las parte en lotes deU_PrintingLineslíneas (11 por defecto). Crea una Devolución por lote y grabaU_TargetType/U_TargetEntryen las líneas. - Descargar PDF: por cada Devolución, se pide el PDF de la Guía de despacho (DTE tipo 52)
al web service SOAP de BES, usando las credenciales de
@DTE_CONFC. Opción de unir todos los PDF en uno.
Decisiones tomadas (no volver a discutir)
- Solo se crean Devoluciones (ObjType 21), no Solicitudes de devolución. Solo se cargan las series de Devoluciones.
U_Statusde cabecera eliminado. El estado usa elStatusestándar del UDO (O/C).- Se usan
U_TipoDespachoyU_IndicadorTrasladoen@LIB_ODEV. Se envían a la Devolución comoU_BFE_TipoDespacho/U_BFE_IndTraslado. - Cantidades y referencias son enteros (
db_Numeric):Quantity,OnHand,OpenQty,AllocatedQty,BaseEntry,BaseLine,BaseRef,TargetEntryyU_Series. - Producción en HANA; ambiente de pruebas en MSSQL. Toda consulta SQL debe tener las
dos variantes, según
DB_TYPE. - Sin control de concurrencia: solo puede haber una planificación abierta. Para crear una nueva, todas las anteriores tienen que estar cerradas.
- Después de Procesar, el usuario elige qué líneas devolver. Una línea con
TargetEntryya no se puede desmarcar (verpuedeCambiarSeleccionenlibs/shared). - La planificación se cierra a mano (
POST /planificaciones/:id/cerrar). - Reprocesar (botón Rehacer FIFO del Detalle) solo vale para una planificación abierta y sin Devoluciones. Las filas de un UDO no se pueden quitar por Service Layer, así que se crea una nueva (heredando la cabecera) y, después de crearla, se cierra la anterior (si el cierre falla se avisa para cerrarla a mano).
- Crear devoluciones: resultado parcial por documento. Cada Devolución se crea aparte; las
buenas graban
U_TargetType/U_TargetEntry, las fallidasU_ErrCode/U_ErrMsgy se reintentan. - El "stock" que se muestra y valida es la Consignación, no
OITW.OnHand:DP_Stock(v3) sumaPDN1.OpenInvQtyde las líneas abiertas de Entradas conU_DOT_TipoRecepcion = 'C'del almacén, o sea lo mismo que consume el FIFO y lo único que se puede devolver. Sin consignación no hay fila: entoncesDP_Articulodistingue artículo existente (0, queda "Sin stock") de inexistente (error). El analizador SQL del Service Layer no admiteCOALESCEni subconsultas (errorCannot support this function or expression): solo JOIN + GROUP BY. La columna se sigue llamandoOnHanden la consulta, el modelo y el UDO (U_OnHand); en pantalla y en Excel el encabezado es Consignación. Las planificaciones guardadas antes de la 2.0.0 conservan el valor viejo hasta rehacer el FIFO. - Consultas SQL por
SQLQueriesdel Service Layer (no hay conexión directa a la BD). La API crea/actualizaDP_StockyDP_EntradasAbiertassola (variante segúnDB_TYPE). El analizador SQL del Service Layer no admiteFORMATy SAP reescribe elSqlText: al cambiar el SQL hay que subirversionensql-queries.definitions.ts. Los parámetros van entre comillas simples.DP_EntradasAbiertas(v3) lee la cantidad abierta dePDN1.OpenInvQty(noOpenQty); el campo de usuarioU_OpenQtydel UDO conserva su nombre. - Listado de planificaciones paginado en SAP (
$top/$skip/$inlinecount, OData v3 del SL v1), más nueva primero, con filtros por estado, almacén y rango de fechas. Por defecto 20 por página, máximo 100. En la pantalla: botones Atrás / Siguiente; filtros y página quedan en la URL. - PDF: no se usa el paquete
soap;bes-soap.tsarma el sobre y lee la respuesta a mano yBesClientlo envía con axios (SOAPActionhttp://tempuri.org/Consulta_TXTDTE). Credenciales y URL salen deU_DTE_CONFCy nunca se registran ni se devuelven al navegador. Solo se consulta con elFolioNumberde la Devolución: si todavía no tiene folio no se llama a BES y esa Devolución informa "todavía no tiene folio" (el addon usaba elDocNumcomo respaldo; se descartó porque podría traer el DTE de otro documento). Unir conpdf-lib; un fallo en una Devolución no impide las demás. - Exportar a Excel (solo en el navegador, sin pasar por la API): "Artículos validados" (Nueva) sale tal cual
la tabla; "Líneas" (Detalle) sale con una fila por Hijo (
S) que repite los datos de su Padre (F); un Padre sin Hijos sale solo.core/excel.tsarma el .xlsx a mano (ZIP sin compresión, sin dependencias). - Valores por defecto de la cabecera (Detalle, solo con la planificación abierta y el campo vacío): fecha,
fecha de salida y de llegada = hoy; hora de salida = hora actual; tipo de despacho
1; indicador de traslado7(Guía de devolución), estos dos solo si existen en el catálogo. Lo pone la web; se guardan con Guardar cabecera o al crear las Devoluciones. Lo ya guardado no se pisa. - Transporte en la Devolución (ORPD):
RutTransporte→U_BFE_RutTrasporte(sic),Patente→U_BFE_Patente,RutChofer→U_BFE_RutChofer,NombreChofer→U_BFE_NombreChofer. ORPD no tiene UDF para fechas ni hora de salida/llegada: solo se guardan en@LIB_ODEV. - Despliegue en Windows: un solo servicio. La API sirve también la web compilada (mismo origen: sin
CORS y con la cookie
SameSite=strict), con HTTPS directo en Node (HTTPS_PFX_PATHoHTTPS_KEY_PATH+HTTPS_CERT_PATH) y redirección opcional de HTTP (HTTP_REDIRECT_PORT). El servicio lo administra NSSM (decisión del usuario; se evaluó WinSW y no se lo consideró lo bastante estable); nginx no es necesario. NSSM 2.24 estable puede no arrancar en Windows recientes: usar la 2.24-101.npm run publicararmarelease/devolucion-proveedores/(API + web + solo dependencias de producción, ~55 MB, sin enlaces simbólicos:@dp/sharedva como copia real, porque la API lo importa al ejecutarse y un enlace no sobrevive a copiar de Mac a Windows). Guía completa:deploy/windows/DEPLOY.md. - Versión: el número es el
versiondelpackage.jsonraíz. Cada release sube la versión y el número lo decide el usuario (preguntarle antes):npm run version:parche|menor|mayory luegonpm run publicar, que dejaapp/version.json(número, fecha y commit). La API lo lee al arrancar (src/version.ts), lo informa en/auth/infoy/auth/me, y la web lo muestra en el login y en la barra superior (en desarrollo dice "desarrollo"). - Logs: la aplicación no escribe logs a archivo; usa el
Loggerde Nest por consola. En producción NSSM captura la salida y la rota a los 10 MB (no limita cuántos archivos conserva: hay que limpiar los viejos). ConNO_COLOR=1el log sale sin códigos de color. Solo se registran errores 4xx/5xx, fallos o lentitud del Service Layer y eventos de negocio. - Aplicación web independiente. Login con el usuario de SAP B1 vía Login del Service Layer.
- Metadatos (UDT/UDF/UDO) definidos en JSON con formato del Service Layer. La web tiene la pantalla Configuración → Metadatos con el botón Reconstruir, y NestJS los crea. Los cambios que borran datos (campo con tipo distinto o campo sobrante) solo se hacen con autorización explícita.
- Después de cada login se verifica que existan las tablas, los campos y el UDO. Si
falta algo, se redirige a Configuración → Metadatos. El login tiene una casilla "Verificar
los metadatos de SAP al ingresar" (activa por defecto; se recuerda en
localStorage). Desactivada, el guard omite la verificación; Configuración → Metadatos siempre verifica.
Estructura
devolucion-proveedores-web/ npm workspaces
├── libs/shared/ @dp/shared tipos + lógica de dominio pura (ESM, se compila a dist/)
│ └── src/
│ ├── models/ auth, metadata, planificacion, catalogos (catálogos, páginas, PDF)
│ └── domain/ portapapeles.ts (parseo Excel), fifo.ts (FIFO, consolidación)
├── apps/api/ NestJS 12 ESM ("type": "module"), imports con extensión .js
│ ├── .env(.example)
│ └── src/
│ ├── config/ configuration.ts (lee .env)
│ ├── service-layer/ ServiceLayerClient (axios, cola por sesión), SessionStore (memoria)
│ ├── auth/ login/logout/me/info, SessionGuard global + @Public()
│ ├── sql-queries/ SqlQueriesService + definiciones (DP_Stock, DP_EntradasAbiertas)
│ ├── catalogos/ almacenes, series, valores UDF
│ ├── planificaciones/ service, controller, dto, udo.mapper (puro, con tests)
│ ├── pdf/ bes-soap (puro), BesClient, PdfDteService (descarga y unión)
│ ├── metadata/ analyzer (puro), service, controller
│ │ └── definitions/ user-tables.json, user-fields.json, user-objects.json,
│ │ external-dependencies.json ← fuente de verdad
│ ├── health/
│ ├── http-exception.logger.ts filtro global: deja en el log todo error 4xx/5xx
│ ├── web-estatico.ts sirve el build de Angular, fallback a index.html, cabeceras de seguridad
│ └── https.ts lectura del certificado y redirección HTTP → HTTPS
├── scripts/publicar.mjs arma la carpeta de despliegue (release/, ignorada por git)
├── deploy/windows/ NSSM: instalar/desinstalar-servicio.ps1, DEPLOY.md (van al release)
└── apps/web/ Angular 22 standalone, signals, zoneless, CSS (no SCSS)
└── src/app/
├── core/ auth.service, metadata.service, planificaciones.service,
│ guards, auth.interceptor, http-error
├── layout/ shell.ts (barra y navegación), stepper.ts (4 pasos)
└── pages/
├── login/ con la casilla de verificar metadatos
├── metadatos/ Configuración → Metadatos
└── planificaciones/ listado (filtros + paginación) · nueva/ (pasos 1-2) · detalle/ (pasos 3-4)
API (prefijo /api)
| Método | Ruta | Descripción |
|---|---|---|
| GET | /health |
público |
| GET | /auth/info |
público: CompanyDB y DB_TYPE configurados |
| POST | /auth/login |
{userName, password}: Login en el Service Layer y cookie httpOnly dp_sid |
| POST | /auth/logout |
Logout en el Service Layer |
| GET | /auth/me |
usuario de la sesión |
| GET | /metadata/status |
compara las definiciones contra la base SAP |
| POST | /metadata/rebuild |
{permitirDestructivos: boolean}: crea y corrige |
| GET | /catalogos |
almacenes, series de Devoluciones (con la por defecto), valores de BFE_TipoDespacho/BFE_IndTraslado |
| GET | /catalogos/almacenes |
solo almacenes (filtros del listado) |
| GET | /planificaciones?status=&whsCode=&desde=&hasta=&pagina=&tamano= |
una página ({items,total,pagina,tamano,totalPaginas}), más nueva primero; filtros y salto en SAP. tamano máx. 100, por defecto 20 |
| GET | /planificaciones/abierta |
la abierta ({abierta: null} si no hay; siempre JSON) |
| GET | /planificaciones/:docEntry |
detalle: cabecera y líneas |
| POST | /planificaciones/pegar |
{whsCode, texto}: valida Excel contra stock. No guarda |
| POST | /planificaciones |
procesa: FIFO + guarda en LIB_ODEV. 409 si ya hay una abierta |
| PATCH | /planificaciones/:id/cabecera |
serie, fecha, despacho, traslado, transporte |
| PATCH | /planificaciones/:id/lineas/:lineId |
{applied}. 409 si la línea ya tiene TargetEntry |
| POST | /planificaciones/:id/devoluciones |
crea las Devoluciones de las líneas marcadas |
| POST | /planificaciones/:id/reprocesar |
rehace el FIFO: crea una planificación nueva y cierra la anterior. Solo si está abierta y sin Devoluciones (409 si no) |
| POST | /planificaciones/:id/pdf |
{unir?: boolean}: pide a BES el PDF de la Guía de despacho (DTE 52) de cada Devolución; devuelve los archivos en base64 y el resultado por Devolución |
| POST | /planificaciones/:id/cerrar |
cierra la planificación |
La sesión del Service Layer (B1SESSION/ROUTEID) vive en el servidor; el navegador solo
tiene dp_sid. SessionStore es en memoria: alcanza para una sola instancia de la API y se
pierde al reiniciar la API (hay que volver a iniciar sesión).
Metadatos (definitions/*.json)
- Tablas:
LIB_ODEV(bott_Document) yLIB_DEV1(bott_DocumentLines). - UDO:
LIB_ODEV(boud_Document), hijaLIB_DEV1. CanFind/CanClose/CanCancel = tYES, CanDelete = tNO, CanLog = tYES (ALIB_ODEV), sin formulario por defecto. Columnas de búsqueda:DocEntry,U_WhsCode,U_DocDate. - Campos de
@LIB_ODEV: WhsCode, DocDate, Series (num), ObjType, PrintingLines (num), TipoDespacho, IndicadorTraslado, RutTransporte, RutChofer, NombreChofer, FechaSalida, HoraSalida (st_Time), FechaLlegada, Patente. - Campos de
@LIB_DEV1: LineType (F/S), ItemCode, ItemDescription, Quantity, OnHand, Status, BaseType, BaseEntry, BaseLine, BaseRef, BaseDocDate, OpenQty, AllocatedQty, Applied (Y/N), TargetType, TargetEntry, CardCode, CardName, ErrCode, ErrMsg. - Dependencias externas (solo se verifican, no se crean):
OPDN.U_DOT_TipoRecepcion,OINV.U_BFE_TipoDespacho,OINV.U_BFE_IndTrasladoy la tabla@DTE_CONFC.
Preparar el entorno (otro computador)
- Node 22 LTS o superior (
node -v). - Red con Fortinet (inspección SSL): npm falla con
SELF_SIGNED_CERT_IN_CHAIN. Exportar antes de usar npm u ng:export NODE_EXTRA_CA_CERTS="/ruta/a/fortinet-root.crt" # está en ../fortinet-root.crt # alternativa, si el certificado está en el llavero de macOS: export NODE_OPTIONS=--use-system-ca export NG_CLI_ANALYTICS=false # si no, `ng` puede quedar esperando el prompt - Instalar y configurar:
Variables del
npm install cp apps/api/.env.example apps/api/.env # completar SL_URL, SL_COMPANY_DB, DB_TYPE.envde la API:PORT(3000),WEB_ORIGIN(CORS),SL_URL(con/b1s/v1),SL_COMPANY_DB,SL_REJECT_UNAUTHORIZED(falsesi el certificado es autofirmado),DB_TYPE(HANA/MSSQL) yCOOKIE_SECURE(truesolo con HTTPS). El usuario y la contraseña de SAP no van en el.env: se escriben en el login. Solo para producción:HTTPS_PFX_PATH/HTTPS_PFX_PASSPHRASEoHTTPS_KEY_PATH/HTTPS_CERT_PATH,HTTP_REDIRECT_PORTyWEB_DIST_DIR(opcional). - Desarrollo, en dos terminales:
El puerto de
npm run dev:api # compila @dp/shared y levanta NestJS en el PORT del .env (3015 en el servidor de pruebas) npm run dev:web # compila @dp/shared y levanta Angular en :4215 (proxy /api → :3015)ng serveestá enapps/web/angular.jsony el destino del proxy enapps/web/src/proxy.conf.json: si cambiaPORT, hay que cambiar también el proxy. En el servidor Windows de pruebas el servicio instalado usa el mismo 3015: detener uno antes de arrancar el otro. En Windows,nest start --watcha veces no logra terminar el proceso anterior al recompilar y se cae: basta con volver a ejecutarnpm run dev:api.dev:webusang serve --poll 1500: en el Windows Server de pruebas (Node 24) el vigilante nativo (@parcel/watcher) hace caerng servecon0xC0000005justo después de "Watch mode enabled". Solo afecta al desarrollo; producción sirve la web compilada y no vigila archivos. - Verificación:
npm test # shared + api + web npm run test:e2e -w api npm run lint -w api npm run build npm run publicar # carpeta de despliegue en release/ (ver deploy/windows/DEPLOY.md)
Reglas:
@dp/sharedse consume compilado (dist/). Si se cambia, ejecutarnpm run build:sharedy reiniciarng serve: Vite sirve una copia vieja y la página falla con "does not provide an export named ..." (los tests no lo detectan).- En la API, los tipos que aparecen en firmas con decoradores se importan con
import type(porisolatedModules+emitDecoratorMetadata). Los DTO (clases) sí se importan como valor. - Al cambiar código del backend con
start:devla API se reinicia y se pierden las sesiones. - Los tests del frontend no usan zone.js: tras un
flushhay que esperar conawait new Promise((r) => setTimeout(r))y luegofixture.detectChanges()(whenStableno basta). - Para ver la UI sin la extensión de Chrome se puede abrir Chrome sin cabeza con
--remote-debugging-porty manejarlo por DevTools Protocol (WebSocketde Node 22).
Estado al 2026-10-13
Hecho y verificado contra el Service Layer real (SBOLibros, MSSQL):
- Validación contra Consignación (2026-10-19, 2.0.1):
DP_Stockv3 (SUM+GROUP BY) yDP_Articulofuncionan contra el Service Layer real (confirmado por el usuario; la variante probada fue HANA, la de la v2 rechazada). - Login, Metadatos y Reconstruir (creación de tablas, campos y UDO desde cero).
libs/shared: modelos y dominio portado del .NET. 14 tests.apps/api: Service Layer, autenticación, metadatos, catálogos, planificaciones y PDF. 81 tests unitarios + 6 e2e (incluye la cookie de sesión:HttpOnly,Secure,SameSite=Strict), lint limpio. Prueba de extremo a extremo manual OK: pegar → procesar (FIFO) → cabecera (fecha/hora) → marcar/desmarcar → crear Devolución (ORPD con base en OPDN) →TargetEntrygrabado → cerrar. Varios proveedores OK contra SAP (una planificación con 4 líneas generó 3 Devoluciones, con un proveedor reuniendo dos líneas). Las reglas 409 (una sola abierta, línea con destino, cerrada) OK. Listado paginado y filtrado (total, saltos, estado/almacén/fechas) OK. PDF contra BES (certificación) con 4 Devoluciones reales: individuales y unificado (3 páginas) OK.apps/web: login, guards, Metadatos y Planificaciones completas (listado con filtros y paginación, Nueva, Detalle con crear devoluciones, Rehacer FIFO, PDF, exportar a Excel y cerrar). 46 tests. El .xlsx se abrió en Excel 16 (servidor de pruebas) sin aviso de reparación. Verificado en Chrome real (sin cabeza): login, listado con filtros, selector de almacenes, validación de pegado y la descarga de PDF del Detalle.
Aprendido del Service Layer real:
- Una sesión no admite peticiones simultáneas: las procesa en serie y cada una espera ~7 s tras
la anterior (seis en paralelo = ~40 s; una a una = ~40 ms).
ServiceLayerClient.requestlas encola por sesión; no lo saltes con otro cliente HTTP. Avisa en el log si una tarda > 2 s. CanFind = tYESexigeUserObjectMD_FindColumns; sin ellas SAP lo deja entNOen silencio.GET UserFieldsMDinforma los campos de hora comodb_Numeric/st_Time(el analizador lo normaliza).PATCHdeLIB_DEV1Collectionactualiza las filas porLineIdsin tocar las demás; no se pueden quitar filas de un UDO.- Los campos numéricos sin llenar del UDO vuelven como
0: el mapeo los trata comonull. - Lo que se puede devolver de una Entrada es
PDN1.OpenInvQty, noOpenQty:OpenQtysolo baja con las Devoluciones;OpenInvQtytambién descuenta lo facturado. ConOpenQtySAP rechaza la Devolución con-10 Quantity cannot exceed the quantity in the base document(visto en HANA,SBOTSTLIBROS). POST /LIB_ODEV(n)/Closecierra el UDO. El UDO no se puede borrar (CanDelete = tNO).- Si no hay resultado, el Service Layer responde cuerpo vacío: por eso
/planificaciones/abiertadevuelve siempre un JSON ({abierta: null}).
Sin verificar:
- HANA: el usuario confirmó (2026-10-13) que las consultas
DP_StockyDP_EntradasAbiertas(conPDN1.OpenInvQty, filtro por almacén y parámetros con nombre) funcionan en HANA a través del Service Layer. Falta confirmar en HANA el paginado ($inlinecount/odata.countes de/b1s/v1; en/b1s/v2sería$count) y los tipos que informa en los metadatos (Reconstruir). - Reprocesar solo con tests (no contra SAP).
- Fallo parcial al crear Devoluciones (solo se probó el caso feliz). El corte en lotes de
U_PrintingLineslíneas está probado con tests del algoritmo portado del addon, no contra SAP. - El
PATCHde Reconstruir sobre un UDO ya existente con columnas de búsqueda. - Paginación con varias páginas en la interfaz (hoy hay pocas planificaciones; sí está probada con la API real y con tests de la pantalla).
- Recorrido con clics de Procesar en un navegador real.
- PDF en la base de pruebas: las Devoluciones de
SBOLibrosno tienenFolioNum, así que ahora su descarga informa el error de folio. El PDF contra BES se probó antes del cambio, usando elDocNum. Para repetirlo hace falta una Devolución con folio (la que emite el addon de facturación electrónica). - Despliegue en Windows: el usuario instaló el servicio
DevolucionProveedores(NSSM) en el servidor de pruebas, enC:\tmp\devolucion-proveedores(PORT=3015,SBOTSTLIBROS, HANA), con un release publicado desde el Mac.npm run publicarfunciona también en Windows (2026-10-13). Falta verificar la cookie de sesión en un navegador real sobre HTTPS. OpenInvQty(2026-10-13): la consultaDP_EntradasAbiertasv3 todavía no se ha ejecutado contra el Service Layer de HANA. Se prueba con Rehacer FIFO de la planificación 7 deSBOTSTLIBROS.- Versión del release (2026-10-15): release 1.1.0 generado en el servidor Windows de pruebas (Excel,
valores por defecto de la cabecera, versión visible). Lleva "(sin git)": git no está instalado en ese
servidor, aunque la carpeta es un repositorio con commits y remoto
origin(http://100.68.242.27:30000/george/devolucion_proveedores.git). Los cambios del 2026-10-15 no están en ningún commit: hacerlo (ypush) cuando haya git, antes de seguir trabajando desde el Mac. - Estilos: son CSS porque, en el Mac con macOS 12, el compilador nativo de Sass exige macOS 14 o superior. En Sequoia se puede volver a SCSS si se prefiere.
Siguientes pasos
- Validar en HANA (ver "Sin verificar"):
DB_TYPE=HANA, Reconstruir, repetir la prueba de extremo a extremo. - Probar los casos sin verificar (fallo parcial y reprocesar contra SAP).
- Actualizar el servicio de pruebas con el release que trae
OpenInvQty(DEPLOY.md, sección 11) y rehacer el FIFO de la planificación 7. El botón de pegar desde el portapapeles solo funciona enlocalhosto HTTPS. - Archivar
../SolucionLibros/cuando HANA esté validado (../SolucionLibros/no está bajogit).
Preguntas abiertas para el usuario
- ¿Se agregan UDF de fecha/hora de salida y llegada a la Devolución (ORPD)? Hoy no existen.
- Tamaño de
U_TipoDespacho/U_IndicadorTrasladoen@LIB_ODEV(10) frente a ORPD (1 y 2).
Referencias en el addon .NET (../SolucionLibros/DevolucionProveedores/)
Ya está todo portado; sirve para comparar resultados (sobre todo al validar en HANA).
FormularioDevolucionProveedores.Negocio.cs: parseo, consultas OnHand/FIFO, consolidación.FormularioDevolucionProveedores.Botones.cs: Crear devoluciones (BOM yAdd), descarga de PDF.FormularioDevolucionProveedores.Bom.cs: guardado del UDO (GeneralService) y BOM de la Devolución.WebServices/ConsultaDTE.cs,WebServices/PdfUtils.cs: SOAP BES y unión de PDF.ESPECIFICACION.md: especificación original del proceso.bin/x64/Debug/UserFields.xml(UTF-16): versión XML anterior de los metadatos, ya reemplazada por los JSON deapps/api/src/metadata/definitions/.