Compulandia CRM · Gestión de Oportunidades de Venta¶
Documento de contexto y análisis · v0.3¶
Propósito: captura del estado del análisis desarrollado hasta la fecha. Usar como punto de partida para continuar la iteración desde cualquier entorno. Última actualización: febrero 2026.
Índice¶
- Problema y propuesta
- Actores del sistema
- Entidades SAP involucradas
- Módulo de comentarios Firebase
- Flujo operativo — Ciclo completo
- Quotation draft con líneas de texto
- Flujo de la Activity
- Timeline unificado
- Reglas operativas
- Meta-plan de documentación
- Prompts UI generados
- Puntos abiertos
1. Problema y propuesta¶
Situación actual¶
- La comunicación vendedor↔comprador vive en Notion (chat con imágenes)
- Los documentos formales viven en SAP B1
- No hay trazabilidad entre ambos
- El módulo de comentarios Firebase existe pero solo se usa en Activities
- El vendedor arma cotizaciones con precios no validados formalmente
Solución propuesta — tres niveles¶
Nivel 1 — Flujo operativo Proceso guiado que ancla cada conversación al documento SAP correcto. No reemplaza herramientas — modela el proceso sobre las entidades existentes.
Nivel 2 — Entidades SAP
SalesOpportunities (OOPR) como nodo central.
Todos los documentos generados durante el ciclo vinculados mediante SalesOpportunityLines.
Nivel 3 — Arquitectura de implementación
Backend NestJS orquesta SAP Service Layer + Firebase.
El módulo de comentarios Firebase se extiende a OQUT y ORDR sin refactorizar.
Principio rector¶
No reemplazar herramientas. Modelar el proceso. SAP tiene las entidades correctas. Firebase tiene la infraestructura de comunicación. Lo que falta es el flujo explícito que guíe a vendedor y comprador a operar sobre las entidades correctas, en el orden correcto, con las conversaciones ancladas a los documentos correspondientes.
2. Actores del sistema¶
| Código | Actor | Rol en el flujo |
|---|---|---|
VEND |
Vendedor | Gestiona la oportunidad de inicio a fin. Genera cotizaciones. Interlocutor con el cliente. |
COMP |
Compras | Interviene cuando el producto cotizado es nuevo y no existe en SAP. Crea el ítem y habilita el Quotation formal post-aceptación. |
CLI |
Cliente (externo) | Recibe cotizaciones, negocia, decide. Opera vía comentarios en el hilo del Quotation. |
GER |
Gerencia | Consulta pipeline y forecast. No opera en el ciclo salvo aprobaciones de excepción. |
3. Entidades SAP involucradas¶
Mapa de entidades¶
SalesOpportunity (OOPR)
│
├── SalesOpportunityLines[]
│ ├── LineNum
│ ├── StageKey → SalesStages
│ ├── PercentageRate
│ ├── DocumentType (bodt_Quotation / bodt_Order / bodt_MinusOne)
│ └── DocumentNumber → OQUT.DocEntry / ORDR.DocEntry
│
├── Activities (OACT)
│ ├── SalesOpportunityId ← vínculo a OOPR
│ ├── SalesOpportunityLine
│ └── HandledBy
│
├── Quotations (OQUT) ← cotización al cliente
│ └── BaseEntry → OOPR (si se crea desde la oportunidad)
│
└── Orders (ORDR) ← confirmación de venta
└── BaseEntry → OQUT.DocEntry
Campos clave por entidad¶
OOPR — SalesOpportunities¶
| Campo | Tipo | Quién lo llena | Cuándo |
|---|---|---|---|
OpportunityName |
string | Vendedor | Alta |
CardCode |
string | Vendedor | Alta |
SalesPerson |
int | Sistema (usuario logueado) | Alta automática |
StartDate |
date | Sistema | Alta automática (hoy) |
PredictedClosingDate |
date | Sistema / Vendedor | Alta (+30d), editable |
MaxLocalTotal |
decimal | Vendedor | Alta |
CurrentStageNo |
int | Sistema | Al avanzar etapa |
ClosingPercentage |
decimal | Sistema | Calculado por etapa |
Status |
enum | Sistema | sos_Open → sos_Won / sos_Missed |
ReasonForClosing |
string | Vendedor | Solo al cerrar como perdida |
WeightedSumLC |
decimal | SAP (calculado) | Automático |
OQUT — Quotations (draft con texto libre)¶
{
"CardCode": "C01048",
"DocObjectCode": "oQuotations",
"DocumentSpecialLines": [
{
"LineNum": 0,
"AfterLineNumber": -1,
"OrderNumber": 1,
"LineType": "dslt_Text",
"LineText": "NOTEBOOK ACER ASPIRE 15 BXT i7 2990GHz - 255GB - NEGRO. PRECIO GS 5370000"
}
]
}
Nota: el Quotation draft usa
DocumentSpecialLinesconLineType: dslt_Text. No tiene totales calculados por SAP — el precio está embebido en el texto. Es una propuesta orientativa, no un documento financiero formal. El Quotation con ítems reales se genera después de que el cliente acepta.
OACT — Activities¶
| Campo | Tipo | Descripción |
|---|---|---|
SalesOpportunityId |
int | Vínculo a la oportunidad — obligatorio para que aparezca en el timeline |
SalesOpportunityLine |
int | Línea de etapa específica |
ActivityType |
enum | Reunión / Llamada / Tarea / Email |
HandledBy |
int | Empleado responsable |
ActivityDate |
date | Fecha de la actividad |
Status |
enum | open / closed |
DocType + DocEntry |
int | Si genera un documento vinculado |
Estados de la oportunidad (OOPR.Status)¶
| Estado SAP | Descripción | Quién lo activa |
|---|---|---|
sos_Open |
Oportunidad activa | Sistema al crear |
sos_Won |
Cerrada ganada | Sistema al convertir OQUT → ORDR |
sos_Missed |
Cerrada perdida | Vendedor al registrar rechazo |
4. Módulo de comentarios Firebase¶
Esquema de rutas (no modificar)¶
comments/
{entity_code}/ ← OOPR / OACT / OQUT / ORDR
{record_id}/
{comment_id}
body: string
author_id: string
author_type: "internal" | "customer"
created_at: timestamp
attachments: []
parent_id: string | null ← para replies
Contextos de uso por paso del flujo¶
| Paso | Entidad donde se comenta | Quién comenta |
|---|---|---|
| 1 — Apertura | OOPR/{opp_id} |
Vendedor |
| 2 — Reunión | OACT/{act_id} |
Vendedor + Cliente |
| 2B — Tarea Compras | OACT/{tarea_id} |
Vendedor + Compras |
| 3 — Negociación | OQUT/{quot_id} |
Vendedor + Cliente |
| 5 — Confirmación | OQUT/{quot_id} o OOPR/{opp_id} |
Vendedor |
| 6 — Rechazo | OOPR/{opp_id} |
Vendedor |
Construcción del timeline (backend NestJS)¶
1. GET SalesOpportunities(id)?$expand=SalesOpportunitiesLines,Activities
2. Construir índice de nodos: { entity_code, record_id } por cada entidad vinculada
3. Promise.all → consultas Firebase paralelas por cada nodo
4. Merge cronológico por fecha del documento SAP padre
5. Push notificaciones al actor correspondiente
5. Flujo operativo — Ciclo completo¶
Diagrama de flujo (Mermaid — pendiente de generar en A1)¶
INICIO
│
▼
[1] VEND: Alta de oportunidad (OOPR)
│ → OpportunityName + CardCode + MaxLocalTotal + StageKey=Lead
│
▼
[2] ¿El ítem existe en SAP?
│
├── SÍ ──▶ [2A] VEND: Genera Quotation draft (OQUT con dslt_Text)
│ → Sin intervención de Compras
│ → Editable libremente durante la negociación
│
└── NO ──▶ [2B] VEND: Crea tarea (OACT) → asigna a COMP
│ → Lista de ítems + specs + fecha límite
│
▼
COMP: Crea ítem en SAP
│ → Código + precio de costo + descripción
│
▼
VEND: Genera Quotation draft con el ítem habilitado
│
▼
[3] VEND: Envía cotización al cliente
│ → PDF adjunto en comentario del hilo OQUT
│
▼
[3-loop] CLI: Revisa y responde en el hilo
│ → ¿Cambios? → VEND edita líneas de texto del Quotation → vuelve a enviar
│ → Este loop se repite sin límite sin necesidad de Compras
│
▼
[4] ¿El cliente decide?
│
├── ACEPTA ──▶ [5] VEND: Formaliza el Quotation
│ │ → Reemplaza líneas de texto por ítems reales (si había draft)
│ │ → COMP crea ítems nuevos si es necesario (solo aquí)
│ │ → Convierte OQUT → ORDR
│ │ → Oportunidad: Status = sos_Won
│ └──▶ FIN (ganado)
│
└── RECHAZA ──▶ [6] VEND: Registra motivo (ReasonForClosing)
│ → Oportunidad: Status = sos_Missed
└──▶ FIN (perdido)
Quién hace qué — tabla completa¶
| Acción | Responsable | Condición |
|---|---|---|
| Abrir la oportunidad | Vendedor | Siempre |
| Generar Quotation draft (líneas de texto) | Vendedor | Siempre — ítem exista o no en SAP |
| Crear tarea a Compras | Vendedor | Solo si necesita ítem nuevo para el Quotation formal |
| Crear ítem nuevo en SAP | Compras | Solo cuando hay ítems nuevos y el cliente ya aceptó |
| Enviar cotización al cliente | Vendedor | Siempre |
| Editar líneas del Quotation draft | Vendedor | Cada vez que el cliente pide cambios |
| Responder y negociar | Cliente | En el hilo del OQUT |
| Formalizar Quotation (ítems reales) | Vendedor + Compras | Solo al confirmar aceptación |
| Convertir OQUT → ORDR | Vendedor | Al confirmar aceptación |
| Registrar motivo de rechazo | Vendedor | Al confirmar rechazo |
Estados de la oportunidad¶
sos_Open
├── [avanza etapa] → sigue en sos_Open con StageKey actualizado
├── [convierte a ORDR] → sos_Won
└── [registra rechazo] → sos_Missed
6. Quotation draft con líneas de texto¶
Por qué resuelve el problema de negociación¶
El flujo sin líneas de texto tenía el problema de que cada ítem nuevo requería un ciclo completo hacia Compras, lo que hacía la negociación lenta e impráctica.
Con DocumentSpecialLines + dslt_Text:
- El vendedor genera el Quotation inmediatamente, con descripción y precio en texto libre
- El cliente negocia sobre ese draft
- Los cambios se hacen editando líneas de texto — sin Compras, sin espera
- Compras solo entra después de que el cliente acepta, para formalizar los ítems
Limitación conocida¶
El Quotation draft no tiene totales calculados por SAP. El precio está en el texto de la línea. Política a comunicar al equipo:
El Quotation draft es una propuesta orientativa. El documento con valor formal es el Quotation con ítems reales generado después de la aceptación.
Formato del payload¶
{
"CardCode": "C01048",
"DocObjectCode": "oQuotations",
"DocumentSpecialLines": [
{
"LineNum": 0,
"AfterLineNumber": -1,
"OrderNumber": 1,
"LineType": "dslt_Text",
"LineText": "NOTEBOOK ACER ASPIRE 15 i7 - 16GB - 512GB SSD - PRECIO GS 5.370.000"
},
{
"LineNum": 1,
"AfterLineNumber": -1,
"OrderNumber": 2,
"LineType": "dslt_Text",
"LineText": "ADAPTADOR DISPLAY PORT A HDMI - PRECIO GS 209.000"
}
]
}
7. Flujo de la Activity¶
⚠️ Este bloque está pendiente de desarrollo detallado (Bloque B del meta-plan). Lo siguiente es el estado actual del análisis.
Tipos de Activity en el flujo¶
| Tipo | Quién la crea | Vinculada a | Propósito |
|---|---|---|---|
| Reunión / Llamada | Vendedor | Oportunidad | Registro de contacto con el cliente |
| Tarea interna | Vendedor | Oportunidad | Solicitud a Compras para ítems nuevos |
Vínculo Activity ↔ Oportunidad (campos clave)¶
{
"SalesOpportunityId": 142, // vínculo a OOPR — obligatorio
"SalesOpportunityLine": 1, // línea de etapa específica
"HandledBy": 115, // empleado asignado
"ActivityType": "task", // meeting / call / task / email
"Status": "open" // open / closed
}
Regla crítica¶
Si
SalesOpportunityIdno está seteado, la Activity no aparece en el timeline de la oportunidad. Toda Activity relacionada con una oportunidad debe crearse desde dentro de la oportunidad.
8. Timeline unificado¶
Estructura de nodos por ciclo completo¶
OOPR/142 ← nota inicial del vendedor al abrir
OACT/365 ← reunión con el cliente + archivos compartidos
OACT/366 ← tarea interna a Compras (solo si hay ítems nuevos)
OQUT/90 ← cotización draft + toda la negociación
ORDR/201 ← orden de venta (solo si ganado)
Cómo se ve en la UI¶
Cada nodo es un grupo en el timeline con: - Badge de entidad (código + número, color por tipo) - Título descriptivo - Hilo de comentarios Firebase anclado a esa entidad - Link "Ver documento →" que abre el documento SAP
Arquitectura de construcción (NestJS)¶
// OpportunityLinksService
async getLinkedEntities(opportunityId: number) {
const opp = await sapClient.get(
`SalesOpportunities(${opportunityId})?$expand=SalesOpportunitiesLines,Activities`
);
return buildNodeIndex(opp); // { OOPR, OACT[], OQUT[], ORDR[] }
}
// TimelineService
async buildTimeline(opportunityId: number) {
const nodes = await this.getLinkedEntities(opportunityId);
const comments = await Promise.all(
nodes.map(n => firebase.get(`comments/${n.entityCode}/${n.recordId}`))
);
return mergeChronological(nodes, comments);
}
9. Reglas operativas¶
-
Todo documento se crea desde la oportunidad. No se generan Quotations ni Activities por fuera de OOPR. Un documento creado por fuera no aparece en el timeline.
-
La negociación ocurre en el hilo del Quotation. No en Notion, no en WhatsApp. El hilo del OQUT es la fuente de verdad.
-
Cada paso requiere al menos un comentario. No es válido avanzar sin registrar qué ocurrió y cuál es el próximo paso.
-
El motivo de rechazo es obligatorio al cerrar como perdida. Sin
ReasonForClosingel sistema no permite cambiarStatus = sos_Missed. -
Una oportunidad perdida nunca se elimina. Queda como historial del cliente para futuras oportunidades.
-
El Quotation draft (líneas de texto) es orientativo, no formal. El documento con valor financiero formal es el Quotation con ítems reales, generado solo después de la aceptación del cliente.
10. Meta-plan de documentación¶
Bloque A — Oportunidad¶
| # | Artefacto | Formato | Contenido | Estado |
|---|---|---|---|---|
| A1 | Diagrama de flujo principal | Mermaid flowchart | Pasos 1→6, bifurcaciones, responsable por paso | ⏳ pendiente |
| A2 | Diagrama de estados | Mermaid stateDiagram | Estados OOPR: Open → Won / Missed | ⏳ pendiente |
| A3 | Diagrama de entidades | Mermaid erDiagram | OOPR + OQUT + ORDR + OACT. Campos clave | ⏳ pendiente |
| A4 | Tabla de campos SAP | MD table | Por entidad: campo, tipo, quién, cuándo, default | ✅ en este doc (sección 3) |
| A5 | Reglas y restricciones | MD lista | Qué bloquea avanzar, qué es obligatorio | ✅ en este doc (sección 9) |
Bloque B — Activity¶
| # | Artefacto | Formato | Contenido | Estado |
|---|---|---|---|---|
| B1 | Diagrama de flujo Activity | Mermaid flowchart | Creación → asignación → ejecución → cierre | ⏳ pendiente |
| B2 | Diagrama de estados | Mermaid stateDiagram | Open / In Progress / Closed / Cancelled | ⏳ pendiente |
| B3 | Tabla de tipos de Activity | MD table | Reunión, Llamada, Tarea. Campos por tipo | ⏳ pendiente |
| B4 | Vínculo Activity ↔ Oportunidad | MD + snippet | SalesOpportunityId + Line, qué rompe el vínculo | ✅ en este doc (sección 7) |
Bloque C — Comentarios (transversal)¶
| # | Artefacto | Formato | Contenido | Estado |
|---|---|---|---|---|
| C1 | Diagrama estructura Firebase | Mermaid graph | Esquema de rutas por entidad | ⏳ pendiente |
| C2 | Tabla de contextos de comentario | MD table | Entidad + quién comenta + en qué paso | ✅ en este doc (sección 4) |
Orden sugerido de producción¶
A1 → A2 (flujo y estados — base conceptual)
A3 → A4 (entidades — base técnica) ← A4 ya está
A5 (restricciones) ← A5 ya está
B1 → B2 (flujo y estados de Activity)
B3 → B4 (detalle técnico y vínculo) ← B4 ya está
C1 → C2 (comentarios) ← C2 ya está
11. Prompts UI generados¶
Los siguientes prompts están listos para pasar a un agente de UI (v0, Bolt, Cursor, Lovable):
| # | Pantalla | Descripción |
|---|---|---|
| P1 | Pipeline Gerencial | Dashboard con KPIs, kanban 3 columnas, tabla y feed de actividad |
| P2 | Detalle de Oportunidad + Timeline | Header SAP, barra de etapas, timeline unificado con hilos Firebase |
| P3 | Cotización + Panel de Negociación | Documento OQUT izquierda + hilo de comentarios derecha |
| P4 | Vista Móvil del Vendedor | Frame iPhone, oportunidad activa, timeline compacto, acciones rápidas |
| P5 | Alta de Oportunidad (simplificada) | Card único, 5 campos, alta en < 60 segundos |
| P6 | Tarea de Compras | Solicitud del vendedor + carga de precios + margen en tiempo real |
Los textos completos de los prompts están en la conversación original. Pendiente: exportarlos a archivos
.txtindividuales para uso offline.
12. Puntos abiertos¶
| # | Pregunta | Bloquea |
|---|---|---|
| 1 | ¿Cuáles son las etapas definitivas y sus % de cierre? | A1, A2, P1 |
| 2 | ¿Cómo se notifica a Compras al crear la tarea? ¿Push, email o ambos? | B1, P6 |
| 3 | ¿El vendedor ve el precio de costo al generar el Quotation? | Regla de margen |
| 4 | ¿Existe margen mínimo requerido? ¿Quién aprueba excepciones? | A5 |
| 5 | ¿Cuáles son los motivos de pérdida definitivos a configurar en SAP? | A1, A5 |
| 6 | ¿Cómo accede el cliente al portal de comentarios? ¿Login o token? | C1, P3 |
| 7 | ¿Qué pasa con el Quotation draft al formalizar post-aceptación? ¿Se cancela o se reemplaza? | A1 |
| 8 | ¿Hay umbral de días sin actividad que marque una oportunidad como inactiva en el dashboard? | P1 |
Compulandia · Análisis interno · No distribuir · v0.3