feat: Initial commit - michangarrito

Marketplace móvil para negocios locales mexicanos.

Estructura inicial:
- apps/backend (NestJS API)
- apps/frontend (React Web)
- apps/mobile (Expo/React Native)
- apps/mcp-server (Claude MCP Server)
- apps/whatsapp-service (WhatsApp Business API)
- database/ (PostgreSQL DDL)
- docs/ (Documentación)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rckrdmrd 2026-01-07 04:41:02 -06:00
commit 48dea7a5d0
63870 changed files with 7730821 additions and 0 deletions

432
.env Normal file
View File

@ -0,0 +1,432 @@
# =============================================================================
# MICHANGARRITO - VARIABLES DE ENTORNO
# =============================================================================
# Copiar este archivo a .env y configurar los valores
# Generado: 2026-01-04
# =============================================================================
# -----------------------------------------------------------------------------
# APLICACION
# -----------------------------------------------------------------------------
NODE_ENV=development
APP_NAME=michangarrito
APP_VERSION=1.0.0
APP_DESCRIPTION="POS inteligente para micro-negocios"
# -----------------------------------------------------------------------------
# PUERTOS DE SERVICIOS
# -----------------------------------------------------------------------------
WEB_PORT=3140
BACKEND_PORT=3141
MCP_PORT=3142
WHATSAPP_PORT=3143
MOBILE_METRO_PORT=8081
# -----------------------------------------------------------------------------
# BASE DE DATOS POSTGRESQL
# -----------------------------------------------------------------------------
DB_HOST=localhost
DB_PORT=5432
DB_NAME=michangarrito_dev
DB_USER=michangarrito_dev
# Generar con: openssl rand -base64 32
DB_PASSWORD=MCh_dev_2025_secure
# URL de conexion completa (se construye automaticamente)
DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}
# Pool de conexiones
DB_POOL_SIZE=10
DB_POOL_MIN=2
# SSL (solo produccion)
DB_SSL=false
# -----------------------------------------------------------------------------
# REDIS
# -----------------------------------------------------------------------------
REDIS_HOST=localhost
REDIS_PORT=6389
REDIS_PASSWORD=
REDIS_DATABASE=0
REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}/${REDIS_DATABASE}
# -----------------------------------------------------------------------------
# JWT / AUTENTICACION
# -----------------------------------------------------------------------------
# Generar con: openssl rand -base64 64
JWT_SECRET=MCh_jwt_dev_2025_AcB7x9KmPq2sWfHg4jL8nRt0vYzXw1CdEiFoGhJkLmN3pQrSuVxY5bZ6aBeC8fDg
JWT_EXPIRES_IN=7d
JWT_REFRESH_EXPIRES_IN=30d
# OTP para login
OTP_EXPIRES_MINUTES=5
OTP_LENGTH=6
# PIN de acceso rapido
PIN_LENGTH=4
PIN_MAX_ATTEMPTS=3
PIN_LOCKOUT_MINUTES=30
# Biometrico
BIOMETRIC_ENABLED=true
# -----------------------------------------------------------------------------
# WHATSAPP BUSINESS API (Meta)
# -----------------------------------------------------------------------------
# Verificacion del webhook
WHATSAPP_VERIFY_TOKEN=MCh_wa_verify_2025_x7k9m3p5q8r2t4w6y0
# Token de acceso (obtener de Meta Business)
WHATSAPP_ACCESS_TOKEN=
# ID del numero de telefono
WHATSAPP_PHONE_NUMBER_ID=
# ID de la cuenta de negocio
WHATSAPP_BUSINESS_ACCOUNT_ID=
# Webhook URL (para configurar en Meta)
WHATSAPP_WEBHOOK_URL=https://tu-dominio.com/webhook/whatsapp
# Numero compartido de la plataforma (para multi-tenant)
WHATSAPP_PLATFORM_NUMBER=+52XXXXXXXXXX
# -----------------------------------------------------------------------------
# LLM / INTELIGENCIA ARTIFICIAL
# -----------------------------------------------------------------------------
# Proveedor: openrouter, openai, anthropic, ollama
LLM_PROVIDER=openrouter
# API Key del proveedor
LLM_API_KEY=
# Modelo a usar
# OpenRouter: anthropic/claude-3-haiku, openai/gpt-4o-mini, meta-llama/llama-3-8b
# OpenAI: gpt-4o-mini, gpt-4o
# Anthropic: claude-3-haiku-20240307, claude-3-sonnet-20240229
# Ollama: llama3, mistral, phi3
LLM_MODEL=anthropic/claude-3-haiku
# URL base del proveedor
# OpenRouter: https://openrouter.ai/api/v1
# OpenAI: https://api.openai.com/v1
# Anthropic: https://api.anthropic.com/v1
# Ollama: http://localhost:11434/api
LLM_BASE_URL=https://openrouter.ai/api/v1
# Limites
LLM_MAX_TOKENS=4096
LLM_TEMPERATURE=0.7
# Tokens por defecto para nuevos usuarios
LLM_DEFAULT_TOKENS=500
# -----------------------------------------------------------------------------
# MCP SERVER (Model Context Protocol)
# -----------------------------------------------------------------------------
MCP_SERVER_NAME=michangarrito-mcp
MCP_SERVER_VERSION=1.0.0
MCP_LOG_LEVEL=info
# Herramientas habilitadas (separadas por coma)
MCP_ENABLED_TOOLS=ventas,productos,inventario,clientes,fiados,pedidos,reportes,configuracion
# -----------------------------------------------------------------------------
# STRIPE (Suscripciones y Pagos)
# -----------------------------------------------------------------------------
# Claves de API (modo test)
# Obtener de: https://dashboard.stripe.com/test/apikeys
STRIPE_SECRET_KEY=sk_test_
STRIPE_PUBLISHABLE_KEY=pk_test_
# Webhook secret
# Obtener de: https://dashboard.stripe.com/test/webhooks
STRIPE_WEBHOOK_SECRET=whsec_
# IDs de productos/precios (crear en Stripe Dashboard)
STRIPE_PRICE_CHANGARRITO=price_
STRIPE_PRICE_TIENDITA=price_
STRIPE_PRICE_TOKENS_1000=price_
STRIPE_PRICE_TOKENS_3000=price_
STRIPE_PRICE_TOKENS_8000=price_
STRIPE_PRICE_TOKENS_20000=price_
# Referencia OXXO
STRIPE_OXXO_ENABLED=true
# -----------------------------------------------------------------------------
# MERCADO PAGO (Terminal de Pago)
# -----------------------------------------------------------------------------
# Access Token (obtener de Mercado Pago Developers)
MERCADOPAGO_ACCESS_TOKEN=
# Public Key
MERCADOPAGO_PUBLIC_KEY=
# User ID (para Point integration)
MERCADOPAGO_USER_ID=
# Device ID de la terminal
MERCADOPAGO_DEVICE_ID=
# Webhook URL
MERCADOPAGO_WEBHOOK_URL=https://tu-dominio.com/webhook/mercadopago
# -----------------------------------------------------------------------------
# CLIP (Terminal de Pago)
# -----------------------------------------------------------------------------
# API Key (obtener de Clip Dashboard)
CLIP_API_KEY=
# Merchant ID
CLIP_MERCHANT_ID=
# Device ID de la terminal
CLIP_DEVICE_ID=
# Webhook URL
CLIP_WEBHOOK_URL=https://tu-dominio.com/webhook/clip
# -----------------------------------------------------------------------------
# CODI (Banxico QR)
# -----------------------------------------------------------------------------
# Habilitado
CODI_ENABLED=true
# CLABE virtual para recepcion
CODI_CLABE=
# Certificado (path o base64)
CODI_CERTIFICATE=
# Llave privada (path o base64)
CODI_PRIVATE_KEY=
# -----------------------------------------------------------------------------
# FIREBASE (Push Notifications)
# -----------------------------------------------------------------------------
# Project ID
FIREBASE_PROJECT_ID=
# Service Account (JSON)
# Opcion 1: Path al archivo
FIREBASE_SERVICE_ACCOUNT_PATH=./firebase-service-account.json
# Opcion 2: JSON como string (para produccion)
FIREBASE_SERVICE_ACCOUNT_JSON=
# Habilitado
FIREBASE_PUSH_ENABLED=true
# -----------------------------------------------------------------------------
# OCR / VISION (Procesamiento de Imagenes)
# -----------------------------------------------------------------------------
# Google Cloud Vision
# Obtener de: https://console.cloud.google.com/apis/credentials
GOOGLE_VISION_API_KEY=
# Alternativa: Tesseract local
OCR_PROVIDER=google
# OCR_PROVIDER=tesseract
# -----------------------------------------------------------------------------
# TRANSCRIPCION DE AUDIO
# -----------------------------------------------------------------------------
# OpenAI Whisper API
# Obtener de: https://platform.openai.com/api-keys
OPENAI_API_KEY=
# Modelo de Whisper
WHISPER_MODEL=whisper-1
# Alternativa: Whisper local
TRANSCRIPTION_PROVIDER=openai
# TRANSCRIPTION_PROVIDER=local
# -----------------------------------------------------------------------------
# ALMACENAMIENTO
# -----------------------------------------------------------------------------
# Tipo: local, s3, cloudinary
STORAGE_TYPE=local
# Local
STORAGE_LOCAL_PATH=./uploads
STORAGE_MAX_SIZE_MB=10
# AWS S3 (para produccion)
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=us-east-1
AWS_S3_BUCKET=michangarrito-uploads
# Cloudinary (alternativa)
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=
# -----------------------------------------------------------------------------
# CORS
# -----------------------------------------------------------------------------
FRONTEND_URL=http://localhost:3140
ALLOWED_ORIGINS=http://localhost:3140,http://localhost:3141,exp://localhost:8081
# -----------------------------------------------------------------------------
# LOGGING
# -----------------------------------------------------------------------------
LOG_LEVEL=debug
LOG_FORMAT=pretty
# LOG_FORMAT=json (para produccion)
# Sentry (error tracking - produccion)
SENTRY_DSN=
SENTRY_ENVIRONMENT=development
# -----------------------------------------------------------------------------
# SMTP / EMAIL (Opcional)
# -----------------------------------------------------------------------------
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_USER=
SMTP_PASSWORD=
SMTP_FROM=noreply@michangarrito.com
# Mailhog para desarrollo
MAILHOG_WEB_PORT=8025
# -----------------------------------------------------------------------------
# RATE LIMITING
# -----------------------------------------------------------------------------
RATE_LIMIT_TTL=60
RATE_LIMIT_LIMIT=100
# WhatsApp rate limits
WHATSAPP_RATE_LIMIT_MESSAGES=1000
WHATSAPP_RATE_LIMIT_WINDOW=86400
# -----------------------------------------------------------------------------
# CACHE
# -----------------------------------------------------------------------------
CACHE_TTL=300
CACHE_MAX_ITEMS=1000
# Cache de productos (mas largo)
CACHE_PRODUCTS_TTL=3600
# -----------------------------------------------------------------------------
# JOBS / QUEUES (Bull)
# -----------------------------------------------------------------------------
BULL_REDIS_HOST=${REDIS_HOST}
BULL_REDIS_PORT=${REDIS_PORT}
# Colas disponibles
QUEUE_NOTIFICATIONS=notifications
QUEUE_REPORTS=reports
QUEUE_SYNC=sync
QUEUE_WHATSAPP=whatsapp
# -----------------------------------------------------------------------------
# EXPO / MOBILE
# -----------------------------------------------------------------------------
EXPO_PROJECT_ID=
EXPO_OWNER=
# EAS Build
EAS_PROJECT_ID=
# Updates
EXPO_UPDATES_URL=https://u.expo.dev/
# -----------------------------------------------------------------------------
# MULTI-TENANT
# -----------------------------------------------------------------------------
# Modo: single (un negocio por instancia) o multi (varios negocios)
TENANT_MODE=multi
# Deteccion de tenant por:
# - subdomain: negocio.michangarrito.com
# - header: X-Tenant-ID
# - whatsapp: detectar por contexto de conversacion
TENANT_DETECTION=whatsapp
# -----------------------------------------------------------------------------
# NEGOCIO / CONFIGURACION
# -----------------------------------------------------------------------------
# Moneda por defecto
DEFAULT_CURRENCY=MXN
# Zona horaria
DEFAULT_TIMEZONE=America/Mexico_City
# Idioma
DEFAULT_LANGUAGE=es-MX
# Impuestos
DEFAULT_TAX_RATE=16
TAX_INCLUDED=true
# Tickets
TICKET_COMPANY_NAME=MiChangarrito
TICKET_FOOTER=Gracias por su compra
# Horario de operacion por defecto
DEFAULT_OPENING_HOUR=8
DEFAULT_CLOSING_HOUR=22
# -----------------------------------------------------------------------------
# DESARROLLO
# -----------------------------------------------------------------------------
# Hot reload
HOT_RELOAD=true
# Debug
DEBUG=true
DEBUG_SQL=false
# Seed data
SEED_ENABLED=true
# Swagger docs
SWAGGER_ENABLED=true
SWAGGER_PATH=/api/docs
# -----------------------------------------------------------------------------
# PRODUCCION (descomentar para produccion)
# -----------------------------------------------------------------------------
# NODE_ENV=production
# DEBUG=false
# LOG_FORMAT=json
# SWAGGER_ENABLED=false
# DB_SSL=true
# STORAGE_TYPE=s3
# =============================================================================
# FIN DE CONFIGURACION
# =============================================================================

62
.env.docker Normal file
View File

@ -0,0 +1,62 @@
# =============================================================================
# MiChangarrito - Docker Environment Variables
# =============================================================================
# Copiar este archivo a .env y configurar los valores
# cp .env.docker .env
# =============================================================================
# -----------------------------------------------------------------------------
# DATABASE
# -----------------------------------------------------------------------------
DB_USERNAME=michangarrito_dev
DB_PASSWORD=MCh_dev_2025_secure
DB_DATABASE=michangarrito_dev
DB_PORT=5432
# -----------------------------------------------------------------------------
# REDIS
# -----------------------------------------------------------------------------
REDIS_PORT=6379
# -----------------------------------------------------------------------------
# BACKEND
# -----------------------------------------------------------------------------
BACKEND_PORT=3141
JWT_SECRET=CHANGE_THIS_IN_PRODUCTION_MIN_64_CHARS_RANDOM_STRING_HERE_1234567890
JWT_EXPIRES_IN=7d
CORS_ORIGIN=http://localhost:3140
# -----------------------------------------------------------------------------
# FRONTEND
# -----------------------------------------------------------------------------
FRONTEND_PORT=3140
VITE_API_URL=http://localhost:3141/api/v1
# -----------------------------------------------------------------------------
# WHATSAPP SERVICE
# -----------------------------------------------------------------------------
WHATSAPP_PORT=3143
WHATSAPP_TOKEN=
WHATSAPP_PHONE_NUMBER_ID=
WHATSAPP_BUSINESS_ACCOUNT_ID=
WHATSAPP_VERIFY_TOKEN=
# -----------------------------------------------------------------------------
# STRIPE
# -----------------------------------------------------------------------------
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_PUBLISHABLE_KEY=
# -----------------------------------------------------------------------------
# LLM / AI
# -----------------------------------------------------------------------------
OPENAI_API_KEY=
LLM_MODEL=gpt-4o-mini
# -----------------------------------------------------------------------------
# PRODUCTION (uncomment for production)
# -----------------------------------------------------------------------------
# COMPOSE_PROFILES=production
# CORS_ORIGIN=https://michangarrito.com
# VITE_API_URL=https://api.michangarrito.com/api/v1

433
.env.example Normal file
View File

@ -0,0 +1,433 @@
# =============================================================================
# MICHANGARRITO - VARIABLES DE ENTORNO
# =============================================================================
# Copiar este archivo a .env y configurar los valores
# Generado: 2026-01-04
# =============================================================================
# -----------------------------------------------------------------------------
# APLICACION
# -----------------------------------------------------------------------------
NODE_ENV=development
APP_NAME=michangarrito
APP_VERSION=1.0.0
APP_DESCRIPTION="POS inteligente para micro-negocios"
# -----------------------------------------------------------------------------
# PUERTOS DE SERVICIOS
# -----------------------------------------------------------------------------
WEB_PORT=3140
BACKEND_PORT=3141
MCP_PORT=3142
WHATSAPP_PORT=3143
MOBILE_METRO_PORT=8081
# -----------------------------------------------------------------------------
# BASE DE DATOS POSTGRESQL
# -----------------------------------------------------------------------------
DB_HOST=localhost
DB_PORT=5432
DB_NAME=michangarrito_dev
DB_USER=michangarrito_dev
# Generar con: openssl rand -base64 32
DB_PASSWORD=
# URL de conexion completa (se construye automaticamente)
DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}
# Pool de conexiones
DB_POOL_SIZE=10
DB_POOL_MIN=2
# SSL (solo produccion)
DB_SSL=false
# -----------------------------------------------------------------------------
# REDIS
# -----------------------------------------------------------------------------
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DATABASE=8
# Nota: Puerto 6379 es instancia compartida del workspace, DB 8 asignado a MiChangarrito
REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}/${REDIS_DATABASE}
# -----------------------------------------------------------------------------
# JWT / AUTENTICACION
# -----------------------------------------------------------------------------
# Generar con: openssl rand -base64 64
JWT_SECRET=
JWT_EXPIRES_IN=7d
JWT_REFRESH_EXPIRES_IN=30d
# OTP para login
OTP_EXPIRES_MINUTES=5
OTP_LENGTH=6
# PIN de acceso rapido
PIN_LENGTH=4
PIN_MAX_ATTEMPTS=3
PIN_LOCKOUT_MINUTES=30
# Biometrico
BIOMETRIC_ENABLED=true
# -----------------------------------------------------------------------------
# WHATSAPP BUSINESS API (Meta)
# -----------------------------------------------------------------------------
# Verificacion del webhook
WHATSAPP_VERIFY_TOKEN=
# Token de acceso (obtener de Meta Business)
WHATSAPP_ACCESS_TOKEN=
# ID del numero de telefono
WHATSAPP_PHONE_NUMBER_ID=
# ID de la cuenta de negocio
WHATSAPP_BUSINESS_ACCOUNT_ID=
# Webhook URL (para configurar en Meta)
WHATSAPP_WEBHOOK_URL=https://tu-dominio.com/webhook/whatsapp
# Numero compartido de la plataforma (para multi-tenant)
WHATSAPP_PLATFORM_NUMBER=+52XXXXXXXXXX
# -----------------------------------------------------------------------------
# LLM / INTELIGENCIA ARTIFICIAL
# -----------------------------------------------------------------------------
# Proveedor: openrouter, openai, anthropic, ollama
LLM_PROVIDER=openrouter
# API Key del proveedor
LLM_API_KEY=
# Modelo a usar
# OpenRouter: anthropic/claude-3-haiku, openai/gpt-4o-mini, meta-llama/llama-3-8b
# OpenAI: gpt-4o-mini, gpt-4o
# Anthropic: claude-3-haiku-20240307, claude-3-sonnet-20240229
# Ollama: llama3, mistral, phi3
LLM_MODEL=anthropic/claude-3-haiku
# URL base del proveedor
# OpenRouter: https://openrouter.ai/api/v1
# OpenAI: https://api.openai.com/v1
# Anthropic: https://api.anthropic.com/v1
# Ollama: http://localhost:11434/api
LLM_BASE_URL=https://openrouter.ai/api/v1
# Limites
LLM_MAX_TOKENS=4096
LLM_TEMPERATURE=0.7
# Tokens por defecto para nuevos usuarios
LLM_DEFAULT_TOKENS=500
# -----------------------------------------------------------------------------
# MCP SERVER (Model Context Protocol)
# -----------------------------------------------------------------------------
MCP_SERVER_NAME=michangarrito-mcp
MCP_SERVER_VERSION=1.0.0
MCP_LOG_LEVEL=info
# Herramientas habilitadas (separadas por coma)
MCP_ENABLED_TOOLS=ventas,productos,inventario,clientes,fiados,pedidos,reportes,configuracion
# -----------------------------------------------------------------------------
# STRIPE (Suscripciones y Pagos)
# -----------------------------------------------------------------------------
# Claves de API (modo test)
# Obtener de: https://dashboard.stripe.com/test/apikeys
STRIPE_SECRET_KEY=sk_test_
STRIPE_PUBLISHABLE_KEY=pk_test_
# Webhook secret
# Obtener de: https://dashboard.stripe.com/test/webhooks
STRIPE_WEBHOOK_SECRET=whsec_
# IDs de productos/precios (crear en Stripe Dashboard)
STRIPE_PRICE_CHANGARRITO=price_
STRIPE_PRICE_TIENDITA=price_
STRIPE_PRICE_TOKENS_1000=price_
STRIPE_PRICE_TOKENS_3000=price_
STRIPE_PRICE_TOKENS_8000=price_
STRIPE_PRICE_TOKENS_20000=price_
# Referencia OXXO
STRIPE_OXXO_ENABLED=true
# -----------------------------------------------------------------------------
# MERCADO PAGO (Terminal de Pago)
# -----------------------------------------------------------------------------
# Access Token (obtener de Mercado Pago Developers)
MERCADOPAGO_ACCESS_TOKEN=
# Public Key
MERCADOPAGO_PUBLIC_KEY=
# User ID (para Point integration)
MERCADOPAGO_USER_ID=
# Device ID de la terminal
MERCADOPAGO_DEVICE_ID=
# Webhook URL
MERCADOPAGO_WEBHOOK_URL=https://tu-dominio.com/webhook/mercadopago
# -----------------------------------------------------------------------------
# CLIP (Terminal de Pago)
# -----------------------------------------------------------------------------
# API Key (obtener de Clip Dashboard)
CLIP_API_KEY=
# Merchant ID
CLIP_MERCHANT_ID=
# Device ID de la terminal
CLIP_DEVICE_ID=
# Webhook URL
CLIP_WEBHOOK_URL=https://tu-dominio.com/webhook/clip
# -----------------------------------------------------------------------------
# CODI (Banxico QR)
# -----------------------------------------------------------------------------
# Habilitado
CODI_ENABLED=true
# CLABE virtual para recepcion
CODI_CLABE=
# Certificado (path o base64)
CODI_CERTIFICATE=
# Llave privada (path o base64)
CODI_PRIVATE_KEY=
# -----------------------------------------------------------------------------
# FIREBASE (Push Notifications)
# -----------------------------------------------------------------------------
# Project ID
FIREBASE_PROJECT_ID=
# Service Account (JSON)
# Opcion 1: Path al archivo
FIREBASE_SERVICE_ACCOUNT_PATH=./firebase-service-account.json
# Opcion 2: JSON como string (para produccion)
FIREBASE_SERVICE_ACCOUNT_JSON=
# Habilitado
FIREBASE_PUSH_ENABLED=true
# -----------------------------------------------------------------------------
# OCR / VISION (Procesamiento de Imagenes)
# -----------------------------------------------------------------------------
# Google Cloud Vision
# Obtener de: https://console.cloud.google.com/apis/credentials
GOOGLE_VISION_API_KEY=
# Alternativa: Tesseract local
OCR_PROVIDER=google
# OCR_PROVIDER=tesseract
# -----------------------------------------------------------------------------
# TRANSCRIPCION DE AUDIO
# -----------------------------------------------------------------------------
# OpenAI Whisper API
# Obtener de: https://platform.openai.com/api-keys
OPENAI_API_KEY=
# Modelo de Whisper
WHISPER_MODEL=whisper-1
# Alternativa: Whisper local
TRANSCRIPTION_PROVIDER=openai
# TRANSCRIPTION_PROVIDER=local
# -----------------------------------------------------------------------------
# ALMACENAMIENTO
# -----------------------------------------------------------------------------
# Tipo: local, s3, cloudinary
STORAGE_TYPE=local
# Local
STORAGE_LOCAL_PATH=./uploads
STORAGE_MAX_SIZE_MB=10
# AWS S3 (para produccion)
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=us-east-1
AWS_S3_BUCKET=michangarrito-uploads
# Cloudinary (alternativa)
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=
# -----------------------------------------------------------------------------
# CORS
# -----------------------------------------------------------------------------
FRONTEND_URL=http://localhost:3140
ALLOWED_ORIGINS=http://localhost:3140,http://localhost:3141,exp://localhost:8081
# -----------------------------------------------------------------------------
# LOGGING
# -----------------------------------------------------------------------------
LOG_LEVEL=debug
LOG_FORMAT=pretty
# LOG_FORMAT=json (para produccion)
# Sentry (error tracking - produccion)
SENTRY_DSN=
SENTRY_ENVIRONMENT=development
# -----------------------------------------------------------------------------
# SMTP / EMAIL (Opcional)
# -----------------------------------------------------------------------------
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_USER=
SMTP_PASSWORD=
SMTP_FROM=noreply@michangarrito.com
# Mailhog para desarrollo
MAILHOG_WEB_PORT=8025
# -----------------------------------------------------------------------------
# RATE LIMITING
# -----------------------------------------------------------------------------
RATE_LIMIT_TTL=60
RATE_LIMIT_LIMIT=100
# WhatsApp rate limits
WHATSAPP_RATE_LIMIT_MESSAGES=1000
WHATSAPP_RATE_LIMIT_WINDOW=86400
# -----------------------------------------------------------------------------
# CACHE
# -----------------------------------------------------------------------------
CACHE_TTL=300
CACHE_MAX_ITEMS=1000
# Cache de productos (mas largo)
CACHE_PRODUCTS_TTL=3600
# -----------------------------------------------------------------------------
# JOBS / QUEUES (Bull)
# -----------------------------------------------------------------------------
BULL_REDIS_HOST=${REDIS_HOST}
BULL_REDIS_PORT=${REDIS_PORT}
# Colas disponibles
QUEUE_NOTIFICATIONS=notifications
QUEUE_REPORTS=reports
QUEUE_SYNC=sync
QUEUE_WHATSAPP=whatsapp
# -----------------------------------------------------------------------------
# EXPO / MOBILE
# -----------------------------------------------------------------------------
EXPO_PROJECT_ID=
EXPO_OWNER=
# EAS Build
EAS_PROJECT_ID=
# Updates
EXPO_UPDATES_URL=https://u.expo.dev/
# -----------------------------------------------------------------------------
# MULTI-TENANT
# -----------------------------------------------------------------------------
# Modo: single (un negocio por instancia) o multi (varios negocios)
TENANT_MODE=multi
# Deteccion de tenant por:
# - subdomain: negocio.michangarrito.com
# - header: X-Tenant-ID
# - whatsapp: detectar por contexto de conversacion
TENANT_DETECTION=whatsapp
# -----------------------------------------------------------------------------
# NEGOCIO / CONFIGURACION
# -----------------------------------------------------------------------------
# Moneda por defecto
DEFAULT_CURRENCY=MXN
# Zona horaria
DEFAULT_TIMEZONE=America/Mexico_City
# Idioma
DEFAULT_LANGUAGE=es-MX
# Impuestos
DEFAULT_TAX_RATE=16
TAX_INCLUDED=true
# Tickets
TICKET_COMPANY_NAME=MiChangarrito
TICKET_FOOTER=Gracias por su compra
# Horario de operacion por defecto
DEFAULT_OPENING_HOUR=8
DEFAULT_CLOSING_HOUR=22
# -----------------------------------------------------------------------------
# DESARROLLO
# -----------------------------------------------------------------------------
# Hot reload
HOT_RELOAD=true
# Debug
DEBUG=true
DEBUG_SQL=false
# Seed data
SEED_ENABLED=true
# Swagger docs
SWAGGER_ENABLED=true
SWAGGER_PATH=/api/docs
# -----------------------------------------------------------------------------
# PRODUCCION (descomentar para produccion)
# -----------------------------------------------------------------------------
# NODE_ENV=production
# DEBUG=false
# LOG_FORMAT=json
# SWAGGER_ENABLED=false
# DB_SSL=true
# STORAGE_TYPE=s3
# =============================================================================
# FIN DE CONFIGURACION
# =============================================================================

