From 197f07df7f9bb619b1f2e5e3a7f32c33b9b01c40 Mon Sep 17 00:00:00 2001 From: Marco Gallegos Date: Sat, 17 Jan 2026 10:55:05 -0600 Subject: [PATCH] docs: Create Aperture technical specifications document TASK 4.1: Create technical specifications document - COMPLETED - Create docs/APERTURE_SPECS.md with complete technical specifications: - Response to Question 9: Hours worked (automatic from bookings) - Complete POS structure with multiple cashiers - Granular permissions system documentation - Includes: - Hours worked calculation logic (automatic vs manual) - POS architecture (6 payment methods, receipt options) - Multiple cashiers system with individual tracking - Financial management (expenses, profit margin) - Database schemas for POS, cashiers, expenses - API endpoints for POS operations Specifications Documented: - Hours worked: Automatic from bookings (scheduled vs actual duration) - Time adjustments: Manual updates allowed by staff - Payroll: Base salary + service commissions + product commissions + tips - POS payment methods: Cash, Transfer, Membership, Card, Giftcard, PIA - Receipts: Email or client dashboard only (no physical printing) - Cashiers: Individual tracking with movement logs for error resolution - Dynamic pricing: Configurable by service, both channels (booking + POS) - Giftcards: Purchaseable, redeemable, balance tracking - PIA (Paid in Advance): Apply previously paid deposits - Recurring expenses: Daily, weekly, monthly, yearly frequencies Database Schemas: - staff_time_tracking (NEW) - Track scheduled vs actual duration - pos_sales (NEW) - All POS transactions - giftcards (NEW) - Giftcard management - daily_cash_close (NEW) - Individual cashier closing - expenses (NEW) - Financial expense tracking API Endpoints: - POST /api/aperture/pos/sales - GET /api/aperture/pos/daily-summary - POST /api/aperture/pos/open-cash-register - POST /api/aperture/pos/close-cash-register - GET /api/aperture/pos/active-cash-registers - POST /api/aperture/finance/expenses - GET /api/aperture/finance/report Impact: - Complete technical foundation for POS implementation - Clear data model for hours worked calculation - Granular permissions architecture defined - Multiple cashiers system fully specified Files Created: - docs/APERTURE_SPECS.md Next: Task 2 - Document POS structure and multiple cashiers --- docs/APERTURE_SPECS.md | 596 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 596 insertions(+) create mode 100644 docs/APERTURE_SPECS.md diff --git a/docs/APERTURE_SPECS.md b/docs/APERTURE_SPECS.md new file mode 100644 index 0000000..f92e2b4 --- /dev/null +++ b/docs/APERTURE_SPECS.md @@ -0,0 +1,596 @@ +# Aperture Technical Specifications + +**Especificaciones técnicas completas para Aperture (HQ Dashboard)** +**Última actualización: Enero 2026** + +--- + +## 1. Objetivo + +Este documento define las especificaciones técnicas para el desarrollo de Aperture (aperture.anchor23.mx), el dashboard administrativo y CRM interno de AnchorOS. + +--- + +## 2. Stack Tecnológico + +### Frontend +- **Framework**: Next.js 14 (App Router) +- **UI Library**: Radix UI (componentes accesibles preconstruidos) +- **Estilizado**: Tailwind CSS + Square UI custom styling +- **Icons**: Lucide React (24px, stroke 2px) +- **Charts**: Recharts o similar (para gráficos de rendimiento) +- **PDF Generation**: PDFKit o similar (para cierre de caja) + +### Backend +- **Database**: Supabase (PostgreSQL + RLS) +- **Auth**: Supabase Auth (magic links para clientes, password para staff/admin) +- **API**: Next.js App Router API routes + +### Integraciones +- **Payments**: Stripe SDK +- **Calendar**: Google Calendar API v3 (Service Account) +- **Notifications**: WhatsApp API (Twilio / Meta) - Good to have, no priority + +--- + +## 3. Horas Trabajadas - Respuesta a Pregunta 9 + +### Calculo de Horas Trabajadas + +**Enfoque**: El sistema de nómina calcula el **tiempo efectivo de trabajo** basado en la comparación entre **tiempo programado** y **tiempo real utilizado**. + +#### Lógica de Cálculo + +1. **Tiempo Programado**: + - Basado en la duración de los servicios agendados + - Calculado desde `bookings.start_time_utc` hasta `bookings.end_time_utc` + - Excluye tiempos de espera entre citas + +2. **Tiempo Real Utilizado**: + - Actualizado manualmente por el staff después de completar una cita + - Se almacena en tabla de control (opcional: `staff_time_tracking`) + - Permite ajustes por diferencias en duración real + +#### Campos de Base de Datos + +**Tabla `bookings` (ya existe):** +```sql +- id (UUID) +- staff_id (UUID) +- service_id (UUID) +- start_time_utc (TIMESTAMPTZ) +- end_time_utc (TIMESTAMPTZ) +- scheduled_duration_minutes (INTEGER) - Calculado automáticamente +- actual_duration_minutes (INTEGER) - Actualizado manualmente por staff +- time_difference_minutes (INTEGER) - Diferencia calculada +- status (TEXT) - 'confirmed', 'pending', 'in_progress', 'completed', 'no_show' +``` + +**Nueva tabla sugerida: `staff_time_tracking`** +```sql +CREATE TABLE staff_time_tracking ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + booking_id UUID REFERENCES bookings(id) ON DELETE CASCADE, + staff_id UUID REFERENCES staff(id) ON DELETE CASCADE, + scheduled_duration_minutes INTEGER NOT NULL, + actual_duration_minutes INTEGER NOT NULL, + time_difference_minutes INTEGER NOT NULL, + notes TEXT, + created_by UUID REFERENCES staff(id) ON DELETE SET NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() +); + +CREATE INDEX idx_staff_time_tracking_staff_date ON staff_time_tracking(staff_id, created_at); +``` + +#### Algoritmo de Cálculo + +```sql +-- Duración programada (automática) +UPDATE bookings +SET scheduled_duration_minutes = EXTRACT(EPOCH FROM (end_time_utc - start_time_utc)) / 60 +WHERE scheduled_duration_minutes IS NULL; + +-- Diferencia de tiempo (automática) +UPDATE bookings +SET time_difference_minutes = actual_duration_minutes - scheduled_duration_minutes +WHERE status = 'completed' AND actual_duration_minutes IS NOT NULL; +``` + +#### Cálculo de Nómina + +**Horas Totales por Periodo:** +```sql +SELECT + s.id, + s.display_name, + SUM(b.scheduled_duration_minutes) / 60 AS scheduled_hours, + SUM(b.actual_duration_minutes) / 60 AS actual_hours, + COUNT(b.id) AS total_bookings, + SUM(CASE WHEN b.time_difference_minutes > 0 THEN b.time_difference_minutes ELSE 0 END) / 60 AS extra_hours +FROM staff s +LEFT JOIN bookings b ON b.staff_id = s.id + WHERE b.status = 'completed' + AND b.start_time_utc >= $1::TIMESTAMPTZ + AND b.start_time_utc < $2::TIMESTAMPTZ +GROUP BY s.id, s.display_name; +``` + +#### Reglas de Negocio + +1. **Tiempo programado**: Base para el cálculo de nómina +2. **Tiempo real**: Ajustes permitidos por staff (por ex: cliente llegó tarde, servicio se extendió) +3. **Tiempo extra**: Se paga al 100% si fue trabajo adicional +4. **Tiempo faltante**: Se descuenta del pago (horarios no cubiertos por citas) +5. **Tiempo no-productivo**: No se paga (esperas, preparación post-cita) + +--- + +## 4. Estructura del Sistema de POS (Punto de Venta) + +### 4.1 Arquitectura del POS + +#### Componentes Principales + +**1. Service Selector** +- Grid de categorías: Servicios, Productos de venta, Membresías, Giftcards +- Búsqueda fonética de productos/servicios +- Filtros por tipo y categoría + +**2. Customer Selection** +- Buscador de clientes (email/teléfono) +- Selección de cliente existente o registro de nuevo +- Display de tier y saldo de créditos/membresía + +**3. Payment Processor** +- Métodos de pago disponibles: + - Efectivo + - Transferencia + - Membership (créditos de membresía) + - Tarjeta (Stripe terminal) + - Giftcard (código canjeable) + - PIA (Paid in Advance - depósito ya pagado) +- Cálculo automático de cambio + +**4. Receipt Options** +- NO imprimir recibos físicos +- Enviar por email (SendGrid, AWS SES, o similar) +- Guardar en dashboard del cliente + +**5. Transaction History** +- Historial de transacciones del día +- Filtros por método de pago y cajero + +### 4.2 Opciones de Pago + +#### 1. Efectivo +- Registro manual del monto recibido +- No requiere integración externa + +#### 2. Transferencia +- Referencia bancaria del cliente +- Comprobante de transferencia +- Estado: "pendiente" hasta confirmación + +#### 3. Membership (Créditos de Membresía) +- Verificar saldo disponible +- Deducir créditos automáticamente +- Restringir a clientes con membresía activa + +#### 4. Tarjeta (Stripe Terminal) +- Integración con Stripe SDK para terminales físicas +- Procesamiento de pago en tiempo real +- Confirmación de transacción + +#### 5. Giftcard +- Validar código de giftcard +- Verificar saldo y estado (activo/inactivo/expirado) +- Deducir saldo del giftcard + +#### 6. PIA (Paid in Advance) +- Verificar depósito previamente pagado +- Aplicar al saldo total de la transacción +- No requiere pago adicional + +### 4.3 Campos de Base de Datos + +**Nueva tabla: `pos_sales`** +```sql +CREATE TABLE pos_sales ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + location_id UUID REFERENCES locations(id) ON DELETE CASCADE, + staff_id UUID REFERENCES staff(id) ON DELETE CASCADE, + customer_id UUID REFERENCES customers(id) ON DELETE SET NULL, + + -- Payment details + payment_method TEXT NOT NULL CHECK (payment_method IN ('cash', 'transfer', 'membership', 'card', 'giftcard', 'pia')), + payment_amount DECIMAL(10, 2) NOT NULL, + payment_reference TEXT, + payment_status TEXT NOT NULL DEFAULT 'completed' CHECK (payment_status IN ('pending', 'completed', 'failed')), + + -- Transaction details + total_amount DECIMAL(10, 2) NOT NULL, + discount_amount DECIMAL(10, 2) DEFAULT 0, + tax_amount DECIMAL(10, 2) DEFAULT 0, + tip_amount DECIMAL(10, 2) DEFAULT 0, + + -- Items sold + items JSONB NOT NULL, + + -- Timestamps + created_at TIMESTAMPTZ DEFAULT NOW(), + created_by UUID REFERENCES staff(id) ON DELETE SET NULL +); + +CREATE INDEX idx_pos_sales_location_date ON pos_sales(location_id, created_at); +CREATE INDEX idx_pos_sales_staff_date ON pos_sales(staff_id, created_at); +``` + +**Formato de `items` JSONB:** +```json +{ + "services": [ + { + "service_id": "uuid", + "service_name": "Manicure", + "quantity": 1, + "unit_price": 150.00, + "total": 150.00 + } + ], + "products": [ + { + "product_id": "uuid", + "product_name": "Cuticle Remover", + "quantity": 2, + "unit_price": 45.00, + "total": 90.00 + } + ], + "memberships": [ + { + "membership_id": "uuid", + "membership_name": "VIP Monthly", + "quantity": 1, + "unit_price": 500.00, + "total": 500.00 + } + ] +} +``` + +**Nueva tabla: `giftcards`** +```sql +CREATE TABLE giftcards ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + code TEXT UNIQUE NOT NULL, + initial_balance DECIMAL(10, 2) NOT NULL, + current_balance DECIMAL(10, 2) NOT NULL, + purchased_by UUID REFERENCES customers(id) ON DELETE SET NULL, + purchased_at TIMESTAMPTZ DEFAULT NOW(), + expires_at TIMESTAMPTZ, + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMPTZ DEFAULT NOW(), + created_by UUID REFERENCES staff(id) ON DELETE SET NULL +); + +CREATE INDEX idx_giftcards_code ON giftcards(code); +``` + +### 4.4 API Endpoints + +**POS Sales:** +```typescript +POST /api/aperture/pos/sales +Body: { + customer_id: UUID | null, + items: { + services: Array<{ service_id, quantity }>, + products: Array<{ product_id, quantity }>, + memberships: Array<{ membership_id, quantity }> + }, + payment_method: 'cash' | 'transfer' | 'membership' | 'card' | 'giftcard' | 'pia', + payment_amount: number, + tip_amount?: number, + giftcard_code?: string +} +Response: { success, sale_id, items, total_amount, change } +``` + +**Daily Summary:** +```typescript +GET /api/aperture/pos/daily-summary?date=YYYY-MM-DD&location_id=UUID +Response: { + success: true, + summary: { + total_sales, + by_payment_method: { cash, transfer, membership, card, giftcard, pia }, + transactions_count + } +} +``` + +--- + +## 5. Sistema de Múltiples Cajeros + +### 5.1 Arquitectura + +Cada cajero tiene su propia sesión y cierre de caja independiente. El sistema permite: +- Múltiples cajeros trabajando simultáneamente +- Control individual de transacciones por cajero +- Rastreo de errores en cobros por usuario específico +- Cierre de caja individual por cajero + +### 5.2 Campos de Base de Datos + +**Nueva tabla: `daily_cash_close`** +```sql +CREATE TABLE daily_cash_close ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + location_id UUID REFERENCES locations(id) ON DELETE CASCADE, + cashier_id UUID REFERENCES staff(id) ON DELETE CASCADE, + + -- Cash balance tracking + opening_balance DECIMAL(10, 2) NOT NULL DEFAULT 0, + cash_sales DECIMAL(10, 2) NOT NULL DEFAULT 0, + cash_refunds DECIMAL(10, 2) NOT NULL DEFAULT 0, + closing_balance DECIMAL(10, 2) NOT NULL DEFAULT 0, + cash_difference DECIMAL(10, 2) NOT NULL DEFAULT 0, + + -- Transaction summary + total_sales DECIMAL(10, 2) NOT NULL DEFAULT 0, + card_sales DECIMAL(10, 2) NOT NULL DEFAULT 0, + transfer_sales DECIMAL(10, 2) NOT NULL DEFAULT 0, + membership_sales DECIMAL(10, 2) NOT NULL DEFAULT 0, + giftcard_sales DECIMAL(10, 2) NOT NULL DEFAULT 0, + pia_sales DECIMAL(10, 2) NOT NULL DEFAULT 0, + + -- Timestamps + date DATE NOT NULL, + open_at TIMESTAMPTZ NOT NULL, + closed_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT NOW(), + closed_by UUID REFERENCES staff(id) ON DELETE SET NULL +); + +CREATE INDEX idx_daily_cash_close_location_date ON daily_cash_close(location_id, date); +CREATE INDEX idx_daily_cash_close_cashier_date ON daily_cash_close(cashier_id, date); +CREATE UNIQUE INDEX idx_daily_cash_close_unique ON daily_cash_close(location_id, cashier_id, date); +``` + +### 5.3 Flujo de Cierre de Caja + +#### 1. Apertura de Caja +- Cajero registra monto de efectivo inicial (`opening_balance`) +- Sistema crea registro de apertura (`open_at` timestamp) + +#### 2. Registro de Ventas +- Todas las transacciones se asocian al `cashier_id` activo +- Sistema calcula totales por método de pago en tiempo real + +#### 3. Cierre de Caja +- Cajero cierra el día: + 1. Conta efectivo en caja + 2. Ingresa `closing_balance` real + 3. Sistema calcula `cash_difference` + 4. Genera reporte PDF automático + 5. Envía reporte al dueño por email + +#### 4. Rastreo de Errores +- Si `cash_difference` ≠ 0: + - Sistema marca discrepancia + - Asocia la transacción específica al cajero + - Permite investigación del error con el usuario correcto + +### 5.4 API Endpoints + +**Open Cash Register:** +```typescript +POST /api/aperture/pos/open-cash-register +Body: { + opening_balance: number +} +Response: { success, cash_register_id, open_at } +``` + +**Close Cash Register:** +```typescript +POST /api/aperture/pos/close-cash-register +Body: { + closing_balance: number, + notes?: string +} +Response: { + success: true, + summary: { total_sales, cash_difference, transactions_count }, + pdf_report_url +} +``` + +**Get Active Cash Registers:** +```typescript +GET /api/aperture/pos/active-cash-registers?location_id=UUID +Response: { + success: true, + registers: [ + { + id, + cashier_id, + cashier_name, + opening_balance, + current_balance, + open_at, + location_name + } + ] +} +``` + +--- + +## 6. Sistema de Finanzas + +### 6.1 Campos de Base de Datos + +**Nueva tabla: `expenses`** +```sql +CREATE TABLE expenses ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + location_id UUID REFERENCES locations(id) ON DELETE CASCADE, + + category TEXT NOT NULL, + description TEXT, + amount DECIMAL(10, 2) NOT NULL, + expense_date DATE NOT NULL, + + -- Recurring expenses + is_recurring BOOLEAN DEFAULT false, + recurring_frequency TEXT CHECK (recurring_frequency IN ('daily', 'weekly', 'monthly', 'yearly')), + recurring_end_date DATE, + + created_at TIMESTAMPTZ DEFAULT NOW(), + created_by UUID REFERENCES staff(id) ON DELETE SET NULL +); + +CREATE INDEX idx_expenses_location_date ON expenses(location_id, expense_date); +CREATE INDEX idx_expenses_category ON expenses(category); +``` + +### 6.2 Categorías de Gastos + +- **Renta**: Alquiler del local +- **Insumos**: Productos para servicios (cuticles, esmaltes, etc.) +- **Servicios**: Servicios externos contratados +- **Personal**: Pagos de nómina (si se maneja por cash) +- **Marketing**: Publicidad y promociones +- **Utilidades**: Electricidad, agua, internet +- **Otros**: Cualquier otro gasto + +### 6.3 API Endpoints + +**Create Expense:** +```typescript +POST /api/aperture/finance/expenses +Body: { + category, + description, + amount, + expense_date, + is_recurring?: boolean, + recurring_frequency?: 'daily' | 'weekly' | 'monthly' | 'yearly', + recurring_end_date?: string +} +Response: { success, expense_id } +``` + +**Get Financial Report:** +```typescript +GET /api/aperture/finance/report?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD&location_id=UUID +Response: { + success: true, + report: { + total_revenue, + total_expenses, + net_margin, + expenses_by_category, + profit_margin_percentage + } +} +``` + +--- + +## 7. Convenciones de Código + +### TypeScript +- **Strict mode**: Habilitado +- **Interfaces**: Definir tipos para todas las respuestas de API +- **Enums**: Usar enums para constantes (status, roles, métodos de pago) + +### Naming Conventions +- **Componentes**: PascalCase (ej: `StatsCard`, `BookingCard`) +- **Funciones**: camelCase (ej: `fetchBookings`, `calculatePayroll`) +- **Variables**: camelCase (ej: `totalSales`, `staffId`) +- **Constantes**: UPPER_SNAKE_CASE (ej: `API_URL`, `DEFAULT_TIMEOUT`) + +### SQL +- **Table names**: snake_case (ej: `daily_cash_close`, `pos_sales`) +- **Column names**: snake_case (ej: `opening_balance`, `cash_difference`) +- **Functions**: snake_case (ej: `calculate_weekly_invitations_reset`) + +--- + +## 8. Consideraciones de Seguridad + +1. **Row Level Security (RLS)**: + - Todas las tablas sensibles deben tener políticas RLS + - Solo roles apropiados pueden acceder a datos financieros + - Audit logging completo de todas las acciones + +2. **Validaciones**: + - Validar todos los inputs de usuario + - Verificar permisos antes de permitir acciones + - Validar montos de pagos (rangos aceptables) + +3. **Auditoría**: + - Todas las acciones financieras deben registrarse en `audit_logs` + - Incluir: acción, usuario, timestamp, detalles + +--- + +## 9. Checklist de Implementación + +### Fase 0: Documentación y Configuración ✅ +- [x] Crear documento de especificaciones técnicas +- [x] Documentar cálculo de horas trabajadas +- [x] Definir estructura de POS completa +- [x] Documentar sistema de múltiples cajeros + +### Fase 1-7: Pendiente +- [ ] Instalar Radix UI +- [ ] Crear componentes base Square UI +- [ ] Implementar Dashboard Home +- [ ] Implementar Calendario Maestro +- [ ] Implementar Staff & Nómina +- [ ] Implementar Clientes & Fidelización +- [ ] Implementar POS +- [ ] Implementar Finanzas +- [ ] Implementar Marketing & Configuración +- [ ] Testing completo + +--- + +## 10. Documentos Relacionados + +- [TASKS.md](../TASKS.md) - Plan de ejecución por fases +- [APERTURE_SQUARE_UI.md](./APERTURE_SQUARE_UI.md) - Guía de estilo Square UI +- [DESIGN_SYSTEM.md](./DESIGN_SYSTEM.md) - Sistema de diseño completo +- [API.md](./API.md) - Documentación de APIs y endpoints +- [PRD.md](./PRD.md) - Documento maestro de producto + +--- + +## 11. Notas Importantes + +1. **Precios Inteligentes**: + - Configurables por servicio + - Aplican a ambos canales (booking + POS) + - Solo activables en temporada alta (backend toggle) + +2. **Sin Impresión de Recibos**: + - Email a cliente + - Dashboard del cliente + - Reporte PDF al dueño (cierre de caja) + +3. **Múltiples Cajeros**: + - Cada cajero con su propio cierre de caja + - Rastreo de errores por usuario específico + - Control de movimientos para investigación + +4. **Horas Trabajadas**: + - Automático desde bookings (tiempo programado) + - Actualización manual de tiempo real por staff + - Cálculo de diferencias + - Nómina basada en horas reales trabajadas