workspace-v1/projects/erp-retail/orchestration/planes/fase-2-analisis-modulos/ANALISIS-RT-005-clientes.md
rckrdmrd 66161b1566 feat: Workspace-v1 complete migration with NEXUS v3.4
Sistema NEXUS v3.4 migrado con:

Estructura principal:
- core/orchestration: Sistema SIMCO + CAPVED (27 directivas, 28 perfiles)
- core/catalog: Catalogo de funcionalidades reutilizables
- shared/knowledge-base: Base de conocimiento compartida
- devtools/scripts: Herramientas de desarrollo
- control-plane/registries: Control de servicios y CI/CD
- orchestration/: Configuracion de orquestacion de agentes

Proyectos incluidos (11):
- gamilit (submodule -> GitHub)
- trading-platform (OrbiquanTIA)
- erp-suite con 5 verticales:
  - erp-core, construccion, vidrio-templado
  - mecanicas-diesel, retail, clinicas
- betting-analytics
- inmobiliaria-analytics
- platform_marketing_content
- pos-micro, erp-basico

Configuracion:
- .gitignore completo para Node.js/Python/Docker
- gamilit como submodule (git@github.com:rckrdmrd/gamilit-workspace.git)
- Sistema de puertos estandarizado (3005-3199)

Generated with NEXUS v3.4 Migration System
EPIC-010: Configuracion Git y Repositorios
2026-01-04 03:37:42 -06:00

12 KiB

ANALISIS MODULO RT-005: CLIENTES Y PROGRAMA DE LEALTAD

Fecha: 2025-12-18 Fase: 2 - Analisis por Modulo Modulo: RT-005 Clientes Herencia: 40% Story Points: 34 Prioridad: P1


1. DESCRIPCION GENERAL

1.1 Proposito

Gestion de clientes con programa de fidelizacion basado en puntos, niveles de membresia y recompensas.

1.2 Funcionalidades Principales

Funcionalidad Descripcion Criticidad
Registro cliente Datos minimos en POS Alta
Programa puntos Acumulacion y canje Alta
Niveles membresia Bronce, Plata, Oro Media
Historial compras 3 anos Media
Segmentacion Por comportamiento Baja

2. HERENCIA DEL CORE

2.1 Componentes Heredados (40%)

Componente Core % Uso Accion
core.partners 100% HEREDAR (clientes)
core.partner_contacts 100% HEREDAR
crm.leads 20% OPCIONAL

2.2 Servicios a Heredar

import { PartnersService } from '@erp-core/core';
import { ContactsService } from '@erp-core/core';

2.3 Servicios a Extender

class RetailCustomerService extends PartnersService {
  // Busqueda rapida
  async quickSearch(phone: string): Promise<Customer>;
  async quickSearch(email: string): Promise<Customer>;

  // Programa lealtad
  async getPoints(customerId: string): Promise<LoyaltyInfo>;
  async getLoyaltyCard(customerId: string): Promise<LoyaltyCard>;
}

3. COMPONENTES NUEVOS

3.1 Entidades (TypeORM)

// 1. LoyaltyProgram - Programa de lealtad
@Entity('loyalty_programs', { schema: 'retail' })
export class LoyaltyProgram {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column('uuid')
  tenantId: string;

  @Column()
  code: string;

  @Column()
  name: string;

  @Column({ nullable: true })
  description: string;

  @Column({ type: 'decimal', precision: 5, scale: 2, default: 1 })
  pointsPerCurrency: number;  // 1 punto por $10

  @Column({ type: 'decimal', precision: 5, scale: 2, default: 0.01 })
  currencyPerPoint: number;   // $0.10 por punto

  @Column({ type: 'int', default: 100 })
  minPointsRedeem: number;

  @Column({ type: 'int', nullable: true })
  pointsExpiryDays: number;  // null = no expiran

  @Column({ type: 'boolean', default: true })
  isActive: boolean;
}

// 2. LoyaltyCard - Tarjeta de cliente
@Entity('loyalty_cards', { schema: 'retail' })
export class LoyaltyCard {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column('uuid')
  tenantId: string;

  @ManyToOne(() => LoyaltyProgram)
  program: LoyaltyProgram;

  @ManyToOne(() => Partner)
  customer: Partner;

  @Column()
  cardNumber: string;

  @Column({ type: 'date' })
  issueDate: Date;

  @Column({ type: 'int', default: 0 })
  pointsBalance: number;

  @Column({ type: 'int', default: 0 })
  pointsEarned: number;

  @Column({ type: 'int', default: 0 })
  pointsRedeemed: number;

  @Column({ type: 'int', default: 0 })
  pointsExpired: number;

  @Column({ type: 'boolean', default: true })
  isActive: boolean;
}

