Files
cotizador/AGENTS.md
T
urieljarethandClaude Fable 5 69c74faabb Restaurar funcionalidades desde stash + setup de despliegue Coolify
- Recupera el trabajo revertido (doble propuesta, retainer, niveles, API Python) desde el stash de GitHub Desktop
- Dockerfile multi-stage para Next.js (migraciones automáticas al arrancar, seed opcional via RUN_SEED)
- docker-compose.coolify.yml: postgres + web + api con healthchecks y SERVICE_FQDN_*
- Endpoint público /api/health (verifica BD) para healthchecks
- seed.ts parametrizado: conexión DB_* y credenciales SEED_* por entorno (sin passwords hardcodeadas)
- .env.example, .dockerignore, uvicorn con --proxy-headers
- Limpieza: .xlsx y __pycache__ fuera del repo

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-10 02:21:18 -06:00

97 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md — Cotizador E3
## Quick Reference
| Command | Purpose |
|---------|---------|
| `npm run dev` | Start Next.js dev server on port 3000 |
| `npm run build` | Production build (runs TypeScript check) |
| `npm run lint` | ESLint (flat config, eslint-config-next) |
| `npx tsx prisma/seed.ts` | Run seed (upserts all data, idempotent) |
| `npx prisma migrate dev` | Create/apply migration |
| `npx prisma generate` | Regenerate Prisma client |
| `npx prisma studio` | Prisma Studio GUI |
**Windows environment.** Use `start.bat` / `stop.bat` to manage Docker PostgreSQL + Next.js together. PowerShell is the shell. Paths with brackets (e.g. `[id]`) require `-LiteralPath` in PowerShell, not `-Path`.
**Two backends, one database.** The Next.js app (`src/`, port 3000) and a standalone Python FastAPI service (`api/`, port 8000) both talk to the same PostgreSQL DB. Prisma owns the schema/migrations; the Python API reads/writes the same tables independently. `docker-compose.yml` runs `postgres` + the `api` service; Next.js is run separately via `npm run dev` / `start.bat`.
## Prisma 7 — Critical Gotchas
- **Prisma client is NOT at `@prisma/client`.** It's generated to `src/generated/prisma/` and imported as `@/generated/prisma/client`.
- **Uses `PrismaPg` driver adapter** (from `@prisma/adapter-pg`) with individual connection params (host/port/user/password/database), NOT a connection string. The `connectionString` approach causes SCRAM auth errors with postgres:postgres.
- **`prisma.config.ts`** has no `seed` property (unsupported by PrismaConfig type). Run seed manually with `npx tsx prisma/seed.ts`.
- After changing `schema.prisma`: run `npx prisma migrate dev`, then `npx prisma generate`.
- The `globalForPrisma` singleton pattern in `src/lib/db.ts` prevents hot-reload connection leaks in dev.
## PDFKit — Server-Side PDF
- `pdfkit` is excluded from Next.js bundling via `serverExternalPackages` in `next.config.ts`. Without this, font metric files (`.afm`) can't be resolved at runtime.
- **Footer auto-page-break bug:** `doc.text()` at `y > page.height - margins.bottom` triggers pdfkit's automatic page addition. To write footers in the bottom margin, temporarily set `doc.page.margins.bottom = 0`, write, then restore. Use `bufferPages: true` + `switchToPage()` to add footers after all content.
- Returns `Promise<Buffer>` — convert to `Uint8Array` for `NextResponse` body.
## Next.js 16 Conventions
- **Async params:** Dynamic route params are `Promise<{ id: string }>` — must `await params` before use.
- **All pages are `force-dynamic`** — no static generation or ISR.
- **Tailwind CSS v4** — no `tailwind.config.*` file. Config is in `postcss.config.mjs` (using `@tailwindcss/postcss`) and CSS custom properties via `@theme inline` in `src/app/globals.css`.
## Data Model Notes
- **Configuracion** is a key-value store (`clave`/`valor`), not a traditional settings model. New config keys: `color_primario`, `color_secundario`, `logo_base64` (branding for PDF export).
- **Cascading deletes:** Cotizacion → ServicioCotizado, PlanBucefaloCotizacion use `onDelete: Cascade`.
- Cotización number format: `UJ{YY}{MM}{AsesorInitials}{seq}` (e.g. `UJ2605AG001`) — see `generarNumeroCotizacion` in `src/lib/calculators.ts`. Sequence is 3 digits, zero-padded.
- Vigencia = 15 business days from fecha (excludes Sat/Sun) — `calcularVigencia`.
- IVA_RATE = 0.16 (16%).
- **Bucéfalo CRM plan prices** (in `calculators.ts`, NOT the DB): basico=$1,000, estandar=$3,500, premium=$4,500, empresarial=$7,500 (monthly).
- **Financing** lives in the `FinanciamientoPlan` table (3/6/9/12 months). Formula: `comisionTotal = monto × comision%`, `pagoMensual = (monto + comisionTotal) × (1 + tasa) / meses`, then add 16% IVA. Both backends must keep this formula identical.
## Auth
- **JWT sessions** (`src/lib/auth.ts`) signed with `jose` (HS256, 7-day expiry), stored in the `cotizador-session` httpOnly cookie. `JWT_SECRET` env var is **required** (throws at startup if missing).
- **`src/middleware.ts`** gates everything except `PUBLIC_PATHS` (`/login`, `/api/auth/login`, `/api/auth/logout`, `/api/health`). Unauthenticated API calls → 401 JSON; pages → redirect to `/login`. On success it injects `x-user-id` / `x-user-role` request headers for downstream handlers.
- Passwords hashed with `bcryptjs`. Roles: `asesor` (default), and others stored as free-form strings.
## Deployment (Coolify)
- Production stack: `docker-compose.coolify.yml` (postgres + web + api). In Coolify set **Docker Compose Location** to `/docker-compose.coolify.yml`. Local dev keeps using `docker-compose.yml` + `start.bat`.
- The `web` container runs `prisma migrate deploy` on every start, and the seed only when `RUN_SEED=true`. Seed users/passwords come from `SEED_*` env vars (see `.env.example`) — `prisma/seed.ts` never overwrites an existing password unless the env var is set.
- `DATABASE_URL` is only used by the Prisma CLI (migrations); the apps use individual `DB_*` vars. Both must point to the same DB.
- Healthchecks: web `GET /api/health` (public in middleware, pings DB), api `GET /health`.
- Root `Dockerfile` builds Next.js with `JWT_SECRET=placeholder-build-only` (auth.ts throws at import without it; all pages are force-dynamic so nothing gets baked).
- `*.xlsx` is gitignored (business files must never be committed).
## Architecture
```
src/
app/
(app)/ # Authed route group: dashboard, cotizaciones, clientes, catalogo, configuracion (has its own layout.tsx + Sidebar)
api/ # Next.js route handlers (REST): auth, catalogo, categorias, cotizaciones, configuracion, paquetes, export, import
login/ # Public login page
components/ # CotizacionForm, ExportButtons, EstadoBadge, layout/Sidebar
lib/ # auth, db, store, calculators, pdf-generator, schemas, config-helpers
generated/prisma/ # Prisma client output (gitignored)
prisma/
schema.prisma # 13 models (User, Cliente, Cotizacion, Categoria, Paquete, FasePaquete,
# ServicioCatalogo, ServicioPaquete, ServicioCotizado, PlanBucefaloCotizacion,
# Configuracion, Bono, FinanciamientoPlan)
seed.ts # All catalog data (services, categorias, bonos, planes, config) — idempotent upserts
migrations/ # 3 migrations
api/ # Standalone Python FastAPI + MCP server (see below)
```
- **Zustand store** (`src/lib/store.ts`) holds the cotización draft. Used by both the new (`cotizaciones/nueva`) and edit (`cotizaciones/[id]/editar`) pages, both of which render `CotizacionForm.tsx`.
- **ExportButtons.tsx** has 4 variants: `ExportExcelButtonSaved` / `ExportPDFButtonSaved` (GET by ID) and `ExportExcelButtonDraft` / `ExportPDFButtonDraft` (POST with body).
## Python API (`api/`) — Optional Second Backend
- **FastAPI app** (`api/main.py`) exposing the same domain as REST, **plus an MCP server at `/mcp`** for AI agents (n8n, Claude, ChatGPT). Tools/resources defined in `api/app/mcp/`.
- Auth: **API key** (`X-API-Key` or `Authorization: Bearer`) for agents; JWT for human login. Routers in `api/app/routers/`, business logic in `api/app/services/` (its own `calculators.py`, `pdf_generator.py`, `excel_generator.py` — mirror the TS versions).
- Run: `cd api && pip install -r requirements.txt && uvicorn main:app --reload --port 8000`. Swagger at `/docs`. Full endpoint reference in `api/COTIZADOR_API_SKILL.md`.
- It reads `DB_*`, `API_KEY`, `JWT_SECRET` env vars (same DB as Prisma).
## Domain
Quotation system for Consultoría E3 (digital marketing agency in Querétaro, MX). Services organized in 4 phases: Fase 0 (Auditorías), Fase 1 (Setup/Infra), Fase 2 (Publicidad/Manejo), Fase 3 (Contenido/SEO). Two payment types: `unico` (one-time) and `mensual` (recurring). Optional Bucéfalo CRM plans and Openpay financing.