erp-core/docs/04-modelado/trazabilidad/TRACEABILITY-MGN-014.yaml

1093 lines
41 KiB
YAML

# TRACEABILITY-MGN-014.yaml
# Matriz de Trazabilidad - MGN-014: Mensajería y Notificaciones
# Fecha: 2025-11-24
# Versión: 1.0
module:
id: MGN-014
name: "Mensajería y Notificaciones"
description: "Sistema Chatter (mail.thread), notificaciones in-app y email, tracking de cambios, actividades, followers y templates"
priority: P0
story_points: 47
status: Diseñado
metadata:
total_rf: 6
total_et_backend: 6
total_et_frontend: 6
total_tables: 6
total_tests: 120
coverage: 100%
requirements:
- rf_id: RF-MGN-014-001
rf_title: "Sistema de Mensajes (Chatter)"
rf_file: "requerimientos-funcionales/mgn-014/RF-MGN-014-001-sistema-de-mensajes-chatter.md"
priority: P0
story_points: 13
et_backend:
file: "especificaciones-tecnicas/backend/mgn-014/ET-BACKEND-MGN-014-001-sistema-de-mensajes-chatter.md"
endpoints:
- method: POST
path: /api/v1/messages
description: "Publicar mensaje"
- method: GET
path: /api/v1/messages
description: "Listar mensajes"
- method: GET
path: /api/v1/messages/:id
description: "Obtener mensaje por ID"
- method: DELETE
path: /api/v1/messages/:id
description: "Eliminar mensaje"
- method: POST
path: /api/v1/messages/:id/reply
description: "Responder mensaje"
services:
- name: "MessageService"
file: "src/modules/messaging/services/message.service.ts"
methods:
- create
- findAll
- findOne
- remove
- reply
- validateBusinessRules
controllers:
- name: "MessageController"
file: "src/modules/messaging/controllers/message.controller.ts"
dtos:
- name: "CreateMessageDto"
file: "src/modules/messaging/dto/create-message.dto.ts"
- name: "MessageResponseDto"
file: "src/modules/messaging/dto/message-response.dto.ts"
- name: "FilterMessageDto"
file: "src/modules/messaging/dto/filter-message.dto.ts"
- name: "ReplyMessageDto"
file: "src/modules/messaging/dto/reply-message.dto.ts"
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-014/ET-FRONTEND-MGN-014-001-sistema-de-mensajes-chatter.md"
routes:
- path: "/messages"
component: "MessagesPage"
- path: "/*/:modelId/chatter"
component: "ChatterWidget"
components:
- name: "ChatterWidget"
file: "src/widgets/chatter-widget/ui/ChatterWidget.tsx"
type: widget
- name: "MessageComposer"
file: "src/features/message-composer/ui/MessageComposer.tsx"
type: feature
- name: "MessageCard"
file: "src/entities/message/ui/MessageCard.tsx"
type: entity
- name: "MessagesPage"
file: "src/pages/messaging/MessagesPage.tsx"
type: page
api_client:
- name: "messageApi"
file: "src/entities/message/api/message.api.ts"
methods:
- getAll
- getById
- create
- reply
- delete
state_management:
- name: "useMessageStore"
file: "src/entities/message/model/message.store.ts"
type: zustand
- name: "useMessages"
file: "src/entities/message/api/message.queries.ts"
type: react-query
database_tables:
- schema: system
table: messages
file: "database-design/schemas/system-schema-ddl.sql"
operations:
- SELECT
- INSERT
- UPDATE
- DELETE
indices:
- idx_messages_tenant_id
- idx_messages_res_model_res_id
- idx_messages_author_id
- idx_messages_message_type
rls_policy: tenant_isolation_messages
- schema: system
table: attachments
file: "database-design/schemas/system-schema-ddl.sql"
operations:
- SELECT
- INSERT
- DELETE
indices:
- idx_attachments_message_id
- idx_attachments_res_model_res_id
rls_policy: tenant_isolation_attachments
tests:
backend:
unit_tests:
- file: "src/modules/messaging/services/message.service.spec.ts"
test_cases:
- "should post message to any model"
- "should support message types (comment, notification, email)"
- "should attach files to message"
- "should reply to message"
- "should notify followers on new message"
integration_tests:
- file: "test/messaging/message.controller.e2e-spec.ts"
test_cases:
- "POST /api/v1/messages should post message"
- "GET /api/v1/messages should return messages"
- "GET /api/v1/messages/:id should return message"
- "DELETE /api/v1/messages/:id should delete message"
- "POST /api/v1/messages/:id/reply should reply"
- "should enforce tenant isolation"
- "should require authentication"
- "should check permissions"
frontend:
component_tests:
- file: "src/widgets/chatter-widget/ui/ChatterWidget.test.tsx"
test_cases:
- "should render message thread"
- "should post new message"
- "should reply to message"
- file: "src/features/message-composer/ui/MessageComposer.test.tsx"
test_cases:
- "should compose message"
- "should attach files"
- "should mention users"
e2e_tests:
- file: "e2e/messaging/chatter.spec.ts"
test_cases:
- "should post message successfully"
- "should reply to message"
- "should attach files"
- "should enforce permissions"
acceptance_criteria:
- id: AC-001
description: "Usuario puede publicar mensajes en cualquier registro (mail.thread)"
status: Pending
test_reference: "test/messaging/message.controller.e2e-spec.ts:35"
- id: AC-002
description: "Mensajes soportan adjuntos de archivos"
status: Pending
test_reference: "src/modules/messaging/services/message.service.spec.ts:72"
- id: AC-003
description: "Sistema notifica a followers automáticamente"
status: Pending
test_reference: "e2e/messaging/chatter.spec.ts:108"
business_rules:
- id: RN-001
description: "Mensaje debe estar asociado a un modelo y registro (res_model, res_id)"
implementation: "src/modules/messaging/dto/create-message.dto.ts:@IsNotEmpty()"
test_reference: "src/modules/messaging/services/message.service.spec.ts:95"
- id: RN-002
description: "Tipos de mensaje: comment, notification, email, note"
implementation: "database-design/schemas/system-schema-ddl.sql:messages.message_type"
test_reference: "src/modules/messaging/services/message.service.spec.ts:122"
- id: RN-003
description: "Nuevo mensaje notifica a todos los followers del registro"
implementation: "src/modules/messaging/services/message.service.ts:create()"
test_reference: "src/modules/messaging/services/message.service.spec.ts:148"
dependencies:
rf_dependencies: []
module_dependencies:
- MGN-001
external_dependencies:
- name: "@nestjs/common"
version: "^10.0.0"
- name: "multer"
version: "^1.4.5-lts.1"
- rf_id: RF-MGN-014-002
rf_title: "Notificaciones In-App y Email"
rf_file: "requerimientos-funcionales/mgn-014/RF-MGN-014-002-notificaciones-in-app-y-email.md"
priority: P0
story_points: 8
et_backend:
file: "especificaciones-tecnicas/backend/mgn-014/ET-BACKEND-MGN-014-002-notificaciones-in-app-y-email.md"
endpoints:
- method: GET
path: /api/v1/notifications
description: "Listar notificaciones del usuario"
- method: GET
path: /api/v1/notifications/unread-count
description: "Contar notificaciones no leídas"
- method: POST
path: /api/v1/notifications/:id/mark-read
description: "Marcar como leída"
- method: POST
path: /api/v1/notifications/mark-all-read
description: "Marcar todas como leídas"
- method: DELETE
path: /api/v1/notifications/:id
description: "Eliminar notificación"
services:
- name: "NotificationService"
file: "src/modules/messaging/services/notification.service.ts"
methods:
- findAll
- getUnreadCount
- markRead
- markAllRead
- remove
- send
controllers:
- name: "NotificationController"
file: "src/modules/messaging/controllers/notification.controller.ts"
dtos:
- name: "NotificationResponseDto"
file: "src/modules/messaging/dto/notification-response.dto.ts"
- name: "FilterNotificationDto"
file: "src/modules/messaging/dto/filter-notification.dto.ts"
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-014/ET-FRONTEND-MGN-014-002-notificaciones-in-app-y-email.md"
routes:
- path: "/notifications"
component: "NotificationsPage"
components:
- name: "NotificationBell"
file: "src/widgets/notification-bell/ui/NotificationBell.tsx"
type: widget
- name: "NotificationList"
file: "src/features/notification-list/ui/NotificationList.tsx"
type: feature
- name: "NotificationCard"
file: "src/entities/notification/ui/NotificationCard.tsx"
type: entity
- name: "NotificationsPage"
file: "src/pages/messaging/NotificationsPage.tsx"
type: page
api_client:
- name: "notificationApi"
file: "src/entities/notification/api/notification.api.ts"
methods:
- getAll
- getUnreadCount
- markRead
- markAllRead
- delete
state_management:
- name: "useNotificationStore"
file: "src/entities/notification/model/notification.store.ts"
type: zustand
- name: "useNotifications"
file: "src/entities/notification/api/notification.queries.ts"
type: react-query
database_tables:
- schema: system
table: notifications
file: "database-design/schemas/system-schema-ddl.sql"
operations:
- SELECT
- INSERT
- UPDATE
- DELETE
indices:
- idx_notifications_tenant_id
- idx_notifications_user_id
- idx_notifications_is_read
- idx_notifications_created_at
rls_policy: tenant_isolation_notifications
tests:
backend:
unit_tests:
- file: "src/modules/messaging/services/notification.service.spec.ts"
test_cases:
- "should send in-app notification"
- "should send email notification"
- "should mark notification as read"
- "should get unread count"
- "should delete old notifications"
integration_tests:
- file: "test/messaging/notification.controller.e2e-spec.ts"
test_cases:
- "GET /api/v1/notifications should return notifications"
- "GET /api/v1/notifications/unread-count should return count"
- "POST /api/v1/notifications/:id/mark-read should mark read"
- "POST /api/v1/notifications/mark-all-read should mark all"
- "DELETE /api/v1/notifications/:id should delete"
- "should enforce tenant isolation"
- "should require authentication"
frontend:
component_tests:
- file: "src/widgets/notification-bell/ui/NotificationBell.test.tsx"
test_cases:
- "should show unread count badge"
- "should open notification dropdown"
- "should mark as read on click"
- file: "src/features/notification-list/ui/NotificationList.test.tsx"
test_cases:
- "should render notifications"
- "should mark all as read"
- "should navigate to related record"
e2e_tests:
- file: "e2e/messaging/notifications.spec.ts"
test_cases:
- "should receive notification"
- "should mark as read"
- "should navigate to record"
- "should receive email notification"
acceptance_criteria:
- id: AC-001
description: "Usuario recibe notificaciones in-app en tiempo real"
status: Pending
test_reference: "test/messaging/notification.controller.e2e-spec.ts:42"
- id: AC-002
description: "Notificaciones se envían también por email según preferencias"
status: Pending
test_reference: "src/modules/messaging/services/notification.service.spec.ts:78"
- id: AC-003
description: "Badge muestra contador de notificaciones no leídas"
status: Pending
test_reference: "src/widgets/notification-bell/ui/NotificationBell.test.tsx:115"
business_rules:
- id: RN-001
description: "Notificaciones se entregan por canal: in-app, email o ambos"
implementation: "src/modules/messaging/services/notification.service.ts:send()"
test_reference: "src/modules/messaging/services/notification.service.spec.ts:102"
- id: RN-002
description: "Usuario puede configurar preferencias de notificación"
implementation: "database-design/schemas/auth-schema-ddl.sql:users.notification_preferences"
test_reference: "src/modules/messaging/services/notification.service.spec.ts:128"
- id: RN-003
description: "Notificaciones in-app se eliminan automáticamente después de 30 días"
implementation: "src/modules/messaging/services/notification.service.ts:cleanupOld()"
test_reference: "src/modules/messaging/services/notification.service.spec.ts:155"
dependencies:
rf_dependencies: []
module_dependencies:
- MGN-001
external_dependencies:
- name: "nodemailer"
version: "^6.9.0"
- name: "socket.io"
version: "^4.6.0"
- rf_id: RF-MGN-014-003
rf_title: "Tracking Automático de Cambios"
rf_file: "requerimientos-funcionales/mgn-014/RF-MGN-014-003-tracking-automático-de-cambios.md"
priority: P0
story_points: 8
et_backend:
file: "especificaciones-tecnicas/backend/mgn-014/ET-BACKEND-MGN-014-003-tracking-automático-de-cambios.md"
endpoints:
- method: GET
path: /api/v1/tracking/changes
description: "Listar cambios de un registro"
- method: GET
path: /api/v1/tracking/changes/:id
description: "Obtener cambio por ID"
- method: GET
path: /api/v1/tracking/audit-log
description: "Audit log del sistema"
services:
- name: "TrackingService"
file: "src/modules/messaging/services/tracking.service.ts"
methods:
- trackChange
- findChanges
- findOne
- getAuditLog
- generateMessage
controllers:
- name: "TrackingController"
file: "src/modules/messaging/controllers/tracking.controller.ts"
dtos:
- name: "ChangeResponseDto"
file: "src/modules/messaging/dto/change-response.dto.ts"
- name: "FilterChangeDto"
file: "src/modules/messaging/dto/filter-change.dto.ts"
- name: "AuditLogDto"
file: "src/modules/messaging/dto/audit-log.dto.ts"
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-014/ET-FRONTEND-MGN-014-003-tracking-automático-de-cambios.md"
routes:
- path: "/tracking/audit-log"
component: "AuditLogPage"
- path: "/*/:modelId/history"
component: "RecordHistoryWidget"
components:
- name: "RecordHistoryWidget"
file: "src/widgets/record-history-widget/ui/RecordHistoryWidget.tsx"
type: widget
- name: "ChangeTimeline"
file: "src/features/change-timeline/ui/ChangeTimeline.tsx"
type: feature
- name: "ChangeCard"
file: "src/entities/change/ui/ChangeCard.tsx"
type: entity
- name: "AuditLogPage"
file: "src/pages/tracking/AuditLogPage.tsx"
type: page
api_client:
- name: "trackingApi"
file: "src/entities/tracking/api/tracking.api.ts"
methods:
- getChanges
- getById
- getAuditLog
state_management:
- name: "useTrackingStore"
file: "src/entities/tracking/model/tracking.store.ts"
type: zustand
- name: "useTracking"
file: "src/entities/tracking/api/tracking.queries.ts"
type: react-query
database_tables:
- schema: system
table: chatter_logs
file: "database-design/schemas/system-schema-ddl.sql"
operations:
- SELECT
- INSERT
indices:
- idx_chatter_logs_tenant_id
- idx_chatter_logs_res_model_res_id
- idx_chatter_logs_user_id
- idx_chatter_logs_created_at
rls_policy: tenant_isolation_chatter_logs
tests:
backend:
unit_tests:
- file: "src/modules/messaging/services/tracking.service.spec.ts"
test_cases:
- "should track field changes automatically"
- "should generate readable change message"
- "should store old and new values"
- "should track create/update/delete operations"
- "should filter sensitive fields"
integration_tests:
- file: "test/messaging/tracking.controller.e2e-spec.ts"
test_cases:
- "GET /api/v1/tracking/changes should return changes"
- "GET /api/v1/tracking/changes/:id should return change"
- "GET /api/v1/tracking/audit-log should return audit log"
- "should track changes on entity updates"
- "should enforce tenant isolation"
- "should require authentication"
- "should check permissions"
frontend:
component_tests:
- file: "src/widgets/record-history-widget/ui/RecordHistoryWidget.test.tsx"
test_cases:
- "should render change history"
- "should show field changes"
- "should highlight modified fields"
- file: "src/features/change-timeline/ui/ChangeTimeline.test.tsx"
test_cases:
- "should render timeline"
- "should filter by date"
- "should filter by user"
e2e_tests:
- file: "e2e/tracking/changes.spec.ts"
test_cases:
- "should track record updates"
- "should view change history"
- "should view audit log"
- "should enforce permissions"
acceptance_criteria:
- id: AC-001
description: "Sistema registra automáticamente cambios en registros"
status: Pending
test_reference: "test/messaging/tracking.controller.e2e-spec.ts:45"
- id: AC-002
description: "Historial muestra qué cambió, cuándo y quién lo cambió"
status: Pending
test_reference: "src/modules/messaging/services/tracking.service.spec.ts:82"
- id: AC-003
description: "Cambios se muestran en formato legible en chatter"
status: Pending
test_reference: "e2e/tracking/changes.spec.ts:118"
business_rules:
- id: RN-001
description: "Solo campos tracked se registran en historial"
implementation: "src/modules/messaging/services/tracking.service.ts:trackChange()"
test_reference: "src/modules/messaging/services/tracking.service.spec.ts:105"
- id: RN-002
description: "Campos sensibles (passwords) no se registran"
implementation: "src/modules/messaging/services/tracking.service.ts:filterSensitive()"
test_reference: "src/modules/messaging/services/tracking.service.spec.ts:132"
- id: RN-003
description: "Cambios generan mensaje automático en chatter"
implementation: "src/modules/messaging/services/tracking.service.ts:generateMessage()"
test_reference: "src/modules/messaging/services/tracking.service.spec.ts:158"
dependencies:
rf_dependencies:
- RF-MGN-014-001
module_dependencies:
- MGN-001
external_dependencies:
- name: "@nestjs/common"
version: "^10.0.0"
- rf_id: RF-MGN-014-004
rf_title: "Actividades Programadas"
rf_file: "requerimientos-funcionales/mgn-014/RF-MGN-014-004-actividades-programadas.md"
priority: P1
story_points: 8
et_backend:
file: "especificaciones-tecnicas/backend/mgn-014/ET-BACKEND-MGN-014-004-actividades-programadas.md"
endpoints:
- method: POST
path: /api/v1/activities
description: "Crear actividad"
- method: GET
path: /api/v1/activities
description: "Listar actividades"
- method: GET
path: /api/v1/activities/:id
description: "Obtener actividad por ID"
- method: PUT
path: /api/v1/activities/:id
description: "Actualizar actividad"
- method: POST
path: /api/v1/activities/:id/complete
description: "Marcar como completada"
services:
- name: "ActivityService"
file: "src/modules/messaging/services/activity.service.ts"
methods:
- create
- findAll
- findOne
- update
- complete
- validateBusinessRules
controllers:
- name: "ActivityController"
file: "src/modules/messaging/controllers/activity.controller.ts"
dtos:
- name: "CreateActivityDto"
file: "src/modules/messaging/dto/create-activity.dto.ts"
- name: "UpdateActivityDto"
file: "src/modules/messaging/dto/update-activity.dto.ts"
- name: "ActivityResponseDto"
file: "src/modules/messaging/dto/activity-response.dto.ts"
- name: "FilterActivityDto"
file: "src/modules/messaging/dto/filter-activity.dto.ts"
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-014/ET-FRONTEND-MGN-014-004-actividades-programadas.md"
routes:
- path: "/activities"
component: "ActivitiesPage"
- path: "/activities/:id"
component: "ViewActivityPage"
components:
- name: "ActivitiesCalendar"
file: "src/widgets/activities-calendar/ui/ActivitiesCalendar.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: "ActivitiesPage"
file: "src/pages/activities/ActivitiesPage.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: system
table: activities
file: "database-design/schemas/system-schema-ddl.sql"
operations:
- SELECT
- INSERT
- UPDATE
- DELETE (soft)
indices:
- idx_activities_tenant_id
- idx_activities_res_model_res_id
- idx_activities_assigned_to
- idx_activities_due_date
rls_policy: tenant_isolation_activities
tests:
backend:
unit_tests:
- file: "src/modules/messaging/services/activity.service.spec.ts"
test_cases:
- "should create activity linked to record"
- "should send notification on activity assignment"
- "should complete activity successfully"
- "should send reminder before due date"
- "should find overdue activities"
integration_tests:
- file: "test/messaging/activity.controller.e2e-spec.ts"
test_cases:
- "POST /api/v1/activities should create activity"
- "GET /api/v1/activities should return activities"
- "GET /api/v1/activities/:id should return activity"
- "PUT /api/v1/activities/:id should update activity"
- "POST /api/v1/activities/:id/complete should complete"
- "should enforce tenant isolation"
- "should require authentication"
- "should check permissions"
frontend:
component_tests:
- file: "src/widgets/activities-calendar/ui/ActivitiesCalendar.test.tsx"
test_cases:
- "should render calendar view"
- "should show activities by date"
- "should create activity from calendar"
- 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/activities/activities.spec.ts"
test_cases:
- "should create activity successfully"
- "should complete activity"
- "should receive reminders"
- "should enforce permissions"
acceptance_criteria:
- id: AC-001
description: "Usuario puede crear actividades vinculadas a cualquier registro"
status: Pending
test_reference: "test/messaging/activity.controller.e2e-spec.ts:48"
- id: AC-002
description: "Sistema envía recordatorios antes de fecha límite"
status: Pending
test_reference: "src/modules/messaging/services/activity.service.spec.ts:85"
- id: AC-003
description: "Actividades se muestran en calendario y lista"
status: Pending
test_reference: "src/widgets/activities-calendar/ui/ActivitiesCalendar.test.tsx:122"
business_rules:
- id: RN-001
description: "Actividad debe estar vinculada a un registro (res_model, res_id)"
implementation: "src/modules/messaging/dto/create-activity.dto.ts:@IsNotEmpty()"
test_reference: "src/modules/messaging/services/activity.service.spec.ts:108"
- id: RN-002
description: "Recordatorio se envía 1 día antes de due_date"
implementation: "src/modules/messaging/services/activity.service.ts:sendReminders()"
test_reference: "src/modules/messaging/services/activity.service.spec.ts:135"
- id: RN-003
description: "Actividad completada genera mensaje en chatter"
implementation: "src/modules/messaging/services/activity.service.ts:complete()"
test_reference: "src/modules/messaging/services/activity.service.spec.ts:162"
dependencies:
rf_dependencies:
- RF-MGN-014-001
- RF-MGN-014-002
module_dependencies:
- MGN-001
external_dependencies:
- name: "@nestjs/schedule"
version: "^4.0.0"
- rf_id: RF-MGN-014-005
rf_title: "Followers (Seguidores)"
rf_file: "requerimientos-funcionales/mgn-014/RF-MGN-014-005-followers-seguidores.md"
priority: P1
story_points: 5
et_backend:
file: "especificaciones-tecnicas/backend/mgn-014/ET-BACKEND-MGN-014-005-followers-seguidores.md"
endpoints:
- method: POST
path: /api/v1/followers
description: "Agregar follower a registro"
- method: GET
path: /api/v1/followers
description: "Listar followers de un registro"
- method: DELETE
path: /api/v1/followers/:id
description: "Eliminar follower"
- method: POST
path: /api/v1/followers/subscribe
description: "Seguir registro"
- method: POST
path: /api/v1/followers/unsubscribe
description: "Dejar de seguir registro"
services:
- name: "FollowerService"
file: "src/modules/messaging/services/follower.service.ts"
methods:
- addFollower
- findFollowers
- removeFollower
- subscribe
- unsubscribe
- notifyFollowers
controllers:
- name: "FollowerController"
file: "src/modules/messaging/controllers/follower.controller.ts"
dtos:
- name: "AddFollowerDto"
file: "src/modules/messaging/dto/add-follower.dto.ts"
- name: "FollowerResponseDto"
file: "src/modules/messaging/dto/follower-response.dto.ts"
- name: "SubscribeDto"
file: "src/modules/messaging/dto/subscribe.dto.ts"
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-014/ET-FRONTEND-MGN-014-005-followers-seguidores.md"
routes:
- path: "/*/:modelId/followers"
component: "FollowersWidget"
components:
- name: "FollowersWidget"
file: "src/widgets/followers-widget/ui/FollowersWidget.tsx"
type: widget
- name: "AddFollowerButton"
file: "src/features/add-follower/ui/AddFollowerButton.tsx"
type: feature
- name: "FollowerCard"
file: "src/entities/follower/ui/FollowerCard.tsx"
type: entity
- name: "FollowersPage"
file: "src/pages/followers/FollowersPage.tsx"
type: page
api_client:
- name: "followerApi"
file: "src/entities/follower/api/follower.api.ts"
methods:
- getFollowers
- addFollower
- removeFollower
- subscribe
- unsubscribe
state_management:
- name: "useFollowerStore"
file: "src/entities/follower/model/follower.store.ts"
type: zustand
- name: "useFollowers"
file: "src/entities/follower/api/follower.queries.ts"
type: react-query
database_tables:
- schema: system
table: message_followers
file: "database-design/schemas/system-schema-ddl.sql"
operations:
- SELECT
- INSERT
- DELETE
indices:
- idx_message_followers_tenant_id
- idx_message_followers_res_model_res_id
- idx_message_followers_user_id
rls_policy: tenant_isolation_message_followers
tests:
backend:
unit_tests:
- file: "src/modules/messaging/services/follower.service.spec.ts"
test_cases:
- "should add follower to record"
- "should remove follower from record"
- "should notify followers on updates"
- "should prevent duplicate followers"
- "should list followers of record"
integration_tests:
- file: "test/messaging/follower.controller.e2e-spec.ts"
test_cases:
- "POST /api/v1/followers should add follower"
- "GET /api/v1/followers should return followers"
- "DELETE /api/v1/followers/:id should remove follower"
- "POST /api/v1/followers/subscribe should subscribe"
- "POST /api/v1/followers/unsubscribe should unsubscribe"
- "should enforce tenant isolation"
- "should require authentication"
frontend:
component_tests:
- file: "src/widgets/followers-widget/ui/FollowersWidget.test.tsx"
test_cases:
- "should render followers list"
- "should add follower"
- "should remove follower"
- file: "src/features/add-follower/ui/AddFollowerButton.test.tsx"
test_cases:
- "should show user selector"
- "should add selected users"
- "should subscribe self"
e2e_tests:
- file: "e2e/followers/followers.spec.ts"
test_cases:
- "should add follower successfully"
- "should receive notifications as follower"
- "should unsubscribe from record"
- "should enforce permissions"
acceptance_criteria:
- id: AC-001
description: "Usuario puede seguir cualquier registro para recibir actualizaciones"
status: Pending
test_reference: "test/messaging/follower.controller.e2e-spec.ts:52"
- id: AC-002
description: "Followers reciben notificaciones de mensajes y cambios"
status: Pending
test_reference: "src/modules/messaging/services/follower.service.spec.ts:88"
- id: AC-003
description: "Usuario puede dejar de seguir registro en cualquier momento"
status: Pending
test_reference: "e2e/followers/followers.spec.ts:125"
business_rules:
- id: RN-001
description: "Usuario no puede ser follower duplicado del mismo registro"
implementation: "database-design/schemas/system-schema-ddl.sql:CONSTRAINT uq_followers"
test_reference: "src/modules/messaging/services/follower.service.spec.ts:112"
- id: RN-002
description: "Creador de registro se convierte en follower automáticamente"
implementation: "src/modules/messaging/services/follower.service.ts:addCreatorAsFollower()"
test_reference: "src/modules/messaging/services/follower.service.spec.ts:138"
- id: RN-003
description: "Followers reciben notificación en cada mensaje nuevo"
implementation: "src/modules/messaging/services/message.service.ts:notifyFollowers()"
test_reference: "src/modules/messaging/services/follower.service.spec.ts:165"
dependencies:
rf_dependencies:
- RF-MGN-014-001
- RF-MGN-014-002
module_dependencies:
- MGN-001
external_dependencies:
- name: "@nestjs/common"
version: "^10.0.0"
- rf_id: RF-MGN-014-006
rf_title: "Templates de Email"
rf_file: "requerimientos-funcionales/mgn-014/RF-MGN-014-006-templates-de-email.md"
priority: P1
story_points: 5
et_backend:
file: "especificaciones-tecnicas/backend/mgn-014/ET-BACKEND-MGN-014-006-templates-de-email.md"
endpoints:
- method: POST
path: /api/v1/email-templates
description: "Crear template"
- method: GET
path: /api/v1/email-templates
description: "Listar templates"
- method: GET
path: /api/v1/email-templates/:id
description: "Obtener template por ID"
- method: PUT
path: /api/v1/email-templates/:id
description: "Actualizar template"
- method: POST
path: /api/v1/email-templates/:id/preview
description: "Vista previa del template"
services:
- name: "EmailTemplateService"
file: "src/modules/messaging/services/email-template.service.ts"
methods:
- create
- findAll
- findOne
- update
- preview
- render
controllers:
- name: "EmailTemplateController"
file: "src/modules/messaging/controllers/email-template.controller.ts"
dtos:
- name: "CreateEmailTemplateDto"
file: "src/modules/messaging/dto/create-email-template.dto.ts"
- name: "UpdateEmailTemplateDto"
file: "src/modules/messaging/dto/update-email-template.dto.ts"
- name: "EmailTemplateResponseDto"
file: "src/modules/messaging/dto/email-template-response.dto.ts"
- name: "PreviewTemplateDto"
file: "src/modules/messaging/dto/preview-template.dto.ts"
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-014/ET-FRONTEND-MGN-014-006-templates-de-email.md"
routes:
- path: "/email-templates"
component: "EmailTemplatesPage"
- path: "/email-templates/create"
component: "CreateEmailTemplatePage"
- path: "/email-templates/:id/edit"
component: "EditEmailTemplatePage"
components:
- name: "EmailTemplatesTable"
file: "src/widgets/email-templates-table/ui/EmailTemplatesTable.tsx"
type: widget
- name: "TemplateEditor"
file: "src/features/template-editor/ui/TemplateEditor.tsx"
type: feature
- name: "EmailTemplateCard"
file: "src/entities/email-template/ui/EmailTemplateCard.tsx"
type: entity
- name: "EmailTemplatesPage"
file: "src/pages/messaging/EmailTemplatesPage.tsx"
type: page
api_client:
- name: "emailTemplateApi"
file: "src/entities/email-template/api/email-template.api.ts"
methods:
- getAll
- getById
- create
- update
- preview
state_management:
- name: "useEmailTemplateStore"
file: "src/entities/email-template/model/email-template.store.ts"
type: zustand
- name: "useEmailTemplates"
file: "src/entities/email-template/api/email-template.queries.ts"
type: react-query
database_tables:
- schema: system
table: email_templates
file: "database-design/schemas/system-schema-ddl.sql"
operations:
- SELECT
- INSERT
- UPDATE
- DELETE (soft)
indices:
- idx_email_templates_tenant_id
- idx_email_templates_model
rls_policy: tenant_isolation_email_templates
tests:
backend:
unit_tests:
- file: "src/modules/messaging/services/email-template.service.spec.ts"
test_cases:
- "should create email template with variables"
- "should render template with data"
- "should validate template syntax"
- "should preview template"
- "should support HTML and plain text"
integration_tests:
- file: "test/messaging/email-template.controller.e2e-spec.ts"
test_cases:
- "POST /api/v1/email-templates should create template"
- "GET /api/v1/email-templates should return templates"
- "GET /api/v1/email-templates/:id should return template"
- "PUT /api/v1/email-templates/:id should update template"
- "POST /api/v1/email-templates/:id/preview should preview"
- "should enforce tenant isolation"
- "should require authentication"
- "should check permissions"
frontend:
component_tests:
- file: "src/widgets/email-templates-table/ui/EmailTemplatesTable.test.tsx"
test_cases:
- "should render templates"
- "should filter by model"
- "should preview template"
- file: "src/features/template-editor/ui/TemplateEditor.test.tsx"
test_cases:
- "should edit template HTML"
- "should insert variables"
- "should preview rendering"
e2e_tests:
- file: "e2e/messaging/email-templates.spec.ts"
test_cases:
- "should create template successfully"
- "should edit template successfully"
- "should preview with sample data"
- "should enforce permissions"
acceptance_criteria:
- id: AC-001
description: "Usuario puede crear templates con variables dinámicas"
status: Pending
test_reference: "test/messaging/email-template.controller.e2e-spec.ts:55"
- id: AC-002
description: "Templates soportan sintaxis Handlebars para variables"
status: Pending
test_reference: "src/modules/messaging/services/email-template.service.spec.ts:92"
- id: AC-003
description: "Sistema valida sintaxis al guardar template"
status: Pending
test_reference: "e2e/messaging/email-templates.spec.ts:128"
business_rules:
- id: RN-001
description: "Template debe estar asociado a un modelo (ej: invoice, quotation)"
implementation: "src/modules/messaging/dto/create-email-template.dto.ts:@IsNotEmpty()"
test_reference: "src/modules/messaging/services/email-template.service.spec.ts:115"
- id: RN-002
description: "Variables se reemplazan usando sintaxis {{ variable }}"
implementation: "src/modules/messaging/services/email-template.service.ts:render()"
test_reference: "src/modules/messaging/services/email-template.service.spec.ts:142"
- id: RN-003
description: "Template invalido no puede guardarse (validación sintaxis)"
implementation: "src/modules/messaging/services/email-template.service.ts:validateSyntax()"
test_reference: "src/modules/messaging/services/email-template.service.spec.ts:168"
dependencies:
rf_dependencies: []
module_dependencies:
- MGN-001
external_dependencies:
- name: "handlebars"
version: "^4.7.0"
- name: "nodemailer"
version: "^6.9.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: 30
total_components: 24
total_tables: 6
total_test_cases: 120
estimated_duration_sprints: 3