23
.env.ports Normal file
View File

@ -0,0 +1,23 @@
# =============================================================================
# MICHANGARRITO - ASIGNACION DE PUERTOS
# =============================================================================
# Referencia rapida de puertos del proyecto
# =============================================================================
# Aplicaciones
WEB_PORT=3140
BACKEND_PORT=3141
MCP_PORT=3142
WHATSAPP_PORT=3143
MOBILE_METRO_PORT=8081
# Infraestructura
DB_PORT=5432
REDIS_PORT=6379
REDIS_DB=8 # Database number dentro de instancia compartida
# Desarrollo (opcionales)
MAILHOG_SMTP_PORT=1025
MAILHOG_WEB_PORT=8025
# =============================================================================

250
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,250 @@
# =============================================================================
# MiChangarrito - CI/CD Pipeline
# =============================================================================
# Triggers: Push to main/develop, Pull Requests
# =============================================================================
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
env:
NODE_VERSION: '20'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# ===========================================================================
# BACKEND - Lint, Test, Build
# ===========================================================================
backend:
name: Backend CI
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/backend
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: apps/backend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm run test -- --passWithNoTests
- name: Build application
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: backend-dist
path: apps/backend/dist
retention-days: 1
# ===========================================================================
# FRONTEND - Lint, Build
# ===========================================================================
frontend:
name: Frontend CI
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/frontend
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: apps/frontend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint || true
- name: Build application
run: npm run build
env:
VITE_API_URL: ${{ vars.VITE_API_URL || 'http://localhost:3141/api/v1' }}
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: apps/frontend/dist
retention-days: 1
# ===========================================================================
# WHATSAPP SERVICE - Lint, Build
# ===========================================================================
whatsapp-service:
name: WhatsApp Service CI
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/whatsapp-service
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: apps/whatsapp-service/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint || true
- name: Build application
run: npm run build
# ===========================================================================
# MOBILE - Type Check
# ===========================================================================
mobile:
name: Mobile CI
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/mobile
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: apps/mobile/package-lock.json
- name: Install dependencies
run: npm ci
- name: Type check
run: npx tsc --noEmit
# ===========================================================================
# DOCKER BUILD (only on main/develop push)
# ===========================================================================
docker-build:
name: Docker Build
runs-on: ubuntu-latest
needs: [backend, frontend, whatsapp-service]
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')
permissions:
contents: read
packages: write
strategy:
matrix:
service: [backend, frontend, whatsapp-service]
include:
- service: backend
context: ./apps/backend
dockerfile: ./apps/backend/Dockerfile
- service: frontend
context: ./apps/frontend
dockerfile: ./apps/frontend/Dockerfile
- service: whatsapp-service
context: ./apps/whatsapp-service
dockerfile: ./apps/whatsapp-service/Dockerfile
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-${{ matrix.service }}
tags: |
type=ref,event=branch
type=sha,prefix=
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: ${{ matrix.context }}
file: ${{ matrix.dockerfile }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
target: production
# ===========================================================================
# DEPLOY (only on main push)
# ===========================================================================
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
needs: [docker-build]
if: github.ref == 'refs/heads/main'
environment: production
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Deploy to server
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd /opt/michangarrito
docker-compose pull
docker-compose up -d --remove-orphans
docker system prune -f
- name: Notify deployment
if: success()
run: |
echo "Deployment successful!"
# Add Slack/Discord notification here if needed

119
README.md Normal file
View File

@ -0,0 +1,119 @@
# MiChangarrito - Plataforma POS Multi-tenant
**Version:** 0.1.0
**Estado:** Desarrollo
**Tipo:** STANDALONE (SaaS)
**Sistema:** SIMCO + NEXUS v3.4
---
## Descripcion
Plataforma de punto de venta (POS) para tiendas pequenas y medianas en Mexico. Sistema multi-tenant con soporte para multiples canales: app movil, web, WhatsApp y asistente IA.
### Funcionalidades Principales
- Punto de venta rapido y facil
- Gestion de productos e inventario
- Control de clientes y fiados
- Pedidos y ordenes
- Integracion WhatsApp Business
- Asistente IA para ventas
- Reportes y analytics
- Pagos: Stripe, MercadoPago, Clip, CoDi, SPEI
---
## Stack Tecnologico
| Capa | Tecnologia |
|------|------------|
| Backend | NestJS + TypeScript |
| Frontend Web | React + Vite |
| Mobile | React Native |
| Database | PostgreSQL 16+ con RLS (Multi-tenant) |
| Cache | Redis |
| AI/LLM | OpenRouter, OpenAI, Claude, Ollama |
| WhatsApp | Meta Business API |
| Payments | Stripe, MercadoPago, Clip |
---
## Estructura del Proyecto
```
michangarrito/
├── apps/
│ ├── backend/ # API NestJS
│ ├── frontend/ # Web React
│ ├── mobile/ # React Native
│ ├── web/ # Landing page
│ ├── mcp-server/ # Servidor MCP para LLM
│ └── whatsapp-service/ # Integracion WhatsApp
├── database/
│ ├── schemas/ # DDL
│ └── seeds/ # Datos iniciales
├── docs/
│ ├── 00-vision-general/
│ ├── 01-epicas/
│ ├── 02-especificaciones/
│ └── 90-transversal/
└── orchestration/
├── 00-guidelines/
├── inventarios/
├── environment/
└── trazas/
```
---
## Modulos Principales
1. **Auth** - Autenticacion y autorizacion
2. **Tenants** - Multi-tenancy
3. **Products** - Catalogo de productos
4. **Inventory** - Control de inventario
5. **Sales** - Punto de venta
6. **Customers** - Clientes y fiados
7. **Orders** - Pedidos
8. **Reports** - Reportes y analytics
9. **Subscriptions** - Planes y suscripciones
10. **Payments** - Integracion de pagos
---
## Inicio Rapido
```bash
# Backend
cd apps/backend && npm install && npm run start:dev
# Frontend
cd apps/frontend && npm install && npm run dev
# MCP Server
cd apps/mcp-server && npm install && npm run start
# WhatsApp Service
cd apps/whatsapp-service && npm install && npm run start
```
---
## Variables de Entorno
Ver `.env.example` para la configuracion requerida.
---
## Referencias
- Vision: `docs/00-vision-general/VISION-PROYECTO.md`
- Arquitectura: `docs/00-vision-general/ARQUITECTURA-TECNICA.md`
- Requerimientos: `docs/00-vision-general/REQUERIMIENTOS-FUNCIONALES.md`
- Contexto: `orchestration/00-guidelines/`
---
**Creado:** 2026-01-04
**Actualizado:** 2026-01-07

View File

@ -0,0 +1,41 @@
# Dependencies
node_modules
npm-debug.log
yarn-error.log
# Build output
dist
# IDE
.idea
.vscode
*.swp
*.swo
# Testing
coverage
.nyc_output
# Environment
.env
.env.*
!.env.example
# Git
.git
.gitignore
# Docker
Dockerfile
docker-compose*.yml
.dockerignore
# Documentation
README.md
docs
# Tests
test
*.spec.ts
*.test.ts
jest.config.js

33
apps/backend/.env Normal file
View File

@ -0,0 +1,33 @@
# MiChangarrito Backend - Environment Variables
NODE_ENV=development
# Server
PORT=3141
API_PREFIX=api/v1
# Database
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=michangarrito_dev
DB_PASSWORD=MCh_dev_2025_secure
DB_DATABASE=michangarrito_dev
DB_SCHEMA=public
# JWT
JWT_SECRET=MCh_jwt_dev_2025_AcB7x9KmPq2sWfHg4jL8nRt0vYzXw1CdEiFoGhJkLmN3pQrSuVxY5bZ6aBeC8fDg
JWT_EXPIRES_IN=7d
# CORS
CORS_ORIGIN=http://localhost:3140
# Logging
LOG_LEVEL=debug
# Stripe (Monetization)
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_PUBLISHABLE_KEY=
# LLM / AI (for token consumption)
OPENAI_API_KEY=
LLM_MODEL=gpt-4o-mini

89
apps/backend/Dockerfile Normal file
View File

@ -0,0 +1,89 @@
# =============================================================================
# MiChangarrito - Backend Dockerfile
# =============================================================================
# Multi-stage build for NestJS application
# Puerto: 3141
# =============================================================================
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci
# Copy source code
COPY . .
# Build application
RUN npm run build
# Production stage
FROM node:20-alpine AS production
# Labels
LABEL maintainer="ISEM"
LABEL description="MiChangarrito Backend API"
LABEL version="1.0.0"
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install only production dependencies
RUN npm ci --only=production && npm cache clean --force
# Copy built application
COPY --from=builder /app/dist ./dist
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nestjs -u 1001 -G nodejs
# Set ownership
RUN chown -R nestjs:nodejs /app
USER nestjs
# Environment
ENV NODE_ENV=production
ENV PORT=3141
# Expose port
EXPOSE 3141
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3141/api/v1/health || exit 1
# Start application
CMD ["node", "dist/main"]
# Development stage
FROM node:20-alpine AS development
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install all dependencies
RUN npm ci
# Copy source code
COPY . .
# Environment
ENV NODE_ENV=development
ENV PORT=3141
# Expose port
EXPOSE 3141
# Start in development mode
CMD ["npm", "run", "start:dev"]

2
apps/backend/dist/app.module.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
export declare class AppModule {
}

67
apps/backend/dist/app.module.js vendored Normal file
View File

@ -0,0 +1,67 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppModule = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const typeorm_1 = require("@nestjs/typeorm");
const auth_module_1 = require("./modules/auth/auth.module");
const products_module_1 = require("./modules/products/products.module");
const categories_module_1 = require("./modules/categories/categories.module");
const sales_module_1 = require("./modules/sales/sales.module");
const payments_module_1 = require("./modules/payments/payments.module");
const customers_module_1 = require("./modules/customers/customers.module");
const inventory_module_1 = require("./modules/inventory/inventory.module");
const orders_module_1 = require("./modules/orders/orders.module");
const subscriptions_module_1 = require("./modules/subscriptions/subscriptions.module");
const messaging_module_1 = require("./modules/messaging/messaging.module");
const billing_module_1 = require("./modules/billing/billing.module");
const integrations_module_1 = require("./modules/integrations/integrations.module");
let AppModule = class AppModule {
};
exports.AppModule = AppModule;
exports.AppModule = AppModule = __decorate([
(0, common_1.Module)({
imports: [
config_1.ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env.local', '.env'],
}),
typeorm_1.TypeOrmModule.forRootAsync({
imports: [config_1.ConfigModule],
inject: [config_1.ConfigService],
useFactory: (configService) => ({
type: 'postgres',
host: configService.get('DB_HOST', 'localhost'),
port: configService.get('DB_PORT', 5432),
username: configService.get('DB_USERNAME', 'michangarrito_dev'),
password: configService.get('DB_PASSWORD', 'MCh_dev_2025_secure'),
database: configService.get('DB_DATABASE', 'michangarrito_dev'),
schema: configService.get('DB_SCHEMA', 'public'),
autoLoadEntities: true,
synchronize: false,
logging: configService.get('NODE_ENV') === 'development',
ssl: configService.get('DB_SSL') === 'true' ? { rejectUnauthorized: false } : false,
}),
}),
auth_module_1.AuthModule,
products_module_1.ProductsModule,
categories_module_1.CategoriesModule,
sales_module_1.SalesModule,
payments_module_1.PaymentsModule,
customers_module_1.CustomersModule,
inventory_module_1.InventoryModule,
orders_module_1.OrdersModule,
subscriptions_module_1.SubscriptionsModule,
messaging_module_1.MessagingModule,
billing_module_1.BillingModule,
integrations_module_1.IntegrationsModule,
],
})
], AppModule);
//# sourceMappingURL=app.module.js.map

1
apps/backend/dist/app.module.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2CAA6D;AAC7D,6CAAgD;AAChD,4DAAwD;AACxD,wEAAoE;AACpE,8EAA0E;AAC1E,+DAA2D;AAC3D,wEAAoE;AACpE,2EAAuE;AACvE,2EAAuE;AACvE,kEAA8D;AAC9D,uFAAmF;AACnF,2EAAuE;AACvE,qEAAiE;AACjE,oFAAgF;AA4CzE,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IA1CrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YAEP,qBAAY,CAAC,OAAO,CAAC;gBACnB,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,CAAC,YAAY,EAAE,MAAM,CAAC;aACpC,CAAC;YAGF,uBAAa,CAAC,YAAY,CAAC;gBACzB,OAAO,EAAE,CAAC,qBAAY,CAAC;gBACvB,MAAM,EAAE,CAAC,sBAAa,CAAC;gBACvB,UAAU,EAAE,CAAC,aAA4B,EAAE,EAAE,CAAC,CAAC;oBAC7C,IAAI,EAAE,UAAU;oBAChB,IAAI,EAAE,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC;oBAC/C,IAAI,EAAE,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC;oBACxC,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAC,aAAa,EAAE,mBAAmB,CAAC;oBAC/D,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAC,aAAa,EAAE,qBAAqB,CAAC;oBACjE,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAC,aAAa,EAAE,mBAAmB,CAAC;oBAC/D,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC;oBAChD,gBAAgB,EAAE,IAAI;oBACtB,WAAW,EAAE,KAAK;oBAClB,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,aAAa;oBACxD,GAAG,EAAE,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK;iBACpF,CAAC;aACH,CAAC;YAGF,wBAAU;YACV,gCAAc;YACd,oCAAgB;YAChB,0BAAW;YACX,gCAAc;YACd,kCAAe;YACf,kCAAe;YACf,4BAAY;YACZ,0CAAmB;YACnB,kCAAe;YACf,8BAAa;YACb,wCAAkB;SACnB;KACF,CAAC;GACW,SAAS,CAAG"}

1
apps/backend/dist/main.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export {};

64
apps/backend/dist/main.js vendored Normal file
View File

@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@nestjs/core");
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const swagger_1 = require("@nestjs/swagger");
const helmet_1 = require("helmet");
const app_module_1 = require("./app.module");
async function bootstrap() {
const app = await core_1.NestFactory.create(app_module_1.AppModule);
const configService = app.get(config_1.ConfigService);
app.use((0, helmet_1.default)());
app.enableCors({
origin: configService.get('CORS_ORIGIN', 'http://localhost:3140'),
credentials: true,
});
app.setGlobalPrefix('api');
app.useGlobalPipes(new common_1.ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
transformOptions: {
enableImplicitConversion: true,
},
}));
const config = new swagger_1.DocumentBuilder()
.setTitle('MiChangarrito API')
.setDescription('POS inteligente para micro-negocios con integración WhatsApp y LLM. Target: Changarros, tienditas, fondas.')
.setVersion('1.0')
.addBearerAuth()
.addTag('auth', 'Autenticación y registro')
.addTag('products', 'Gestión de productos')
.addTag('categories', 'Categorías de productos')
.addTag('sales', 'Ventas y tickets')
.addTag('inventory', 'Control de inventario')
.addTag('customers', 'Clientes y fiados')
.addTag('orders', 'Pedidos por WhatsApp')
.addTag('payments', 'Métodos de pago')
.addTag('subscriptions', 'Planes y tokens')
.addTag('messaging', 'WhatsApp y notificaciones')
.addTag('reports', 'Reportes y analytics')
.build();
const document = swagger_1.SwaggerModule.createDocument(app, config);
swagger_1.SwaggerModule.setup('docs', app, document, {
swaggerOptions: {
persistAuthorization: true,
},
});
const port = configService.get('PORT', 3000);
await app.listen(port);
console.log(`
MICHANGARRITO API
POS Inteligente para Micro-Negocios
Status: Running
Port: ${port}
Environment: ${configService.get('NODE_ENV', 'development')}
Docs: http://localhost:${port}/docs ║
`);
}
bootstrap();
//# sourceMappingURL=main.js.map

1
apps/backend/dist/main.js.map vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,2CAAgD;AAChD,2CAA+C;AAC/C,6CAAiE;AACjE,mCAA4B;AAC5B,6CAAyC;AAEzC,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,GAAG,CAAC,GAAG,CAAC,sBAAa,CAAC,CAAC;IAG7C,GAAG,CAAC,GAAG,CAAC,IAAA,gBAAM,GAAE,CAAC,CAAC;IAGlB,GAAG,CAAC,UAAU,CAAC;QACb,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,aAAa,EAAE,uBAAuB,CAAC;QACjE,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;IAGH,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAG3B,GAAG,CAAC,cAAc,CAChB,IAAI,uBAAc,CAAC;QACjB,SAAS,EAAE,IAAI;QACf,oBAAoB,EAAE,IAAI;QAC1B,SAAS,EAAE,IAAI;QACf,gBAAgB,EAAE;YAChB,wBAAwB,EAAE,IAAI;SAC/B;KACF,CAAC,CACH,CAAC;IAGF,MAAM,MAAM,GAAG,IAAI,yBAAe,EAAE;SACjC,QAAQ,CAAC,mBAAmB,CAAC;SAC7B,cAAc,CACb,4GAA4G,CAC7G;SACA,UAAU,CAAC,KAAK,CAAC;SACjB,aAAa,EAAE;SACf,MAAM,CAAC,MAAM,EAAE,0BAA0B,CAAC;SAC1C,MAAM,CAAC,UAAU,EAAE,sBAAsB,CAAC;SAC1C,MAAM,CAAC,YAAY,EAAE,yBAAyB,CAAC;SAC/C,MAAM,CAAC,OAAO,EAAE,kBAAkB,CAAC;SACnC,MAAM,CAAC,WAAW,EAAE,uBAAuB,CAAC;SAC5C,MAAM,CAAC,WAAW,EAAE,mBAAmB,CAAC;SACxC,MAAM,CAAC,QAAQ,EAAE,sBAAsB,CAAC;SACxC,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAAC;SACrC,MAAM,CAAC,eAAe,EAAE,iBAAiB,CAAC;SAC1C,MAAM,CAAC,WAAW,EAAE,2BAA2B,CAAC;SAChD,MAAM,CAAC,SAAS,EAAE,sBAAsB,CAAC;SACzC,KAAK,EAAE,CAAC;IAEX,MAAM,QAAQ,GAAG,uBAAa,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC3D,uBAAa,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE;QACzC,cAAc,EAAE;YACd,oBAAoB,EAAE,IAAI;SAC3B;KACF,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC7C,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEvB,OAAO,CAAC,GAAG,CAAC;;;;;;kBAMI,IAAI;kBACJ,aAAa,CAAC,GAAG,CAAC,UAAU,EAAE,aAAa,CAAC;mCAC3B,IAAI;;GAEpC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,EAAE,CAAC"}

View File

@ -0,0 +1,19 @@
import { AuthService } from './auth.service';
import { RegisterDto, LoginDto, RefreshTokenDto } from './dto/register.dto';
export declare class AuthController {
private readonly authService;
constructor(authService: AuthService);
register(dto: RegisterDto): Promise<import("./auth.service").AuthResponse>;
login(dto: LoginDto): Promise<import("./auth.service").AuthResponse>;
refreshToken(dto: RefreshTokenDto): Promise<import("./auth.service").AuthResponse>;
changePin(req: {
user: {
sub: string;
};
}, body: {
currentPin: string;
newPin: string;
}): Promise<{
message: string;
}>;
}

View File