// 3. LoyaltyTransaction - Movimiento de puntos
@Entity('loyalty_transactions', { schema: 'retail' })
export class LoyaltyTransaction {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column('uuid')
  tenantId: string;

  @ManyToOne(() => LoyaltyCard)
  card: LoyaltyCard;

  @Column({ type: 'enum', enum: LoyaltyTransactionType })
  transactionType: LoyaltyTransactionType;  // earn, redeem, expire, adjust

  @Column({ type: 'int' })
  pointsAmount: number;

  @Column({ nullable: true })
  referenceType: string;  // pos_order, promotion, manual

  @Column({ type: 'uuid', nullable: true })
  referenceId: string;

  @Column({ nullable: true })
  notes: string;

  @Column({ type: 'timestamptz' })
  createdAt: Date;
}

// 4. MembershipLevel - Niveles
@Entity('membership_levels', { schema: 'retail' })
export class MembershipLevel {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column('uuid')
  tenantId: string;

  @ManyToOne(() => LoyaltyProgram)
  program: LoyaltyProgram;

  @Column()
  name: string;  // Bronce, Plata, Oro

  @Column({ type: 'int' })
  minPointsYear: number;  // Puntos minimos para alcanzar

  @Column({ type: 'decimal', precision: 3, scale: 2, default: 1 })
  multiplier: number;  // 1x, 1.5x, 2x

  @Column({ type: 'jsonb', nullable: true })
  benefits: object;  // Beneficios adicionales
}

3.2 Servicios Backend

Servicio Metodos Principales
RetailCustomerService quickSearch(), register(), getHistory()
LoyaltyProgramService create(), configure(), getActive()
LoyaltyCardService issue(), activate(), getBalance()
LoyaltyTransactionService earnPoints(), redeemPoints(), expire()
MembershipService calculateLevel(), upgrade(), downgrade()

3.3 Controladores

@Controller('customers')
export class RetailCustomerController {
  // Clientes
  @Get('search')
  quickSearch(@Query('q') query: string): Promise<Customer[]>;

  @Post('quick-register')
  quickRegister(@Body() dto: QuickRegisterDto): Promise<Customer>;

  @Get(':id/history')
  getPurchaseHistory(@Param('id') id: string): Promise<PurchaseHistory>;

  // Lealtad
  @Get(':id/loyalty')
  getLoyaltyInfo(@Param('id') id: string): Promise<LoyaltyInfo>;

  @Get(':id/loyalty/card')
  getLoyaltyCard(@Param('id') id: string): Promise<LoyaltyCard>;

  @Post(':id/loyalty/earn')
  earnPoints(@Param('id') id: string, @Body() dto: EarnPointsDto): Promise<LoyaltyTransaction>;

  @Post(':id/loyalty/redeem')
  redeemPoints(@Param('id') id: string, @Body() dto: RedeemPointsDto): Promise<LoyaltyTransaction>;

  @Get(':id/loyalty/transactions')
  getTransactions(@Param('id') id: string): Promise<LoyaltyTransaction[]>;
}

@Controller('loyalty')
export class LoyaltyController {
  // Programas
  @Get('programs')
  getPrograms(): Promise<LoyaltyProgram[]>;

  @Post('programs')
  createProgram(@Body() dto: CreateProgramDto): Promise<LoyaltyProgram>;

  // Tarjetas
  @Post('cards/issue')
  issueCard(@Body() dto: IssueCardDto): Promise<LoyaltyCard>;

  @Get('cards/:number')
  getCardByNumber(@Param('number') number: string): Promise<LoyaltyCard>;
}

4. SISTEMA DE PUNTOS

4.1 Configuracion

loyalty_config:
  points:
    earn_rate: 1          # 1 punto por cada $10
    redeem_rate: 100      # 100 puntos = $10 descuento
    min_redeem: 100       # Minimo para canjear
    max_discount_percent: 50  # Maximo descuento por puntos
    expiry_days: null     # No expiran (o 365)

  levels:
    - name: "Bronce"
      min_points_year: 0
      multiplier: 1.0
      benefits: []

    - name: "Plata"
      min_points_year: 1000
      multiplier: 1.5
      benefits:
        - "Envio gratis e-commerce"
        - "Ofertas exclusivas"

    - name: "Oro"
      min_points_year: 5000
      multiplier: 2.0
      benefits:
        - "Envio gratis e-commerce"
        - "Ofertas exclusivas"
        - "Acceso VIP preventas"
        - "Descuento cumpleaños 20%"

4.2 Calculo de Puntos

