Este PR consolida mejoras críticas de integridad transaccional, reglas de negocio en el POS y saneamiento de deuda técnica en el monorepo (apps/api y apps/web):
Endpoints atómicos en productos: Reemplazo de flujos secuenciales y llamadas en ráfaga por operaciones atómicas en backend para precios y categorías.
Control y autorización de descuentos en POS: Reglas de tope al subtotal, umbral del 15% para cajeros con modal de autorización de administrador y validaciones defensivas en API.
Limpieza de deuda técnica e higiene de código: Eliminación de APIs deprecadas de React, unificación de modelos de usuario, centralización de configuración de temas, validación de rutas dinámicas y remoción de código/dependencias huérfanas.
🛠️ Cambios Detallados
1. Operaciones Atómicas de Productos (Backend & Frontend)
Actualización de precios (POST /api/products/:id/price):
Backend (ProductController, ProductService, ProductRepository): Recibe { unitPrice, costUnit }. Archiva en una sola transacción los registros de precio con estado active previos y crea el nuevo con active: true.
Frontend (apps/web/.../productos/page.tsx):handlePriceSubmit ahora ejecuta un único apiClient.post(...), eliminando la secuencia de requests fragmentados que arriesgaba dejar al producto sin precio activo ante fallos de red.
Sincronización de categorías (PATCH /api/products/:id/categories):
Backend: Recibe { categoryIds: number[] }. Limpia las relaciones anteriores e inserta el nuevo conjunto en una única operación atómica.
Frontend (apps/web/.../productos/page.tsx):syncCategories sustituye la ráfaga de Promise.all (POST/DELETE múltiples) por una sola llamada apiClient.patch(...).
2. Límite de Descuentos y Flujo de Autorización (POS & API)
Reglas en POS (apps/web/src/app/pos/page.tsx):
Tope al subtotal: Se configuró max={subtotal} en el input y se calculó effectiveDiscount = Math.min(discount, subtotal) para imposibilitar totales negativos o descuentos inflados.
Límite del 15% sin autorización: Cálculo en tiempo real de maxUnauthDiscount visible como referencia en la cabecera del campo.
Modal de autorización para cajeros: Al intentar aplicar un descuento superior al 15% (si el usuario no es admin ni fue previamente autorizado en ese carrito), se despliega un diálogo (Dialog) para validar credenciales de un administrador contra /api/auth/login.
Checkout: Envío y guardado estricto de effectiveDiscount.
Validaciones en API (apps/api/src/modules/shop-cart/shop-cart.service.ts):
Validación defensiva en métodos create y update: rechazo si discount < 0 o totalPrice < 0.
3. Deuda Técnica e Higiene de Código
Migración de APIs deprecadas: Reemplazo de React.FormEvent por React.SubmitEvent<HTMLFormElement> en 14 ocurrencias sobre 11 archivos:
Eliminado el componente en desuso SectionPlaceholder (apps/web/src/app/_core/components/section-placeholder.tsx).
Removido @prisma/client innecesario de apps/api/package.json (manejado vía @prisma/orm-postgres).
Unificación de tipos y estados compartidos:
auth-user.ts: se unificó AuthUser como alias directo de AppUser (entities.ts), eliminando la redundancia de contratos.
use-theme.tsx: se centralizaron THEME_MODES, THEME_ICONS, THEME_LABELS y el tipo ThemeMode, consumidos homogéneamente por theme-toggle.tsx y user-menu.tsx.
Seguridad en rutas dinámicas:
Validación con Number.isNaN(...) e invocación a notFound() de next/navigation en admin/inventario/[id]/page.tsx y admin/proveedores/[id]/page.tsx para evitar solicitudes a /api/.../NaN.
✅ Verificación y Calidad
Linting:npm run lint validado con 0 errores y 0 warnings en todo el workspace.
Build:npm run build completado exitosamente tanto para @pdv/web (Next.js) como para @pdv/api (NestJS).
Flujo transaccional probado: Verificado el archivado y alta atómica de precios y categorías sin inconsistencias en BD.
Flujo POS probado: Verificada la apertura del diálogo de autorización al superar el 15% de descuento con credenciales de cajero.
## 📌 Descripción General
Este PR consolida mejoras críticas de integridad transaccional, reglas de negocio en el POS y saneamiento de deuda técnica en el monorepo (`apps/api` y `apps/web`):
1. **Endpoints atómicos en productos:** Reemplazo de flujos secuenciales y llamadas en ráfaga por operaciones atómicas en backend para precios y categorías.
2. **Control y autorización de descuentos en POS:** Reglas de tope al subtotal, umbral del 15% para cajeros con modal de autorización de administrador y validaciones defensivas en API.
3. **Limpieza de deuda técnica e higiene de código:** Eliminación de APIs deprecadas de React, unificación de modelos de usuario, centralización de configuración de temas, validación de rutas dinámicas y remoción de código/dependencias huérfanas.
---
## 🛠️ Cambios Detallados
### 1. Operaciones Atómicas de Productos (Backend & Frontend)
- **Actualización de precios (`POST /api/products/:id/price`):**
- **Backend (`ProductController`, `ProductService`, `ProductRepository`):** Recibe `{ unitPrice, costUnit }`. Archiva en una sola transacción los registros de precio con estado `active` previos y crea el nuevo con `active: true`.
- **Frontend (`apps/web/.../productos/page.tsx`):** `handlePriceSubmit` ahora ejecuta un único `apiClient.post(...)`, eliminando la secuencia de requests fragmentados que arriesgaba dejar al producto sin precio activo ante fallos de red.
- **Sincronización de categorías (`PATCH /api/products/:id/categories`):**
- **Backend:** Recibe `{ categoryIds: number[] }`. Limpia las relaciones anteriores e inserta el nuevo conjunto en una única operación atómica.
- **Frontend (`apps/web/.../productos/page.tsx`):** `syncCategories` sustituye la ráfaga de `Promise.all` (`POST`/`DELETE` múltiples) por una sola llamada `apiClient.patch(...)`.
---
### 2. Límite de Descuentos y Flujo de Autorización (POS & API)
- **Reglas en POS (`apps/web/src/app/pos/page.tsx`):**
- **Tope al subtotal:** Se configuró `max={subtotal}` en el input y se calculó `effectiveDiscount = Math.min(discount, subtotal)` para imposibilitar totales negativos o descuentos inflados.
- **Límite del 15% sin autorización:** Cálculo en tiempo real de `maxUnauthDiscount` visible como referencia en la cabecera del campo.
- **Modal de autorización para cajeros:** Al intentar aplicar un descuento superior al 15% (si el usuario no es admin ni fue previamente autorizado en ese carrito), se despliega un diálogo (`Dialog`) para validar credenciales de un administrador contra `/api/auth/login`.
- **Checkout:** Envío y guardado estricto de `effectiveDiscount`.
- **Validaciones en API (`apps/api/src/modules/shop-cart/shop-cart.service.ts`):**
- Validación defensiva en métodos `create` y `update`: rechazo si `discount < 0` o `totalPrice < 0`.
---
### 3. Deuda Técnica e Higiene de Código
- **Migración de APIs deprecadas:** Reemplazo de `React.FormEvent` por `React.SubmitEvent<HTMLFormElement>` en 14 ocurrencias sobre 11 archivos:
- `admin/clientes/page.tsx`
- `admin/inventario/page.tsx`
- `admin/inventario/[id]/page.tsx` (2 handlers)
- `admin/inventario/categorias/page.tsx`
- `admin/inventario/productos/page.tsx` (2 handlers)
- `admin/proveedores/page.tsx`
- `admin/usuarios/page.tsx`
- `login/_components/password-form.tsx`
- `pos/page.tsx`
- `pos/_components/cash-register-control.tsx` (3 handlers)
- **Depuración de dependencias y código muerto:**
- Eliminado el componente en desuso `SectionPlaceholder` (`apps/web/src/app/_core/components/section-placeholder.tsx`).
- Removido `@prisma/client` innecesario de `apps/api/package.json` (manejado vía `@prisma/orm-postgres`).
- **Unificación de tipos y estados compartidos:**
- `auth-user.ts`: se unificó `AuthUser` como alias directo de `AppUser` (`entities.ts`), eliminando la redundancia de contratos.
- `use-theme.tsx`: se centralizaron `THEME_MODES`, `THEME_ICONS`, `THEME_LABELS` y el tipo `ThemeMode`, consumidos homogéneamente por `theme-toggle.tsx` y `user-menu.tsx`.
- **Seguridad en rutas dinámicas:**
- Validación con `Number.isNaN(...)` e invocación a `notFound()` de `next/navigation` en `admin/inventario/[id]/page.tsx` y `admin/proveedores/[id]/page.tsx` para evitar solicitudes a `/api/.../NaN`.
---
## ✅ Verificación y Calidad
- [x] **Linting:** `npm run lint` validado con 0 errores y 0 warnings en todo el workspace.
- [x] **Build:** `npm run build` completado exitosamente tanto para `@pdv/web` (Next.js) como para `@pdv/api` (NestJS).
- [x] **Flujo transaccional probado:** Verificado el archivado y alta atómica de precios y categorías sin inconsistencias en BD.
- [x] **Flujo POS probado:** Verificada la apertura del diálogo de autorización al superar el 15% de descuento con credenciales de cajero.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📌 Descripción General
Este PR consolida mejoras críticas de integridad transaccional, reglas de negocio en el POS y saneamiento de deuda técnica en el monorepo (
apps/apiyapps/web):🛠️ Cambios Detallados
1. Operaciones Atómicas de Productos (Backend & Frontend)
POST /api/products/:id/price):ProductController,ProductService,ProductRepository): Recibe{ unitPrice, costUnit }. Archiva en una sola transacción los registros de precio con estadoactiveprevios y crea el nuevo conactive: true.apps/web/.../productos/page.tsx):handlePriceSubmitahora ejecuta un únicoapiClient.post(...), eliminando la secuencia de requests fragmentados que arriesgaba dejar al producto sin precio activo ante fallos de red.PATCH /api/products/:id/categories):{ categoryIds: number[] }. Limpia las relaciones anteriores e inserta el nuevo conjunto en una única operación atómica.apps/web/.../productos/page.tsx):syncCategoriessustituye la ráfaga dePromise.all(POST/DELETEmúltiples) por una sola llamadaapiClient.patch(...).2. Límite de Descuentos y Flujo de Autorización (POS & API)
apps/web/src/app/pos/page.tsx):max={subtotal}en el input y se calculóeffectiveDiscount = Math.min(discount, subtotal)para imposibilitar totales negativos o descuentos inflados.maxUnauthDiscountvisible como referencia en la cabecera del campo.Dialog) para validar credenciales de un administrador contra/api/auth/login.effectiveDiscount.apps/api/src/modules/shop-cart/shop-cart.service.ts):createyupdate: rechazo sidiscount < 0ototalPrice < 0.3. Deuda Técnica e Higiene de Código
React.FormEventporReact.SubmitEvent<HTMLFormElement>en 14 ocurrencias sobre 11 archivos:admin/clientes/page.tsxadmin/inventario/page.tsxadmin/inventario/[id]/page.tsx(2 handlers)admin/inventario/categorias/page.tsxadmin/inventario/productos/page.tsx(2 handlers)admin/proveedores/page.tsxadmin/usuarios/page.tsxlogin/_components/password-form.tsxpos/page.tsxpos/_components/cash-register-control.tsx(3 handlers)SectionPlaceholder(apps/web/src/app/_core/components/section-placeholder.tsx).@prisma/clientinnecesario deapps/api/package.json(manejado vía@prisma/orm-postgres).auth-user.ts: se unificóAuthUsercomo alias directo deAppUser(entities.ts), eliminando la redundancia de contratos.use-theme.tsx: se centralizaronTHEME_MODES,THEME_ICONS,THEME_LABELSy el tipoThemeMode, consumidos homogéneamente portheme-toggle.tsxyuser-menu.tsx.Number.isNaN(...)e invocación anotFound()denext/navigationenadmin/inventario/[id]/page.tsxyadmin/proveedores/[id]/page.tsxpara evitar solicitudes a/api/.../NaN.✅ Verificación y Calidad
npm run lintvalidado con 0 errores y 0 warnings en todo el workspace.npm run buildcompletado exitosamente tanto para@pdv/web(Next.js) como para@pdv/api(NestJS).View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.