@ -0,0 +1,136 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthController = void 0;
const common_1 = require("@nestjs/common");
const swagger_1 = require("@nestjs/swagger");
const auth_service_1 = require("./auth.service");
const register_dto_1 = require("./dto/register.dto");
const jwt_auth_guard_1 = require("./guards/jwt-auth.guard");
let AuthController = class AuthController {
constructor(authService) {
this.authService = authService;
}
async register(dto) {
return this.authService.register(dto);
}
async login(dto) {
return this.authService.login(dto);
}
async refreshToken(dto) {
return this.authService.refreshToken(dto.refreshToken);
}
async changePin(req, body) {
await this.authService.changePin(req.user.sub, body.currentPin, body.newPin);
return { message: 'PIN cambiado exitosamente' };
}
};
exports.AuthController = AuthController;
__decorate([
(0, common_1.Post)('register'),
(0, swagger_1.ApiOperation)({
summary: 'Registrar nuevo negocio',
description: 'Crea un nuevo tenant y usuario con periodo de prueba de 14 días',
}),
(0, swagger_1.ApiResponse)({
status: 201,
description: 'Registro exitoso',
schema: {
properties: {
accessToken: { type: 'string' },
refreshToken: { type: 'string' },
user: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
isOwner: { type: 'boolean' },
},
},
tenant: {
type: 'object',
properties: {
id: { type: 'string' },
businessName: { type: 'string' },
plan: { type: 'string' },
subscriptionStatus: { type: 'string' },
trialEndsAt: { type: 'string', format: 'date-time' },
},
},
},
},
}),
(0, swagger_1.ApiResponse)({ status: 409, description: 'Teléfono ya registrado' }),
__param(0, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [register_dto_1.RegisterDto]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "register", null);
__decorate([
(0, common_1.Post)('login'),
(0, common_1.HttpCode)(common_1.HttpStatus.OK),
(0, swagger_1.ApiOperation)({
summary: 'Iniciar sesión',
description: 'Autenticación con teléfono y PIN',
}),
(0, swagger_1.ApiResponse)({
status: 200,
description: 'Login exitoso',
}),
(0, swagger_1.ApiResponse)({ status: 401, description: 'Credenciales inválidas' }),
__param(0, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [register_dto_1.LoginDto]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "login", null);
__decorate([
(0, common_1.Post)('refresh'),
(0, common_1.HttpCode)(common_1.HttpStatus.OK),
(0, swagger_1.ApiOperation)({
summary: 'Refrescar token',
description: 'Obtiene un nuevo access token usando el refresh token',
}),
(0, swagger_1.ApiResponse)({
status: 200,
description: 'Token refrescado exitosamente',
}),
(0, swagger_1.ApiResponse)({ status: 401, description: 'Token inválido o expirado' }),
__param(0, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [register_dto_1.RefreshTokenDto]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "refreshToken", null);
__decorate([
(0, common_1.Post)('change-pin'),
(0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard),
(0, swagger_1.ApiBearerAuth)(),
(0, common_1.HttpCode)(common_1.HttpStatus.OK),
(0, swagger_1.ApiOperation)({
summary: 'Cambiar PIN',
description: 'Cambiar el PIN de acceso',
}),
(0, swagger_1.ApiResponse)({ status: 200, description: 'PIN cambiado exitosamente' }),
(0, swagger_1.ApiResponse)({ status: 401, description: 'PIN actual incorrecto' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "changePin", null);
exports.AuthController = AuthController = __decorate([
(0, swagger_1.ApiTags)('auth'),
(0, common_1.Controller)('v1/auth'),
__metadata("design:paramtypes", [auth_service_1.AuthService])
], AuthController);
//# sourceMappingURL=auth.controller.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"auth.controller.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.controller.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,2 @@
export declare class AuthModule {
}

View File

@ -0,0 +1,45 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthModule = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const jwt_1 = require("@nestjs/jwt");
const passport_1 = require("@nestjs/passport");
const config_1 = require("@nestjs/config");
const auth_controller_1 = require("./auth.controller");
const auth_service_1 = require("./auth.service");
const tenant_entity_1 = require("./entities/tenant.entity");
const user_entity_1 = require("./entities/user.entity");
const jwt_strategy_1 = require("./strategies/jwt.strategy");
const jwt_auth_guard_1 = require("./guards/jwt-auth.guard");
let AuthModule = class AuthModule {
};
exports.AuthModule = AuthModule;
exports.AuthModule = AuthModule = __decorate([
(0, common_1.Module)({
imports: [
typeorm_1.TypeOrmModule.forFeature([tenant_entity_1.Tenant, user_entity_1.User]),
passport_1.PassportModule.register({ defaultStrategy: 'jwt' }),
jwt_1.JwtModule.registerAsync({
imports: [config_1.ConfigModule],
inject: [config_1.ConfigService],
useFactory: (configService) => ({
secret: configService.get('JWT_SECRET'),
signOptions: {
expiresIn: configService.get('JWT_EXPIRES_IN', '24h'),
},
}),
}),
],
controllers: [auth_controller_1.AuthController],
providers: [auth_service_1.AuthService, jwt_strategy_1.JwtStrategy, jwt_auth_guard_1.JwtAuthGuard],
exports: [auth_service_1.AuthService, jwt_auth_guard_1.JwtAuthGuard, typeorm_1.TypeOrmModule],
})
], AuthModule);
//# sourceMappingURL=auth.module.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,qCAAwC;AACxC,+CAAkD;AAClD,2CAA6D;AAC7D,uDAAmD;AACnD,iDAA6C;AAC7C,4DAAkD;AAClD,wDAA8C;AAC9C,4DAAwD;AACxD,4DAAuD;AAqBhD,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IAnBtB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC,CAAC,sBAAM,EAAE,kBAAI,CAAC,CAAC;YACxC,yBAAc,CAAC,QAAQ,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;YACnD,eAAS,CAAC,aAAa,CAAC;gBACtB,OAAO,EAAE,CAAC,qBAAY,CAAC;gBACvB,MAAM,EAAE,CAAC,sBAAa,CAAC;gBACvB,UAAU,EAAE,CAAC,aAA4B,EAAE,EAAE,CAAC,CAAC;oBAC7C,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC;oBACvC,WAAW,EAAE;wBACX,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,CAAC;qBACtD;iBACF,CAAC;aACH,CAAC;SACH;QACD,WAAW,EAAE,CAAC,gCAAc,CAAC;QAC7B,SAAS,EAAE,CAAC,0BAAW,EAAE,0BAAW,EAAE,6BAAY,CAAC;QACnD,OAAO,EAAE,CAAC,0BAAW,EAAE,6BAAY,EAAE,uBAAa,CAAC;KACpD,CAAC;GACW,UAAU,CAAG"}

View File

@ -0,0 +1,42 @@
import { Repository } from 'typeorm';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { Tenant } from './entities/tenant.entity';
import { User } from './entities/user.entity';
import { RegisterDto, LoginDto } from './dto/register.dto';
export interface TokenPayload {
sub: string;
tenantId: string;
phone: string;
role: string;
}
export interface AuthResponse {
accessToken: string;
refreshToken: string;
user: {
id: string;
name: string;
role: string;
phone: string;
};
tenant: {
id: string;
name: string;
slug: string;
businessType: string;
subscriptionStatus: string;
};
}
export declare class AuthService {
private readonly tenantRepository;
private readonly userRepository;
private readonly jwtService;
private readonly configService;
constructor(tenantRepository: Repository<Tenant>, userRepository: Repository<User>, jwtService: JwtService, configService: ConfigService);
private generateSlug;
register(dto: RegisterDto): Promise<AuthResponse>;
login(dto: LoginDto): Promise<AuthResponse>;
refreshToken(refreshToken: string): Promise<AuthResponse>;
changePin(userId: string, currentPin: string, newPin: string): Promise<void>;
private generateTokens;
}

View File

@ -0,0 +1,192 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const jwt_1 = require("@nestjs/jwt");
const config_1 = require("@nestjs/config");
const bcrypt = require("bcrypt");
const tenant_entity_1 = require("./entities/tenant.entity");
const user_entity_1 = require("./entities/user.entity");
let AuthService = class AuthService {
constructor(tenantRepository, userRepository, jwtService, configService) {
this.tenantRepository = tenantRepository;
this.userRepository = userRepository;
this.jwtService = jwtService;
this.configService = configService;
}
generateSlug(name) {
return name
.toLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/[^a-z0-9]+/g, '-')
.replace(/(^-|-$)/g, '')
.substring(0, 45) + '-' + Date.now().toString(36).slice(-4);
}
async register(dto) {
const existingTenant = await this.tenantRepository.findOne({
where: { phone: dto.phone },
});
if (existingTenant) {
throw new common_1.ConflictException('Este teléfono ya está registrado');
}
const pinHash = await bcrypt.hash(dto.pin, 10);
const slug = this.generateSlug(dto.name);
const tenant = this.tenantRepository.create({
name: dto.name,
slug,
businessType: dto.businessType,
phone: dto.phone,
email: dto.email,
address: dto.address,
city: dto.city,
whatsappNumber: dto.whatsapp || dto.phone,
subscriptionStatus: 'trial',
status: 'active',
});
const savedTenant = await this.tenantRepository.save(tenant);
const user = this.userRepository.create({
tenantId: savedTenant.id,
phone: dto.phone,
name: dto.ownerName,
pinHash,
role: 'owner',
status: 'active',
});
const savedUser = await this.userRepository.save(user);
return this.generateTokens(savedUser, savedTenant);
}
async login(dto) {
const user = await this.userRepository.findOne({
where: { phone: dto.phone },
});
if (!user) {
throw new common_1.UnauthorizedException('Teléfono o PIN incorrectos');
}
const tenant = await this.tenantRepository.findOne({
where: { id: user.tenantId },
});
if (!tenant) {
throw new common_1.UnauthorizedException('Teléfono o PIN incorrectos');
}
if (tenant.subscriptionStatus === 'cancelled') {
throw new common_1.UnauthorizedException('Tu suscripción ha sido cancelada');
}
if (tenant.subscriptionStatus === 'suspended' || tenant.status === 'suspended') {
throw new common_1.UnauthorizedException('Tu cuenta está suspendida. Contacta soporte.');
}
if (user.status !== 'active') {
throw new common_1.UnauthorizedException('Tu cuenta está inactiva');
}
if (user.lockedUntil && user.lockedUntil > new Date()) {
throw new common_1.UnauthorizedException('Cuenta bloqueada temporalmente. Intenta más tarde.');
}
const isValidPin = await bcrypt.compare(dto.pin, user.pinHash);
if (!isValidPin) {
user.failedAttempts = (user.failedAttempts || 0) + 1;
if (user.failedAttempts >= 5) {
user.lockedUntil = new Date(Date.now() + 15 * 60 * 1000);
}
await this.userRepository.save(user);
throw new common_1.UnauthorizedException('Teléfono o PIN incorrectos');
}
user.failedAttempts = 0;
user.lockedUntil = null;
user.lastLoginAt = new Date();
await this.userRepository.save(user);
return this.generateTokens(user, tenant);
}
async refreshToken(refreshToken) {
try {
const payload = this.jwtService.verify(refreshToken, {
secret: this.configService.get('JWT_SECRET'),
});
const user = await this.userRepository.findOne({
where: { id: payload.sub },
});
if (!user) {
throw new common_1.UnauthorizedException('Token inválido');
}
const tenant = await this.tenantRepository.findOne({
where: { id: user.tenantId },
});
if (!tenant) {
throw new common_1.UnauthorizedException('Token inválido');
}
return this.generateTokens(user, tenant);
}
catch {
throw new common_1.UnauthorizedException('Token inválido o expirado');
}
}
async changePin(userId, currentPin, newPin) {
const user = await this.userRepository.findOne({
where: { id: userId },
});
if (!user) {
throw new common_1.BadRequestException('Usuario no encontrado');
}
const isValidPin = await bcrypt.compare(currentPin, user.pinHash);
if (!isValidPin) {
throw new common_1.UnauthorizedException('PIN actual incorrecto');
}
user.pinHash = await bcrypt.hash(newPin, 10);
await this.userRepository.save(user);
}
generateTokens(user, tenant) {
const payload = {
sub: user.id,
tenantId: tenant.id,
phone: user.phone,
role: user.role,
};
const accessToken = this.jwtService.sign(payload, {
expiresIn: this.configService.get('JWT_EXPIRES_IN', '24h'),
});
const refreshToken = this.jwtService.sign(payload, {
expiresIn: this.configService.get('JWT_REFRESH_EXPIRES_IN', '7d'),
});
return {
accessToken,
refreshToken,
user: {
id: user.id,
name: user.name,
role: user.role,
phone: user.phone,
},
tenant: {
id: tenant.id,
name: tenant.name,
slug: tenant.slug,
businessType: tenant.businessType,
subscriptionStatus: tenant.subscriptionStatus,
},
};
}
};
exports.AuthService = AuthService;
exports.AuthService = AuthService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(tenant_entity_1.Tenant)),
__param(1, (0, typeorm_1.InjectRepository)(user_entity_1.User)),
__metadata("design:paramtypes", [typeorm_2.Repository,
typeorm_2.Repository,
jwt_1.JwtService,
config_1.ConfigService])
], AuthService);
//# sourceMappingURL=auth.service.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,18 @@
export declare class RegisterDto {
name: string;
ownerName: string;
businessType: string;
phone: string;
pin: string;
whatsapp?: string;
email?: string;
address?: string;
city?: string;
}
export declare class LoginDto {
phone: string;
pin: string;
}
export declare class RefreshTokenDto {
refreshToken: string;
}

View File

@ -0,0 +1,164 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RefreshTokenDto = exports.LoginDto = exports.RegisterDto = void 0;
const swagger_1 = require("@nestjs/swagger");
const class_validator_1 = require("class-validator");
class RegisterDto {
}
exports.RegisterDto = RegisterDto;
__decorate([
(0, swagger_1.ApiProperty)({
description: 'Nombre del negocio',
example: 'Tacos El Güero',
minLength: 2,
maxLength: 100,
}),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)(),
(0, class_validator_1.MinLength)(2),
(0, class_validator_1.MaxLength)(100),
__metadata("design:type", String)
], RegisterDto.prototype, "name", void 0);
__decorate([
(0, swagger_1.ApiProperty)({
description: 'Nombre del propietario',
example: 'Juan Pérez',
minLength: 2,
maxLength: 100,
}),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)(),
(0, class_validator_1.MinLength)(2),
(0, class_validator_1.MaxLength)(100),
__metadata("design:type", String)
], RegisterDto.prototype, "ownerName", void 0);
__decorate([
(0, swagger_1.ApiProperty)({
description: 'Tipo de negocio',
example: 'tiendita',
enum: ['tiendita', 'fonda', 'taqueria', 'abarrotes', 'tortilleria', 'otro'],
}),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)(),
(0, class_validator_1.MaxLength)(50),
__metadata("design:type", String)
], RegisterDto.prototype, "businessType", void 0);
__decorate([
(0, swagger_1.ApiProperty)({
description: 'Teléfono (10 dígitos)',
example: '5512345678',
}),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)(),
(0, class_validator_1.Matches)(/^[0-9]{10}$/, {
message: 'El teléfono debe tener exactamente 10 dígitos',
}),
__metadata("design:type", String)
], RegisterDto.prototype, "phone", void 0);
__decorate([
(0, swagger_1.ApiProperty)({
description: 'PIN de acceso rápido (4-6 dígitos)',
example: '1234',
minLength: 4,
maxLength: 6,
}),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)(),
(0, class_validator_1.Matches)(/^[0-9]{4,6}$/, {
message: 'El PIN debe tener entre 4 y 6 dígitos',
}),
__metadata("design:type", String)
], RegisterDto.prototype, "pin", void 0);
__decorate([
(0, swagger_1.ApiProperty)({
description: 'Número de WhatsApp (opcional)',
example: '5512345678',
required: false,
}),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
(0, class_validator_1.Matches)(/^[0-9]{10}$/, {
message: 'El WhatsApp debe tener exactamente 10 dígitos',
}),
__metadata("design:type", String)
], RegisterDto.prototype, "whatsapp", void 0);
__decorate([
(0, swagger_1.ApiProperty)({
description: 'Email (opcional)',
example: 'juan@ejemplo.com',
required: false,
}),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsEmail)({}, { message: 'Email inválido' }),
__metadata("design:type", String)
], RegisterDto.prototype, "email", void 0);
__decorate([
(0, swagger_1.ApiProperty)({
description: 'Dirección del negocio (opcional)',
example: 'Calle Principal #123, Colonia Centro',
required: false,
}),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
(0, class_validator_1.MaxLength)(500),
__metadata("design:type", String)
], RegisterDto.prototype, "address", void 0);
__decorate([
(0, swagger_1.ApiProperty)({
description: 'Ciudad (opcional)',
example: 'Ciudad de México',
required: false,
}),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
(0, class_validator_1.MaxLength)(50),
__metadata("design:type", String)
], RegisterDto.prototype, "city", void 0);
class LoginDto {
}
exports.LoginDto = LoginDto;
__decorate([
(0, swagger_1.ApiProperty)({
description: 'Teléfono registrado',
example: '5512345678',
}),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)(),
(0, class_validator_1.Matches)(/^[0-9]{10}$/, {
message: 'El teléfono debe tener exactamente 10 dígitos',
}),
__metadata("design:type", String)
], LoginDto.prototype, "phone", void 0);
__decorate([
(0, swagger_1.ApiProperty)({
description: 'PIN de acceso',
example: '1234',
}),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)(),
(0, class_validator_1.Matches)(/^[0-9]{4,6}$/, {
message: 'El PIN debe tener entre 4 y 6 dígitos',
}),
__metadata("design:type", String)
], LoginDto.prototype, "pin", void 0);
class RefreshTokenDto {
}
exports.RefreshTokenDto = RefreshTokenDto;
__decorate([
(0, swagger_1.ApiProperty)({
description: 'Token de refresco',
}),
(0, class_validator_1.IsString)(),
(0, class_validator_1.IsNotEmpty)(),
__metadata("design:type", String)
], RefreshTokenDto.prototype, "refreshToken", void 0);
//# sourceMappingURL=register.dto.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"register.dto.js","sourceRoot":"","sources":["../../../../src/modules/auth/dto/register.dto.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,40 @@
import { User } from './user.entity';
export declare enum SubscriptionStatus {
TRIAL = "trial",
ACTIVE = "active",
SUSPENDED = "suspended",
CANCELLED = "cancelled"
}
export declare enum TenantStatus {
ACTIVE = "active",
INACTIVE = "inactive",
SUSPENDED = "suspended"
}
export declare class Tenant {
id: string;
name: string;
slug: string;
businessType: string;
phone: string;
email: string;
address: string;
city: string;
state: string;
zipCode: string;
timezone: string;
currency: string;
taxRate: number;
taxIncluded: boolean;
whatsappNumber: string;
whatsappVerified: boolean;
usesPlatformNumber: boolean;
preferredLlmProvider: string;
preferredPaymentProvider: string;
currentPlanId: string;
subscriptionStatus: string;
status: string;
onboardingCompleted: boolean;
createdAt: Date;
updatedAt: Date;
users: User[];
}

View File

@ -0,0 +1,145 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Tenant = exports.TenantStatus = exports.SubscriptionStatus = void 0;
const typeorm_1 = require("typeorm");
const user_entity_1 = require("./user.entity");
var SubscriptionStatus;
(function (SubscriptionStatus) {
SubscriptionStatus["TRIAL"] = "trial";
SubscriptionStatus["ACTIVE"] = "active";
SubscriptionStatus["SUSPENDED"] = "suspended";
SubscriptionStatus["CANCELLED"] = "cancelled";
})(SubscriptionStatus || (exports.SubscriptionStatus = SubscriptionStatus = {}));
var TenantStatus;
(function (TenantStatus) {
TenantStatus["ACTIVE"] = "active";
TenantStatus["INACTIVE"] = "inactive";
TenantStatus["SUSPENDED"] = "suspended";
})(TenantStatus || (exports.TenantStatus = TenantStatus = {}));
let Tenant = class Tenant {
};
exports.Tenant = Tenant;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
__metadata("design:type", String)
], Tenant.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 100 }),
__metadata("design:type", String)
], Tenant.prototype, "name", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 50, unique: true }),
__metadata("design:type", String)
], Tenant.prototype, "slug", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'business_type', length: 50 }),
__metadata("design:type", String)
], Tenant.prototype, "businessType", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 20 }),
__metadata("design:type", String)
], Tenant.prototype, "phone", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 100, nullable: true }),
__metadata("design:type", String)
], Tenant.prototype, "email", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
__metadata("design:type", String)
], Tenant.prototype, "address", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 50, nullable: true }),
__metadata("design:type", String)
], Tenant.prototype, "city", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 50, nullable: true }),
__metadata("design:type", String)
], Tenant.prototype, "state", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'zip_code', length: 10, nullable: true }),
__metadata("design:type", String)
], Tenant.prototype, "zipCode", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 50, default: 'America/Mexico_City' }),
__metadata("design:type", String)
], Tenant.prototype, "timezone", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 3, default: 'MXN' }),
__metadata("design:type", String)
], Tenant.prototype, "currency", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'tax_rate', type: 'decimal', precision: 5, scale: 2, default: 16.0 }),
__metadata("design:type", Number)
], Tenant.prototype, "taxRate", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'tax_included', default: true }),
__metadata("design:type", Boolean)
], Tenant.prototype, "taxIncluded", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'whatsapp_number', length: 20, nullable: true }),
__metadata("design:type", String)
], Tenant.prototype, "whatsappNumber", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'whatsapp_verified', default: false }),
__metadata("design:type", Boolean)
], Tenant.prototype, "whatsappVerified", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'uses_platform_number', default: true }),
__metadata("design:type", Boolean)
], Tenant.prototype, "usesPlatformNumber", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'preferred_llm_provider', length: 20, default: 'openai' }),
__metadata("design:type", String)
], Tenant.prototype, "preferredLlmProvider", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'preferred_payment_provider', length: 20, default: 'stripe' }),
__metadata("design:type", String)
], Tenant.prototype, "preferredPaymentProvider", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'current_plan_id', type: 'uuid', nullable: true }),
__metadata("design:type", String)
], Tenant.prototype, "currentPlanId", void 0);
__decorate([
(0, typeorm_1.Column)({
name: 'subscription_status',
length: 20,
default: 'trial',
}),
__metadata("design:type", String)
], Tenant.prototype, "subscriptionStatus", void 0);
__decorate([
(0, typeorm_1.Column)({
length: 20,
default: 'active',
}),
__metadata("design:type", String)
], Tenant.prototype, "status", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'onboarding_completed', default: false }),
__metadata("design:type", Boolean)
], Tenant.prototype, "onboardingCompleted", void 0);
__decorate([
(0, typeorm_1.CreateDateColumn)({ name: 'created_at' }),
__metadata("design:type", Date)
], Tenant.prototype, "createdAt", void 0);
__decorate([
(0, typeorm_1.UpdateDateColumn)({ name: 'updated_at' }),
__metadata("design:type", Date)
], Tenant.prototype, "updatedAt", void 0);
__decorate([
(0, typeorm_1.OneToMany)(() => user_entity_1.User, (user) => user.tenant),
__metadata("design:type", Array)
], Tenant.prototype, "users", void 0);
exports.Tenant = Tenant = __decorate([
(0, typeorm_1.Entity)({ schema: 'public', name: 'tenants' })
], Tenant);
//# sourceMappingURL=tenant.entity.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"tenant.entity.js","sourceRoot":"","sources":["../../../../src/modules/auth/entities/tenant.entity.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,30 @@
import { Tenant } from './tenant.entity';
export declare enum UserRole {
OWNER = "owner",
ADMIN = "admin",
EMPLOYEE = "employee"
}
export declare enum UserStatus {
ACTIVE = "active",
INACTIVE = "inactive",
SUSPENDED = "suspended"
}
export declare class User {
id: string;
tenantId: string;
phone: string;
email: string;
name: string;
pinHash: string;
biometricEnabled: boolean;
biometricKey: string;
role: string;
permissions: Record<string, unknown>;
status: string;
lastLoginAt: Date;
failedAttempts: number;
lockedUntil: Date;
createdAt: Date;
updatedAt: Date;
tenant: Tenant;
}

View File

@ -0,0 +1,102 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.User = exports.UserStatus = exports.UserRole = void 0;
const typeorm_1 = require("typeorm");
const tenant_entity_1 = require("./tenant.entity");
var UserRole;
(function (UserRole) {
UserRole["OWNER"] = "owner";
UserRole["ADMIN"] = "admin";
UserRole["EMPLOYEE"] = "employee";
})(UserRole || (exports.UserRole = UserRole = {}));
var UserStatus;
(function (UserStatus) {
UserStatus["ACTIVE"] = "active";
UserStatus["INACTIVE"] = "inactive";
UserStatus["SUSPENDED"] = "suspended";
})(UserStatus || (exports.UserStatus = UserStatus = {}));
let User = class User {
};
exports.User = User;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
__metadata("design:type", String)
], User.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'tenant_id' }),
__metadata("design:type", String)
], User.prototype, "tenantId", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 20 }),
__metadata("design:type", String)
], User.prototype, "phone", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 100, nullable: true }),
__metadata("design:type", String)
], User.prototype, "email", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 100 }),
__metadata("design:type", String)
], User.prototype, "name", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'pin_hash', length: 255, nullable: true }),
__metadata("design:type", String)
], User.prototype, "pinHash", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'biometric_enabled', default: false }),
__metadata("design:type", Boolean)
], User.prototype, "biometricEnabled", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'biometric_key', type: 'text', nullable: true }),
__metadata("design:type", String)
], User.prototype, "biometricKey", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 20, default: 'owner' }),
__metadata("design:type", String)
], User.prototype, "role", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'jsonb', default: {} }),
__metadata("design:type", Object)
], User.prototype, "permissions", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 20, default: 'active' }),
__metadata("design:type", String)
], User.prototype, "status", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'last_login_at', type: 'timestamptz', nullable: true }),
__metadata("design:type", Date)
], User.prototype, "lastLoginAt", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'failed_attempts', default: 0 }),
__metadata("design:type", Number)
], User.prototype, "failedAttempts", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'locked_until', type: 'timestamptz', nullable: true }),
__metadata("design:type", Date)
], User.prototype, "lockedUntil", void 0);
__decorate([
(0, typeorm_1.CreateDateColumn)({ name: 'created_at' }),
__metadata("design:type", Date)
], User.prototype, "createdAt", void 0);
__decorate([
(0, typeorm_1.UpdateDateColumn)({ name: 'updated_at' }),
__metadata("design:type", Date)
], User.prototype, "updatedAt", void 0);
__decorate([
(0, typeorm_1.ManyToOne)(() => tenant_entity_1.Tenant, (tenant) => tenant.users),
(0, typeorm_1.JoinColumn)({ name: 'tenant_id' }),
__metadata("design:type", tenant_entity_1.Tenant)
], User.prototype, "tenant", void 0);
exports.User = User = __decorate([
(0, typeorm_1.Entity)({ schema: 'auth', name: 'users' })
], User);
//# sourceMappingURL=user.entity.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"user.entity.js","sourceRoot":"","sources":["../../../../src/modules/auth/entities/user.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAQiB;AACjB,mDAAyC;AAEzC,IAAY,QAIX;AAJD,WAAY,QAAQ;IAClB,2BAAe,CAAA;IACf,2BAAe,CAAA;IACf,iCAAqB,CAAA;AACvB,CAAC,EAJW,QAAQ,wBAAR,QAAQ,QAInB;AAED,IAAY,UAIX;AAJD,WAAY,UAAU;IACpB,+BAAiB,CAAA;IACjB,mCAAqB,CAAA;IACrB,qCAAuB,CAAA;AACzB,CAAC,EAJW,UAAU,0BAAV,UAAU,QAIrB;AAGM,IAAM,IAAI,GAAV,MAAM,IAAI;CAqDhB,CAAA;AArDY,oBAAI;AAEf;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;gCACpB;AAGX;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;;sCACb;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;mCACT;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mCAC1B;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;kCACX;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qCAC1C;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;;8CAC5B;AAG1B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CAC3C;AAGrB;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;;kCAC5B;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;;yCACF;AAGrC;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;;oCAC3B;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BAC1D,IAAI;yCAAC;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;4CACzB;AAGvB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BACzD,IAAI;yCAAC;AAGlB;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;8BAC9B,IAAI;uCAAC;AAGhB;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;8BAC9B,IAAI;uCAAC;AAKhB;IAFC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,sBAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC;IACjD,IAAA,oBAAU,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;8BAC1B,sBAAM;oCAAC;eApDJ,IAAI;IADhB,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;GAC7B,IAAI,CAqDhB"}

View File

@ -0,0 +1,7 @@
import { ExecutionContext } from '@nestjs/common';
declare const JwtAuthGuard_base: import("@nestjs/passport").Type<import("@nestjs/passport").IAuthGuard>;
export declare class JwtAuthGuard extends JwtAuthGuard_base {
canActivate(context: ExecutionContext): boolean | Promise<boolean> | import("rxjs").Observable<boolean>;
handleRequest<TUser>(err: Error | null, user: TUser): TUser;
}
export {};

View File

@ -0,0 +1,27 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.JwtAuthGuard = void 0;
const common_1 = require("@nestjs/common");
const passport_1 = require("@nestjs/passport");
let JwtAuthGuard = class JwtAuthGuard extends (0, passport_1.AuthGuard)('jwt') {
canActivate(context) {
return super.canActivate(context);
}
handleRequest(err, user) {
if (err || !user) {
throw err || new common_1.UnauthorizedException('Token inválido o expirado');
}
return user;
}
};
exports.JwtAuthGuard = JwtAuthGuard;
exports.JwtAuthGuard = JwtAuthGuard = __decorate([
(0, common_1.Injectable)()
], JwtAuthGuard);
//# sourceMappingURL=jwt-auth.guard.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"jwt-auth.guard.js","sourceRoot":"","sources":["../../../../src/modules/auth/guards/jwt-auth.guard.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAqF;AACrF,+CAA6C;AAGtC,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,IAAA,oBAAS,EAAC,KAAK,CAAC;IAChD,WAAW,CAAC,OAAyB;QACnC,OAAO,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,aAAa,CAAQ,GAAiB,EAAE,IAAW;QACjD,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,GAAG,IAAI,IAAI,8BAAqB,CAAC,2BAA2B,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAXY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;GACA,YAAY,CAWxB"}

View File

@ -0,0 +1,17 @@
import { Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';
import { Repository } from 'typeorm';
import { User } from '../entities/user.entity';
import { TokenPayload } from '../auth.service';
declare const JwtStrategy_base: new (...args: any[]) => Strategy;
export declare class JwtStrategy extends JwtStrategy_base {
private readonly configService;
private readonly userRepository;
constructor(configService: ConfigService, userRepository: Repository<User>);
validate(payload: TokenPayload): Promise<{
sub: string;
tenantId: string;
phone: string;
}>;
}
export {};

View File

@ -0,0 +1,54 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.JwtStrategy = void 0;
const common_1 = require("@nestjs/common");
const passport_1 = require("@nestjs/passport");
const passport_jwt_1 = require("passport-jwt");
const config_1 = require("@nestjs/config");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const user_entity_1 = require("../entities/user.entity");
let JwtStrategy = class JwtStrategy extends (0, passport_1.PassportStrategy)(passport_jwt_1.Strategy) {
constructor(configService, userRepository) {
super({
jwtFromRequest: passport_jwt_1.ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: configService.get('JWT_SECRET'),
});
this.configService = configService;
this.userRepository = userRepository;
}
async validate(payload) {
const user = await this.userRepository.findOne({
where: { id: payload.sub },
});
if (!user) {
throw new common_1.UnauthorizedException('Usuario no encontrado');
}
return {
sub: payload.sub,
tenantId: payload.tenantId,
phone: payload.phone,
};
}
};
exports.JwtStrategy = JwtStrategy;
exports.JwtStrategy = JwtStrategy = __decorate([
(0, common_1.Injectable)(),
__param(1, (0, typeorm_1.InjectRepository)(user_entity_1.User)),
__metadata("design:paramtypes", [config_1.ConfigService,
typeorm_2.Repository])
], JwtStrategy);
//# sourceMappingURL=jwt.strategy.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"jwt.strategy.js","sourceRoot":"","sources":["../../../../src/modules/auth/strategies/jwt.strategy.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAmE;AACnE,+CAAoD;AACpD,+CAAoD;AACpD,2CAA+C;AAC/C,6CAAmD;AACnD,qCAAqC;AACrC,yDAA+C;AAIxC,IAAM,WAAW,GAAjB,MAAM,WAAY,SAAQ,IAAA,2BAAgB,EAAC,uBAAQ,CAAC;IACzD,YACmB,aAA4B,EAE5B,cAAgC;QAEjD,KAAK,CAAC;YACJ,cAAc,EAAE,yBAAU,CAAC,2BAA2B,EAAE;YACxD,gBAAgB,EAAE,KAAK;YACvB,WAAW,EAAE,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC;SAC7C,CAAC,CAAC;QARc,kBAAa,GAAb,aAAa,CAAe;QAE5B,mBAAc,GAAd,cAAc,CAAkB;IAOnD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,OAAqB;QAClC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC;YAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,GAAG,EAAE;SAC3B,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,8BAAqB,CAAC,uBAAuB,CAAC,CAAC;QAC3D,CAAC;QAED,OAAO;YACL,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;SACrB,CAAC;IACJ,CAAC;CACF,CAAA;AA5BY,kCAAW;sBAAX,WAAW;IADvB,IAAA,mBAAU,GAAE;IAIR,WAAA,IAAA,0BAAgB,EAAC,kBAAI,CAAC,CAAA;qCADS,sBAAa;QAEZ,oBAAU;GAJlC,WAAW,CA4BvB"}

View File

@ -0,0 +1,38 @@
import { BillingService } from './billing.service';
export declare class BillingController {
private readonly billingService;
constructor(billingService: BillingService);
getPlans(): Promise<import("../subscriptions/entities/plan.entity").Plan[]>;
getTokenPackages(): Promise<import("./billing.service").TokenPackage[]>;
getBillingSummary(req: any): Promise<{
subscription: import("../subscriptions/entities/subscription.entity").Subscription | null;
plan: import("../subscriptions/entities/plan.entity").Plan | null;
tokenBalance: import("../subscriptions/entities/token-balance.entity").TokenBalance | null;
invoices: any[];
}>;
getTokenBalance(req: any): Promise<import("../subscriptions/entities/token-balance.entity").TokenBalance | {
availableTokens: number;
usedTokens: number;
totalTokens: number;
}>;
getTokenUsage(req: any, limit?: string): Promise<import("../subscriptions/entities/token-usage.entity").TokenUsage[]>;
createSubscriptionCheckout(req: any, body: {
planCode: string;
successUrl: string;
cancelUrl: string;
}): Promise<{
checkoutUrl: string;
}>;
createTokenCheckout(req: any, body: {
packageCode: string;
successUrl: string;
cancelUrl: string;
}): Promise<{
checkoutUrl: string;
}>;
createPortalSession(req: any, body: {
returnUrl: string;
}): Promise<{
portalUrl: string;
}>;
}

View File

@ -0,0 +1,113 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BillingController = void 0;
const common_1 = require("@nestjs/common");
const jwt_auth_guard_1 = require("../auth/guards/jwt-auth.guard");
const billing_service_1 = require("./billing.service");
let BillingController = class BillingController {
constructor(billingService) {
this.billingService = billingService;
}
async getPlans() {
return this.billingService.getPlans();
}
async getTokenPackages() {
return this.billingService.getTokenPackages();
}
async getBillingSummary(req) {
return this.billingService.getBillingSummary(req.user.tenantId);
}
async getTokenBalance(req) {
const balance = await this.billingService.getTokenBalance(req.user.tenantId);
return balance || { availableTokens: 0, usedTokens: 0, totalTokens: 0 };
}
async getTokenUsage(req, limit) {
return this.billingService.getTokenUsageHistory(req.user.tenantId, limit ? parseInt(limit, 10) : 50);
}
async createSubscriptionCheckout(req, body) {
return this.billingService.createSubscriptionCheckout(req.user.tenantId, body.planCode, body.successUrl, body.cancelUrl);
}
async createTokenCheckout(req, body) {
return this.billingService.createTokenPurchaseCheckout(req.user.tenantId, body.packageCode, body.successUrl, body.cancelUrl);
}
async createPortalSession(req, body) {
return this.billingService.createPortalSession(req.user.tenantId, body.returnUrl);
}
};
exports.BillingController = BillingController;
__decorate([
(0, common_1.Get)('plans'),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], BillingController.prototype, "getPlans", null);
__decorate([
(0, common_1.Get)('token-packages'),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], BillingController.prototype, "getTokenPackages", null);
__decorate([
(0, common_1.Get)('summary'),
__param(0, (0, common_1.Request)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], BillingController.prototype, "getBillingSummary", null);
__decorate([
(0, common_1.Get)('token-balance'),
__param(0, (0, common_1.Request)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], BillingController.prototype, "getTokenBalance", null);
__decorate([
(0, common_1.Get)('token-usage'),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Query)('limit')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], BillingController.prototype, "getTokenUsage", null);
__decorate([
(0, common_1.Post)('checkout/subscription'),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], BillingController.prototype, "createSubscriptionCheckout", null);
__decorate([
(0, common_1.Post)('checkout/tokens'),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], BillingController.prototype, "createTokenCheckout", null);
__decorate([
(0, common_1.Post)('portal'),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], BillingController.prototype, "createPortalSession", null);
exports.BillingController = BillingController = __decorate([
(0, common_1.Controller)('billing'),
(0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard),
__metadata("design:paramtypes", [billing_service_1.BillingService])
], BillingController);
//# sourceMappingURL=billing.controller.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"billing.controller.js","sourceRoot":"","sources":["../../../src/modules/billing/billing.controller.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,2 @@
export declare class BillingModule {
}

View File

@ -0,0 +1,40 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BillingModule = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const typeorm_1 = require("@nestjs/typeorm");
const billing_service_1 = require("./billing.service");
const billing_controller_1 = require("./billing.controller");
const stripe_service_1 = require("./stripe.service");
const webhooks_controller_1 = require("./webhooks.controller");
const subscription_entity_1 = require("../subscriptions/entities/subscription.entity");
const plan_entity_1 = require("../subscriptions/entities/plan.entity");
const token_balance_entity_1 = require("../subscriptions/entities/token-balance.entity");
const token_usage_entity_1 = require("../subscriptions/entities/token-usage.entity");
let BillingModule = class BillingModule {
};
exports.BillingModule = BillingModule;
exports.BillingModule = BillingModule = __decorate([
(0, common_1.Module)({
imports: [
config_1.ConfigModule,
typeorm_1.TypeOrmModule.forFeature([
subscription_entity_1.Subscription,
plan_entity_1.Plan,
token_balance_entity_1.TokenBalance,
token_usage_entity_1.TokenUsage,
]),
],
controllers: [billing_controller_1.BillingController, webhooks_controller_1.WebhooksController],
providers: [billing_service_1.BillingService, stripe_service_1.StripeService],
exports: [billing_service_1.BillingService, stripe_service_1.StripeService],
})
], BillingModule);
//# sourceMappingURL=billing.module.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"billing.module.js","sourceRoot":"","sources":["../../../src/modules/billing/billing.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2CAA8C;AAC9C,6CAAgD;AAChD,uDAAmD;AACnD,6DAAyD;AACzD,qDAAiD;AACjD,+DAA2D;AAC3D,uFAA6E;AAC7E,uEAA6D;AAC7D,yFAA8E;AAC9E,qFAA0E;AAgBnE,IAAM,aAAa,GAAnB,MAAM,aAAa;CAAG,CAAA;AAAhB,sCAAa;wBAAb,aAAa;IAdzB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY;YACZ,uBAAa,CAAC,UAAU,CAAC;gBACvB,kCAAY;gBACZ,kBAAI;gBACJ,mCAAY;gBACZ,+BAAU;aACX,CAAC;SACH;QACD,WAAW,EAAE,CAAC,sCAAiB,EAAE,wCAAkB,CAAC;QACpD,SAAS,EAAE,CAAC,gCAAc,EAAE,8BAAa,CAAC;QAC1C,OAAO,EAAE,CAAC,gCAAc,EAAE,8BAAa,CAAC;KACzC,CAAC;GACW,aAAa,CAAG"}

View File

@ -0,0 +1,47 @@
import { Repository } from 'typeorm';
import { StripeService } from './stripe.service';
import { Subscription } from '../subscriptions/entities/subscription.entity';
import { Plan } from '../subscriptions/entities/plan.entity';
import { TokenBalance } from '../subscriptions/entities/token-balance.entity';
import { TokenUsage } from '../subscriptions/entities/token-usage.entity';
export interface TokenPackage {
code: string;
name: string;
tokens: number;
priceMxn: number;
stripePriceId?: string;
}
export declare class BillingService {
private stripeService;
private subscriptionRepo;
private planRepo;
private tokenBalanceRepo;
private tokenUsageRepo;
private readonly logger;
private readonly tokenPackages;
constructor(stripeService: StripeService, subscriptionRepo: Repository<Subscription>, planRepo: Repository<Plan>, tokenBalanceRepo: Repository<TokenBalance>, tokenUsageRepo: Repository<TokenUsage>);
getPlans(): Promise<Plan[]>;
getTokenPackages(): TokenPackage[];
createSubscriptionCheckout(tenantId: string, planCode: string, successUrl: string, cancelUrl: string): Promise<{
checkoutUrl: string;
}>;
createTokenPurchaseCheckout(tenantId: string, packageCode: string, successUrl: string, cancelUrl: string): Promise<{
checkoutUrl: string;
}>;
createPortalSession(tenantId: string, returnUrl: string): Promise<{
portalUrl: string;
}>;
handleSubscriptionCreated(stripeSubscriptionId: string, stripeCustomerId: string, stripePriceId: string): Promise<void>;
handleSubscriptionCancelled(stripeSubscriptionId: string): Promise<void>;
handleTokenPurchase(tenantId: string, packageCode: string, tokens: number): Promise<void>;
getTokenBalance(tenantId: string): Promise<TokenBalance | null>;
addTokensToBalance(tenantId: string, tokens: number, source: 'subscription' | 'purchase' | 'bonus'): Promise<TokenBalance>;
consumeTokens(tenantId: string, tokens: number, action: string, description?: string): Promise<boolean>;
getTokenUsageHistory(tenantId: string, limit?: number): Promise<TokenUsage[]>;
getBillingSummary(tenantId: string): Promise<{
subscription: Subscription | null;
plan: Plan | null;
tokenBalance: TokenBalance | null;
invoices: any[];
}>;
}

View File

@ -0,0 +1,220 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var BillingService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BillingService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const stripe_service_1 = require("./stripe.service");
const subscription_entity_1 = require("../subscriptions/entities/subscription.entity");
const plan_entity_1 = require("../subscriptions/entities/plan.entity");
const token_balance_entity_1 = require("../subscriptions/entities/token-balance.entity");
const token_usage_entity_1 = require("../subscriptions/entities/token-usage.entity");
let BillingService = BillingService_1 = class BillingService {
constructor(stripeService, subscriptionRepo, planRepo, tokenBalanceRepo, tokenUsageRepo) {
this.stripeService = stripeService;
this.subscriptionRepo = subscriptionRepo;
this.planRepo = planRepo;
this.tokenBalanceRepo = tokenBalanceRepo;
this.tokenUsageRepo = tokenUsageRepo;
this.logger = new common_1.Logger(BillingService_1.name);
this.tokenPackages = [
{ code: 'tokens_1000', name: '1,000 Tokens', tokens: 1000, priceMxn: 29 },
{ code: 'tokens_3000', name: '3,000 Tokens', tokens: 3000, priceMxn: 69 },
{ code: 'tokens_8000', name: '8,000 Tokens', tokens: 8000, priceMxn: 149 },
{ code: 'tokens_20000', name: '20,000 Tokens', tokens: 20000, priceMxn: 299 },
];
}
async getPlans() {
return this.planRepo.find({
where: { status: 'active' },
order: { priceMonthly: 'ASC' },
});
}
getTokenPackages() {
return this.tokenPackages;
}
async createSubscriptionCheckout(tenantId, planCode, successUrl, cancelUrl) {
const plan = await this.planRepo.findOne({ where: { code: planCode, status: 'active' } });
if (!plan || !plan.stripePriceIdMonthly) {
throw new common_1.NotFoundException('Plan no encontrado');
}
const subscription = await this.subscriptionRepo.findOne({ where: { tenantId } });
if (!subscription?.stripeCustomerId) {
throw new common_1.BadRequestException('Cliente de Stripe no configurado');
}
const session = await this.stripeService.createCheckoutSession({
customerId: subscription.stripeCustomerId,
priceId: plan.stripePriceIdMonthly,
mode: 'subscription',
successUrl,
cancelUrl,
metadata: { tenantId, planCode },
});
return { checkoutUrl: session.url };
}
async createTokenPurchaseCheckout(tenantId, packageCode, successUrl, cancelUrl) {
const tokenPackage = this.tokenPackages.find((p) => p.code === packageCode);
if (!tokenPackage) {
throw new common_1.NotFoundException('Paquete de tokens no encontrado');
}
const subscription = await this.subscriptionRepo.findOne({ where: { tenantId } });
if (!subscription?.stripeCustomerId) {
throw new common_1.BadRequestException('Cliente de Stripe no configurado');
}
const paymentIntent = await this.stripeService.createPaymentIntent({
amount: tokenPackage.priceMxn * 100,
customerId: subscription.stripeCustomerId,
metadata: {
tenantId,
packageCode,
tokens: tokenPackage.tokens.toString(),
},
});
return { checkoutUrl: paymentIntent.client_secret };
}
async createPortalSession(tenantId, returnUrl) {
const subscription = await this.subscriptionRepo.findOne({ where: { tenantId } });
if (!subscription?.stripeCustomerId) {
throw new common_1.BadRequestException('Cliente de Stripe no configurado');
}
const session = await this.stripeService.createPortalSession({
customerId: subscription.stripeCustomerId,
returnUrl,
});
return { portalUrl: session.url };
}
async handleSubscriptionCreated(stripeSubscriptionId, stripeCustomerId, stripePriceId) {
let plan = await this.planRepo.findOne({ where: { stripePriceIdMonthly: stripePriceId } });
if (!plan) {
plan = await this.planRepo.findOne({ where: { stripePriceIdYearly: stripePriceId } });
}
if (!plan) {
this.logger.warn(`Plan not found for price: ${stripePriceId}`);
return;
}
const subscription = await this.subscriptionRepo.findOne({
where: { stripeCustomerId },
});
if (subscription) {
subscription.planId = plan.id;
subscription.stripeSubscriptionId = stripeSubscriptionId;
subscription.status = subscription_entity_1.SubscriptionStatus.ACTIVE;
subscription.currentPeriodStart = new Date();
subscription.currentPeriodEnd = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
await this.subscriptionRepo.save(subscription);
if (plan.includedTokens > 0) {
await this.addTokensToBalance(subscription.tenantId, plan.includedTokens, 'subscription');
}
this.logger.log(`Subscription activated for tenant: ${subscription.tenantId}`);
}
}
async handleSubscriptionCancelled(stripeSubscriptionId) {
const subscription = await this.subscriptionRepo.findOne({
where: { stripeSubscriptionId },
});
if (subscription) {
subscription.status = subscription_entity_1.SubscriptionStatus.CANCELLED;
subscription.cancelledAt = new Date();
await this.subscriptionRepo.save(subscription);
this.logger.log(`Subscription cancelled for tenant: ${subscription.tenantId}`);
}
}
async handleTokenPurchase(tenantId, packageCode, tokens) {
await this.addTokensToBalance(tenantId, tokens, 'purchase');
this.logger.log(`Added ${tokens} tokens to tenant: ${tenantId}`);
}
async getTokenBalance(tenantId) {
return this.tokenBalanceRepo.findOne({ where: { tenantId } });
}
async addTokensToBalance(tenantId, tokens, source) {
let balance = await this.tokenBalanceRepo.findOne({ where: { tenantId } });
if (!balance) {
balance = this.tokenBalanceRepo.create({
tenantId,
usedTokens: 0,
availableTokens: 0,
});
}
balance.availableTokens += tokens;
return this.tokenBalanceRepo.save(balance);
}
async consumeTokens(tenantId, tokens, action, description) {
const balance = await this.tokenBalanceRepo.findOne({ where: { tenantId } });
if (!balance || balance.availableTokens < tokens) {
return false;
}
balance.usedTokens += tokens;
balance.availableTokens -= tokens;
await this.tokenBalanceRepo.save(balance);
const usage = this.tokenUsageRepo.create({
tenantId,
tokensUsed: tokens,
action,
description,
});
await this.tokenUsageRepo.save(usage);
return true;
}
async getTokenUsageHistory(tenantId, limit = 50) {
return this.tokenUsageRepo.find({
where: { tenantId },
order: { createdAt: 'DESC' },
take: limit,
});
}
async getBillingSummary(tenantId) {
const subscription = await this.subscriptionRepo.findOne({
where: { tenantId },
relations: ['plan'],
});
const tokenBalance = await this.getTokenBalance(tenantId);
let invoices = [];
if (subscription?.stripeCustomerId) {
try {
invoices = await this.stripeService.listInvoices(subscription.stripeCustomerId, 5);
}
catch (error) {
this.logger.warn('Could not fetch invoices from Stripe');
}
}
return {
subscription,
plan: subscription?.plan || null,
tokenBalance,
invoices: invoices.map((inv) => ({
id: inv.id,
amount: inv.amount_paid / 100,
status: inv.status,
date: new Date(inv.created * 1000),
pdfUrl: inv.invoice_pdf,
})),
};
}
};
exports.BillingService = BillingService;
exports.BillingService = BillingService = BillingService_1 = __decorate([
(0, common_1.Injectable)(),
__param(1, (0, typeorm_1.InjectRepository)(subscription_entity_1.Subscription)),
__param(2, (0, typeorm_1.InjectRepository)(plan_entity_1.Plan)),
__param(3, (0, typeorm_1.InjectRepository)(token_balance_entity_1.TokenBalance)),
__param(4, (0, typeorm_1.InjectRepository)(token_usage_entity_1.TokenUsage)),
__metadata("design:paramtypes", [stripe_service_1.StripeService,
typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository])
], BillingService);
//# sourceMappingURL=billing.service.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,56 @@
import { ConfigService } from '@nestjs/config';
import Stripe from 'stripe';
export declare class StripeService {
private configService;
private readonly logger;
private stripe;
constructor(configService: ConfigService);
private ensureStripe;
createCustomer(params: {
email?: string;
phone: string;
name: string;
tenantId: string;
}): Promise<Stripe.Customer>;
getCustomer(customerId: string): Promise<Stripe.Customer | null>;
createSubscription(params: {
customerId: string;
priceId: string;
trialDays?: number;
}): Promise<Stripe.Subscription>;
cancelSubscription(subscriptionId: string): Promise<Stripe.Subscription>;
getSubscription(subscriptionId: string): Promise<Stripe.Subscription | null>;
updateSubscription(subscriptionId: string, params: Stripe.SubscriptionUpdateParams): Promise<Stripe.Subscription>;
createPaymentIntent(params: {
amount: number;
customerId: string;
metadata?: Record<string, string>;
}): Promise<Stripe.PaymentIntent>;
createCheckoutSession(params: {
customerId: string;
priceId: string;
mode: 'subscription' | 'payment';
successUrl: string;
cancelUrl: string;
metadata?: Record<string, string>;
}): Promise<Stripe.Checkout.Session>;
createPortalSession(params: {
customerId: string;
returnUrl: string;
}): Promise<Stripe.BillingPortal.Session>;
constructWebhookEvent(payload: string | Buffer, signature: string, webhookSecret: string): Stripe.Event;
createProduct(params: {
name: string;
description?: string;
metadata?: Record<string, string>;
}): Promise<Stripe.Product>;
createPrice(params: {
productId: string;
unitAmount: number;
recurring?: {
interval: 'month' | 'year';
};
metadata?: Record<string, string>;
}): Promise<Stripe.Price>;
listInvoices(customerId: string, limit?: number): Promise<Stripe.Invoice[]>;
}

View File

@ -0,0 +1,160 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var StripeService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.StripeService = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const stripe_1 = require("stripe");
let StripeService = StripeService_1 = class StripeService {
constructor(configService) {
this.configService = configService;
this.logger = new common_1.Logger(StripeService_1.name);
const secretKey = this.configService.get('STRIPE_SECRET_KEY');
if (!secretKey) {
this.logger.warn('STRIPE_SECRET_KEY not configured - billing features disabled');
return;
}
this.stripe = new stripe_1.default(secretKey);
}
ensureStripe() {
if (!this.stripe) {
throw new Error('Stripe not configured');
}
}
async createCustomer(params) {
this.ensureStripe();
return this.stripe.customers.create({
email: params.email,
phone: params.phone,
name: params.name,
metadata: {
tenantId: params.tenantId,
},
});
}
async getCustomer(customerId) {
this.ensureStripe();
try {
const customer = await this.stripe.customers.retrieve(customerId);
return customer.deleted ? null : customer;
}
catch (error) {
return null;
}
}
async createSubscription(params) {
this.ensureStripe();
const subscriptionParams = {
customer: params.customerId,
items: [{ price: params.priceId }],
payment_behavior: 'default_incomplete',
payment_settings: {
save_default_payment_method: 'on_subscription',
},
expand: ['latest_invoice.payment_intent'],
};
if (params.trialDays) {
subscriptionParams.trial_period_days = params.trialDays;
}
return this.stripe.subscriptions.create(subscriptionParams);
}
async cancelSubscription(subscriptionId) {
this.ensureStripe();
return this.stripe.subscriptions.cancel(subscriptionId);
}
async getSubscription(subscriptionId) {
this.ensureStripe();
try {
return await this.stripe.subscriptions.retrieve(subscriptionId);
}
catch (error) {
return null;
}
}
async updateSubscription(subscriptionId, params) {
this.ensureStripe();
return this.stripe.subscriptions.update(subscriptionId, params);
}
async createPaymentIntent(params) {
this.ensureStripe();
return this.stripe.paymentIntents.create({
amount: params.amount,
currency: 'mxn',
customer: params.customerId,
metadata: params.metadata || {},
automatic_payment_methods: {
enabled: true,
},
});
}
async createCheckoutSession(params) {
this.ensureStripe();
return this.stripe.checkout.sessions.create({
customer: params.customerId,
mode: params.mode,
line_items: [{ price: params.priceId, quantity: 1 }],
success_url: params.successUrl,
cancel_url: params.cancelUrl,
metadata: params.metadata || {},
locale: 'es',
payment_method_types: params.mode === 'subscription'
? ['card']
: ['card', 'oxxo'],
});
}
async createPortalSession(params) {
this.ensureStripe();
return this.stripe.billingPortal.sessions.create({
customer: params.customerId,
return_url: params.returnUrl,
});
}
constructWebhookEvent(payload, signature, webhookSecret) {
this.ensureStripe();
return this.stripe.webhooks.constructEvent(payload, signature, webhookSecret);
}
async createProduct(params) {
this.ensureStripe();
return this.stripe.products.create({
name: params.name,
description: params.description,
metadata: params.metadata || {},
});
}
async createPrice(params) {
this.ensureStripe();
const priceParams = {
product: params.productId,
unit_amount: params.unitAmount,
currency: 'mxn',
metadata: params.metadata || {},
};
if (params.recurring) {
priceParams.recurring = params.recurring;
}
return this.stripe.prices.create(priceParams);
}
async listInvoices(customerId, limit = 10) {
this.ensureStripe();
const invoices = await this.stripe.invoices.list({
customer: customerId,
limit,
});
return invoices.data;
}
};
exports.StripeService = StripeService;
exports.StripeService = StripeService = StripeService_1 = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [config_1.ConfigService])
], StripeService);
//# sourceMappingURL=stripe.service.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"stripe.service.js","sourceRoot":"","sources":["../../../src/modules/billing/stripe.service.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,19 @@
import { RawBodyRequest } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Request } from 'express';
import { StripeService } from './stripe.service';
import { BillingService } from './billing.service';
export declare class WebhooksController {
private stripeService;
private billingService;
private configService;
private readonly logger;
constructor(stripeService: StripeService, billingService: BillingService, configService: ConfigService);
handleStripeWebhook(req: RawBodyRequest<Request>, signature: string): Promise<{
received: boolean;
error?: undefined;
} | {
error: string;
received?: undefined;
}>;
}

View File

@ -0,0 +1,105 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var WebhooksController_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebhooksController = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const stripe_service_1 = require("./stripe.service");
const billing_service_1 = require("./billing.service");
let WebhooksController = WebhooksController_1 = class WebhooksController {
constructor(stripeService, billingService, configService) {
this.stripeService = stripeService;
this.billingService = billingService;
this.configService = configService;
this.logger = new common_1.Logger(WebhooksController_1.name);
}
async handleStripeWebhook(req, signature) {
const webhookSecret = this.configService.get('STRIPE_WEBHOOK_SECRET');
if (!webhookSecret) {
this.logger.warn('STRIPE_WEBHOOK_SECRET not configured');
return { received: true };
}
let event;
try {
event = this.stripeService.constructWebhookEvent(req.rawBody, signature, webhookSecret);
}
catch (err) {
this.logger.error(`Webhook signature verification failed: ${err.message}`);
return { error: 'Invalid signature' };
}
this.logger.log(`Received Stripe event: ${event.type}`);
try {
switch (event.type) {
case 'customer.subscription.created':
case 'customer.subscription.updated': {
const subscription = event.data.object;
await this.billingService.handleSubscriptionCreated(subscription.id, subscription.customer, subscription.items.data[0].price.id);
break;
}
case 'customer.subscription.deleted': {
const subscription = event.data.object;
await this.billingService.handleSubscriptionCancelled(subscription.id);
break;
}
case 'payment_intent.succeeded': {
const paymentIntent = event.data.object;
const metadata = paymentIntent.metadata;
if (metadata.packageCode && metadata.tenantId && metadata.tokens) {
await this.billingService.handleTokenPurchase(metadata.tenantId, metadata.packageCode, parseInt(metadata.tokens, 10));
}
break;
}
case 'invoice.payment_succeeded': {
const invoice = event.data.object;
this.logger.log(`Invoice paid: ${invoice.id}`);
break;
}
case 'invoice.payment_failed': {
const invoice = event.data.object;
this.logger.warn(`Invoice payment failed: ${invoice.id}`);
break;
}
case 'checkout.session.completed': {
const session = event.data.object;
this.logger.log(`Checkout completed: ${session.id}`);
break;
}
default:
this.logger.debug(`Unhandled event type: ${event.type}`);
}
}
catch (error) {
this.logger.error(`Error processing webhook: ${error.message}`);
}
return { received: true };
}
};
exports.WebhooksController = WebhooksController;
__decorate([
(0, common_1.Post)('stripe'),
(0, common_1.HttpCode)(200),
__param(0, (0, common_1.Req)()),
__param(1, (0, common_1.Headers)('stripe-signature')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], WebhooksController.prototype, "handleStripeWebhook", null);
exports.WebhooksController = WebhooksController = WebhooksController_1 = __decorate([
(0, common_1.Controller)('webhooks'),
__metadata("design:paramtypes", [stripe_service_1.StripeService,
billing_service_1.BillingService,
config_1.ConfigService])
], WebhooksController);
//# sourceMappingURL=webhooks.controller.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"webhooks.controller.js","sourceRoot":"","sources":["../../../src/modules/billing/webhooks.controller.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,36 @@
import { CategoriesService } from './categories.service';
import { CreateCategoryDto, UpdateCategoryDto } from './dto/category.dto';
export declare class CategoriesController {
private readonly categoriesService;
constructor(categoriesService: CategoriesService);
findAll(req: {
user: {
tenantId: string;
};
}, includeInactive?: boolean): Promise<import("./entities/category.entity").Category[]>;
findOne(req: {
user: {
tenantId: string;
};
}, id: string): Promise<import("./entities/category.entity").Category>;
create(req: {
user: {
tenantId: string;
};
}, dto: CreateCategoryDto): Promise<import("./entities/category.entity").Category>;
update(req: {
user: {
tenantId: string;
};
}, id: string, dto: UpdateCategoryDto): Promise<import("./entities/category.entity").Category>;
toggleActive(req: {
user: {
tenantId: string;
};
}, id: string): Promise<import("./entities/category.entity").Category>;
delete(req: {
user: {
tenantId: string;
};
}, id: string): Promise<void>;
}

View File

@ -0,0 +1,113 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CategoriesController = void 0;
const common_1 = require("@nestjs/common");
const swagger_1 = require("@nestjs/swagger");
const categories_service_1 = require("./categories.service");
const category_dto_1 = require("./dto/category.dto");
const jwt_auth_guard_1 = require("../auth/guards/jwt-auth.guard");
let CategoriesController = class CategoriesController {
constructor(categoriesService) {
this.categoriesService = categoriesService;
}
async findAll(req, includeInactive) {
return this.categoriesService.findAll(req.user.tenantId, includeInactive);
}
async findOne(req, id) {
return this.categoriesService.findOne(req.user.tenantId, id);
}
async create(req, dto) {
return this.categoriesService.create(req.user.tenantId, dto);
}
async update(req, id, dto) {
return this.categoriesService.update(req.user.tenantId, id, dto);
}
async toggleActive(req, id) {
return this.categoriesService.toggleActive(req.user.tenantId, id);
}
async delete(req, id) {
await this.categoriesService.delete(req.user.tenantId, id);
}
};
exports.CategoriesController = CategoriesController;
__decorate([
(0, common_1.Get)(),
(0, swagger_1.ApiOperation)({ summary: 'Listar categorías' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Query)('includeInactive')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Boolean]),
__metadata("design:returntype", Promise)
], CategoriesController.prototype, "findAll", null);
__decorate([
(0, common_1.Get)(':id'),
(0, swagger_1.ApiOperation)({ summary: 'Obtener categoría por ID' }),
(0, swagger_1.ApiParam)({ name: 'id', description: 'ID de la categoría' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('id', common_1.ParseUUIDPipe)),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], CategoriesController.prototype, "findOne", null);
__decorate([
(0, common_1.Post)(),
(0, swagger_1.ApiOperation)({ summary: 'Crear categoría' }),
(0, swagger_1.ApiResponse)({ status: 201, description: 'Categoría creada' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, category_dto_1.CreateCategoryDto]),
__metadata("design:returntype", Promise)
], CategoriesController.prototype, "create", null);
__decorate([
(0, common_1.Put)(':id'),
(0, swagger_1.ApiOperation)({ summary: 'Actualizar categoría' }),
(0, swagger_1.ApiParam)({ name: 'id', description: 'ID de la categoría' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('id', common_1.ParseUUIDPipe)),
__param(2, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String, category_dto_1.UpdateCategoryDto]),
__metadata("design:returntype", Promise)
], CategoriesController.prototype, "update", null);
__decorate([
(0, common_1.Patch)(':id/toggle-active'),
(0, swagger_1.ApiOperation)({ summary: 'Activar/desactivar categoría' }),
(0, swagger_1.ApiParam)({ name: 'id', description: 'ID de la categoría' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('id', common_1.ParseUUIDPipe)),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], CategoriesController.prototype, "toggleActive", null);
__decorate([
(0, common_1.Delete)(':id'),
(0, common_1.HttpCode)(common_1.HttpStatus.NO_CONTENT),
(0, swagger_1.ApiOperation)({ summary: 'Eliminar categoría' }),
(0, swagger_1.ApiParam)({ name: 'id', description: 'ID de la categoría' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('id', common_1.ParseUUIDPipe)),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], CategoriesController.prototype, "delete", null);
exports.CategoriesController = CategoriesController = __decorate([
(0, swagger_1.ApiTags)('categories'),
(0, swagger_1.ApiBearerAuth)(),
(0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard),
(0, common_1.Controller)('v1/categories'),
__metadata("design:paramtypes", [categories_service_1.CategoriesService])
], CategoriesController);
//# sourceMappingURL=categories.controller.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"categories.controller.js","sourceRoot":"","sources":["../../../src/modules/categories/categories.controller.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,2 @@
export declare class CategoriesModule {
}

View File

@ -0,0 +1,30 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CategoriesModule = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const categories_controller_1 = require("./categories.controller");
const categories_service_1 = require("./categories.service");
const category_entity_1 = require("./entities/category.entity");
const auth_module_1 = require("../auth/auth.module");
let CategoriesModule = class CategoriesModule {
};
exports.CategoriesModule = CategoriesModule;
exports.CategoriesModule = CategoriesModule = __decorate([
(0, common_1.Module)({
imports: [
typeorm_1.TypeOrmModule.forFeature([category_entity_1.Category]),
auth_module_1.AuthModule,
],
controllers: [categories_controller_1.CategoriesController],
providers: [categories_service_1.CategoriesService],
exports: [categories_service_1.CategoriesService, typeorm_1.TypeOrmModule],
})
], CategoriesModule);
//# sourceMappingURL=categories.module.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"categories.module.js","sourceRoot":"","sources":["../../../src/modules/categories/categories.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,mEAA+D;AAC/D,6DAAyD;AACzD,gEAAsD;AACtD,qDAAiD;AAW1C,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;CAAG,CAAA;AAAnB,4CAAgB;2BAAhB,gBAAgB;IAT5B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC,CAAC,0BAAQ,CAAC,CAAC;YACpC,wBAAU;SACX;QACD,WAAW,EAAE,CAAC,4CAAoB,CAAC;QACnC,SAAS,EAAE,CAAC,sCAAiB,CAAC;QAC9B,OAAO,EAAE,CAAC,sCAAiB,EAAE,uBAAa,CAAC;KAC5C,CAAC;GACW,gBAAgB,CAAG"}

View File

@ -0,0 +1,13 @@
import { Repository } from 'typeorm';
import { Category } from './entities/category.entity';
import { CreateCategoryDto, UpdateCategoryDto } from './dto/category.dto';
export declare class CategoriesService {
private readonly categoryRepository;
constructor(categoryRepository: Repository<Category>);
findAll(tenantId: string, includeInactive?: boolean): Promise<Category[]>;
findOne(tenantId: string, id: string): Promise<Category>;
create(tenantId: string, dto: CreateCategoryDto): Promise<Category>;
update(tenantId: string, id: string, dto: UpdateCategoryDto): Promise<Category>;
delete(tenantId: string, id: string): Promise<void>;
toggleActive(tenantId: string, id: string): Promise<Category>;
}

View File

@ -0,0 +1,90 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CategoriesService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const category_entity_1 = require("./entities/category.entity");
const MAX_CATEGORIES = 20;
let CategoriesService = class CategoriesService {
constructor(categoryRepository) {
this.categoryRepository = categoryRepository;
}
async findAll(tenantId, includeInactive = false) {
const where = { tenantId };
if (!includeInactive) {
where.status = 'active';
}
return this.categoryRepository.find({
where,
order: { sortOrder: 'ASC', name: 'ASC' },
});
}
async findOne(tenantId, id) {
const category = await this.categoryRepository.findOne({
where: { id, tenantId },
});
if (!category) {
throw new common_1.NotFoundException('Categoría no encontrada');
}
return category;
}
async create(tenantId, dto) {
const count = await this.categoryRepository.count({ where: { tenantId } });
if (count >= MAX_CATEGORIES) {
throw new common_1.BadRequestException(`Has alcanzado el límite de ${MAX_CATEGORIES} categorías`);
}
const existing = await this.categoryRepository.findOne({
where: { tenantId, name: dto.name },
});
if (existing) {
throw new common_1.ConflictException('Ya existe una categoría con ese nombre');
}
const category = this.categoryRepository.create({
...dto,
tenantId,
});
return this.categoryRepository.save(category);
}
async update(tenantId, id, dto) {
const category = await this.findOne(tenantId, id);
if (dto.name && dto.name !== category.name) {
const existing = await this.categoryRepository.findOne({
where: { tenantId, name: dto.name },
});
if (existing) {
throw new common_1.ConflictException('Ya existe una categoría con ese nombre');
}
}
Object.assign(category, dto);
return this.categoryRepository.save(category);
}
async delete(tenantId, id) {
const category = await this.findOne(tenantId, id);
await this.categoryRepository.remove(category);
}
async toggleActive(tenantId, id) {
const category = await this.findOne(tenantId, id);
category.status = category.status === 'active' ? 'inactive' : 'active';
return this.categoryRepository.save(category);
}
};
exports.CategoriesService = CategoriesService;
exports.CategoriesService = CategoriesService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(category_entity_1.Category)),
__metadata("design:paramtypes", [typeorm_2.Repository])
], CategoriesService);
//# sourceMappingURL=categories.service.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"categories.service.js","sourceRoot":"","sources":["../../../src/modules/categories/categories.service.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,15 @@
export declare class CreateCategoryDto {
name: string;
description?: string;
color?: string;
icon?: string;
sortOrder?: number;
}
export declare class UpdateCategoryDto {
name?: string;
description?: string;
color?: string;
icon?: string;
sortOrder?: number;
status?: string;
}

View File

@ -0,0 +1,80 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UpdateCategoryDto = exports.CreateCategoryDto = void 0;
const class_validator_1 = require("class-validator");
class CreateCategoryDto {
}
exports.CreateCategoryDto = CreateCategoryDto;
__decorate([
(0, class_validator_1.IsString)(),
(0, class_validator_1.MaxLength)(50),
__metadata("design:type", String)
], CreateCategoryDto.prototype, "name", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], CreateCategoryDto.prototype, "description", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
(0, class_validator_1.MaxLength)(7),
__metadata("design:type", String)
], CreateCategoryDto.prototype, "color", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
(0, class_validator_1.MaxLength)(50),
__metadata("design:type", String)
], CreateCategoryDto.prototype, "icon", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsNumber)(),
__metadata("design:type", Number)
], CreateCategoryDto.prototype, "sortOrder", void 0);
class UpdateCategoryDto {
}
exports.UpdateCategoryDto = UpdateCategoryDto;
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
(0, class_validator_1.MaxLength)(50),
__metadata("design:type", String)
], UpdateCategoryDto.prototype, "name", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], UpdateCategoryDto.prototype, "description", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
(0, class_validator_1.MaxLength)(7),
__metadata("design:type", String)
], UpdateCategoryDto.prototype, "color", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
(0, class_validator_1.MaxLength)(50),
__metadata("design:type", String)
], UpdateCategoryDto.prototype, "icon", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsNumber)(),
__metadata("design:type", Number)
], UpdateCategoryDto.prototype, "sortOrder", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], UpdateCategoryDto.prototype, "status", void 0);
//# sourceMappingURL=category.dto.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"category.dto.js","sourceRoot":"","sources":["../../../../src/modules/categories/dto/category.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAA4E;AAE5E,MAAa,iBAAiB;CAsB7B;AAtBD,8CAsBC;AAnBC;IAFC,IAAA,0BAAQ,GAAE;IACV,IAAA,2BAAS,EAAC,EAAE,CAAC;;+CACD;AAIb;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;sDACU;AAKrB;IAHC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,2BAAS,EAAC,CAAC,CAAC;;gDACE;AAKf;IAHC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,2BAAS,EAAC,EAAE,CAAC;;+CACA;AAId;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;oDACQ;AAGrB,MAAa,iBAAiB;CA2B7B;AA3BD,8CA2BC;AAvBC;IAHC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,2BAAS,EAAC,EAAE,CAAC;;+CACA;AAId;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;sDACU;AAKrB;IAHC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,2BAAS,EAAC,CAAC,CAAC;;gDACE;AAKf;IAHC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,2BAAS,EAAC,EAAE,CAAC;;+CACA;AAId;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;oDACQ;AAInB;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;iDACK"}

View File

@ -0,0 +1,14 @@
import { Product } from '../../products/entities/product.entity';
export declare class Category {
id: string;
tenantId: string;
name: string;
description: string;
icon: string;
color: string;
sortOrder: number;
status: string;
createdAt: Date;
updatedAt: Date;
products: Product[];
}

View File

