feat(crm): Cotizaciones/Cotizador vinculados (Fase B)

- Lista de cotizaciones muestra la Solicitud referenciada (QuoteResponse enriquecido
  con service_request_reference; columna con enlace a la solicitud).
- Cotizador vinculable a una solicitud (?service_request_id=) → prellena modo
  (mapper transport+load→RateMode), ruta, peso/dimensiones, etc.
- Cotizador vinculable a una cotización (?quote_id=) → botón "Agregar" por opción
  que crea el concepto de flete + cargos en la cotización y regresa a ella.
- Botón "Cotizador" en el detalle de la cotización (entrada vinculada).

Suite de cotizaciones en verde. svelte-check sin errores nuevos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ernesto Herrera
2026-08-07 07:43:53 -06:00
parent 8431132b10
commit b47dc542f2
6 changed files with 91 additions and 4 deletions

View File

@@ -85,6 +85,7 @@ class QuoteResponse(QuoteBase):
model_config = ConfigDict(from_attributes=True)
id: int
service_request_reference: str | None = None # folio de la solicitud referenciada
status: str
total_cost: Decimal
total_sale: Decimal

View File

@@ -73,7 +73,18 @@ def get_quotes(
query = query.filter(Quote.account_id == account_id)
if search:
query = query.filter(Quote.reference.ilike(f"%{search}%"))
return query.order_by(Quote.created_at.desc()).all()
quotes = query.order_by(Quote.created_at.desc()).all()
# Enriquecer con el folio de la solicitud referenciada (para verlo en la lista)
sr_ids = {q.service_request_id for q in quotes if q.service_request_id}
if sr_ids:
refs = dict(
db.query(ServiceRequest.id, ServiceRequest.reference)
.filter(ServiceRequest.id.in_(sr_ids))
.all()
)
for q in quotes:
q.service_request_reference = refs.get(q.service_request_id)
return quotes
def get_quote(db: Session, quote_id: int, tenant_id: int, company_id: int) -> Quote: