Cierra la decisión pendiente 7. create_item ya no copia solo la descripción del concepto: también hereda product_service_id, unit_of_measure_id y tax_object_id cuando el cliente no los envía, para que la partida capturada por catálogo quede completa para el CFDI. Lo que el cliente sí manda gana sobre el catálogo, para poder facturar con una unidad distinta a la del concepto. update_item pasa por la misma resolución cuando cambia concept_id: revalida que el concepto sea de la empresa (antes el PATCH no lo validaba y admitía apuntar a un concepto de otro tenant) y vuelve a heredar del concepto nuevo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CRM — Aduanasoft
Sistema CRM construido sobre la plantilla Workspace de Aduanasoft (SvelteKit 5 + FastAPI + PostgreSQL, multi-tenant con Keycloak/Hub). Gestiona cuentas, contactos, prospectos, oportunidades (pipeline Kanban) y actividades comerciales.
Base:
plantillas-proyectos. Este repo conserva el core de la plantilla (auth, tenants, permisos, licencias) y agrega el dominio CRM en backend y frontend.
Stack
| Capa | Tecnología |
|---|---|
| Frontend | SvelteKit 5 (runes) + Tailwind + shadcn-svelte |
| Backend | FastAPI + SQLAlchemy 2.0 + Pydantic v2 |
| Auth | Keycloak (OIDC) vía Workspace Hub |
| Base de datos | PostgreSQL (schema crm) |
| Cache / Queue | Valkey (Redis) + Celery |
| Storage | MinIO (S3-compatible) |
| Contenedores | Docker Compose |
Módulo CRM
Esquema dedicado crm con 7 tablas multi-tenant (tenant_id + company_id, soft delete):
| Entidad | Tabla | Descripción |
|---|---|---|
| Cuentas | crm.accounts |
Empresas cliente/prospecto (importador, IMMEX, agencia aduanal, transportista). Incluye RFC y patente aduanal. |
| Contactos | crm.contacts |
Personas asociadas a una cuenta. |
| Prospectos | crm.leads |
Leads sin calificar; se convierten en cuenta + contacto + oportunidad. |
| Embudos | crm.pipelines |
Embudos de venta por compañía. |
| Etapas | crm.pipeline_stages |
Columnas del Kanban (con probabilidad y etapas terminales ganada/perdida). |
| Oportunidades | crm.opportunities |
Negocios que avanzan por el embudo. |
| Actividades | crm.activities |
Llamadas, reuniones, tareas, correos y notas. |
Endpoints (prefijo /v1/crm)
Todos reciben company_id como query param y validan permisos vía Keycloak/PermissionService.
GET|POST /accounts,GET|PATCH|DELETE /accounts/{id}GET|POST /contacts,GET|PATCH|DELETE /contacts/{id}GET|POST /leads,GET|PATCH|DELETE /leads/{id},POST /leads/{id}/convertGET|POST /pipelines,PATCH|DELETE /pipelines/{id}GET|POST /stages,PATCH|DELETE /stages/{id}GET|POST /opportunities,GET|PATCH|DELETE /opportunities/{id},PATCH /opportunities/{id}/moveGET|POST /activities,GET|PATCH|DELETE /activities/{id},PATCH /activities/{id}/completeGET /metrics— KPIs y embudo por etapa para el dashboard
Permisos
Se registran en el PermissionRegistry al arrancar (25 permisos: crm.access +
crm.{account,contact,lead,opportunity,pipeline,activity}.{view,create,edit,delete}).
Para persistirlos en BD: POST /v1/core/permissions/sync (o el CLI de sincronización).
Inicio rápido (dev)
cp .env.example .env # ajustar CORE_DB_NAME, Keycloak, etc.
./scripts/auth-mode.sh local # login local sin workspace
docker compose up -d
Abre http://localhost:5173 → CRM en el sidebar.
Migraciones
cd backend
alembic upgrade head # crea el schema crm y sus tablas (revisión f1a2b3c4d5e6)
alembic downgrade -1 # revierte el CRM (down() probado)
Pruebas
Backend (servicios CRM):
cd backend
pytest tests/ -q # 24 pruebas de servicios (SQLite en memoria)
En CI/PostgreSQL, define TEST_DATABASE_URL para ejercitar el esquema real y las
políticas RLS (ver docs/ARCHITECTURE.md).
Estructura del CRM
backend/api/v1/modules/crm/
├── router.py # agrega submódulos bajo /crm (y registra permisos)
├── permissions.py # alta de permisos CRM en el registry
├── accounts/ contacts/ leads/ pipelines/ opportunities/ activities/ metrics/
│ └── models.py · dto.py · service.py · routes.py
backend/alembic/versions/f1a2b3c4d5e6_crm_schema.py
frontend/src/
├── lib/api/crm/ # clientes API tipados por entidad
├── lib/components/crm/ # helpers de formato/etiquetas
└── routes/dashboard/crm/
├── +page.svelte # panel (KPIs + embudo)
├── cuentas/ contactos/ prospectos/ actividades/ # CRUD
└── oportunidades/ # Kanban con drag & drop
Convenciones
- Commits: Conventional Commits (
feat:,fix:,refactor:,chore:) - Ramas:
feature/AS-###-desc,fix/AS-###-desc - Código: nombres en inglés, comentarios de negocio en español
- Backend: Pydantic v2, routers por dominio, filtros multi-tenant explícitos
- Frontend: runes (
$state,$derived,$effect,$props), Tailwind utility-first