@ -0,0 +1,65 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Category = void 0;
const typeorm_1 = require("typeorm");
const product_entity_1 = require("../../products/entities/product.entity");
let Category = class Category {
};
exports.Category = Category;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
__metadata("design:type", String)
], Category.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'tenant_id' }),
__metadata("design:type", String)
], Category.prototype, "tenantId", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 50 }),
__metadata("design:type", String)
], Category.prototype, "name", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
__metadata("design:type", String)
], Category.prototype, "description", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 50, nullable: true }),
__metadata("design:type", String)
], Category.prototype, "icon", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 7, nullable: true }),
__metadata("design:type", String)
], Category.prototype, "color", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'sort_order', default: 0 }),
__metadata("design:type", Number)
], Category.prototype, "sortOrder", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 20, default: 'active' }),
__metadata("design:type", String)
], Category.prototype, "status", void 0);
__decorate([
(0, typeorm_1.CreateDateColumn)({ name: 'created_at' }),
__metadata("design:type", Date)
], Category.prototype, "createdAt", void 0);
__decorate([
(0, typeorm_1.UpdateDateColumn)({ name: 'updated_at' }),
__metadata("design:type", Date)
], Category.prototype, "updatedAt", void 0);
__decorate([
(0, typeorm_1.OneToMany)(() => product_entity_1.Product, (product) => product.category),
__metadata("design:type", Array)
], Category.prototype, "products", void 0);
exports.Category = Category = __decorate([
(0, typeorm_1.Entity)({ schema: 'catalog', name: 'categories' })
], Category);
//# sourceMappingURL=category.entity.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"category.entity.js","sourceRoot":"","sources":["../../../../src/modules/categories/entities/category.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAOiB;AACjB,2EAAiE;AAG1D,IAAM,QAAQ,GAAd,MAAM,QAAQ;CAkCpB,CAAA;AAlCY,4BAAQ;AAEnB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;oCACpB;AAGX;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;;0CACb;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;sCACV;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;6CACrB;AAGpB;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;sCAC1B;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;uCACxB;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;2CACzB;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;;wCAC3B;AAGf;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;8BAC9B,IAAI;2CAAC;AAGhB;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;8BAC9B,IAAI;2CAAC;AAIhB;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,wBAAO,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC;;0CACpC;mBAjCT,QAAQ;IADpB,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;GACrC,QAAQ,CAkCpB"}

View File

@ -0,0 +1,29 @@
import { CustomersService } from './customers.service';
import { CreateCustomerDto, UpdateCustomerDto, CreateFiadoDto, PayFiadoDto } from './dto/customer.dto';
export declare class CustomersController {
private readonly customersService;
constructor(customersService: CustomersService);
findAll(req: any): Promise<import("./entities/customer.entity").Customer[]>;
getWithFiados(req: any): Promise<import("./entities/customer.entity").Customer[]>;
findByPhone(req: any, phone: string): Promise<import("./entities/customer.entity").Customer>;
findOne(req: any, id: string): Promise<import("./entities/customer.entity").Customer>;
getStats(req: any, id: string): Promise<{
customer: import("./entities/customer.entity").Customer;
stats: {
totalPurchases: number;
purchaseCount: number;
fiadoBalance: number;
fiadoLimit: number;
fiadoAvailable: number;
pendingFiados: number;
};
}>;
create(req: any, dto: CreateCustomerDto): Promise<import("./entities/customer.entity").Customer>;
update(req: any, id: string, dto: UpdateCustomerDto): Promise<import("./entities/customer.entity").Customer>;
toggleActive(req: any, id: string): Promise<import("./entities/customer.entity").Customer>;
getFiados(req: any, customerId?: string): Promise<import("./entities/fiado.entity").Fiado[]>;
getPendingFiados(req: any): Promise<import("./entities/fiado.entity").Fiado[]>;
createFiado(req: any, dto: CreateFiadoDto): Promise<import("./entities/fiado.entity").Fiado>;
payFiado(req: any, id: string, dto: PayFiadoDto): Promise<import("./entities/fiado.entity").Fiado>;
cancelFiado(req: any, id: string): Promise<import("./entities/fiado.entity").Fiado>;
}

View File

@ -0,0 +1,190 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CustomersController = void 0;
const common_1 = require("@nestjs/common");
const swagger_1 = require("@nestjs/swagger");
const jwt_auth_guard_1 = require("../auth/guards/jwt-auth.guard");
const customers_service_1 = require("./customers.service");
const customer_dto_1 = require("./dto/customer.dto");
let CustomersController = class CustomersController {
constructor(customersService) {
this.customersService = customersService;
}
findAll(req) {
return this.customersService.findAll(req.user.tenantId);
}
getWithFiados(req) {
return this.customersService.getWithFiados(req.user.tenantId);
}
findByPhone(req, phone) {
return this.customersService.findByPhone(req.user.tenantId, phone);
}
findOne(req, id) {
return this.customersService.findOne(req.user.tenantId, id);
}
getStats(req, id) {
return this.customersService.getCustomerStats(req.user.tenantId, id);
}
create(req, dto) {
return this.customersService.create(req.user.tenantId, dto);
}
update(req, id, dto) {
return this.customersService.update(req.user.tenantId, id, dto);
}
toggleActive(req, id) {
return this.customersService.toggleActive(req.user.tenantId, id);
}
getFiados(req, customerId) {
return this.customersService.getFiados(req.user.tenantId, customerId);
}
getPendingFiados(req) {
return this.customersService.getPendingFiados(req.user.tenantId);
}
createFiado(req, dto) {
return this.customersService.createFiado(req.user.tenantId, dto);
}
payFiado(req, id, dto) {
return this.customersService.payFiado(req.user.tenantId, id, dto, req.user.id);
}
cancelFiado(req, id) {
return this.customersService.cancelFiado(req.user.tenantId, id);
}
};
exports.CustomersController = CustomersController;
__decorate([
(0, common_1.Get)(),
(0, swagger_1.ApiOperation)({ summary: 'Listar todos los clientes' }),
__param(0, (0, common_1.Request)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "findAll", null);
__decorate([
(0, common_1.Get)('with-fiados'),
(0, swagger_1.ApiOperation)({ summary: 'Listar clientes con fiado habilitado' }),
__param(0, (0, common_1.Request)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "getWithFiados", null);
__decorate([
(0, common_1.Get)('phone/:phone'),
(0, swagger_1.ApiOperation)({ summary: 'Buscar cliente por teléfono' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('phone')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "findByPhone", null);
__decorate([
(0, common_1.Get)(':id'),
(0, swagger_1.ApiOperation)({ summary: 'Obtener cliente por ID' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('id')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "findOne", null);
__decorate([
(0, common_1.Get)(':id/stats'),
(0, swagger_1.ApiOperation)({ summary: 'Obtener estadísticas del cliente' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('id')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "getStats", null);
__decorate([
(0, common_1.Post)(),
(0, swagger_1.ApiOperation)({ summary: 'Crear nuevo cliente' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, customer_dto_1.CreateCustomerDto]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "create", null);
__decorate([
(0, common_1.Put)(':id'),
(0, swagger_1.ApiOperation)({ summary: 'Actualizar cliente' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('id')),
__param(2, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String, customer_dto_1.UpdateCustomerDto]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "update", null);
__decorate([
(0, common_1.Patch)(':id/toggle-active'),
(0, swagger_1.ApiOperation)({ summary: 'Activar/desactivar cliente' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('id')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "toggleActive", null);
__decorate([
(0, common_1.Get)('fiados/all'),
(0, swagger_1.ApiOperation)({ summary: 'Listar todos los fiados' }),
(0, swagger_1.ApiQuery)({ name: 'customerId', required: false }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Query)('customerId')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "getFiados", null);
__decorate([
(0, common_1.Get)('fiados/pending'),
(0, swagger_1.ApiOperation)({ summary: 'Listar fiados pendientes' }),
__param(0, (0, common_1.Request)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "getPendingFiados", null);
__decorate([
(0, common_1.Post)('fiados'),
(0, swagger_1.ApiOperation)({ summary: 'Crear nuevo fiado' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, customer_dto_1.CreateFiadoDto]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "createFiado", null);
__decorate([
(0, common_1.Post)('fiados/:id/pay'),
(0, swagger_1.ApiOperation)({ summary: 'Registrar pago de fiado' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('id')),
__param(2, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String, customer_dto_1.PayFiadoDto]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "payFiado", null);
__decorate([
(0, common_1.Patch)('fiados/:id/cancel'),
(0, swagger_1.ApiOperation)({ summary: 'Cancelar fiado' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('id')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", void 0)
], CustomersController.prototype, "cancelFiado", null);
exports.CustomersController = CustomersController = __decorate([
(0, swagger_1.ApiTags)('customers'),
(0, swagger_1.ApiBearerAuth)(),
(0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard),
(0, common_1.Controller)('v1/customers'),
__metadata("design:paramtypes", [customers_service_1.CustomersService])
], CustomersController);
//# sourceMappingURL=customers.controller.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"customers.controller.js","sourceRoot":"","sources":["../../../src/modules/customers/customers.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAWwB;AACxB,6CAAiF;AACjF,kEAA6D;AAC7D,2DAAuD;AACvD,qDAAuG;AAMhG,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,YAA6B,gBAAkC;QAAlC,qBAAgB,GAAhB,gBAAgB,CAAkB;IAAG,CAAC;IAMnE,OAAO,CAAY,GAAG;QACpB,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1D,CAAC;IAID,aAAa,CAAY,GAAG;QAC1B,OAAO,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChE,CAAC;IAID,WAAW,CAAY,GAAG,EAAkB,KAAa;QACvD,OAAO,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACrE,CAAC;IAID,OAAO,CAAY,GAAG,EAAe,EAAU;QAC7C,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC;IAID,QAAQ,CAAY,GAAG,EAAe,EAAU;QAC9C,OAAO,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACvE,CAAC;IAID,MAAM,CAAY,GAAG,EAAU,GAAsB;QACnD,OAAO,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC9D,CAAC;IAID,MAAM,CAAY,GAAG,EAAe,EAAU,EAAU,GAAsB;QAC5E,OAAO,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;IAClE,CAAC;IAID,YAAY,CAAY,GAAG,EAAe,EAAU;QAClD,OAAO,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACnE,CAAC;IAOD,SAAS,CAAY,GAAG,EAAuB,UAAmB;QAChE,OAAO,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACxE,CAAC;IAID,gBAAgB,CAAY,GAAG;QAC7B,OAAO,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnE,CAAC;IAID,WAAW,CAAY,GAAG,EAAU,GAAmB;QACrD,OAAO,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACnE,CAAC;IAID,QAAQ,CAAY,GAAG,EAAe,EAAU,EAAU,GAAgB;QACxE,OAAO,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjF,CAAC;IAID,WAAW,CAAY,GAAG,EAAe,EAAU;QACjD,OAAO,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAClE,CAAC;CACF,CAAA;AArFY,kDAAmB;AAO9B;IAFC,IAAA,YAAG,GAAE;IACL,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,2BAA2B,EAAE,CAAC;IAC9C,WAAA,IAAA,gBAAO,GAAE,CAAA;;;;kDAEjB;AAID;IAFC,IAAA,YAAG,EAAC,aAAa,CAAC;IAClB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,sCAAsC,EAAE,CAAC;IACnD,WAAA,IAAA,gBAAO,GAAE,CAAA;;;;wDAEvB;AAID;IAFC,IAAA,YAAG,EAAC,cAAc,CAAC;IACnB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,6BAA6B,EAAE,CAAC;IAC5C,WAAA,IAAA,gBAAO,GAAE,CAAA;IAAO,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;;;;sDAE1C;AAID;IAFC,IAAA,YAAG,EAAC,KAAK,CAAC;IACV,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,wBAAwB,EAAE,CAAC;IAC3C,WAAA,IAAA,gBAAO,GAAE,CAAA;IAAO,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;kDAEnC;AAID;IAFC,IAAA,YAAG,EAAC,WAAW,CAAC;IAChB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAC;IACpD,WAAA,IAAA,gBAAO,GAAE,CAAA;IAAO,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;mDAEpC;AAID;IAFC,IAAA,aAAI,GAAE;IACN,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACzC,WAAA,IAAA,gBAAO,GAAE,CAAA;IAAO,WAAA,IAAA,aAAI,GAAE,CAAA;;6CAAM,gCAAiB;;iDAEpD;AAID;IAFC,IAAA,YAAG,EAAC,KAAK,CAAC;IACV,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;IACxC,WAAA,IAAA,gBAAO,GAAE,CAAA;IAAO,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,aAAI,GAAE,CAAA;;qDAAM,gCAAiB;;iDAE7E;AAID;IAFC,IAAA,cAAK,EAAC,mBAAmB,CAAC;IAC1B,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,4BAA4B,EAAE,CAAC;IAC1C,WAAA,IAAA,gBAAO,GAAE,CAAA;IAAO,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;uDAExC;AAOD;IAHC,IAAA,YAAG,EAAC,YAAY,CAAC;IACjB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC;IACpD,IAAA,kBAAQ,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACvC,WAAA,IAAA,gBAAO,GAAE,CAAA;IAAO,WAAA,IAAA,cAAK,EAAC,YAAY,CAAC,CAAA;;;;oDAE7C;AAID;IAFC,IAAA,YAAG,EAAC,gBAAgB,CAAC;IACrB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC;IACpC,WAAA,IAAA,gBAAO,GAAE,CAAA;;;;2DAE1B;AAID;IAFC,IAAA,aAAI,EAAC,QAAQ,CAAC;IACd,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAClC,WAAA,IAAA,gBAAO,GAAE,CAAA;IAAO,WAAA,IAAA,aAAI,GAAE,CAAA;;6CAAM,6BAAc;;sDAEtD;AAID;IAFC,IAAA,aAAI,EAAC,gBAAgB,CAAC;IACtB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC;IAC3C,WAAA,IAAA,gBAAO,GAAE,CAAA;IAAO,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IAAc,WAAA,IAAA,aAAI,GAAE,CAAA;;qDAAM,0BAAW;;mDAEzE;AAID;IAFC,IAAA,cAAK,EAAC,mBAAmB,CAAC;IAC1B,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC/B,WAAA,IAAA,gBAAO,GAAE,CAAA;IAAO,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;sDAEvC;8BApFU,mBAAmB;IAJ/B,IAAA,iBAAO,EAAC,WAAW,CAAC;IACpB,IAAA,uBAAa,GAAE;IACf,IAAA,kBAAS,EAAC,6BAAY,CAAC;IACvB,IAAA,mBAAU,EAAC,cAAc,CAAC;qCAEsB,oCAAgB;GADpD,mBAAmB,CAqF/B"}

View File

@ -0,0 +1,2 @@
export declare class CustomersModule {
}

View File

@ -0,0 +1,28 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CustomersModule = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const customers_controller_1 = require("./customers.controller");
const customers_service_1 = require("./customers.service");
const customer_entity_1 = require("./entities/customer.entity");
const fiado_entity_1 = require("./entities/fiado.entity");
const fiado_payment_entity_1 = require("./entities/fiado-payment.entity");
let CustomersModule = class CustomersModule {
};
exports.CustomersModule = CustomersModule;
exports.CustomersModule = CustomersModule = __decorate([
(0, common_1.Module)({
imports: [typeorm_1.TypeOrmModule.forFeature([customer_entity_1.Customer, fiado_entity_1.Fiado, fiado_payment_entity_1.FiadoPayment])],
controllers: [customers_controller_1.CustomersController],
providers: [customers_service_1.CustomersService],
exports: [customers_service_1.CustomersService],
})
], CustomersModule);
//# sourceMappingURL=customers.module.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"customers.module.js","sourceRoot":"","sources":["../../../src/modules/customers/customers.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,iEAA6D;AAC7D,2DAAuD;AACvD,gEAAsD;AACtD,0DAAgD;AAChD,0EAA+D;AAQxD,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IAN3B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,uBAAa,CAAC,UAAU,CAAC,CAAC,0BAAQ,EAAE,oBAAK,EAAE,mCAAY,CAAC,CAAC,CAAC;QACpE,WAAW,EAAE,CAAC,0CAAmB,CAAC;QAClC,SAAS,EAAE,CAAC,oCAAgB,CAAC;QAC7B,OAAO,EAAE,CAAC,oCAAgB,CAAC;KAC5B,CAAC;GACW,eAAe,CAAG"}

View File

@ -0,0 +1,34 @@
import { Repository } from 'typeorm';
import { Customer } from './entities/customer.entity';
import { Fiado } from './entities/fiado.entity';
import { FiadoPayment } from './entities/fiado-payment.entity';
import { CreateCustomerDto, UpdateCustomerDto, CreateFiadoDto, PayFiadoDto } from './dto/customer.dto';
export declare class CustomersService {
private readonly customerRepo;
private readonly fiadoRepo;
private readonly fiadoPaymentRepo;
constructor(customerRepo: Repository<Customer>, fiadoRepo: Repository<Fiado>, fiadoPaymentRepo: Repository<FiadoPayment>);
findAll(tenantId: string): Promise<Customer[]>;
findOne(tenantId: string, id: string): Promise<Customer>;
findByPhone(tenantId: string, phone: string): Promise<Customer | null>;
create(tenantId: string, dto: CreateCustomerDto): Promise<Customer>;
update(tenantId: string, id: string, dto: UpdateCustomerDto): Promise<Customer>;
toggleActive(tenantId: string, id: string): Promise<Customer>;
getWithFiados(tenantId: string): Promise<Customer[]>;
createFiado(tenantId: string, dto: CreateFiadoDto): Promise<Fiado>;
getFiados(tenantId: string, customerId?: string): Promise<Fiado[]>;
getPendingFiados(tenantId: string): Promise<Fiado[]>;
payFiado(tenantId: string, fiadoId: string, dto: PayFiadoDto, userId?: string): Promise<Fiado>;
cancelFiado(tenantId: string, fiadoId: string): Promise<Fiado>;
getCustomerStats(tenantId: string, customerId: string): Promise<{
customer: Customer;
stats: {
totalPurchases: number;
purchaseCount: number;
fiadoBalance: number;
fiadoLimit: number;
fiadoAvailable: number;
pendingFiados: number;
};
}>;
}

View File

@ -0,0 +1,202 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CustomersService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const customer_entity_1 = require("./entities/customer.entity");
const fiado_entity_1 = require("./entities/fiado.entity");
const fiado_payment_entity_1 = require("./entities/fiado-payment.entity");
let CustomersService = class CustomersService {
constructor(customerRepo, fiadoRepo, fiadoPaymentRepo) {
this.customerRepo = customerRepo;
this.fiadoRepo = fiadoRepo;
this.fiadoPaymentRepo = fiadoPaymentRepo;
}
async findAll(tenantId) {
return this.customerRepo.find({
where: { tenantId, status: 'active' },
order: { name: 'ASC' },
});
}
async findOne(tenantId, id) {
const customer = await this.customerRepo.findOne({
where: { id, tenantId },
relations: ['fiados'],
});
if (!customer) {
throw new common_1.NotFoundException('Cliente no encontrado');
}
return customer;
}
async findByPhone(tenantId, phone) {
return this.customerRepo.findOne({
where: { tenantId, phone },
});
}
async create(tenantId, dto) {
const customer = this.customerRepo.create({
...dto,
tenantId,
});
return this.customerRepo.save(customer);
}
async update(tenantId, id, dto) {
const customer = await this.findOne(tenantId, id);
Object.assign(customer, dto);
return this.customerRepo.save(customer);
}
async toggleActive(tenantId, id) {
const customer = await this.findOne(tenantId, id);
customer.status = customer.status === 'active' ? 'inactive' : 'active';
return this.customerRepo.save(customer);
}
async getWithFiados(tenantId) {
return this.customerRepo.find({
where: { tenantId, fiadoEnabled: true },
order: { currentFiadoBalance: 'DESC' },
});
}
async createFiado(tenantId, dto) {
const customer = await this.findOne(tenantId, dto.customerId);
if (!customer.fiadoEnabled) {
throw new common_1.BadRequestException('El cliente no tiene habilitado el fiado');
}
const newBalance = Number(customer.currentFiadoBalance) + dto.amount;
if (customer.fiadoLimit > 0 && newBalance > customer.fiadoLimit) {
throw new common_1.BadRequestException(`El fiado excede el límite. Límite: $${customer.fiadoLimit}, Balance actual: $${customer.currentFiadoBalance}`);
}
const fiado = this.fiadoRepo.create({
tenantId,
customerId: dto.customerId,
saleId: dto.saleId,
amount: dto.amount,
remainingAmount: dto.amount,
description: dto.description,
dueDate: dto.dueDate ? new Date(dto.dueDate) : null,
status: fiado_entity_1.FiadoStatus.PENDING,
});
await this.fiadoRepo.save(fiado);
customer.currentFiadoBalance = newBalance;
await this.customerRepo.save(customer);
return fiado;
}
async getFiados(tenantId, customerId) {
const where = { tenantId };
if (customerId) {
where.customerId = customerId;
}
return this.fiadoRepo.find({
where,
relations: ['customer', 'payments'],
order: { createdAt: 'DESC' },
});
}
async getPendingFiados(tenantId) {
return this.fiadoRepo.find({
where: [
{ tenantId, status: fiado_entity_1.FiadoStatus.PENDING },
{ tenantId, status: fiado_entity_1.FiadoStatus.PARTIAL },
],
relations: ['customer'],
order: { createdAt: 'ASC' },
});
}
async payFiado(tenantId, fiadoId, dto, userId) {
const fiado = await this.fiadoRepo.findOne({
where: { id: fiadoId, tenantId },
relations: ['customer'],
});
if (!fiado) {
throw new common_1.NotFoundException('Fiado no encontrado');
}
if (fiado.status === fiado_entity_1.FiadoStatus.PAID) {
throw new common_1.BadRequestException('Este fiado ya está pagado');
}
if (dto.amount > Number(fiado.remainingAmount)) {
throw new common_1.BadRequestException(`El monto excede el saldo pendiente: $${fiado.remainingAmount}`);
}
const payment = this.fiadoPaymentRepo.create({
fiadoId,
amount: dto.amount,
paymentMethod: dto.paymentMethod || 'cash',
notes: dto.notes,
receivedBy: userId,
});
await this.fiadoPaymentRepo.save(payment);
fiado.paidAmount = Number(fiado.paidAmount) + dto.amount;
fiado.remainingAmount = Number(fiado.remainingAmount) - dto.amount;
if (fiado.remainingAmount <= 0) {
fiado.status = fiado_entity_1.FiadoStatus.PAID;
fiado.paidAt = new Date();
}
else {
fiado.status = fiado_entity_1.FiadoStatus.PARTIAL;
}
await this.fiadoRepo.save(fiado);
const customer = fiado.customer;
customer.currentFiadoBalance = Number(customer.currentFiadoBalance) - dto.amount;
await this.customerRepo.save(customer);
return fiado;
}
async cancelFiado(tenantId, fiadoId) {
const fiado = await this.fiadoRepo.findOne({
where: { id: fiadoId, tenantId },
relations: ['customer'],
});
if (!fiado) {
throw new common_1.NotFoundException('Fiado no encontrado');
}
if (fiado.status === fiado_entity_1.FiadoStatus.PAID) {
throw new common_1.BadRequestException('No se puede cancelar un fiado pagado');
}
const customer = fiado.customer;
customer.currentFiadoBalance = Number(customer.currentFiadoBalance) - Number(fiado.remainingAmount);
await this.customerRepo.save(customer);
fiado.status = fiado_entity_1.FiadoStatus.CANCELLED;
return this.fiadoRepo.save(fiado);
}
async getCustomerStats(tenantId, customerId) {
const customer = await this.findOne(tenantId, customerId);
const pendingFiados = await this.fiadoRepo.count({
where: [
{ customerId, status: fiado_entity_1.FiadoStatus.PENDING },
{ customerId, status: fiado_entity_1.FiadoStatus.PARTIAL },
],
});
return {
customer,
stats: {
totalPurchases: customer.totalPurchases,
purchaseCount: customer.purchaseCount,
fiadoBalance: customer.currentFiadoBalance,
fiadoLimit: customer.fiadoLimit,
fiadoAvailable: Math.max(0, Number(customer.fiadoLimit) - Number(customer.currentFiadoBalance)),
pendingFiados,
},
};
}
};
exports.CustomersService = CustomersService;
exports.CustomersService = CustomersService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(customer_entity_1.Customer)),
__param(1, (0, typeorm_1.InjectRepository)(fiado_entity_1.Fiado)),
__param(2, (0, typeorm_1.InjectRepository)(fiado_payment_entity_1.FiadoPayment)),
__metadata("design:paramtypes", [typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository])
], CustomersService);
//# sourceMappingURL=customers.service.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,26 @@
export declare class CreateCustomerDto {
name: string;
phone?: string;
whatsapp?: string;
email?: string;
address?: string;
notes?: string;
fiadoEnabled?: boolean;
fiadoLimit?: number;
}
declare const UpdateCustomerDto_base: import("@nestjs/common").Type<Partial<CreateCustomerDto>>;
export declare class UpdateCustomerDto extends UpdateCustomerDto_base {
}
export declare class CreateFiadoDto {
customerId: string;
saleId?: string;
amount: number;
description?: string;
dueDate?: string;
}
export declare class PayFiadoDto {
amount: number;
paymentMethod?: string;
notes?: string;
}
export {};

View File

@ -0,0 +1,127 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PayFiadoDto = exports.CreateFiadoDto = exports.UpdateCustomerDto = exports.CreateCustomerDto = void 0;
const swagger_1 = require("@nestjs/swagger");
const class_validator_1 = require("class-validator");
class CreateCustomerDto {
}
exports.CreateCustomerDto = CreateCustomerDto;
__decorate([
(0, swagger_1.ApiProperty)({ description: 'Nombre del cliente', example: 'Juan Pérez' }),
(0, class_validator_1.IsString)(),
(0, class_validator_1.MaxLength)(200),
__metadata("design:type", String)
], CreateCustomerDto.prototype, "name", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Teléfono', example: '5512345678' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
(0, class_validator_1.MaxLength)(20),
__metadata("design:type", String)
], CreateCustomerDto.prototype, "phone", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'WhatsApp', example: '5512345678' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
(0, class_validator_1.MaxLength)(20),
__metadata("design:type", String)
], CreateCustomerDto.prototype, "whatsapp", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Email', example: 'juan@email.com' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsEmail)(),
(0, class_validator_1.MaxLength)(255),
__metadata("design:type", String)
], CreateCustomerDto.prototype, "email", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Dirección' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], CreateCustomerDto.prototype, "address", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Notas adicionales' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], CreateCustomerDto.prototype, "notes", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Habilitar fiado', default: false }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsBoolean)(),
__metadata("design:type", Boolean)
], CreateCustomerDto.prototype, "fiadoEnabled", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Límite de fiado', example: 500 }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsNumber)(),
(0, class_validator_1.Min)(0),
__metadata("design:type", Number)
], CreateCustomerDto.prototype, "fiadoLimit", void 0);
class UpdateCustomerDto extends (0, swagger_1.PartialType)(CreateCustomerDto) {
}
exports.UpdateCustomerDto = UpdateCustomerDto;
class CreateFiadoDto {
}
exports.CreateFiadoDto = CreateFiadoDto;
__decorate([
(0, swagger_1.ApiProperty)({ description: 'ID del cliente' }),
(0, class_validator_1.IsUUID)(),
__metadata("design:type", String)
], CreateFiadoDto.prototype, "customerId", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'ID de la venta asociada' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsUUID)(),
__metadata("design:type", String)
], CreateFiadoDto.prototype, "saleId", void 0);
__decorate([
(0, swagger_1.ApiProperty)({ description: 'Monto del fiado', example: 150.5 }),
(0, class_validator_1.IsNumber)(),
(0, class_validator_1.Min)(0.01),
__metadata("design:type", Number)
], CreateFiadoDto.prototype, "amount", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Descripción del fiado' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], CreateFiadoDto.prototype, "description", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Fecha de vencimiento' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], CreateFiadoDto.prototype, "dueDate", void 0);
class PayFiadoDto {
}
exports.PayFiadoDto = PayFiadoDto;
__decorate([
(0, swagger_1.ApiProperty)({ description: 'Monto a pagar', example: 50 }),
(0, class_validator_1.IsNumber)(),
(0, class_validator_1.Min)(0.01),
__metadata("design:type", Number)
], PayFiadoDto.prototype, "amount", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Método de pago', default: 'cash' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
(0, class_validator_1.MaxLength)(20),
__metadata("design:type", String)
], PayFiadoDto.prototype, "paymentMethod", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Notas del pago' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], PayFiadoDto.prototype, "notes", void 0);
//# sourceMappingURL=customer.dto.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"customer.dto.js","sourceRoot":"","sources":["../../../../src/modules/customers/dto/customer.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6CAAgF;AAChF,qDASyB;AAEzB,MAAa,iBAAiB;CA4C7B;AA5CD,8CA4CC;AAxCC;IAHC,IAAA,qBAAW,EAAC,EAAE,WAAW,EAAE,oBAAoB,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;IACzE,IAAA,0BAAQ,GAAE;IACV,IAAA,2BAAS,EAAC,GAAG,CAAC;;+CACF;AAMb;IAJC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;IACvE,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,2BAAS,EAAC,EAAE,CAAC;;gDACC;AAMf;IAJC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;IACvE,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,2BAAS,EAAC,EAAE,CAAC;;mDACI;AAMlB;IAJC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IACxE,IAAA,4BAAU,GAAE;IACZ,IAAA,yBAAO,GAAE;IACT,IAAA,2BAAS,EAAC,GAAG,CAAC;;gDACA;AAKf;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;IACjD,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;kDACM;AAKjB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACzD,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;gDACI;AAKf;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACvE,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;uDACW;AAMvB;IAJC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IACrE,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,qBAAG,EAAC,CAAC,CAAC;;qDACa;AAGtB,MAAa,iBAAkB,SAAQ,IAAA,qBAAW,EAAC,iBAAiB,CAAC;CAAG;AAAxE,8CAAwE;AAExE,MAAa,cAAc;CAwB1B;AAxBD,wCAwBC;AArBC;IAFC,IAAA,qBAAW,EAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAC9C,IAAA,wBAAM,GAAE;;kDACU;AAKnB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,yBAAyB,EAAE,CAAC;IAC/D,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,GAAE;;8CACO;AAKhB;IAHC,IAAA,qBAAW,EAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC/D,IAAA,0BAAQ,GAAE;IACV,IAAA,qBAAG,EAAC,IAAI,CAAC;;8CACK;AAKf;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;IAC7D,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;mDACU;AAKrB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,sBAAsB,EAAE,CAAC;IAC5D,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;+CACM;AAGnB,MAAa,WAAW;CAgBvB;AAhBD,kCAgBC;AAZC;IAHC,IAAA,qBAAW,EAAC,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC1D,IAAA,0BAAQ,GAAE;IACV,IAAA,qBAAG,EAAC,IAAI,CAAC;;2CACK;AAMf;IAJC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACvE,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,2BAAS,EAAC,EAAE,CAAC;;kDACS;AAKvB;IAHC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC;IACtD,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;0CACI"}

View File

@ -0,0 +1,24 @@
import { Fiado } from './fiado.entity';
export declare class Customer {
id: string;
tenantId: string;
name: string;
phone: string;
email: string;
address: string;
addressReference: string;
latitude: number;
longitude: number;
fiadoEnabled: boolean;
fiadoLimit: number;
currentFiadoBalance: number;
totalPurchases: number;
purchaseCount: number;
lastPurchaseAt: Date;
whatsappOptIn: boolean;
notes: string;
status: string;
createdAt: Date;
updatedAt: Date;
fiados: Fiado[];
}

View File

@ -0,0 +1,105 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Customer = void 0;
const typeorm_1 = require("typeorm");
const fiado_entity_1 = require("./fiado.entity");
let Customer = class Customer {
};
exports.Customer = Customer;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
__metadata("design:type", String)
], Customer.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'tenant_id' }),
__metadata("design:type", String)
], Customer.prototype, "tenantId", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 100 }),
__metadata("design:type", String)
], Customer.prototype, "name", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 20, nullable: true }),
__metadata("design:type", String)
], Customer.prototype, "phone", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 100, nullable: true }),
__metadata("design:type", String)
], Customer.prototype, "email", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
__metadata("design:type", String)
], Customer.prototype, "address", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'address_reference', type: 'text', nullable: true }),
__metadata("design:type", String)
], Customer.prototype, "addressReference", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'decimal', precision: 10, scale: 8, nullable: true }),
__metadata("design:type", Number)
], Customer.prototype, "latitude", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'decimal', precision: 11, scale: 8, nullable: true }),
__metadata("design:type", Number)
], Customer.prototype, "longitude", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'fiado_enabled', default: true }),
__metadata("design:type", Boolean)
], Customer.prototype, "fiadoEnabled", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'fiado_limit', type: 'decimal', precision: 10, scale: 2, nullable: true }),
__metadata("design:type", Number)
], Customer.prototype, "fiadoLimit", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'current_fiado_balance', type: 'decimal', precision: 10, scale: 2, default: 0 }),
__metadata("design:type", Number)
], Customer.prototype, "currentFiadoBalance", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'total_purchases', type: 'decimal', precision: 12, scale: 2, default: 0 }),
__metadata("design:type", Number)
], Customer.prototype, "totalPurchases", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'purchase_count', default: 0 }),
__metadata("design:type", Number)
], Customer.prototype, "purchaseCount", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'last_purchase_at', type: 'timestamptz', nullable: true }),
__metadata("design:type", Date)
], Customer.prototype, "lastPurchaseAt", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'whatsapp_opt_in', default: false }),
__metadata("design:type", Boolean)
], Customer.prototype, "whatsappOptIn", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
__metadata("design:type", String)
], Customer.prototype, "notes", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 20, default: 'active' }),
__metadata("design:type", String)
], Customer.prototype, "status", void 0);
__decorate([
(0, typeorm_1.CreateDateColumn)({ name: 'created_at' }),
__metadata("design:type", Date)
], Customer.prototype, "createdAt", void 0);
__decorate([
(0, typeorm_1.UpdateDateColumn)({ name: 'updated_at' }),
__metadata("design:type", Date)
], Customer.prototype, "updatedAt", void 0);
__decorate([
(0, typeorm_1.OneToMany)(() => fiado_entity_1.Fiado, (fiado) => fiado.customer),
__metadata("design:type", Array)
], Customer.prototype, "fiados", void 0);
exports.Customer = Customer = __decorate([
(0, typeorm_1.Entity)({ schema: 'customers', name: 'customers' })
], Customer);
//# sourceMappingURL=customer.entity.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"customer.entity.js","sourceRoot":"","sources":["../../../../src/modules/customers/entities/customer.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAOiB;AACjB,iDAAuC;AAGhC,IAAM,QAAQ,GAAd,MAAM,QAAQ;CAgEpB,CAAA;AAhEY,4BAAQ;AAEnB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;oCACpB;AAGX;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;;0CACb;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;sCACX;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;uCACzB;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;uCAC1B;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;yCACzB;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;kDAC3C;AAGzB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CACpD;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACnD;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;8CAC3B;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CACvE;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;qDACpE;AAG5B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;gDACnE;AAGvB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;+CACzB;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BAC1D,IAAI;gDAAC;AAGrB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;;+CAC7B;AAGvB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;uCAC3B;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;;wCAC3B;AAGf;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;8BAC9B,IAAI;2CAAC;AAGhB;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;8BAC9B,IAAI;2CAAC;AAIhB;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,oBAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC;;wCAClC;mBA/DL,QAAQ;IADpB,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;GACtC,QAAQ,CAgEpB"}