// Al cerrar venta
function calculatePoints(order: POSOrder, card: LoyaltyCard): number {
  const program = card.program;
  const level = getMembershipLevel(card);

  // Base: 1 punto por cada $10
  const basePoints = Math.floor(order.total / program.pointsPerCurrency);

  // Multiplicador por nivel
  const multipliedPoints = Math.floor(basePoints * level.multiplier);

  // Puntos extra por promociones
  const bonusPoints = calculateBonusPoints(order);

  return multipliedPoints + bonusPoints;
}

// Canje de puntos
function redeemPoints(points: number, program: LoyaltyProgram): number {
  if (points < program.minPointsRedeem) {
    throw new Error('Puntos insuficientes');
  }

  // Calcular descuento
  const discount = points * program.currencyPerPoint;
  return discount;
}

5. FLUJOS DE NEGOCIO

5.1 Flujo en POS

1. Cliente se identifica (telefono/tarjeta)
      ↓
2. Buscar tarjeta de lealtad
      ↓
3. Mostrar puntos disponibles
      ↓
4. Realizar venta normal
      ↓
5. Preguntar si desea canjear puntos
      ↓
6. Si canjea:
   a. Calcular descuento
   b. Aplicar a orden
   c. Registrar redeem
      ↓
7. Cerrar venta
      ↓
8. Calcular puntos ganados
      ↓
9. Registrar earn
      ↓
10. Imprimir puntos en ticket

5.2 Flujo de Registro

1. Cliente nuevo en POS
      ↓
2. Registro rapido (nombre, telefono)
      ↓
3. Crear partner en core
      ↓
4. Crear tarjeta lealtad
      ↓
5. Asignar nivel Bronce
      ↓
6. Opcional: puntos bienvenida

6. TABLAS DDL

6.1 Tablas Definidas

-- Ya en 03-retail-tables.sql
CREATE TABLE retail.loyalty_programs (...);
CREATE TABLE retail.loyalty_cards (...);
CREATE TABLE retail.loyalty_transactions (...);

-- Agregar niveles
CREATE TABLE retail.membership_levels (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id),
  program_id UUID NOT NULL REFERENCES retail.loyalty_programs(id),
  name VARCHAR(50) NOT NULL,
  min_points_year INT NOT NULL DEFAULT 0,
  multiplier DECIMAL(3,2) NOT NULL DEFAULT 1.00,
  benefits JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

6.2 Indices

-- Busqueda rapida cliente
CREATE INDEX idx_partners_phone ON core.partners(phone);
CREATE INDEX idx_partners_email ON core.partners(email);

-- Tarjetas
CREATE UNIQUE INDEX idx_loyalty_cards_number ON retail.loyalty_cards(tenant_id, card_number);
CREATE INDEX idx_loyalty_cards_customer ON retail.loyalty_cards(customer_id);

-- Transacciones
CREATE INDEX idx_loyalty_transactions_card ON retail.loyalty_transactions(card_id);
CREATE INDEX idx_loyalty_transactions_date ON retail.loyalty_transactions(created_at);

7. DEPENDENCIAS

7.1 Dependencias de Core

Modulo Estado Requerido Para
MGN-005 Catalogs 0% Partners (clientes)
MGN-014 CRM 0% Opcional

7.2 Dependencias de Retail

Modulo Tipo
RT-001 Fundamentos Prerequisito

7.3 Bloquea a

Modulo Razon
RT-002 POS Integracion puntos
RT-009 E-commerce Clientes y puntos

8. CRITERIOS DE ACEPTACION

8.1 Funcionales

  • Buscar cliente por telefono
  • Buscar cliente por email
  • Registro rapido de cliente
  • Crear programa de lealtad
  • Emitir tarjeta a cliente
  • Consultar balance de puntos
  • Acumular puntos al comprar
  • Canjear puntos por descuento
  • Ver historial de transacciones
  • Calcular nivel de membresia
  • Ver historial de compras (3 anos)

8.2 Performance

  • Busqueda cliente < 500ms
  • Consulta puntos < 200ms

9. RIESGOS

Riesgo Probabilidad Impacto Mitigacion
Fraude de puntos Media Alto Auditoria + limites
Performance busqueda Baja Medio Indices

10. ESTIMACION DETALLADA

Componente SP Backend SP Frontend Total
Entities + Migrations 3 - 3
RetailCustomerService 5 - 5
LoyaltyProgramService 3 - 3
LoyaltyCardService 5 - 5
LoyaltyTransactionService 3 - 3
MembershipService 2 - 2
Controllers 3 - 3
Customer Pages - 5 5
Loyalty Pages - 5 5
TOTAL 24 10 34

11. REFERENCIAS

Documento Ubicacion
Epica RT-005 docs/08-epicas/EPIC-RT-005-clientes.md
Modulo Core Partners erp-core/backend/src/modules/partners/

Estado: ANALISIS COMPLETO Bloqueado por: RT-001 Fundamentos