diff --git a/frontend/src/lib/api/crm/commercial.ts b/frontend/src/lib/api/crm/commercial.ts index a131db6..37bd255 100644 --- a/frontend/src/lib/api/crm/commercial.ts +++ b/frontend/src/lib/api/crm/commercial.ts @@ -157,7 +157,32 @@ export const quotesAPI = { reject: (id: number, companyId: number) => unwrap(api.patch(`/v1/crm/quotes/${id}/reject?${qp(companyId)}`, {})), clone: (id: number, companyId: number) => unwrap(api.post(`/v1/crm/quotes/${id}/clone?${qp(companyId)}`, {})), remove: (id: number, companyId: number) => unwrap(api.delete(`/v1/crm/quotes/${id}?${qp(companyId)}`)), - items: (quoteId: number, companyId: number) => unwrap(api.get(`/v1/crm/quotes/${quoteId}/items?${qp(companyId)}`)) + items: (quoteId: number, companyId: number) => unwrap(api.get(`/v1/crm/quotes/${quoteId}/items?${qp(companyId)}`)), + pdfUrl: (id: number, companyId: number) => unwrap<{ url: string }>(api.get(`/v1/crm/quotes/${id}/pdf-url?${qp(companyId)}`)), + sendEmail: (id: number, companyId: number, body: { to?: string | null; subject?: string | null; message?: string | null }) => + unwrap<{ sent_to: string; reference: string }>(api.post(`/v1/crm/quotes/${id}/send-email?${qp(companyId)}`, body)) +}; + +// ---------- Configuración de marca del formato de cotización ---------- +export interface QuoteSettings { + id?: number | null; + emitter_name?: string | null; emitter_rfc?: string | null; emitter_address?: string | null; + emitter_phone?: string | null; emitter_email?: string | null; emitter_website?: string | null; + logo_file_key?: string | null; accent_color?: string | null; quote_prefix?: string | null; + default_terms?: string | null; footer_note?: string | null; +} + +export const quoteSettingsAPI = { + get: (companyId: number) => unwrap(api.get(`/v1/crm/quote-settings?${qp(companyId)}`)), + save: (companyId: number, data: QuoteSettings) => unwrap(api.put(`/v1/crm/quote-settings?${qp(companyId)}`, data)), + logoUrl: (companyId: number) => unwrap<{ url: string | null }>(api.get(`/v1/crm/quote-settings/logo-url?${qp(companyId)}`)), + async uploadLogo(companyId: number, file: File): Promise { + const fd = new FormData(); + fd.append('file', file); + const res = await (api as any).request(`/v1/crm/quote-settings/logo?${qp(companyId)}`, { method: 'POST', body: fd }); + if (res.error) throw new Error(res.error); + return res.data as QuoteSettings; + } }; // ---------- Catálogos de referencia (Incoterms, participantes) ---------- diff --git a/frontend/src/lib/components/sidebar/modules.ts b/frontend/src/lib/components/sidebar/modules.ts index 8f85a92..8d91390 100644 --- a/frontend/src/lib/components/sidebar/modules.ts +++ b/frontend/src/lib/components/sidebar/modules.ts @@ -92,6 +92,10 @@ export function getNavMain(): NavMainItem[] { title: 'Configuración', url: '/dashboard/settings/general', icon: Settings2, + items: [ + { title: 'General', url: '/dashboard/settings/general' }, + { title: 'Formato de cotización', url: '/dashboard/settings/cotizacion' }, + ], }, ]; } diff --git a/frontend/src/routes/dashboard/crm/cotizaciones/[id]/+page.svelte b/frontend/src/routes/dashboard/crm/cotizaciones/[id]/+page.svelte index 0f39db5..585d5c6 100644 --- a/frontend/src/routes/dashboard/crm/cotizaciones/[id]/+page.svelte +++ b/frontend/src/routes/dashboard/crm/cotizaciones/[id]/+page.svelte @@ -1,5 +1,5 @@ + +
+
+

Formato de cotización

+

Marca y encabezados que se imprimen en el PDF de las cotizaciones (por compañía).

+
+ + {#if !companyId} + Selecciona una compañía activa. + {:else} + + Logo + +
+ {#if logoUrl} + Logo + {:else} +
Sin logo
+ {/if} + +
+
+
+ + + Datos del emisor + Aparecen en el encabezado del PDF. + + +
+ + + + + + + + +
+
+
+ + + Textos por defecto + +
+ + +
+
+
+ +
+ {/if} +