View File

@ -0,0 +1,11 @@
import { Fiado } from './fiado.entity';
export declare class FiadoPayment {
id: string;
fiadoId: string;
amount: number;
paymentMethod: string;
notes: string;
receivedBy: string;
createdAt: Date;
fiado: Fiado;
}

View File

@ -0,0 +1,54 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FiadoPayment = void 0;
const typeorm_1 = require("typeorm");
const fiado_entity_1 = require("./fiado.entity");
let FiadoPayment = class FiadoPayment {
};
exports.FiadoPayment = FiadoPayment;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
__metadata("design:type", String)
], FiadoPayment.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'fiado_id' }),
__metadata("design:type", String)
], FiadoPayment.prototype, "fiadoId", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'decimal', precision: 10, scale: 2 }),
__metadata("design:type", Number)
], FiadoPayment.prototype, "amount", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'payment_method', length: 20, default: 'cash' }),
__metadata("design:type", String)
], FiadoPayment.prototype, "paymentMethod", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
__metadata("design:type", String)
], FiadoPayment.prototype, "notes", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'received_by', nullable: true }),
__metadata("design:type", String)
], FiadoPayment.prototype, "receivedBy", void 0);
__decorate([
(0, typeorm_1.CreateDateColumn)({ name: 'created_at' }),
__metadata("design:type", Date)
], FiadoPayment.prototype, "createdAt", void 0);
__decorate([
(0, typeorm_1.ManyToOne)(() => fiado_entity_1.Fiado, (fiado) => fiado.payments),
(0, typeorm_1.JoinColumn)({ name: 'fiado_id' }),
__metadata("design:type", fiado_entity_1.Fiado)
], FiadoPayment.prototype, "fiado", void 0);
exports.FiadoPayment = FiadoPayment = __decorate([
(0, typeorm_1.Entity)({ schema: 'customers', name: 'fiado_payments' })
], FiadoPayment);
//# sourceMappingURL=fiado-payment.entity.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"fiado-payment.entity.js","sourceRoot":"","sources":["../../../../src/modules/customers/entities/fiado-payment.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAOiB;AACjB,iDAAuC;AAGhC,IAAM,YAAY,GAAlB,MAAM,YAAY;CA0BxB,CAAA;AA1BY,oCAAY;AAEvB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;wCACpB;AAGX;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;;6CACb;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;;4CACtC;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;;mDAC1C;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CAC3B;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;gDAC7B;AAGnB;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;8BAC9B,IAAI;+CAAC;AAKhB;IAFC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,oBAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC;IACjD,IAAA,oBAAU,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;8BAC1B,oBAAK;2CAAC;uBAzBF,YAAY;IADxB,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;GAC3C,YAAY,CA0BxB"}

View File

@ -0,0 +1,25 @@
import { Customer } from './customer.entity';
import { FiadoPayment } from './fiado-payment.entity';
export declare enum FiadoStatus {
PENDING = "pending",
PARTIAL = "partial",
PAID = "paid",
CANCELLED = "cancelled"
}
export declare class Fiado {
id: string;
tenantId: string;
customerId: string;
saleId: string;
amount: number;
paidAmount: number;
remainingAmount: number;
description: string;
status: FiadoStatus;
dueDate: Date;
paidAt: Date;
createdAt: Date;
updatedAt: Date;
customer: Customer;
payments: FiadoPayment[];
}

View File

@ -0,0 +1,94 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Fiado = exports.FiadoStatus = void 0;
const typeorm_1 = require("typeorm");
const customer_entity_1 = require("./customer.entity");
const fiado_payment_entity_1 = require("./fiado-payment.entity");
var FiadoStatus;
(function (FiadoStatus) {
FiadoStatus["PENDING"] = "pending";
FiadoStatus["PARTIAL"] = "partial";
FiadoStatus["PAID"] = "paid";
FiadoStatus["CANCELLED"] = "cancelled";
})(FiadoStatus || (exports.FiadoStatus = FiadoStatus = {}));
let Fiado = class Fiado {
};
exports.Fiado = Fiado;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
__metadata("design:type", String)
], Fiado.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'tenant_id' }),
__metadata("design:type", String)
], Fiado.prototype, "tenantId", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'customer_id' }),
__metadata("design:type", String)
], Fiado.prototype, "customerId", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'sale_id', nullable: true }),
__metadata("design:type", String)
], Fiado.prototype, "saleId", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'decimal', precision: 10, scale: 2 }),
__metadata("design:type", Number)
], Fiado.prototype, "amount", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'paid_amount', type: 'decimal', precision: 10, scale: 2, default: 0 }),
__metadata("design:type", Number)
], Fiado.prototype, "paidAmount", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'remaining_amount', type: 'decimal', precision: 10, scale: 2 }),
__metadata("design:type", Number)
], Fiado.prototype, "remainingAmount", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
__metadata("design:type", String)
], Fiado.prototype, "description", void 0);
__decorate([
(0, typeorm_1.Column)({
type: 'varchar',
length: 20,
default: FiadoStatus.PENDING,
}),
__metadata("design:type", String)
], Fiado.prototype, "status", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'due_date', type: 'date', nullable: true }),
__metadata("design:type", Date)
], Fiado.prototype, "dueDate", void 0);
__decorate([
(0, typeorm_1.Column)({ name: 'paid_at', type: 'timestamptz', nullable: true }),
__metadata("design:type", Date)
], Fiado.prototype, "paidAt", void 0);
__decorate([
(0, typeorm_1.CreateDateColumn)({ name: 'created_at' }),
__metadata("design:type", Date)
], Fiado.prototype, "createdAt", void 0);
__decorate([
(0, typeorm_1.UpdateDateColumn)({ name: 'updated_at' }),
__metadata("design:type", Date)
], Fiado.prototype, "updatedAt", void 0);
__decorate([
(0, typeorm_1.ManyToOne)(() => customer_entity_1.Customer, (customer) => customer.fiados),
(0, typeorm_1.JoinColumn)({ name: 'customer_id' }),
__metadata("design:type", customer_entity_1.Customer)
], Fiado.prototype, "customer", void 0);
__decorate([
(0, typeorm_1.OneToMany)(() => fiado_payment_entity_1.FiadoPayment, (payment) => payment.fiado),
__metadata("design:type", Array)
], Fiado.prototype, "payments", void 0);
exports.Fiado = Fiado = __decorate([
(0, typeorm_1.Entity)({ schema: 'customers', name: 'fiados' })
], Fiado);
//# sourceMappingURL=fiado.entity.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"fiado.entity.js","sourceRoot":"","sources":["../../../../src/modules/customers/entities/fiado.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCASiB;AACjB,uDAA6C;AAC7C,iEAAsD;AAEtD,IAAY,WAKX;AALD,WAAY,WAAW;IACrB,kCAAmB,CAAA;IACnB,kCAAmB,CAAA;IACnB,4BAAa,CAAA;IACb,sCAAuB,CAAA;AACzB,CAAC,EALW,WAAW,2BAAX,WAAW,QAKtB;AAGM,IAAM,KAAK,GAAX,MAAM,KAAK;CAmDjB,CAAA;AAnDY,sBAAK;AAEhB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;iCACpB;AAGX;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;;uCACb;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;;yCACb;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qCAC7B;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;;qCACtC;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;yCACnE;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;;8CACvD;AAGxB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CACrB;AAOpB;IALC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,WAAW,CAAC,OAAO;KAC7B,CAAC;;qCACkB;AAGpB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BAClD,IAAI;sCAAC;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BACzD,IAAI;qCAAC;AAGb;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;8BAC9B,IAAI;wCAAC;AAGhB;IADC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;8BAC9B,IAAI;wCAAC;AAKhB;IAFC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,0BAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxD,IAAA,oBAAU,EAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;8BAC1B,0BAAQ;uCAAC;AAGnB;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,mCAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;;uCACjC;gBAlDd,KAAK;IADjB,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;GACnC,KAAK,CAmDjB"}

View File

@ -0,0 +1,72 @@
import { TenantIntegrationsService } from '../services/tenant-integrations.service';
import { UpsertWhatsAppCredentialsDto, UpsertLLMCredentialsDto, CreateIntegrationCredentialDto, IntegrationCredentialResponseDto, IntegrationStatusResponseDto } from '../dto/integration-credentials.dto';
import { IntegrationType, IntegrationProvider } from '../entities/tenant-integration-credential.entity';
export declare class IntegrationsController {
private readonly integrationsService;
constructor(integrationsService: TenantIntegrationsService);
getStatus(req: any): Promise<IntegrationStatusResponseDto>;
getWhatsAppConfig(req: any): Promise<{
configured: boolean;
usesPlatformNumber: boolean;
message: string;
isVerified?: undefined;
lastVerifiedAt?: undefined;
hasAccessToken?: undefined;
phoneNumberId?: undefined;
} | {
configured: boolean;
usesPlatformNumber: boolean;
isVerified: boolean;
lastVerifiedAt: Date;
hasAccessToken: boolean;
phoneNumberId: any;
message?: undefined;
}>;
upsertWhatsAppCredentials(req: any, dto: UpsertWhatsAppCredentialsDto): Promise<{
success: boolean;
message: string;
id: string;
}>;
deleteWhatsAppCredentials(req: any): Promise<void>;
getLLMConfig(req: any): Promise<{
configured: boolean;
usesPlatformDefault: boolean;
message: string;
provider?: undefined;
isVerified?: undefined;
config?: undefined;
hasApiKey?: undefined;
} | {
configured: boolean;
usesPlatformDefault: boolean;
provider: IntegrationProvider;
isVerified: boolean;
config: {
model: any;
maxTokens: any;
temperature: any;
hasSystemPrompt: boolean;
};
hasApiKey: boolean;
message?: undefined;
}>;
upsertLLMCredentials(req: any, dto: UpsertLLMCredentialsDto): Promise<{
success: boolean;
message: string;
id: string;
provider: IntegrationProvider;
}>;
deleteLLMCredentials(req: any, provider: IntegrationProvider): Promise<void>;
getAllCredentials(req: any): Promise<IntegrationCredentialResponseDto[]>;
createCredential(req: any, dto: CreateIntegrationCredentialDto): Promise<{
success: boolean;
message: string;
id: string;
}>;
toggleCredential(req: any, type: IntegrationType, provider: IntegrationProvider, body: {
isActive: boolean;
}): Promise<{
success: boolean;
isActive: boolean;
}>;
}

View File

