feat(crm): PDF de cotización (formato maestro) + marca por tenant + envío por correo
- Modelo crm.quote_settings (emisor, logo, color, prefijo, términos) por compañía
+ columna quotes.pdf_file_key + migración con down().
- Generador PDF (formato maestro: emisor+logo, cliente, carga/ruta, costos,
resumen, condiciones); logo incrustado como JPEG (Pillow).
- Endpoints: GET /quotes/{id}/pdf-url, POST /quotes/{id}/send-email (adjunta PDF),
GET/PUT /quote-settings, POST /quote-settings/logo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -86,6 +86,7 @@ class QuoteResponse(QuoteBase):
|
||||
status: str
|
||||
total_cost: Decimal
|
||||
total_sale: Decimal
|
||||
pdf_file_key: str | None = None
|
||||
sent_at: datetime | None = None
|
||||
accepted_at: datetime | None = None
|
||||
rejected_at: datetime | None = None
|
||||
@@ -100,3 +101,30 @@ class QuoteResponse(QuoteBase):
|
||||
@property
|
||||
def margin(self) -> Decimal:
|
||||
return (self.total_sale or Decimal(0)) - (self.total_cost or Decimal(0))
|
||||
|
||||
|
||||
# ----- Configuración de marca del formato de cotización -----
|
||||
|
||||
class QuoteSettingsInput(BaseModel):
|
||||
emitter_name: str | None = Field(None, max_length=255)
|
||||
emitter_rfc: str | None = Field(None, max_length=13)
|
||||
emitter_address: str | None = None
|
||||
emitter_phone: str | None = Field(None, max_length=60)
|
||||
emitter_email: str | None = Field(None, max_length=255)
|
||||
emitter_website: str | None = Field(None, max_length=255)
|
||||
accent_color: str | None = Field(None, max_length=9)
|
||||
quote_prefix: str | None = Field(None, max_length=12)
|
||||
default_terms: str | None = None
|
||||
footer_note: str | None = None
|
||||
|
||||
|
||||
class QuoteSettingsResponse(QuoteSettingsInput):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
id: int | None = None
|
||||
logo_file_key: str | None = None
|
||||
|
||||
|
||||
class SendQuoteEmailRequest(BaseModel):
|
||||
to: str | None = None
|
||||
subject: str | None = None
|
||||
message: str | None = None
|
||||
|
||||
Reference in New Issue
Block a user