Aplicación web de Devolución a Proveedores para SAP Business One

Reemplaza al addon .NET Framework (SolucionLibros). Genera Devoluciones de mercancías
enlazadas a sus Entradas por compra a partir de un listado pegado desde Excel.

- libs/shared (@dp/shared): modelos y lógica de dominio pura (FIFO, consolidación por
  proveedor, parseo del portapapeles), portada del addon.
- apps/api (NestJS): cliente del Service Layer con cola por sesión, login con la sesión de
  SAP, metadatos (UDT/UDF/UDO) y botón Reconstruir, catálogos, planificaciones (pegar,
  procesar, reprocesar, marcar líneas, crear Devoluciones, cerrar), listado paginado con
  filtros y descarga de PDF de la Guía de despacho desde BES. Sirve también la web
  compilada, con HTTPS opcional.
- apps/web (Angular): login, Configuración → Metadatos y Planificaciones (listado,
  nueva, detalle).
- deploy/windows y scripts/publicar.mjs: despliegue como servicio de Windows con WinSW.

Verificado contra el Service Layer de SQL Server. Sin probar aún: SAP HANA y la
instalación del servicio en Windows (ver CLAUDE.md, sección "Sin verificar").

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
georgeandClaude Sonnet 5.5 committed 2026-10-13 08:09:07 -05:00
commit 287b187c14
126 files changed
+20033

No files matched your search

+64
View File
@@ -0,0 +1,64 @@
import { Body, Controller, Get, HttpCode, Post, Req, Res } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import type { InfoConexion, UsuarioSesion } from '@dp/shared';
import type { Request, Response } from 'express';
import { AppConfig } from '../config/configuration.js';
import { ServiceLayerClient } from '../service-layer/service-layer.client.js';
import { SessionStore } from '../service-layer/session.store.js';
import type { SapSession } from '../service-layer/session.store.js';
import { LoginDto } from './dto/login.dto.js';
import { CurrentSession, Public, SESSION_COOKIE } from './session.guard.js';
function aUsuario(sesion: SapSession): UsuarioSesion {
return { userName: sesion.userName, companyDb: sesion.companyDb, version: sesion.version };
}
@Controller('auth')
export class AuthController {
constructor(
private readonly serviceLayer: ServiceLayerClient,
private readonly sessions: SessionStore,
private readonly config: ConfigService<AppConfig, true>,
) {}
@Public()
@Get('info')
info(): InfoConexion {
return {
companyDb: this.config.get('serviceLayer', { infer: true }).companyDb,
dbType: this.config.get('dbType', { infer: true }),
};
}
@Public()
@Post('login')
@HttpCode(200)
async login(@Body() dto: LoginDto, @Res({ passthrough: true }) res: Response): Promise<UsuarioSesion> {
const sesion = await this.serviceLayer.login(dto.userName, dto.password);
res.cookie(SESSION_COOKIE, sesion.id, {
httpOnly: true,
sameSite: 'strict',
secure: this.config.get('cookieSecure', { infer: true }),
path: '/',
});
return aUsuario(sesion);
}
@Public()
@Post('logout')
@HttpCode(204)
async logout(@Req() req: Request, @Res({ passthrough: true }) res: Response): Promise<void> {
const sesion = this.sessions.obtener(req.cookies?.[SESSION_COOKIE] as string | undefined);
if (sesion) {
await this.serviceLayer.logout(sesion);
}
res.clearCookie(SESSION_COOKIE, { path: '/' });
}
@Get('me')
me(@CurrentSession() sesion: SapSession): UsuarioSesion {
return aUsuario(sesion);
}
}