Files
devolucion_proveedores/CLAUDE.md
T

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):

  1. Pegar: ItemCode + cantidad desde Excel (con el botón Pegar desde el portapapeles o Ctrl/Cmd+V en el cuadro). Se valida contra la Consignación del almacén elegido (ver Decisiones). No guarda.
  2. 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.
  3. Crear devoluciones: agrupa las líneas marcadas sin TargetEntry por proveedor y las parte en lotes de U_PrintingLines líneas (11 por defecto). Crea una Devolución por lote y graba U_TargetType/U_TargetEntry en las líneas.
  4. 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_Status de cabecera eliminado. El estado usa el Status estándar del UDO (O/C).
  • Se usan U_TipoDespacho y U_IndicadorTraslado en @LIB_ODEV. Se envían a la Devolución como U_BFE_TipoDespacho / U_BFE_IndTraslado.
  • Cantidades y referencias son enteros (db_Numeric): Quantity, OnHand, OpenQty, AllocatedQty, BaseEntry, BaseLine, BaseRef, TargetEntry y U_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 TargetEntry ya no se puede desmarcar (ver puedeCambiarSeleccion en libs/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 fallidas U_ErrCode/U_ErrMsg y se reintentan.
  • El "stock" que se muestra y valida es la Consignación, no OITW.OnHand: DP_Stock (v3) suma PDN1.OpenInvQty de las líneas abiertas de Entradas con U_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: entonces DP_Articulo distingue artículo existente (0, queda "Sin stock") de inexistente (error). El analizador SQL del Service Layer no admite COALESCE ni subconsultas (error Cannot support this function or expression): solo JOIN + GROUP BY. La columna se sigue llamando OnHand en 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 SQLQueries del Service Layer (no hay conexión directa a la BD). La API crea/actualiza DP_Stock y DP_EntradasAbiertas sola (variante según DB_TYPE). El analizador SQL del Service Layer no admite FORMAT y SAP reescribe el SqlText: al cambiar el SQL hay que subir version en sql-queries.definitions.ts. Los parámetros van entre comillas simples. DP_EntradasAbiertas (v3) lee la cantidad abierta de PDN1.OpenInvQty (no OpenQty); el campo de usuario U_OpenQty del 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.ts arma el sobre y lee la respuesta a mano y BesClient lo envía con axios (SOAPAction http://tempuri.org/Consulta_TXTDTE). Credenciales y URL salen de U_DTE_CONFC y nunca se registran ni se devuelven al navegador. Solo se consulta con el FolioNumber de 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 el DocNum como respaldo; se descartó porque podría traer el DTE de otro documento). Unir con pdf-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.ts arma 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 traslado 7 (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_PATH o HTTPS_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 publicar arma release/devolucion-proveedores/ (API + web + solo dependencias de producción, ~55 MB, sin enlaces simbólicos: @dp/shared va 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 version del package.json raíz. Cada release sube la versión y el número lo decide el usuario (preguntarle antes): npm run version:parche|menor|mayor y luego npm run publicar, que deja app/version.json (número, fecha y commit). La API lo lee al arrancar (src/version.ts), lo informa en /auth/info y /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 Logger de 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). Con NO_COLOR=1 el 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) y LIB_DEV1 (bott_DocumentLines).
  • UDO: LIB_ODEV (boud_Document), hija LIB_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_IndTraslado y la tabla @DTE_CONFC.

Preparar el entorno (otro computador)

  1. Node 22 LTS o superior (node -v).
  2. 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
    
  3. Instalar y configurar:
    npm install
    cp apps/api/.env.example apps/api/.env   # completar SL_URL, SL_COMPANY_DB, DB_TYPE
    
    Variables del .env de la API: PORT (3000), WEB_ORIGIN (CORS), SL_URL (con /b1s/v1), SL_COMPANY_DB, SL_REJECT_UNAUTHORIZED (false si el certificado es autofirmado), DB_TYPE (HANA/MSSQL) y COOKIE_SECURE (true solo 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_PASSPHRASE o HTTPS_KEY_PATH/HTTPS_CERT_PATH, HTTP_REDIRECT_PORT y WEB_DIST_DIR (opcional).
  4. Desarrollo, en dos terminales:
    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)
    
    El puerto de ng serve está en apps/web/angular.json y el destino del proxy en apps/web/src/proxy.conf.json: si cambia PORT, 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 --watch a veces no logra terminar el proceso anterior al recompilar y se cae: basta con volver a ejecutar npm run dev:api. dev:web usa ng serve --poll 1500: en el Windows Server de pruebas (Node 24) el vigilante nativo (@parcel/watcher) hace caer ng serve con 0xC0000005 justo después de "Watch mode enabled". Solo afecta al desarrollo; producción sirve la web compilada y no vigila archivos.
  5. 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/shared se consume compilado (dist/). Si se cambia, ejecutar npm run build:shared y reiniciar ng 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 (por isolatedModules + emitDecoratorMetadata). Los DTO (clases) sí se importan como valor.
  • Al cambiar código del backend con start:dev la API se reinicia y se pierden las sesiones.
  • Los tests del frontend no usan zone.js: tras un flush hay que esperar con await new Promise((r) => setTimeout(r)) y luego fixture.detectChanges() (whenStable no basta).
  • Para ver la UI sin la extensión de Chrome se puede abrir Chrome sin cabeza con --remote-debugging-port y manejarlo por DevTools Protocol (WebSocket de 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_Stock v3 (SUM + GROUP BY) y DP_Articulo funcionan 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) → TargetEntry grabado → 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.request las encola por sesión; no lo saltes con otro cliente HTTP. Avisa en el log si una tarda > 2 s.
  • CanFind = tYES exige UserObjectMD_FindColumns; sin ellas SAP lo deja en tNO en silencio.
  • GET UserFieldsMD informa los campos de hora como db_Numeric/st_Time (el analizador lo normaliza).
  • PATCH de LIB_DEV1Collection actualiza las filas por LineId sin 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 como null.
  • Lo que se puede devolver de una Entrada es PDN1.OpenInvQty, no OpenQty: OpenQty solo baja con las Devoluciones; OpenInvQty también descuenta lo facturado. Con OpenQty SAP rechaza la Devolución con -10 Quantity cannot exceed the quantity in the base document (visto en HANA, SBOTSTLIBROS).
  • POST /LIB_ODEV(n)/Close cierra el UDO. El UDO no se puede borrar (CanDelete = tNO).
  • Si no hay resultado, el Service Layer responde cuerpo vacío: por eso /planificaciones/abierta devuelve siempre un JSON ({abierta: null}).

Sin verificar:

  • HANA: el usuario confirmó (2026-10-13) que las consultas DP_Stock y DP_EntradasAbiertas (con PDN1.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.count es de /b1s/v1; en /b1s/v2 serí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_PrintingLines líneas está probado con tests del algoritmo portado del addon, no contra SAP.
  • El PATCH de 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 SBOLibros no tienen FolioNum, así que ahora su descarga informa el error de folio. El PDF contra BES se probó antes del cambio, usando el DocNum. 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, en C:\tmp\devolucion-proveedores (PORT=3015, SBOTSTLIBROS, HANA), con un release publicado desde el Mac. npm run publicar funciona 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 consulta DP_EntradasAbiertas v3 todavía no se ha ejecutado contra el Service Layer de HANA. Se prueba con Rehacer FIFO de la planificación 7 de SBOTSTLIBROS.
  • 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 (y push) 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

  1. Validar en HANA (ver "Sin verificar"): DB_TYPE=HANA, Reconstruir, repetir la prueba de extremo a extremo.
  2. Probar los casos sin verificar (fallo parcial y reprocesar contra SAP).
  3. 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 en localhost o HTTPS.
  4. Archivar ../SolucionLibros/ cuando HANA esté validado (../SolucionLibros/ no está bajo git).

Preguntas abiertas para el usuario

  1. ¿Se agregan UDF de fecha/hora de salida y llegada a la Devolución (ORPD)? Hoy no existen.
  2. Tamaño de U_TipoDespacho/U_IndicadorTraslado en @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 y Add), 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 de apps/api/src/metadata/definitions/.