@ -0,0 +1,232 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IntegrationsController = void 0;
const common_1 = require("@nestjs/common");
const swagger_1 = require("@nestjs/swagger");
const jwt_auth_guard_1 = require("../../auth/guards/jwt-auth.guard");
const tenant_integrations_service_1 = require("../services/tenant-integrations.service");
const integration_credentials_dto_1 = require("../dto/integration-credentials.dto");
const tenant_integration_credential_entity_1 = require("../entities/tenant-integration-credential.entity");
let IntegrationsController = class IntegrationsController {
constructor(integrationsService) {
this.integrationsService = integrationsService;
}
async getStatus(req) {
return this.integrationsService.getIntegrationStatus(req.user.tenantId);
}
async getWhatsAppConfig(req) {
const credential = await this.integrationsService.getCredential(req.user.tenantId, tenant_integration_credential_entity_1.IntegrationType.WHATSAPP, tenant_integration_credential_entity_1.IntegrationProvider.META);
if (!credential) {
return {
configured: false,
usesPlatformNumber: true,
message: 'Usando número de plataforma compartido',
};
}
return {
configured: true,
usesPlatformNumber: false,
isVerified: credential.isVerified,
lastVerifiedAt: credential.lastVerifiedAt,
hasAccessToken: !!credential.credentials?.['accessToken'],
phoneNumberId: credential.credentials?.['phoneNumberId'],
};
}
async upsertWhatsAppCredentials(req, dto) {
const credential = await this.integrationsService.upsertCredential(req.user.tenantId, tenant_integration_credential_entity_1.IntegrationType.WHATSAPP, tenant_integration_credential_entity_1.IntegrationProvider.META, {
accessToken: dto.credentials.accessToken,
phoneNumberId: dto.credentials.phoneNumberId,
businessAccountId: dto.credentials.businessAccountId,
verifyToken: dto.credentials.verifyToken,
}, {}, req.user.sub);
if (dto.credentials.phoneNumberId) {
await this.integrationsService.registerWhatsAppNumber(req.user.tenantId, dto.credentials.phoneNumberId, dto.phoneNumber, dto.displayName);
}
return {
success: true,
message: 'Credenciales de WhatsApp configuradas',
id: credential.id,
};
}
async deleteWhatsAppCredentials(req) {
await this.integrationsService.deleteCredential(req.user.tenantId, tenant_integration_credential_entity_1.IntegrationType.WHATSAPP, tenant_integration_credential_entity_1.IntegrationProvider.META);
}
async getLLMConfig(req) {
const credentials = await this.integrationsService.getCredentials(req.user.tenantId);
const llmCred = credentials.find((c) => c.integrationType === tenant_integration_credential_entity_1.IntegrationType.LLM && c.isActive);
if (!llmCred) {
return {
configured: false,
usesPlatformDefault: true,
message: 'Usando configuración LLM de plataforma',
};
}
return {
configured: true,
usesPlatformDefault: false,
provider: llmCred.provider,
isVerified: llmCred.isVerified,
config: {
model: llmCred.config?.['model'],
maxTokens: llmCred.config?.['maxTokens'],
temperature: llmCred.config?.['temperature'],
hasSystemPrompt: !!llmCred.config?.['systemPrompt'],
},
hasApiKey: !!llmCred.credentials?.['apiKey'],
};
}
async upsertLLMCredentials(req, dto) {
const credential = await this.integrationsService.upsertCredential(req.user.tenantId, tenant_integration_credential_entity_1.IntegrationType.LLM, dto.provider, { apiKey: dto.credentials.apiKey }, dto.config || {}, req.user.sub);
return {
success: true,
message: 'Credenciales de LLM configuradas',
id: credential.id,
provider: dto.provider,
};
}
async deleteLLMCredentials(req, provider) {
await this.integrationsService.deleteCredential(req.user.tenantId, tenant_integration_credential_entity_1.IntegrationType.LLM, provider);
}
async getAllCredentials(req) {
const credentials = await this.integrationsService.getCredentials(req.user.tenantId);
return credentials.map((c) => ({
id: c.id,
integrationType: c.integrationType,
provider: c.provider,
hasCredentials: !!c.credentials && Object.keys(c.credentials).length > 0,
isActive: c.isActive,
isVerified: c.isVerified,
lastVerifiedAt: c.lastVerifiedAt,
verificationError: c.verificationError,
config: c.config,
createdAt: c.createdAt,
updatedAt: c.updatedAt,
}));
}
async createCredential(req, dto) {
const credential = await this.integrationsService.upsertCredential(req.user.tenantId, dto.integrationType, dto.provider, dto.credentials, dto.config, req.user.sub);
return {
success: true,
message: 'Credencial creada',
id: credential.id,
};
}
async toggleCredential(req, type, provider, body) {
const credential = await this.integrationsService.toggleCredential(req.user.tenantId, type, provider, body.isActive);
return {
success: true,
isActive: credential.isActive,
};
}
};
exports.IntegrationsController = IntegrationsController;
__decorate([
(0, common_1.Get)('status'),
(0, swagger_1.ApiOperation)({ summary: 'Obtener estado de todas las integraciones del tenant' }),
(0, swagger_1.ApiResponse)({ status: 200, type: integration_credentials_dto_1.IntegrationStatusResponseDto }),
__param(0, (0, common_1.Request)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], IntegrationsController.prototype, "getStatus", null);
__decorate([
(0, common_1.Get)('whatsapp'),
(0, swagger_1.ApiOperation)({ summary: 'Obtener configuración de WhatsApp' }),
__param(0, (0, common_1.Request)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], IntegrationsController.prototype, "getWhatsAppConfig", null);
__decorate([
(0, common_1.Put)('whatsapp'),
(0, swagger_1.ApiOperation)({ summary: 'Configurar credenciales de WhatsApp propias' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, integration_credentials_dto_1.UpsertWhatsAppCredentialsDto]),
__metadata("design:returntype", Promise)
], IntegrationsController.prototype, "upsertWhatsAppCredentials", null);
__decorate([
(0, common_1.Delete)('whatsapp'),
(0, common_1.HttpCode)(common_1.HttpStatus.NO_CONTENT),
(0, swagger_1.ApiOperation)({ summary: 'Eliminar credenciales de WhatsApp (volver a usar plataforma)' }),
__param(0, (0, common_1.Request)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], IntegrationsController.prototype, "deleteWhatsAppCredentials", null);
__decorate([
(0, common_1.Get)('llm'),
(0, swagger_1.ApiOperation)({ summary: 'Obtener configuración de LLM' }),
__param(0, (0, common_1.Request)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], IntegrationsController.prototype, "getLLMConfig", null);
__decorate([
(0, common_1.Put)('llm'),
(0, swagger_1.ApiOperation)({ summary: 'Configurar credenciales de LLM propias' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, integration_credentials_dto_1.UpsertLLMCredentialsDto]),
__metadata("design:returntype", Promise)
], IntegrationsController.prototype, "upsertLLMCredentials", null);
__decorate([
(0, common_1.Delete)('llm/:provider'),
(0, common_1.HttpCode)(common_1.HttpStatus.NO_CONTENT),
(0, swagger_1.ApiOperation)({ summary: 'Eliminar credenciales de LLM (volver a usar plataforma)' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('provider')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], IntegrationsController.prototype, "deleteLLMCredentials", null);
__decorate([
(0, common_1.Get)('credentials'),
(0, swagger_1.ApiOperation)({ summary: 'Obtener todas las credenciales del tenant' }),
__param(0, (0, common_1.Request)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], IntegrationsController.prototype, "getAllCredentials", null);
__decorate([
(0, common_1.Post)('credentials'),
(0, swagger_1.ApiOperation)({ summary: 'Crear credencial de integración genérica' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, integration_credentials_dto_1.CreateIntegrationCredentialDto]),
__metadata("design:returntype", Promise)
], IntegrationsController.prototype, "createCredential", null);
__decorate([
(0, common_1.Put)('credentials/:type/:provider/toggle'),
(0, swagger_1.ApiOperation)({ summary: 'Activar/desactivar una credencial' }),
__param(0, (0, common_1.Request)()),
__param(1, (0, common_1.Param)('type')),
__param(2, (0, common_1.Param)('provider')),
__param(3, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String, String, Object]),
__metadata("design:returntype", Promise)
], IntegrationsController.prototype, "toggleCredential", null);
exports.IntegrationsController = IntegrationsController = __decorate([
(0, swagger_1.ApiTags)('Integrations'),
(0, swagger_1.ApiBearerAuth)(),
(0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard),
(0, common_1.Controller)('integrations'),
__metadata("design:paramtypes", [tenant_integrations_service_1.TenantIntegrationsService])
], IntegrationsController);
//# sourceMappingURL=integrations.controller.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,53 @@
import { ConfigService } from '@nestjs/config';
import { TenantIntegrationsService } from '../services/tenant-integrations.service';
import { IntegrationProvider } from '../entities/tenant-integration-credential.entity';
export declare class InternalIntegrationsController {
private readonly integrationsService;
private readonly configService;
constructor(integrationsService: TenantIntegrationsService, configService: ConfigService);
private validateInternalKey;
getWhatsAppCredentials(tenantId: string, internalKey: string): Promise<{
configured: boolean;
message: string;
credentials?: undefined;
} | {
configured: boolean;
credentials: {
accessToken: any;
phoneNumberId: any;
businessAccountId: any;
verifyToken: any;
};
message?: undefined;
}>;
getLLMConfig(tenantId: string, internalKey: string): Promise<{
configured: boolean;
message: string;
provider?: undefined;
credentials?: undefined;
config?: undefined;
} | {
configured: boolean;
provider: IntegrationProvider;
credentials: {
apiKey: any;
};
config: {
model: any;
maxTokens: any;
temperature: any;
baseUrl: any;
systemPrompt: any;
};
message?: undefined;
}>;
resolveTenantFromPhoneNumberId(phoneNumberId: string, internalKey: string): Promise<{
found: boolean;
message: string;
tenantId?: undefined;
} | {
found: boolean;
tenantId: string;
message?: undefined;
}>;
}

View File

@ -0,0 +1,135 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.InternalIntegrationsController = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const swagger_1 = require("@nestjs/swagger");
const tenant_integrations_service_1 = require("../services/tenant-integrations.service");
const tenant_integration_credential_entity_1 = require("../entities/tenant-integration-credential.entity");
let InternalIntegrationsController = class InternalIntegrationsController {
constructor(integrationsService, configService) {
this.integrationsService = integrationsService;
this.configService = configService;
}
validateInternalKey(internalKey) {
const expectedKey = this.configService.get('INTERNAL_API_KEY');
if (!expectedKey || internalKey !== expectedKey) {
throw new common_1.UnauthorizedException('Invalid internal API key');
}
}
async getWhatsAppCredentials(tenantId, internalKey) {
this.validateInternalKey(internalKey);
const credential = await this.integrationsService.getCredential(tenantId, tenant_integration_credential_entity_1.IntegrationType.WHATSAPP, tenant_integration_credential_entity_1.IntegrationProvider.META);
if (!credential || !credential.isActive) {
return {
configured: false,
message: 'WhatsApp not configured for this tenant',
};
}
return {
configured: true,
credentials: {
accessToken: credential.credentials?.['accessToken'],
phoneNumberId: credential.credentials?.['phoneNumberId'],
businessAccountId: credential.credentials?.['businessAccountId'],
verifyToken: credential.credentials?.['verifyToken'],
},
};
}
async getLLMConfig(tenantId, internalKey) {
this.validateInternalKey(internalKey);
const credentials = await this.integrationsService.getCredentials(tenantId);
const llmCredential = credentials.find((c) => c.integrationType === tenant_integration_credential_entity_1.IntegrationType.LLM && c.isActive);
if (!llmCredential) {
return {
configured: false,
message: 'LLM not configured for this tenant',
};
}
return {
configured: true,
provider: llmCredential.provider,
credentials: {
apiKey: llmCredential.credentials?.['apiKey'],
},
config: {
model: llmCredential.config?.['model'],
maxTokens: llmCredential.config?.['maxTokens'],
temperature: llmCredential.config?.['temperature'],
baseUrl: llmCredential.config?.['baseUrl'],
systemPrompt: llmCredential.config?.['systemPrompt'],
},
};
}
async resolveTenantFromPhoneNumberId(phoneNumberId, internalKey) {
this.validateInternalKey(internalKey);
const tenantId = await this.integrationsService.resolveTenantFromPhoneNumberId(phoneNumberId);
if (!tenantId) {
return {
found: false,
message: 'No tenant found for this phone number ID',
};
}
return {
found: true,
tenantId,
};
}
};
exports.InternalIntegrationsController = InternalIntegrationsController;
__decorate([
(0, common_1.Get)(':tenantId/whatsapp'),
(0, swagger_1.ApiOperation)({ summary: 'Get WhatsApp credentials for a tenant (internal use)' }),
(0, swagger_1.ApiHeader)({ name: 'X-Internal-Key', required: true }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Credentials returned' }),
(0, swagger_1.ApiResponse)({ status: 401, description: 'Invalid internal key' }),
(0, swagger_1.ApiResponse)({ status: 404, description: 'Tenant not found or not configured' }),
__param(0, (0, common_1.Param)('tenantId')),
__param(1, (0, common_1.Headers)('x-internal-key')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], InternalIntegrationsController.prototype, "getWhatsAppCredentials", null);
__decorate([
(0, common_1.Get)(':tenantId/llm'),
(0, swagger_1.ApiOperation)({ summary: 'Get LLM configuration for a tenant (internal use)' }),
(0, swagger_1.ApiHeader)({ name: 'X-Internal-Key', required: true }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Configuration returned' }),
(0, swagger_1.ApiResponse)({ status: 401, description: 'Invalid internal key' }),
__param(0, (0, common_1.Param)('tenantId')),
__param(1, (0, common_1.Headers)('x-internal-key')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], InternalIntegrationsController.prototype, "getLLMConfig", null);
__decorate([
(0, common_1.Get)('resolve-tenant/:phoneNumberId'),
(0, swagger_1.ApiOperation)({ summary: 'Resolve tenant ID from WhatsApp phone number ID' }),
(0, swagger_1.ApiHeader)({ name: 'X-Internal-Key', required: true }),
(0, swagger_1.ApiResponse)({ status: 200, description: 'Tenant ID returned' }),
(0, swagger_1.ApiResponse)({ status: 401, description: 'Invalid internal key' }),
__param(0, (0, common_1.Param)('phoneNumberId')),
__param(1, (0, common_1.Headers)('x-internal-key')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], InternalIntegrationsController.prototype, "resolveTenantFromPhoneNumberId", null);
exports.InternalIntegrationsController = InternalIntegrationsController = __decorate([
(0, swagger_1.ApiTags)('Internal - Integrations'),
(0, common_1.Controller)('internal/integrations'),
__metadata("design:paramtypes", [tenant_integrations_service_1.TenantIntegrationsService,
config_1.ConfigService])
], InternalIntegrationsController);
//# sourceMappingURL=internal-integrations.controller.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"internal-integrations.controller.js","sourceRoot":"","sources":["../../../../src/modules/integrations/controllers/internal-integrations.controller.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,76 @@
import { IntegrationType, IntegrationProvider } from '../entities/tenant-integration-credential.entity';
export declare class WhatsAppCredentialsDto {
accessToken: string;
phoneNumberId: string;
businessAccountId?: string;
verifyToken?: string;
}
export declare class LLMCredentialsDto {
apiKey: string;
}
export declare class LLMConfigDto {
model?: string;
maxTokens?: number;
temperature?: number;
systemPrompt?: string;
baseUrl?: string;
}
export declare class UpsertWhatsAppCredentialsDto {
credentials: WhatsAppCredentialsDto;
phoneNumber?: string;
displayName?: string;
}
export declare class UpsertLLMCredentialsDto {
provider: IntegrationProvider;
credentials: LLMCredentialsDto;
config?: LLMConfigDto;
}
export declare class CreateIntegrationCredentialDto {
integrationType: IntegrationType;
provider: IntegrationProvider;
credentials: Record<string, any>;
config?: Record<string, any>;
isActive?: boolean;
}
export declare class IntegrationCredentialResponseDto {
id: string;
integrationType: IntegrationType;
provider: IntegrationProvider;
hasCredentials: boolean;
isActive: boolean;
isVerified: boolean;
lastVerifiedAt?: Date;
verificationError?: string;
config: Record<string, any>;
createdAt: Date;
updatedAt: Date;
}
export declare class IntegrationStatusResponseDto {
whatsapp: {
configured: boolean;
usesPlatformNumber: boolean;
provider: string;
isVerified: boolean;
};
llm: {
configured: boolean;
usesPlatformDefault: boolean;
provider: string;
model: string;
isVerified: boolean;
};
payments: {
stripe: {
configured: boolean;
isVerified: boolean;
};
mercadopago: {
configured: boolean;
isVerified: boolean;
};
clip: {
configured: boolean;
isVerified: boolean;
};
};
}

View File

@ -0,0 +1,215 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IntegrationStatusResponseDto = exports.IntegrationCredentialResponseDto = exports.CreateIntegrationCredentialDto = exports.UpsertLLMCredentialsDto = exports.UpsertWhatsAppCredentialsDto = exports.LLMConfigDto = exports.LLMCredentialsDto = exports.WhatsAppCredentialsDto = void 0;
const class_validator_1 = require("class-validator");
const class_transformer_1 = require("class-transformer");
const swagger_1 = require("@nestjs/swagger");
const tenant_integration_credential_entity_1 = require("../entities/tenant-integration-credential.entity");
class WhatsAppCredentialsDto {
}
exports.WhatsAppCredentialsDto = WhatsAppCredentialsDto;
__decorate([
(0, swagger_1.ApiProperty)({ description: 'Access Token de Meta/WhatsApp Business API' }),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], WhatsAppCredentialsDto.prototype, "accessToken", void 0);
__decorate([
(0, swagger_1.ApiProperty)({ description: 'Phone Number ID de WhatsApp Business' }),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], WhatsAppCredentialsDto.prototype, "phoneNumberId", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Business Account ID de WhatsApp' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], WhatsAppCredentialsDto.prototype, "businessAccountId", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Verify Token para webhook' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], WhatsAppCredentialsDto.prototype, "verifyToken", void 0);
class LLMCredentialsDto {
}
exports.LLMCredentialsDto = LLMCredentialsDto;
__decorate([
(0, swagger_1.ApiProperty)({ description: 'API Key del proveedor LLM' }),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], LLMCredentialsDto.prototype, "apiKey", void 0);
class LLMConfigDto {
}
exports.LLMConfigDto = LLMConfigDto;
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Modelo a usar', example: 'gpt-4o-mini' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], LLMConfigDto.prototype, "model", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Máximo de tokens', example: 1000 }),
(0, class_validator_1.IsOptional)(),
__metadata("design:type", Number)
], LLMConfigDto.prototype, "maxTokens", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Temperatura (0-2)', example: 0.7 }),
(0, class_validator_1.IsOptional)(),
__metadata("design:type", Number)
], LLMConfigDto.prototype, "temperature", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'System prompt personalizado' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], LLMConfigDto.prototype, "systemPrompt", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Base URL del API (para OpenRouter/Azure)' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], LLMConfigDto.prototype, "baseUrl", void 0);
class UpsertWhatsAppCredentialsDto {
}
exports.UpsertWhatsAppCredentialsDto = UpsertWhatsAppCredentialsDto;
__decorate([
(0, swagger_1.ApiProperty)({ type: WhatsAppCredentialsDto }),
(0, class_validator_1.ValidateNested)(),
(0, class_transformer_1.Type)(() => WhatsAppCredentialsDto),
__metadata("design:type", WhatsAppCredentialsDto)
], UpsertWhatsAppCredentialsDto.prototype, "credentials", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Número de teléfono para display' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], UpsertWhatsAppCredentialsDto.prototype, "phoneNumber", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Nombre para display' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsString)(),
__metadata("design:type", String)
], UpsertWhatsAppCredentialsDto.prototype, "displayName", void 0);
class UpsertLLMCredentialsDto {
}
exports.UpsertLLMCredentialsDto = UpsertLLMCredentialsDto;
__decorate([
(0, swagger_1.ApiProperty)({ enum: tenant_integration_credential_entity_1.IntegrationProvider, description: 'Proveedor LLM' }),
(0, class_validator_1.IsEnum)(tenant_integration_credential_entity_1.IntegrationProvider),
__metadata("design:type", String)
], UpsertLLMCredentialsDto.prototype, "provider", void 0);
__decorate([
(0, swagger_1.ApiProperty)({ type: LLMCredentialsDto }),
(0, class_validator_1.ValidateNested)(),
(0, class_transformer_1.Type)(() => LLMCredentialsDto),
__metadata("design:type", LLMCredentialsDto)
], UpsertLLMCredentialsDto.prototype, "credentials", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ type: LLMConfigDto }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.ValidateNested)(),
(0, class_transformer_1.Type)(() => LLMConfigDto),
__metadata("design:type", LLMConfigDto)
], UpsertLLMCredentialsDto.prototype, "config", void 0);
class CreateIntegrationCredentialDto {
}
exports.CreateIntegrationCredentialDto = CreateIntegrationCredentialDto;
__decorate([
(0, swagger_1.ApiProperty)({ enum: tenant_integration_credential_entity_1.IntegrationType }),
(0, class_validator_1.IsEnum)(tenant_integration_credential_entity_1.IntegrationType),
__metadata("design:type", String)
], CreateIntegrationCredentialDto.prototype, "integrationType", void 0);
__decorate([
(0, swagger_1.ApiProperty)({ enum: tenant_integration_credential_entity_1.IntegrationProvider }),
(0, class_validator_1.IsEnum)(tenant_integration_credential_entity_1.IntegrationProvider),
__metadata("design:type", String)
], CreateIntegrationCredentialDto.prototype, "provider", void 0);
__decorate([
(0, swagger_1.ApiProperty)({ description: 'Credenciales específicas del proveedor' }),
(0, class_validator_1.IsObject)(),
__metadata("design:type", Object)
], CreateIntegrationCredentialDto.prototype, "credentials", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Configuración adicional' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsObject)(),
__metadata("design:type", Object)
], CreateIntegrationCredentialDto.prototype, "config", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)({ description: 'Activar inmediatamente' }),
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsBoolean)(),
__metadata("design:type", Boolean)
], CreateIntegrationCredentialDto.prototype, "isActive", void 0);
class IntegrationCredentialResponseDto {
}
exports.IntegrationCredentialResponseDto = IntegrationCredentialResponseDto;
__decorate([
(0, swagger_1.ApiProperty)(),
__metadata("design:type", String)
], IntegrationCredentialResponseDto.prototype, "id", void 0);
__decorate([
(0, swagger_1.ApiProperty)({ enum: tenant_integration_credential_entity_1.IntegrationType }),
__metadata("design:type", String)
], IntegrationCredentialResponseDto.prototype, "integrationType", void 0);
__decorate([
(0, swagger_1.ApiProperty)({ enum: tenant_integration_credential_entity_1.IntegrationProvider }),
__metadata("design:type", String)
], IntegrationCredentialResponseDto.prototype, "provider", void 0);
__decorate([
(0, swagger_1.ApiProperty)({ description: 'Indica si hay credenciales configuradas' }),
__metadata("design:type", Boolean)
], IntegrationCredentialResponseDto.prototype, "hasCredentials", void 0);
__decorate([
(0, swagger_1.ApiProperty)(),
__metadata("design:type", Boolean)
], IntegrationCredentialResponseDto.prototype, "isActive", void 0);
__decorate([
(0, swagger_1.ApiProperty)(),
__metadata("design:type", Boolean)
], IntegrationCredentialResponseDto.prototype, "isVerified", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)(),
__metadata("design:type", Date)
], IntegrationCredentialResponseDto.prototype, "lastVerifiedAt", void 0);
__decorate([
(0, swagger_1.ApiPropertyOptional)(),
__metadata("design:type", String)
], IntegrationCredentialResponseDto.prototype, "verificationError", void 0);
__decorate([
(0, swagger_1.ApiProperty)(),
__metadata("design:type", Object)
], IntegrationCredentialResponseDto.prototype, "config", void 0);
__decorate([
(0, swagger_1.ApiProperty)(),
__metadata("design:type", Date)
], IntegrationCredentialResponseDto.prototype, "createdAt", void 0);
__decorate([
(0, swagger_1.ApiProperty)(),
__metadata("design:type", Date)
], IntegrationCredentialResponseDto.prototype, "updatedAt", void 0);
class IntegrationStatusResponseDto {
}
exports.IntegrationStatusResponseDto = IntegrationStatusResponseDto;
__decorate([
(0, swagger_1.ApiProperty)(),
__metadata("design:type", Object)
], IntegrationStatusResponseDto.prototype, "whatsapp", void 0);
__decorate([
(0, swagger_1.ApiProperty)(),
__metadata("design:type", Object)
], IntegrationStatusResponseDto.prototype, "llm", void 0);
__decorate([
(0, swagger_1.ApiProperty)(),
__metadata("design:type", Object)
], IntegrationStatusResponseDto.prototype, "payments", void 0);
//# sourceMappingURL=integration-credentials.dto.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"integration-credentials.dto.js","sourceRoot":"","sources":["../../../../src/modules/integrations/dto/integration-credentials.dto.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,57 @@
import { Tenant } from '../../auth/entities/tenant.entity';
export declare enum IntegrationType {
WHATSAPP = "whatsapp",
LLM = "llm",
STRIPE = "stripe",
MERCADOPAGO = "mercadopago",
CLIP = "clip"
}
export declare enum IntegrationProvider {
META = "meta",
OPENAI = "openai",
OPENROUTER = "openrouter",
ANTHROPIC = "anthropic",
OLLAMA = "ollama",
AZURE_OPENAI = "azure_openai",
STRIPE = "stripe",
MERCADOPAGO = "mercadopago",
CLIP = "clip"
}
export interface WhatsAppCredentials {
accessToken: string;
phoneNumberId: string;
businessAccountId?: string;
verifyToken?: string;
}
export interface LLMCredentials {
apiKey: string;
}
export interface LLMConfig {
model?: string;
maxTokens?: number;
temperature?: number;
systemPrompt?: string;
baseUrl?: string;
}
export interface StripeCredentials {
secretKey: string;
publishableKey?: string;
webhookSecret?: string;
}
export declare class TenantIntegrationCredential {
id: string;
tenantId: string;
tenant: Tenant;
integrationType: IntegrationType;
provider: IntegrationProvider;
credentials: WhatsAppCredentials | LLMCredentials | StripeCredentials | Record<string, any>;
config: LLMConfig | Record<string, any>;
isActive: boolean;
isVerified: boolean;
lastVerifiedAt: Date;
verificationError: string;
createdBy: string;
updatedBy: string;
createdAt: Date;
updatedAt: Date;
}

Some files were not shown because too many files have changed in this diff Show More