Jair Cedillo ce09e0d30a feat(fin): partidas de factura capturadas desde el catálogo de conceptos
El selector de concepto de la partida deja de ser una lista fija en el código y
se alimenta del catálogo de conceptos de la empresa: al elegir uno se manda
concept_id y el backend copia la descripción a la columna de texto libre que
consume el PDF. Si el concepto trae precio unitario, se precarga en la partida.

Las claves genéricas anteriores quedan en un segundo grupo del mismo selector,
marcadas como "sin clave del SAT", para no bloquear a las empresas que aún no
tienen catálogo; si está vacío se enlaza al alta de conceptos.

El listado de partidas etiqueta con la clave y descripción del catálogo cuando
la partida lo referencia, y cae al texto libre para las facturas anteriores.

Los tipos de Invoice e InvoiceItem se completan con las claves fiscales que el
backend ya devuelve.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 17:24:50 -05:00

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}/convert
  • GET|POST /pipelines, PATCH|DELETE /pipelines/{id}
  • GET|POST /stages, PATCH|DELETE /stages/{id}
  • GET|POST /opportunities, GET|PATCH|DELETE /opportunities/{id}, PATCH /opportunities/{id}/move
  • GET|POST /activities, GET|PATCH|DELETE /activities/{id}, PATCH /activities/{id}/complete
  • GET /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:5173CRM 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
Description
No description provided
Readme 2 MiB
Languages
Python 48.9%
Svelte 22.1%
TypeScript 14.8%
HTML 13.3%
Shell 0.5%
Other 0.2%