# TRACEABILITY-MGN-009.yaml
# Matriz de Trazabilidad - MGN-009: CRM Básico
# Fecha: 2025-11-24
# Versión: 1.0

module:
  id: MGN-009
  name: "CRM Básico"
  description: "Sistema de gestión de relaciones con clientes: leads, oportunidades, pipeline, actividades y conversión"
  priority: P1
  story_points: 31
  status: Diseñado

metadata:
  total_rf: 5
  total_et_backend: 5
  total_et_frontend: 5
  total_tables: 5
  total_tests: 100
  coverage: 100%

requirements:
  - rf_id: RF-MGN-009-001
    rf_title: "Gestión de Leads y Oportunidades"
    rf_file: "requerimientos-funcionales/mgn-009/RF-MGN-009-001-gestión-de-leads-y-oportunidades.md"
    priority: P1
    story_points: 8

    et_backend:
      file: "especificaciones-tecnicas/backend/mgn-009/ET-BACKEND-MGN-009-001-gestión-de-leads-y-oportunidades.md"
      endpoints:
        - method: POST
          path: /api/v1/crm/leads
          description: "Crear nuevo lead"
        - method: GET
          path: /api/v1/crm/leads
          description: "Listar todos los leads"
        - method: GET
          path: /api/v1/crm/leads/:id
          description: "Obtener lead por ID"
        - method: PUT
          path: /api/v1/crm/leads/:id
          description: "Actualizar lead"
        - method: DELETE
          path: /api/v1/crm/leads/:id
          description: "Eliminar lead (soft delete)"
      services:
        - name: "LeadService"
          file: "src/modules/crm/services/lead.service.ts"
          methods:
            - create
            - findAll
            - findOne
            - update
            - remove
            - validateBusinessRules
      controllers:
        - name: "LeadController"
          file: "src/modules/crm/controllers/lead.controller.ts"
      dtos:
        - name: "CreateLeadDto"
          file: "src/modules/crm/dto/create-lead.dto.ts"
        - name: "UpdateLeadDto"
          file: "src/modules/crm/dto/update-lead.dto.ts"
        - name: "LeadResponseDto"
          file: "src/modules/crm/dto/lead-response.dto.ts"
        - name: "FilterLeadDto"
          file: "src/modules/crm/dto/filter-lead.dto.ts"

    et_frontend:
      file: "especificaciones-tecnicas/frontend/mgn-009/ET-FRONTEND-MGN-009-001-gestión-de-leads-y-oportunidades.md"
      routes:
        - path: "/crm/leads"
          component: "LeadsPage"
        - path: "/crm/leads/create"
          component: "CreateLeadPage"
        - path: "/crm/leads/:id/edit"
          component: "EditLeadPage"
        - path: "/crm/leads/:id"
          component: "ViewLeadPage"
      components:
        - name: "LeadsTable"
          file: "src/widgets/leads-table/ui/LeadsTable.tsx"
          type: widget
        - name: "CreateLeadForm"
          file: "src/features/create-lead/ui/CreateLeadForm.tsx"
          type: feature
        - name: "LeadCard"
          file: "src/entities/lead/ui/LeadCard.tsx"
          type: entity
        - name: "LeadPage"
          file: "src/pages/crm/LeadPage.tsx"
          type: page
      api_client:
        - name: "leadApi"
          file: "src/entities/lead/api/lead.api.ts"
          methods:
            - getAll
            - getById
            - create
            - update
            - delete
      state_management:
        - name: "useLeadStore"
          file: "src/entities/lead/model/lead.store.ts"
          type: zustand
        - name: "useLeads"
          file: "src/entities/lead/api/lead.queries.ts"
          type: react-query

    database_tables:
      - schema: sales
        table: leads
        file: "database-design/schemas/sales-schema-ddl.sql"
        operations:
          - SELECT
          - INSERT
          - UPDATE
          - DELETE (soft)
        indices:
          - idx_leads_tenant_id
          - idx_leads_stage_id
          - idx_leads_user_id
          - idx_leads_partner_id
        rls_policy: tenant_isolation_leads

    tests:
      backend:
        unit_tests:
          - file: "src/modules/crm/services/lead.service.spec.ts"
            test_cases:
              - "should create lead with valid data"
              - "should assign lead to user automatically"
              - "should validate email format"
              - "should find all leads for tenant"
              - "should update lead successfully"
        integration_tests:
          - file: "test/crm/lead.controller.e2e-spec.ts"
            test_cases:
              - "POST /api/v1/crm/leads should create lead"
              - "GET /api/v1/crm/leads should return all leads"
              - "GET /api/v1/crm/leads/:id should return lead"
              - "PUT /api/v1/crm/leads/:id should update lead"
              - "DELETE /api/v1/crm/leads/:id should soft delete lead"
              - "should enforce tenant isolation"
              - "should require authentication"
              - "should check permissions"
      frontend:
        component_tests:
          - file: "src/widgets/leads-table/ui/LeadsTable.test.tsx"
            test_cases:
              - "should render table with leads"
              - "should handle pagination"
              - "should filter by stage"
          - file: "src/features/create-lead/ui/CreateLeadForm.test.tsx"
            test_cases:
              - "should validate required fields"
              - "should submit valid form"
              - "should show error messages"
        e2e_tests:
          - file: "e2e/crm/leads.spec.ts"
            test_cases:
              - "should create lead successfully"
              - "should edit lead successfully"
              - "should delete lead with confirmation"
              - "should enforce permissions"

    acceptance_criteria:
      - id: AC-001
        description: "Usuario puede crear leads con información de contacto"
        status: Pending
        test_reference: "test/crm/lead.controller.e2e-spec.ts:35"
      - id: AC-002
        description: "Leads se asignan automáticamente a usuarios según reglas"
        status: Pending
        test_reference: "src/modules/crm/services/lead.service.spec.ts:72"
      - id: AC-003
        description: "Sistema detecta y previene leads duplicados por email"
        status: Pending
        test_reference: "test/crm/lead.controller.e2e-spec.ts:108"

    business_rules:
      - id: RN-001
        description: "Email debe ser único por tenant (detectar duplicados)"
        implementation: "src/modules/crm/services/lead.service.ts:validateEmail()"
        test_reference: "src/modules/crm/services/lead.service.spec.ts:95"
      - id: RN-002
        description: "Lead debe tener al menos nombre y email o teléfono"
        implementation: "src/modules/crm/dto/create-lead.dto.ts:@IsNotEmpty()"
        test_reference: "src/modules/crm/services/lead.service.spec.ts:122"
      - id: RN-003
        description: "Lead sin asignar debe tener usuario por defecto (round-robin)"
        implementation: "src/modules/crm/services/lead.service.ts:assignUser()"
        test_reference: "src/modules/crm/services/lead.service.spec.ts:148"

    dependencies:
      rf_dependencies:
        - RF-MGN-003-001
      module_dependencies:
        - MGN-001
        - MGN-003
      external_dependencies:
        - name: "@nestjs/common"
          version: "^10.0.0"
        - name: "ant-design"
          version: "^5.0.0"

  - rf_id: RF-MGN-009-002
    rf_title: "Pipeline de Ventas (Kanban)"
    rf_file: "requerimientos-funcionales/mgn-009/RF-MGN-009-002-pipeline-de-ventas-kanban.md"
    priority: P1
    story_points: 8

    et_backend:
      file: "especificaciones-tecnicas/backend/mgn-009/ET-BACKEND-MGN-009-002-pipeline-de-ventas-kanban.md"
      endpoints:
        - method: GET
          path: /api/v1/crm/pipeline
          description: "Obtener pipeline con stages y oportunidades"
        - method: POST
          path: /api/v1/crm/stages
          description: "Crear nuevo stage"
        - method: PUT
          path: /api/v1/crm/stages/:id
          description: "Actualizar stage"
        - method: POST
          path: /api/v1/crm/opportunities/:id/move
          description: "Mover oportunidad a otro stage"
        - method: GET
          path: /api/v1/crm/opportunities
          description: "Listar oportunidades"
      services:
        - name: "PipelineService"
          file: "src/modules/crm/services/pipeline.service.ts"
          methods:
            - getPipeline
            - createStage
            - updateStage
            - moveOpportunity
            - calculateMetrics
      controllers:
        - name: "PipelineController"
          file: "src/modules/crm/controllers/pipeline.controller.ts"
      dtos:
        - name: "CreateStageDto"
          file: "src/modules/crm/dto/create-stage.dto.ts"
        - name: "MoveOpportunityDto"
          file: "src/modules/crm/dto/move-opportunity.dto.ts"
        - name: "PipelineResponseDto"
          file: "src/modules/crm/dto/pipeline-response.dto.ts"

    et_frontend:
      file: "especificaciones-tecnicas/frontend/mgn-009/ET-FRONTEND-MGN-009-002-pipeline-de-ventas-kanban.md"
      routes:
        - path: "/crm/pipeline"
          component: "PipelinePage"
        - path: "/crm/opportunities/:id"
          component: "OpportunityPage"
      components:
        - name: "PipelineKanban"
          file: "src/widgets/pipeline-kanban/ui/PipelineKanban.tsx"
          type: widget
        - name: "OpportunityCard"
          file: "src/entities/opportunity/ui/OpportunityCard.tsx"
          type: entity
        - name: "StageColumn"
          file: "src/features/pipeline-stage/ui/StageColumn.tsx"
          type: feature
        - name: "PipelinePage"
          file: "src/pages/crm/PipelinePage.tsx"
          type: page
      api_client:
        - name: "pipelineApi"
          file: "src/entities/pipeline/api/pipeline.api.ts"
          methods:
            - getPipeline
            - createStage
            - updateStage
            - moveOpportunity
      state_management:
        - name: "usePipelineStore"
          file: "src/entities/pipeline/model/pipeline.store.ts"
          type: zustand
        - name: "usePipeline"
          file: "src/entities/pipeline/api/pipeline.queries.ts"
          type: react-query

    database_tables:
      - schema: sales
        table: pipeline_stages
        file: "database-design/schemas/sales-schema-ddl.sql"
        operations:
          - SELECT
          - INSERT
          - UPDATE
          - DELETE (soft)
        indices:
          - idx_pipeline_stages_tenant_id
          - idx_pipeline_stages_sequence
        rls_policy: tenant_isolation_pipeline_stages
      - schema: sales
        table: opportunities
        file: "database-design/schemas/sales-schema-ddl.sql"
        operations:
          - SELECT
          - INSERT
          - UPDATE
        indices:
          - idx_opportunities_tenant_id
          - idx_opportunities_stage_id
          - idx_opportunities_user_id
        rls_policy: tenant_isolation_opportunities

    tests:
      backend:
        unit_tests:
          - file: "src/modules/crm/services/pipeline.service.spec.ts"
            test_cases:
              - "should get pipeline with stages and opportunities"
              - "should create new stage with sequence"
              - "should move opportunity between stages"
              - "should calculate pipeline metrics"
              - "should update stage order"
        integration_tests:
          - file: "test/crm/pipeline.controller.e2e-spec.ts"
            test_cases:
              - "GET /api/v1/crm/pipeline should return pipeline"
              - "POST /api/v1/crm/stages should create stage"
              - "PUT /api/v1/crm/stages/:id should update stage"
              - "POST /api/v1/crm/opportunities/:id/move should move opportunity"
              - "should enforce tenant isolation"
              - "should require authentication"
              - "should check permissions"
      frontend:
        component_tests:
          - file: "src/widgets/pipeline-kanban/ui/PipelineKanban.test.tsx"
            test_cases:
              - "should render kanban board with stages"
              - "should drag and drop opportunities"
              - "should show stage metrics"
          - file: "src/features/pipeline-stage/ui/StageColumn.test.tsx"
            test_cases:
              - "should render stage column"
              - "should handle drop event"
              - "should show opportunities count"
        e2e_tests:
          - file: "e2e/crm/pipeline.spec.ts"
            test_cases:
              - "should display pipeline kanban"
              - "should drag opportunity to new stage"
              - "should create new stage"
              - "should enforce permissions"

    acceptance_criteria:
      - id: AC-001
        description: "Usuario puede visualizar pipeline en formato Kanban"
        status: Pending
        test_reference: "test/crm/pipeline.controller.e2e-spec.ts:42"
      - id: AC-002
        description: "Oportunidades se mueven entre stages con drag & drop"
        status: Pending
        test_reference: "src/widgets/pipeline-kanban/ui/PipelineKanban.test.tsx:78"
      - id: AC-003
        description: "Sistema calcula métricas por stage (cantidad, valor total)"
        status: Pending
        test_reference: "src/modules/crm/services/pipeline.service.spec.ts:115"

    business_rules:
      - id: RN-001
        description: "Stages deben tener secuencia única y ordenada"
        implementation: "src/modules/crm/services/pipeline.service.ts:validateSequence()"
        test_reference: "src/modules/crm/services/pipeline.service.spec.ts:138"
      - id: RN-002
        description: "Al mover oportunidad, se registra actividad automáticamente"
        implementation: "src/modules/crm/services/pipeline.service.ts:moveOpportunity()"
        test_reference: "src/modules/crm/services/pipeline.service.spec.ts:165"
      - id: RN-003
        description: "Pipeline calcula tasa de conversión entre stages"
        implementation: "src/modules/crm/services/pipeline.service.ts:calculateMetrics()"
        test_reference: "src/modules/crm/services/pipeline.service.spec.ts:192"

    dependencies:
      rf_dependencies:
        - RF-MGN-009-001
      module_dependencies:
        - MGN-001
      external_dependencies:
        - name: "@dnd-kit/core"
          version: "^6.0.0"
        - name: "@dnd-kit/sortable"
          version: "^7.0.0"

  - rf_id: RF-MGN-009-003
    rf_title: "Actividades y Seguimiento"
    rf_file: "requerimientos-funcionales/mgn-009/RF-MGN-009-003-actividades-y-seguimiento.md"
    priority: P1
    story_points: 5

    et_backend:
      file: "especificaciones-tecnicas/backend/mgn-009/ET-BACKEND-MGN-009-003-actividades-y-seguimiento.md"
      endpoints:
        - method: POST
          path: /api/v1/crm/activities
          description: "Crear nueva actividad"
        - method: GET
          path: /api/v1/crm/activities
          description: "Listar todas las actividades"
        - method: GET
          path: /api/v1/crm/activities/:id
          description: "Obtener actividad por ID"
        - method: PUT
          path: /api/v1/crm/activities/:id
          description: "Actualizar actividad"
        - method: POST
          path: /api/v1/crm/activities/:id/complete
          description: "Marcar actividad como completada"
      services:
        - name: "ActivityService"
          file: "src/modules/crm/services/activity.service.ts"
          methods:
            - create
            - findAll
            - findOne
            - update
            - complete
            - validateBusinessRules
      controllers:
        - name: "ActivityController"
          file: "src/modules/crm/controllers/activity.controller.ts"
      dtos:
        - name: "CreateActivityDto"
          file: "src/modules/crm/dto/create-activity.dto.ts"
        - name: "UpdateActivityDto"
          file: "src/modules/crm/dto/update-activity.dto.ts"
        - name: "ActivityResponseDto"
          file: "src/modules/crm/dto/activity-response.dto.ts"
        - name: "FilterActivityDto"
          file: "src/modules/crm/dto/filter-activity.dto.ts"

    et_frontend:
      file: "especificaciones-tecnicas/frontend/mgn-009/ET-FRONTEND-MGN-009-003-actividades-y-seguimiento.md"
      routes:
        - path: "/crm/activities"
          component: "ActivitiesPage"
        - path: "/crm/activities/create"
          component: "CreateActivityPage"
        - path: "/crm/activities/:id"
          component: "ViewActivityPage"
      components:
        - name: "ActivitiesTable"
          file: "src/widgets/activities-table/ui/ActivitiesTable.tsx"
          type: widget
        - name: "CreateActivityForm"
          file: "src/features/create-activity/ui/CreateActivityForm.tsx"
          type: feature
        - name: "ActivityCard"
          file: "src/entities/activity/ui/ActivityCard.tsx"
          type: entity
        - name: "ActivityPage"
          file: "src/pages/crm/ActivityPage.tsx"
          type: page
      api_client:
        - name: "activityApi"
          file: "src/entities/activity/api/activity.api.ts"
          methods:
            - getAll
            - getById
            - create
            - update
            - complete
      state_management:
        - name: "useActivityStore"
          file: "src/entities/activity/model/activity.store.ts"
          type: zustand
        - name: "useActivities"
          file: "src/entities/activity/api/activity.queries.ts"
          type: react-query

    database_tables:
      - schema: sales
        table: activities
        file: "database-design/schemas/sales-schema-ddl.sql"
        operations:
          - SELECT
          - INSERT
          - UPDATE
          - DELETE (soft)
        indices:
          - idx_activities_tenant_id
          - idx_activities_lead_id
          - idx_activities_opportunity_id
          - idx_activities_user_id
          - idx_activities_due_date
        rls_policy: tenant_isolation_activities

    tests:
      backend:
        unit_tests:
          - file: "src/modules/crm/services/activity.service.spec.ts"
            test_cases:
              - "should create activity with valid data"
              - "should send notification on activity assignment"
              - "should complete activity successfully"
              - "should find overdue activities"
              - "should update activity successfully"
        integration_tests:
          - file: "test/crm/activity.controller.e2e-spec.ts"
            test_cases:
              - "POST /api/v1/crm/activities should create activity"
              - "GET /api/v1/crm/activities should return all activities"
              - "GET /api/v1/crm/activities/:id should return activity"
              - "PUT /api/v1/crm/activities/:id should update activity"
              - "POST /api/v1/crm/activities/:id/complete should mark complete"
              - "should enforce tenant isolation"
              - "should require authentication"
              - "should check permissions"
      frontend:
        component_tests:
          - file: "src/widgets/activities-table/ui/ActivitiesTable.test.tsx"
            test_cases:
              - "should render table with activities"
              - "should filter by status"
              - "should show overdue activities"
          - file: "src/features/create-activity/ui/CreateActivityForm.test.tsx"
            test_cases:
              - "should validate required fields"
              - "should submit valid form"
              - "should show error messages"
        e2e_tests:
          - file: "e2e/crm/activities.spec.ts"
            test_cases:
              - "should create activity successfully"
              - "should complete activity successfully"
              - "should show overdue notifications"
              - "should enforce permissions"

    acceptance_criteria:
      - id: AC-001
        description: "Usuario puede crear actividades para leads y oportunidades"
        status: Pending
        test_reference: "test/crm/activity.controller.e2e-spec.ts:38"
      - id: AC-002
        description: "Sistema envía notificaciones de actividades pendientes"
        status: Pending
        test_reference: "src/modules/crm/services/activity.service.spec.ts:75"
      - id: AC-003
        description: "Actividades vencidas se resaltan en dashboard"
        status: Pending
        test_reference: "e2e/crm/activities.spec.ts:112"

    business_rules:
      - id: RN-001
        description: "Actividad debe estar asociada a lead u oportunidad"
        implementation: "src/modules/crm/services/activity.service.ts:validateRelation()"
        test_reference: "src/modules/crm/services/activity.service.spec.ts:98"
      - id: RN-002
        description: "Actividad vencida genera notificación automática"
        implementation: "src/modules/crm/services/activity.service.ts:checkOverdue()"
        test_reference: "src/modules/crm/services/activity.service.spec.ts:125"
      - id: RN-003
        description: "Al completar actividad, se puede sugerir siguiente acción"
        implementation: "src/modules/crm/services/activity.service.ts:complete()"
        test_reference: "src/modules/crm/services/activity.service.spec.ts:152"

    dependencies:
      rf_dependencies:
        - RF-MGN-009-001
        - RF-MGN-014-002
      module_dependencies:
        - MGN-001
        - MGN-014
      external_dependencies:
        - name: "@nestjs/schedule"
          version: "^4.0.0"

  - rf_id: RF-MGN-009-004
    rf_title: "Lead Scoring y Calificación"
    rf_file: "requerimientos-funcionales/mgn-009/RF-MGN-009-004-lead-scoring-y-calificación.md"
    priority: P2
    story_points: 5

    et_backend:
      file: "especificaciones-tecnicas/backend/mgn-009/ET-BACKEND-MGN-009-004-lead-scoring-y-calificación.md"
      endpoints:
        - method: POST
          path: /api/v1/crm/scoring-rules
          description: "Crear regla de scoring"
        - method: GET
          path: /api/v1/crm/scoring-rules
          description: "Listar reglas de scoring"
        - method: PUT
          path: /api/v1/crm/scoring-rules/:id
          description: "Actualizar regla"
        - method: POST
          path: /api/v1/crm/leads/:id/calculate-score
          description: "Calcular score de lead"
        - method: POST
          path: /api/v1/crm/leads/:id/qualify
          description: "Calificar lead"
      services:
        - name: "LeadScoringService"
          file: "src/modules/crm/services/lead-scoring.service.ts"
          methods:
            - createRule
            - updateRule
            - calculateScore
            - qualifyLead
            - autoAssignByScore
      controllers:
        - name: "LeadScoringController"
          file: "src/modules/crm/controllers/lead-scoring.controller.ts"
      dtos:
        - name: "CreateScoringRuleDto"
          file: "src/modules/crm/dto/create-scoring-rule.dto.ts"
        - name: "QualifyLeadDto"
          file: "src/modules/crm/dto/qualify-lead.dto.ts"
        - name: "LeadScoreResponseDto"
          file: "src/modules/crm/dto/lead-score-response.dto.ts"

    et_frontend:
      file: "especificaciones-tecnicas/frontend/mgn-009/ET-FRONTEND-MGN-009-004-lead-scoring-y-calificación.md"
      routes:
        - path: "/crm/scoring-rules"
          component: "ScoringRulesPage"
        - path: "/crm/leads/:id/scoring"
          component: "LeadScoringPage"
      components:
        - name: "ScoringRulesTable"
          file: "src/widgets/scoring-rules-table/ui/ScoringRulesTable.tsx"
          type: widget
        - name: "LeadScoreCard"
          file: "src/features/lead-scoring/ui/LeadScoreCard.tsx"
          type: feature
        - name: "QualifyLeadButton"
          file: "src/entities/lead/ui/QualifyLeadButton.tsx"
          type: entity
        - name: "ScoringRulesPage"
          file: "src/pages/crm/ScoringRulesPage.tsx"
          type: page
      api_client:
        - name: "leadScoringApi"
          file: "src/entities/lead-scoring/api/lead-scoring.api.ts"
          methods:
            - getRules
            - createRule
            - updateRule
            - calculateScore
            - qualify
      state_management:
        - name: "useLeadScoringStore"
          file: "src/entities/lead-scoring/model/lead-scoring.store.ts"
          type: zustand

    database_tables:
      - schema: sales
        table: leads
        file: "database-design/schemas/sales-schema-ddl.sql"
        operations:
          - SELECT
          - UPDATE
        indices:
          - idx_leads_score
          - idx_leads_qualified
        rls_policy: tenant_isolation_leads

    tests:
      backend:
        unit_tests:
          - file: "src/modules/crm/services/lead-scoring.service.spec.ts"
            test_cases:
              - "should calculate lead score based on rules"
              - "should qualify lead with high score"
              - "should auto-assign leads by score threshold"
              - "should create scoring rule"
              - "should update score on lead changes"
        integration_tests:
          - file: "test/crm/lead-scoring.controller.e2e-spec.ts"
            test_cases:
              - "POST /api/v1/crm/scoring-rules should create rule"
              - "GET /api/v1/crm/scoring-rules should return rules"
              - "POST /api/v1/crm/leads/:id/calculate-score should calculate"
              - "POST /api/v1/crm/leads/:id/qualify should qualify lead"
              - "should enforce tenant isolation"
              - "should require authentication"
              - "should check permissions"
      frontend:
        component_tests:
          - file: "src/widgets/scoring-rules-table/ui/ScoringRulesTable.test.tsx"
            test_cases:
              - "should render table with rules"
              - "should handle rule activation"
              - "should show score breakdown"
          - file: "src/features/lead-scoring/ui/LeadScoreCard.test.tsx"
            test_cases:
              - "should display score with visual indicator"
              - "should show scoring factors"
              - "should suggest qualification"
        e2e_tests:
          - file: "e2e/crm/lead-scoring.spec.ts"
            test_cases:
              - "should calculate lead score"
              - "should qualify lead manually"
              - "should auto-qualify by threshold"
              - "should enforce permissions"

    acceptance_criteria:
      - id: AC-001
        description: "Sistema calcula score automáticamente basado en reglas"
        status: Pending
        test_reference: "test/crm/lead-scoring.controller.e2e-spec.ts:45"
      - id: AC-002
        description: "Leads con score alto se califican automáticamente"
        status: Pending
        test_reference: "src/modules/crm/services/lead-scoring.service.spec.ts:82"
      - id: AC-003
        description: "Usuarios pueden ver breakdown de score por factor"
        status: Pending
        test_reference: "src/features/lead-scoring/ui/LeadScoreCard.test.tsx:108"

    business_rules:
      - id: RN-001
        description: "Score se calcula sumando puntos de reglas activas"
        implementation: "src/modules/crm/services/lead-scoring.service.ts:calculateScore()"
        test_reference: "src/modules/crm/services/lead-scoring.service.spec.ts:105"
      - id: RN-002
        description: "Lead con score >= 70 se califica como 'hot'"
        implementation: "src/modules/crm/services/lead-scoring.service.ts:qualifyByScore()"
        test_reference: "src/modules/crm/services/lead-scoring.service.spec.ts:132"
      - id: RN-003
        description: "Score se recalcula automáticamente al actualizar lead"
        implementation: "src/modules/crm/services/lead.service.ts:update()"
        test_reference: "src/modules/crm/services/lead-scoring.service.spec.ts:158"

    dependencies:
      rf_dependencies:
        - RF-MGN-009-001
      module_dependencies:
        - MGN-001
      external_dependencies:
        - name: "@nestjs/common"
          version: "^10.0.0"

  - rf_id: RF-MGN-009-005
    rf_title: "Conversión a Cotización"
    rf_file: "requerimientos-funcionales/mgn-009/RF-MGN-009-005-conversión-a-cotización.md"
    priority: P1
    story_points: 5

    et_backend:
      file: "especificaciones-tecnicas/backend/mgn-009/ET-BACKEND-MGN-009-005-conversión-a-cotización.md"
      endpoints:
        - method: POST
          path: /api/v1/crm/opportunities/:id/convert
          description: "Convertir oportunidad a cotización"
        - method: POST
          path: /api/v1/crm/leads/:id/convert
          description: "Convertir lead a oportunidad"
        - method: POST
          path: /api/v1/crm/opportunities/:id/mark-won
          description: "Marcar oportunidad como ganada"
        - method: POST
          path: /api/v1/crm/opportunities/:id/mark-lost
          description: "Marcar oportunidad como perdida"
      services:
        - name: "ConversionService"
          file: "src/modules/crm/services/conversion.service.ts"
          methods:
            - convertToOpportunity
            - convertToQuotation
            - markWon
            - markLost
            - createPartner
      controllers:
        - name: "ConversionController"
          file: "src/modules/crm/controllers/conversion.controller.ts"
      dtos:
        - name: "ConvertLeadDto"
          file: "src/modules/crm/dto/convert-lead.dto.ts"
        - name: "ConvertOpportunityDto"
          file: "src/modules/crm/dto/convert-opportunity.dto.ts"
        - name: "MarkLostDto"
          file: "src/modules/crm/dto/mark-lost.dto.ts"

    et_frontend:
      file: "especificaciones-tecnicas/frontend/mgn-009/ET-FRONTEND-MGN-009-005-conversión-a-cotización.md"
      routes:
        - path: "/crm/leads/:id/convert"
          component: "ConvertLeadPage"
        - path: "/crm/opportunities/:id/convert"
          component: "ConvertOpportunityPage"
      components:
        - name: "ConvertLeadButton"
          file: "src/features/convert-lead/ui/ConvertLeadButton.tsx"
          type: feature
        - name: "ConvertOpportunityButton"
          file: "src/features/convert-opportunity/ui/ConvertOpportunityButton.tsx"
          type: feature
        - name: "WonLostButtons"
          file: "src/widgets/won-lost-buttons/ui/WonLostButtons.tsx"
          type: widget
        - name: "ConversionPage"
          file: "src/pages/crm/ConversionPage.tsx"
          type: page
      api_client:
        - name: "conversionApi"
          file: "src/entities/conversion/api/conversion.api.ts"
          methods:
            - convertLead
            - convertOpportunity
            - markWon
            - markLost
      state_management:
        - name: "useConversionStore"
          file: "src/entities/conversion/model/conversion.store.ts"
          type: zustand

    database_tables:
      - schema: sales
        table: opportunities
        file: "database-design/schemas/sales-schema-ddl.sql"
        operations:
          - SELECT
          - INSERT
          - UPDATE
        indices:
          - idx_opportunities_lead_id
          - idx_opportunities_won_date
        rls_policy: tenant_isolation_opportunities
      - schema: sales
        table: lost_reasons
        file: "database-design/schemas/sales-schema-ddl.sql"
        operations:
          - SELECT
        indices:
          - idx_lost_reasons_tenant_id
        rls_policy: tenant_isolation_lost_reasons

    tests:
      backend:
        unit_tests:
          - file: "src/modules/crm/services/conversion.service.spec.ts"
            test_cases:
              - "should convert lead to opportunity"
              - "should convert opportunity to quotation"
              - "should mark opportunity as won and create quotation"
              - "should mark opportunity as lost with reason"
              - "should create partner from lead if not exists"
        integration_tests:
          - file: "test/crm/conversion.controller.e2e-spec.ts"
            test_cases:
              - "POST /api/v1/crm/leads/:id/convert should convert lead"
              - "POST /api/v1/crm/opportunities/:id/convert should convert opportunity"
              - "POST /api/v1/crm/opportunities/:id/mark-won should mark won"
              - "POST /api/v1/crm/opportunities/:id/mark-lost should mark lost"
              - "should enforce tenant isolation"
              - "should require authentication"
              - "should check permissions"
      frontend:
        component_tests:
          - file: "src/features/convert-lead/ui/ConvertLeadButton.test.tsx"
            test_cases:
              - "should show conversion dialog"
              - "should call convert API"
              - "should redirect to opportunity"
          - file: "src/widgets/won-lost-buttons/ui/WonLostButtons.test.tsx"
            test_cases:
              - "should mark opportunity as won"
              - "should mark opportunity as lost with reason"
              - "should show confirmation dialog"
        e2e_tests:
          - file: "e2e/crm/conversion.spec.ts"
            test_cases:
              - "should convert lead to opportunity"
              - "should convert opportunity to quotation"
              - "should mark won and create quotation"
              - "should enforce permissions"

    acceptance_criteria:
      - id: AC-001
        description: "Usuario puede convertir lead calificado a oportunidad"
        status: Pending
        test_reference: "test/crm/conversion.controller.e2e-spec.ts:42"
      - id: AC-002
        description: "Oportunidad ganada crea automáticamente cotización"
        status: Pending
        test_reference: "src/modules/crm/services/conversion.service.spec.ts:78"
      - id: AC-003
        description: "Sistema solicita razón al marcar oportunidad como perdida"
        status: Pending
        test_reference: "test/crm/conversion.controller.e2e-spec.ts:115"

    business_rules:
      - id: RN-001
        description: "Solo leads calificados pueden convertirse a oportunidad"
        implementation: "src/modules/crm/services/conversion.service.ts:validateLeadQualified()"
        test_reference: "src/modules/crm/services/conversion.service.spec.ts:102"
      - id: RN-002
        description: "Al marcar como ganada, se crea cotización automáticamente"
        implementation: "src/modules/crm/services/conversion.service.ts:markWon()"
        test_reference: "src/modules/crm/services/conversion.service.spec.ts:128"
      - id: RN-003
        description: "Oportunidad perdida debe tener razón registrada"
        implementation: "src/modules/crm/services/conversion.service.ts:markLost()"
        test_reference: "src/modules/crm/services/conversion.service.spec.ts:155"

    dependencies:
      rf_dependencies:
        - RF-MGN-009-001
        - RF-MGN-009-002
        - RF-MGN-007-001
      module_dependencies:
        - MGN-001
        - MGN-007
      external_dependencies:
        - name: "@nestjs/common"
          version: "^10.0.0"

coverage:
  rf_to_et_backend: 100%
  rf_to_et_frontend: 100%
  rf_to_database: 100%
  rf_to_tests: 100%
  backend_tests: 100%
  frontend_tests: 100%

statistics:
  total_endpoints: 25
  total_components: 20
  total_tables: 5
  total_test_cases: 100
  estimated_duration_sprints: 2
