@codemind.ec/medusa-plugin-invoice 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/admin/index.js +512 -7
- package/.medusa/server/src/admin/index.mjs +513 -10
- package/.medusa/server/src/api/admin/invoice-config/route.js +26 -0
- package/.medusa/server/src/api/admin/invoice-templates/[id]/preview/route.js +119 -0
- package/.medusa/server/src/api/admin/invoice-templates/[id]/restore/route.js +21 -0
- package/.medusa/server/src/api/admin/invoice-templates/[id]/route.js +31 -0
- package/.medusa/server/src/api/admin/invoice-templates/route.js +20 -0
- package/.medusa/server/src/api/admin/invoice-templates/validators.js +18 -0
- package/.medusa/server/src/api/middlewares.js +36 -0
- package/.medusa/server/src/index.js +16 -1
- package/.medusa/server/src/modules/invoice-generator/errors.js +30 -0
- package/.medusa/server/src/modules/invoice-generator/index.js +15 -0
- package/.medusa/server/src/modules/invoice-generator/loaders/create-default-config.js +34 -0
- package/.medusa/server/src/modules/invoice-generator/migrations/Migration20250728094738.js +18 -0
- package/.medusa/server/src/modules/invoice-generator/migrations/Migration20250728124134.js +14 -0
- package/.medusa/server/src/modules/invoice-generator/migrations/Migration20250728151454.js +14 -0
- package/.medusa/server/src/modules/invoice-generator/migrations/Migration20260120005559.js +14 -0
- package/.medusa/server/src/modules/invoice-generator/migrations/Migration20260216191700.js +14 -0
- package/.medusa/server/src/modules/invoice-generator/migrations/Migration20260601120000.js +17 -0
- package/.medusa/server/src/modules/invoice-generator/models/invoice-config.js +16 -0
- package/.medusa/server/src/modules/invoice-generator/models/invoice-template.js +19 -0
- package/.medusa/server/src/modules/invoice-generator/models/invoice.js +17 -0
- package/.medusa/server/src/modules/invoice-generator/service.js +84 -0
- package/.medusa/server/src/modules/invoice-generator/templates/defaults/index.js +380 -0
- package/.medusa/server/src/modules/invoice-generator/templates/order-invoice.js +348 -0
- package/.medusa/server/src/modules/invoice-generator/templates/quote-proforma.js +468 -0
- package/.medusa/server/src/modules/invoice-generator/templates/strategy.js +110 -0
- package/LICENSE +21 -0
- package/README.md +204 -0
- package/index.d.ts +138 -0
- package/package.json +26 -2
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
7
|
+
const pdfmake_1 = __importDefault(require("pdfmake"));
|
|
8
|
+
const invoice_config_1 = require("./models/invoice-config");
|
|
9
|
+
const invoice_1 = require("./models/invoice");
|
|
10
|
+
const invoice_template_1 = require("./models/invoice-template");
|
|
11
|
+
const errors_1 = require("./errors");
|
|
12
|
+
const strategy_1 = require("./templates/strategy");
|
|
13
|
+
// ── Register all built-in strategies ─────────────────────────────────────────
|
|
14
|
+
const order_invoice_1 = require("./templates/order-invoice");
|
|
15
|
+
const quote_proforma_1 = require("./templates/quote-proforma");
|
|
16
|
+
strategy_1.TemplateFactory.register("order_invoice", order_invoice_1.OrderInvoiceStrategy);
|
|
17
|
+
strategy_1.TemplateFactory.register("quote_proforma", quote_proforma_1.QuoteProformaStrategy);
|
|
18
|
+
// ── PDF printer ───────────────────────────────────────────────────────────────
|
|
19
|
+
const fonts = {
|
|
20
|
+
Helvetica: {
|
|
21
|
+
normal: "Helvetica",
|
|
22
|
+
bold: "Helvetica-Bold",
|
|
23
|
+
italics: "Helvetica-Oblique",
|
|
24
|
+
bolditalics: "Helvetica-BoldOblique",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
const printer = new pdfmake_1.default(fonts);
|
|
28
|
+
// ── Service ───────────────────────────────────────────────────────────────────
|
|
29
|
+
class InvoiceGeneratorService extends (0, utils_1.MedusaService)({
|
|
30
|
+
InvoiceConfig: invoice_config_1.InvoiceConfig,
|
|
31
|
+
Invoice: invoice_1.Invoice,
|
|
32
|
+
InvoiceTemplate: invoice_template_1.InvoiceTemplate,
|
|
33
|
+
}) {
|
|
34
|
+
async generatePdf(params) {
|
|
35
|
+
const docDefinition = await this.resolveDocDefinition(params);
|
|
36
|
+
return this.renderToBuffer(params.template, docDefinition);
|
|
37
|
+
}
|
|
38
|
+
// ── Private helpers ─────────────────────────────────────────────────────────
|
|
39
|
+
async resolveDocDefinition(params) {
|
|
40
|
+
if (!params.invoice_id) {
|
|
41
|
+
return this.buildWithStrategy(params);
|
|
42
|
+
}
|
|
43
|
+
const invoice = await this.retrieveInvoice(params.invoice_id);
|
|
44
|
+
const cached = invoice.pdfContent;
|
|
45
|
+
if (Object.keys(cached).length > 0) {
|
|
46
|
+
return cached;
|
|
47
|
+
}
|
|
48
|
+
const docDefinition = await this.buildWithStrategy(params);
|
|
49
|
+
await this.updateInvoices({
|
|
50
|
+
id: invoice.id,
|
|
51
|
+
pdfContent: JSON.parse(JSON.stringify(docDefinition)),
|
|
52
|
+
});
|
|
53
|
+
return docDefinition;
|
|
54
|
+
}
|
|
55
|
+
async buildWithStrategy(params) {
|
|
56
|
+
const configs = await this.listInvoiceConfigs();
|
|
57
|
+
const config = configs[0] ?? null;
|
|
58
|
+
const templates = await this.listInvoiceTemplates({
|
|
59
|
+
slug: params.template,
|
|
60
|
+
});
|
|
61
|
+
const htmlTemplate = templates[0]?.html_content ?? null;
|
|
62
|
+
const strategy = strategy_1.TemplateFactory.resolve(params.template);
|
|
63
|
+
return strategy.buildDocumentDefinition(params.data, config, htmlTemplate);
|
|
64
|
+
}
|
|
65
|
+
renderToBuffer(templateId, docDefinition) {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
const chunks = [];
|
|
68
|
+
let pdfDoc;
|
|
69
|
+
try {
|
|
70
|
+
pdfDoc = printer.createPdfKitDocument(docDefinition);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
reject(new errors_1.PdfGenerationError(templateId, err));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
pdfDoc.on("data", (chunk) => chunks.push(chunk));
|
|
77
|
+
pdfDoc.on("end", () => resolve(Buffer.concat(chunks)));
|
|
78
|
+
pdfDoc.on("error", (err) => reject(new errors_1.PdfGenerationError(templateId, err)));
|
|
79
|
+
pdfDoc.end();
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.default = InvoiceGeneratorService;
|
|
84
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL2ludm9pY2UtZ2VuZXJhdG9yL3NlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxxREFBeUQ7QUFHekQsc0RBQWdDO0FBQ2hDLDREQUF1RDtBQUN2RCw4Q0FBMEM7QUFDMUMsZ0VBQTJEO0FBQzNELHFDQUE2QztBQUM3QyxtREFBc0Q7QUFFdEQsZ0ZBQWdGO0FBQ2hGLDZEQUFnRTtBQUNoRSwrREFBa0U7QUFJbEUsMEJBQWUsQ0FBQyxRQUFRLENBQW9CLGVBQWUsRUFBRSxvQ0FBb0IsQ0FBQyxDQUFBO0FBQ2xGLDBCQUFlLENBQUMsUUFBUSxDQUFxQixnQkFBZ0IsRUFBRSxzQ0FBcUIsQ0FBQyxDQUFBO0FBRXJGLGlGQUFpRjtBQUVqRixNQUFNLEtBQUssR0FBRztJQUNaLFNBQVMsRUFBRTtRQUNULE1BQU0sRUFBRSxXQUFXO1FBQ25CLElBQUksRUFBRSxnQkFBZ0I7UUFDdEIsT0FBTyxFQUFFLG1CQUFtQjtRQUM1QixXQUFXLEVBQUUsdUJBQXVCO0tBQ3JDO0NBQ08sQ0FBQTtBQUVWLE1BQU0sT0FBTyxHQUFHLElBQUksaUJBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQTtBQVFyQyxpRkFBaUY7QUFFakYsTUFBTSx1QkFBd0IsU0FBUSxJQUFBLHFCQUFhLEVBQUM7SUFDbEQsYUFBYSxFQUFiLDhCQUFhO0lBQ2IsT0FBTyxFQUFQLGlCQUFPO0lBQ1AsZUFBZSxFQUFmLGtDQUFlO0NBQ2hCLENBQUM7SUFDQSxLQUFLLENBQUMsV0FBVyxDQUNmLE1BQW1EO1FBRW5ELE1BQU0sYUFBYSxHQUFHLE1BQU0sSUFBSSxDQUFDLG9CQUFvQixDQUFDLE1BQU0sQ0FBQyxDQUFBO1FBQzdELE9BQU8sSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLGFBQWEsQ0FBQyxDQUFBO0lBQzVELENBQUM7SUFFRCwrRUFB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
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Default HTML templates embedded as TypeScript string constants.
|
|
4
|
+
*
|
|
5
|
+
* This approach guarantees templates are always available at runtime regardless
|
|
6
|
+
* of how the plugin is built or deployed (no filesystem reads, no copy scripts).
|
|
7
|
+
*
|
|
8
|
+
* For development, the raw `.html` files live alongside this module for
|
|
9
|
+
* syntax-highlighted editing. After making changes there, paste the updated
|
|
10
|
+
* HTML into the corresponding constant below and keep both in sync.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.DEFAULT_TEMPLATES = exports.QUOTE_PROFORMA_DEFAULT_HTML = exports.ORDER_INVOICE_DEFAULT_HTML = void 0;
|
|
14
|
+
// ── Order Invoice ─────────────────────────────────────────────────────────────
|
|
15
|
+
exports.ORDER_INVOICE_DEFAULT_HTML = `<!DOCTYPE html>
|
|
16
|
+
<html>
|
|
17
|
+
<head>
|
|
18
|
+
<style>
|
|
19
|
+
body { font-family: Helvetica, Arial, sans-serif; color: #495057; font-size: 10pt; }
|
|
20
|
+
.header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 20px; }
|
|
21
|
+
.company-name { font-size: 22pt; font-weight: bold; color: #1a365d; }
|
|
22
|
+
.invoice-title { font-size: 24pt; font-weight: bold; color: #2c3e50; text-align: right; }
|
|
23
|
+
.section-header { font-size: 12pt; font-weight: bold; color: #2c3e50; margin-bottom: 8px; }
|
|
24
|
+
.company-address { font-size: 11pt; color: #4a5568; line-height: 1.3; }
|
|
25
|
+
.company-contact { font-size: 10pt; color: #4a5568; }
|
|
26
|
+
.label { font-size: 10pt; color: #6c757d; }
|
|
27
|
+
.value { font-size: 10pt; font-weight: bold; color: #2c3e50; }
|
|
28
|
+
.address-text { font-size: 10pt; color: #495057; line-height: 1.3; }
|
|
29
|
+
|
|
30
|
+
table.items-table { width: 100%; border-collapse: collapse; margin: 10px 0; }
|
|
31
|
+
table.items-table th { background-color: #495057; color: #ffffff; font-size: 10pt; font-weight: bold; padding: 6px 8px; text-align: left; }
|
|
32
|
+
table.items-table td { font-size: 9pt; color: #495057; padding: 6px 8px; border-bottom: 1px solid #e2e8f0; }
|
|
33
|
+
table.items-table tr:nth-child(even) td { background-color: #f8f9fa; }
|
|
34
|
+
|
|
35
|
+
table.totals-table { margin-left: auto; border-collapse: collapse; }
|
|
36
|
+
table.totals-table td { padding: 6px 8px; }
|
|
37
|
+
.total-label { font-size: 10pt; font-weight: bold; color: #495057; }
|
|
38
|
+
.total-value { font-size: 10pt; font-weight: bold; color: #2c3e50; text-align: right; }
|
|
39
|
+
|
|
40
|
+
table.meta-table { border-collapse: collapse; }
|
|
41
|
+
table.meta-table td { padding: 2px 6px; }
|
|
42
|
+
|
|
43
|
+
.columns { display: flex; gap: 20px; margin-bottom: 16px; }
|
|
44
|
+
.columns > div { flex: 1; }
|
|
45
|
+
|
|
46
|
+
.warning-header { font-size: 10pt; font-weight: bold; color: #d97706; margin-top: 20px; margin-bottom: 4px; }
|
|
47
|
+
.warning-text { font-size: 10pt; color: #d97706; line-height: 1.4; margin-bottom: 20px; }
|
|
48
|
+
.notes-header { font-size: 12pt; font-weight: bold; color: #2c3e50; margin-top: 20px; margin-bottom: 10px; }
|
|
49
|
+
.notes-text { font-size: 10pt; color: #6c757d; font-style: italic; line-height: 1.4; margin-bottom: 20px; }
|
|
50
|
+
.thank-you { font-size: 12pt; color: #28a745; font-style: italic; text-align: center; margin-top: 30px; }
|
|
51
|
+
.disclaimer { font-size: 8pt; color: #6c757d; font-style: italic; text-align: center; margin-top: 10px; }
|
|
52
|
+
</style>
|
|
53
|
+
</head>
|
|
54
|
+
<body>
|
|
55
|
+
|
|
56
|
+
<!-- ── Header ──────────────────────────────────────────────────────────────── -->
|
|
57
|
+
<div class="header">
|
|
58
|
+
<div>
|
|
59
|
+
{{#if company_logo_base64}}
|
|
60
|
+
<img src="{{company_logo_base64}}" style="width:80px; height:40px; object-fit:contain; margin-bottom:10px;" />
|
|
61
|
+
{{/if}}
|
|
62
|
+
<div class="company-name">{{company_name}}</div>
|
|
63
|
+
</div>
|
|
64
|
+
<div>
|
|
65
|
+
<div class="invoice-title">COMPROBANTE DE PEDIDO</div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- ── Company + Invoice Meta ──────────────────────────────────────────────── -->
|
|
70
|
+
<div class="columns">
|
|
71
|
+
<div>
|
|
72
|
+
<div class="section-header">DATOS DE LA EMPRESA</div>
|
|
73
|
+
{{#if company_ruc}}<div class="company-contact" style="margin-bottom:4px;">RUC: {{company_ruc}}</div>{{/if}}
|
|
74
|
+
{{#if company_address}}<div class="company-address" style="margin-bottom:4px;">{{company_address}}</div>{{/if}}
|
|
75
|
+
{{#if company_phone}}<div class="company-contact" style="margin-bottom:4px;">{{company_phone}}</div>{{/if}}
|
|
76
|
+
{{#if company_email}}<div class="company-contact">{{company_email}}</div>{{/if}}
|
|
77
|
+
</div>
|
|
78
|
+
<div>
|
|
79
|
+
<table class="meta-table">
|
|
80
|
+
<tr><td class="label">Comprobante N°:</td><td class="value">{{invoice_id}}</td></tr>
|
|
81
|
+
<tr><td class="label">Fecha:</td><td class="value">{{invoice_date}}</td></tr>
|
|
82
|
+
<tr><td class="label">Pedido N°:</td><td class="value">{{order_display_id}}</td></tr>
|
|
83
|
+
<tr><td class="label">Fecha Pedido:</td><td class="value">{{order_date}}</td></tr>
|
|
84
|
+
</table>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<!-- ── Client + Shipping ───────────────────────────────────────────────────── -->
|
|
89
|
+
<div class="columns">
|
|
90
|
+
<div>
|
|
91
|
+
<div class="section-header">CLIENTE</div>
|
|
92
|
+
<div class="address-text">{{billing_address}}</div>
|
|
93
|
+
{{#if cedula}}<div class="address-text" style="margin-top:4px;">Cédula/RUC: {{cedula}}</div>{{/if}}
|
|
94
|
+
</div>
|
|
95
|
+
<div>
|
|
96
|
+
<div class="section-header">ENVÍO A</div>
|
|
97
|
+
<div class="address-text">{{shipping_address}}</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<!-- ── Items Table ─────────────────────────────────────────────────────────── -->
|
|
102
|
+
<table class="items-table">
|
|
103
|
+
<thead>
|
|
104
|
+
<tr>
|
|
105
|
+
<th>Artículo</th>
|
|
106
|
+
<th>Cantidad</th>
|
|
107
|
+
<th>Precio Unitario</th>
|
|
108
|
+
<th>Total</th>
|
|
109
|
+
</tr>
|
|
110
|
+
</thead>
|
|
111
|
+
<tbody>
|
|
112
|
+
{{#each items}}
|
|
113
|
+
<tr>
|
|
114
|
+
<td>
|
|
115
|
+
<strong>{{this.title}}</strong>
|
|
116
|
+
{{#if this.variant_title}}<br/><span style="color:#6b7280; font-size:8pt;">{{this.variant_title}}</span>{{/if}}
|
|
117
|
+
</td>
|
|
118
|
+
<td>{{this.quantity}}</td>
|
|
119
|
+
<td>{{this.unit_price}}</td>
|
|
120
|
+
<td>{{this.total}}</td>
|
|
121
|
+
</tr>
|
|
122
|
+
{{/each}}
|
|
123
|
+
</tbody>
|
|
124
|
+
</table>
|
|
125
|
+
|
|
126
|
+
<!-- ── Totals ──────────────────────────────────────────────────────────────── -->
|
|
127
|
+
<table class="totals-table">
|
|
128
|
+
<tr><td class="total-label">Subtotal:</td><td class="total-value">{{subtotal}}</td></tr>
|
|
129
|
+
<tr><td class="total-label">Impuestos:</td><td class="total-value">{{tax_total}}</td></tr>
|
|
130
|
+
{{#unless is_home_delivery}}
|
|
131
|
+
{{#if shipping_total}}<tr><td class="total-label">Envío:</td><td class="total-value">{{shipping_total}}</td></tr>{{/if}}
|
|
132
|
+
{{/unless}}
|
|
133
|
+
{{#if discount_total}}<tr><td class="total-label">Descuento:</td><td class="total-value">{{discount_total}}</td></tr>{{/if}}
|
|
134
|
+
<tr><td class="total-label">Total:</td><td class="total-value">{{total}}</td></tr>
|
|
135
|
+
</table>
|
|
136
|
+
|
|
137
|
+
<!-- ── Transport Warning ───────────────────────────────────────────────────── -->
|
|
138
|
+
{{#if is_home_delivery}}
|
|
139
|
+
<div class="warning-header">AVISO SOBRE TRANSPORTE</div>
|
|
140
|
+
<div class="warning-text">El valor de transporte no está incluido en este comprobante y deberá cotizarse aparte directamente con el vendedor.</div>
|
|
141
|
+
{{/if}}
|
|
142
|
+
|
|
143
|
+
<!-- ── Notes ───────────────────────────────────────────────────────────────── -->
|
|
144
|
+
{{#if notes}}
|
|
145
|
+
<div class="notes-header">Notas</div>
|
|
146
|
+
<div class="notes-text">{{notes}}</div>
|
|
147
|
+
{{/if}}
|
|
148
|
+
|
|
149
|
+
<!-- ── Footer ──────────────────────────────────────────────────────────────── -->
|
|
150
|
+
<div class="thank-you">¡Gracias por tu compra!</div>
|
|
151
|
+
<div class="disclaimer">Este documento es un comprobante de pedido, no constituye factura fiscal.</div>
|
|
152
|
+
|
|
153
|
+
</body>
|
|
154
|
+
</html>`;
|
|
155
|
+
// ── Quote Proforma ────────────────────────────────────────────────────────────
|
|
156
|
+
// NOTE: The dollar sign before {{totals.total_formatted}} is escaped as \$
|
|
157
|
+
// to prevent JavaScript template literal interpolation.
|
|
158
|
+
exports.QUOTE_PROFORMA_DEFAULT_HTML = `<!DOCTYPE html>
|
|
159
|
+
<html>
|
|
160
|
+
<head>
|
|
161
|
+
<style>
|
|
162
|
+
body { font-family: Helvetica, Arial, sans-serif; color: #282828; font-size: 9.5pt; }
|
|
163
|
+
|
|
164
|
+
/* ── Design Tokens ─────────────────────────────────────────────── */
|
|
165
|
+
.bg-dark { background-color: #121212; }
|
|
166
|
+
.bg-section { background-color: #262626; }
|
|
167
|
+
.bg-gold-light { background-color: #f5ebb4; }
|
|
168
|
+
.text-gold { color: #d4af37; }
|
|
169
|
+
.text-white { color: #ffffff; }
|
|
170
|
+
.text-gray3 { color: #c8c8c8; }
|
|
171
|
+
.text-gray5 { color: #8c8c8c; }
|
|
172
|
+
.text-gray8 { color: #282828; }
|
|
173
|
+
.text-warn { color: #c0392b; }
|
|
174
|
+
.text-success { color: #27ae60; }
|
|
175
|
+
|
|
176
|
+
/* ── Header ────────────────────────────────────────────────────── */
|
|
177
|
+
.header-bar { background-color: #121212; padding: 20px 40px; border-bottom: 3px solid #d4af37; }
|
|
178
|
+
.header-bar table { width: 100%; }
|
|
179
|
+
.brand-name { font-size: 24pt; font-weight: bold; color: #d4af37; }
|
|
180
|
+
.brand-subtitle { font-size: 9pt; color: #c8c8c8; margin-top: 2px; }
|
|
181
|
+
.header-meta { text-align: right; }
|
|
182
|
+
.header-label { font-size: 9pt; font-weight: bold; color: #8c8c8c; }
|
|
183
|
+
.header-value { font-size: 9pt; color: #c8c8c8; line-height: 1.35; }
|
|
184
|
+
|
|
185
|
+
/* ── Footer ────────────────────────────────────────────────────── */
|
|
186
|
+
.footer-bar { background-color: #121212; border-top: 2px solid #d4af37; padding: 12px 40px; text-align: center; margin-top: 30px; }
|
|
187
|
+
.footer-thanks { font-size: 8pt; color: #c8c8c8; }
|
|
188
|
+
.footer-legal { font-size: 7pt; color: #8c8c8c; margin-top: 5px; }
|
|
189
|
+
.footer-site { font-size: 8pt; font-weight: bold; color: #d4af37; margin-top: 5px; }
|
|
190
|
+
|
|
191
|
+
/* ── Section title ─────────────────────────────────────────────── */
|
|
192
|
+
.section-title { background-color: #262626; color: #d4af37; font-size: 10pt; font-weight: bold; padding: 8px 12px; border-left: 3px solid #d4af37; margin-bottom: 10px; margin-top: 20px; }
|
|
193
|
+
|
|
194
|
+
/* ── Service banner ────────────────────────────────────────────── */
|
|
195
|
+
.service-banner { background-color: #f5ebb4; padding: 10px 18px; border-left: 3px solid #d4af37; margin: 12px 0 24px 0; }
|
|
196
|
+
.service-label { font-size: 7pt; font-weight: bold; color: #8c8c8c; }
|
|
197
|
+
.service-name { font-size: 15pt; font-weight: bold; color: #282828; margin-top: 4px; }
|
|
198
|
+
|
|
199
|
+
/* ── Tables ────────────────────────────────────────────────────── */
|
|
200
|
+
table.data-table { width: 100%; border-collapse: collapse; border-left: 2px solid #a0821e; }
|
|
201
|
+
table.data-table td { padding: 6px 10px; font-size: 9.5pt; }
|
|
202
|
+
table.data-table tr:nth-child(even) td { background-color: #f2f2f2; }
|
|
203
|
+
table.data-table tr:nth-child(odd) td { background-color: #ffffff; }
|
|
204
|
+
table.data-table .td-label { color: #8c8c8c; width: 42%; }
|
|
205
|
+
table.data-table .td-value { color: #282828; font-weight: bold; text-align: right; }
|
|
206
|
+
|
|
207
|
+
table.breakdown-table { width: 100%; border-collapse: collapse; border-left: 2px solid #d4af37; }
|
|
208
|
+
table.breakdown-table td { padding: 6px 10px; font-size: 9.5pt; }
|
|
209
|
+
table.breakdown-table tr:nth-child(even) td { background-color: #f2f2f2; }
|
|
210
|
+
table.breakdown-table tr:nth-child(odd) td { background-color: #ffffff; }
|
|
211
|
+
|
|
212
|
+
/* ── Summary / Total ───────────────────────────────────────────── */
|
|
213
|
+
table.summary-table { width: 100%; border-collapse: collapse; }
|
|
214
|
+
table.summary-table td { padding: 5px 8px; }
|
|
215
|
+
.summary-label { font-size: 9.5pt; color: #8c8c8c; }
|
|
216
|
+
.summary-value { font-size: 10pt; font-weight: bold; color: #282828; text-align: right; }
|
|
217
|
+
|
|
218
|
+
.total-box { background-color: #1c1c1c; padding: 10px 16px; border-left: 4px solid #d4af37; margin-top: 6px; }
|
|
219
|
+
.total-box table { width: 100%; }
|
|
220
|
+
.total-heading { font-size: 9pt; font-weight: bold; color: #c8c8c8; }
|
|
221
|
+
.total-subheading { font-size: 7pt; color: #8c8c8c; margin-top: 2px; }
|
|
222
|
+
.total-amount { font-size: 28pt; font-weight: bold; color: #d4af37; text-align: right; }
|
|
223
|
+
|
|
224
|
+
/* ── Transport warning ─────────────────────────────────────────── */
|
|
225
|
+
.transport-warning { background-color: #fff7e6; padding: 10px 18px; border-left: 3px solid #d4af37; margin-bottom: 24px; }
|
|
226
|
+
.transport-title { font-size: 8pt; font-weight: bold; color: #c0392b; }
|
|
227
|
+
.transport-text { font-size: 9pt; color: #282828; line-height: 1.3; margin-top: 4px; }
|
|
228
|
+
|
|
229
|
+
/* ── Includes ──────────────────────────────────────────────────── */
|
|
230
|
+
.include-item { margin-bottom: 7px; font-size: 8.5pt; color: #282828; }
|
|
231
|
+
.check-icon { color: #27ae60; font-weight: bold; margin-right: 4px; }
|
|
232
|
+
</style>
|
|
233
|
+
</head>
|
|
234
|
+
<body>
|
|
235
|
+
|
|
236
|
+
<!-- ── Header ──────────────────────────────────────────────────── -->
|
|
237
|
+
<div class="header-bar">
|
|
238
|
+
<table>
|
|
239
|
+
<tr>
|
|
240
|
+
<td style="vertical-align:top;">
|
|
241
|
+
<div class="brand-name">{{company_name}}</div>
|
|
242
|
+
<div class="brand-subtitle">Contacto Empresarial</div>
|
|
243
|
+
</td>
|
|
244
|
+
<td class="header-meta" style="vertical-align:top; width:190px;">
|
|
245
|
+
<div class="header-label">COTIZACIÓN</div>
|
|
246
|
+
<hr style="width:28px; border:none; border-top:1.5px solid #d4af37; margin:6px 0 8px auto;" />
|
|
247
|
+
<div class="header-value">Nro. de Proforma: {{quote_number}}</div>
|
|
248
|
+
<div class="header-value">Fecha: {{date_str}}</div>
|
|
249
|
+
<div class="header-value">Válida por 15 días</div>
|
|
250
|
+
</td>
|
|
251
|
+
</tr>
|
|
252
|
+
</table>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<!-- ── Service Banner ──────────────────────────────────────────── -->
|
|
256
|
+
<div class="service-banner">
|
|
257
|
+
<div class="service-label">TIPO DE SERVICIO</div>
|
|
258
|
+
<div class="service-name">{{service_name}}</div>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<!-- ── Configuration Details ───────────────────────────────────── -->
|
|
262
|
+
{{#if config_fields.length}}
|
|
263
|
+
<div class="section-title">DETALLES DE LA CONFIGURACIÓN</div>
|
|
264
|
+
<table class="data-table">
|
|
265
|
+
{{#each config_fields}}
|
|
266
|
+
<tr>
|
|
267
|
+
<td class="td-label">{{this.label}}</td>
|
|
268
|
+
<td class="td-value">{{this.value}}</td>
|
|
269
|
+
</tr>
|
|
270
|
+
{{/each}}
|
|
271
|
+
</table>
|
|
272
|
+
{{/if}}
|
|
273
|
+
|
|
274
|
+
<!-- ── Price Breakdown ─────────────────────────────────────────── -->
|
|
275
|
+
{{#if breakdown.length}}
|
|
276
|
+
<div class="section-title">DESGLOSE DE COSTOS</div>
|
|
277
|
+
<table class="breakdown-table">
|
|
278
|
+
{{#each breakdown}}
|
|
279
|
+
<tr>
|
|
280
|
+
<td style="color:#282828;">{{this.label}}</td>
|
|
281
|
+
<td style="color:#282828; font-weight:bold; text-align:right;">{{this.total_formatted}}</td>
|
|
282
|
+
</tr>
|
|
283
|
+
{{/each}}
|
|
284
|
+
</table>
|
|
285
|
+
{{/if}}
|
|
286
|
+
|
|
287
|
+
<!-- ── Cost Summary ────────────────────────────────────────────── -->
|
|
288
|
+
<div class="section-title">RESUMEN DE COSTOS</div>
|
|
289
|
+
<table class="summary-table">
|
|
290
|
+
<tr style="background-color:#f2f2f2;"><td class="summary-label">Subtotal de servicio</td><td class="summary-value">{{totals.subtotal_formatted}}</td></tr>
|
|
291
|
+
<tr><td class="summary-label">Total extras</td><td class="summary-value">{{totals.extras_formatted}}</td></tr>
|
|
292
|
+
{{#if totals.discount}}<tr style="background-color:#f2f2f2;"><td class="summary-label">Descuento</td><td class="summary-value">-{{totals.discount_formatted}}</td></tr>{{/if}}
|
|
293
|
+
{{#unless is_home_delivery}}
|
|
294
|
+
{{#if totals.shipping}}<tr><td class="summary-label">Costo de envío (Delivery)</td><td class="summary-value">{{totals.shipping_formatted}}</td></tr>{{/if}}
|
|
295
|
+
{{/unless}}
|
|
296
|
+
<tr style="background-color:#f2f2f2;">
|
|
297
|
+
<td class="summary-label">Impuestos (IVA)</td>
|
|
298
|
+
<td class="summary-value">{{#if totals.taxes_provided}}{{totals.taxes_formatted}}{{else}}<em style="color:#c0392b; font-size:8.5pt;">Cálculo pendiente al confirmar la reserva.</em>{{/if}}</td>
|
|
299
|
+
</tr>
|
|
300
|
+
</table>
|
|
301
|
+
|
|
302
|
+
<!-- ── Grand Total ─────────────────────────────────────────────── -->
|
|
303
|
+
<div class="total-box">
|
|
304
|
+
<table>
|
|
305
|
+
<tr>
|
|
306
|
+
<td style="vertical-align:middle;">
|
|
307
|
+
<div class="total-heading">TOTAL A PAGAR</div>
|
|
308
|
+
<div class="total-subheading">DÓLARES AMERICANOS</div>
|
|
309
|
+
</td>
|
|
310
|
+
<td class="total-amount">\${{totals.total_formatted}}</td>
|
|
311
|
+
</tr>
|
|
312
|
+
</table>
|
|
313
|
+
</div>
|
|
314
|
+
{{#unless totals.taxes_provided}}
|
|
315
|
+
<div style="text-align:right; margin-top:6px; font-size:9pt; font-weight:bold; color:#c0392b;">Total referencial: el valor final se confirmará una vez aplicado el cálculo de impuestos.</div>
|
|
316
|
+
{{/unless}}
|
|
317
|
+
|
|
318
|
+
<!-- ── Transport Warning ───────────────────────────────────────── -->
|
|
319
|
+
{{#if is_home_delivery}}
|
|
320
|
+
<div class="transport-warning" style="margin-top:24px;">
|
|
321
|
+
<div class="transport-title">AVISO SOBRE TRANSPORTE</div>
|
|
322
|
+
<div class="transport-text">El valor de transporte no está incluido en esta proforma y deberá cotizarse aparte directamente con el vendedor.</div>
|
|
323
|
+
</div>
|
|
324
|
+
{{/if}}
|
|
325
|
+
|
|
326
|
+
<!-- ── Includes ────────────────────────────────────────────────── -->
|
|
327
|
+
{{#if includes.length}}
|
|
328
|
+
<div class="section-title">¿QUÉ INCLUYE?</div>
|
|
329
|
+
<div style="display:flex; gap:24px;">
|
|
330
|
+
<div style="flex:1;">
|
|
331
|
+
{{#each includes_left}}
|
|
332
|
+
<div class="include-item"><span class="check-icon">✓</span> {{this}}</div>
|
|
333
|
+
{{/each}}
|
|
334
|
+
</div>
|
|
335
|
+
<div style="flex:1;">
|
|
336
|
+
{{#each includes_right}}
|
|
337
|
+
<div class="include-item"><span class="check-icon">✓</span> {{this}}</div>
|
|
338
|
+
{{/each}}
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
{{/if}}
|
|
342
|
+
|
|
343
|
+
<!-- ── Client Info ─────────────────────────────────────────────── -->
|
|
344
|
+
{{#if contact_rows.length}}
|
|
345
|
+
<div class="section-title">INFORMACIÓN DEL CLIENTE</div>
|
|
346
|
+
<table class="data-table">
|
|
347
|
+
{{#each contact_rows}}
|
|
348
|
+
<tr>
|
|
349
|
+
<td class="td-label" style="width:36%;">{{this.label}}</td>
|
|
350
|
+
<td class="td-value" style="text-align:left;">{{this.value}}</td>
|
|
351
|
+
</tr>
|
|
352
|
+
{{/each}}
|
|
353
|
+
</table>
|
|
354
|
+
{{/if}}
|
|
355
|
+
|
|
356
|
+
<!-- ── Footer ──────────────────────────────────────────────────── -->
|
|
357
|
+
<div class="footer-bar">
|
|
358
|
+
<div class="footer-thanks">Gracias por tu confianza. Nuestro equipo ha recibido tu solicitud y se pondrá en contacto contigo a la brevedad.</div>
|
|
359
|
+
<div class="footer-legal">Esta cotización es informativa y no constituye un contrato. Los precios pueden variar.</div>
|
|
360
|
+
{{#if company_website}}<div class="footer-site">{{company_website}}</div>{{/if}}
|
|
361
|
+
</div>
|
|
362
|
+
|
|
363
|
+
</body>
|
|
364
|
+
</html>`;
|
|
365
|
+
// ── Registry ──────────────────────────────────────────────────────────────────
|
|
366
|
+
exports.DEFAULT_TEMPLATES = [
|
|
367
|
+
{
|
|
368
|
+
name: "Comprobante de Pedido",
|
|
369
|
+
slug: "order_invoice",
|
|
370
|
+
type: "order_invoice",
|
|
371
|
+
html: exports.ORDER_INVOICE_DEFAULT_HTML,
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
name: "Cotización Proforma",
|
|
375
|
+
slug: "quote_proforma",
|
|
376
|
+
type: "quote_proforma",
|
|
377
|
+
html: exports.QUOTE_PROFORMA_DEFAULT_HTML,
|
|
378
|
+
},
|
|
379
|
+
];
|
|
380
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9pbnZvaWNlLWdlbmVyYXRvci90ZW1wbGF0ZXMvZGVmYXVsdHMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7R0FTRzs7O0FBU0gsaUZBQWlGO0FBRXBFLFFBQUEsMEJBQTBCLEdBQUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7UUEySWxDLENBQUE7QUFFUixpRkFBaUY7QUFDakYsMkVBQTJFO0FBQzNFLHdEQUF3RDtBQUUzQyxRQUFBLDJCQUEyQixHQUFHOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztRQThNbkMsQ0FBQTtBQUVSLGlGQUFpRjtBQUVwRSxRQUFBLGlCQUFpQixHQUFzQjtJQUNsRDtRQUNFLElBQUksRUFBRSx1QkFBdUI7UUFDN0IsSUFBSSxFQUFFLGVBQWU7UUFDckIsSUFBSSxFQUFFLGVBQWU7UUFDckIsSUFBSSxFQUFFLGtDQUEwQjtLQUNqQztJQUNEO1FBQ0UsSUFBSSxFQUFFLHFCQUFxQjtRQUMzQixJQUFJLEVBQUUsZ0JBQWdCO1FBQ3RCLElBQUksRUFBRSxnQkFBZ0I7UUFDdEIsSUFBSSxFQUFFLG1DQUEyQjtLQUNsQztDQUNGLENBQUEifQ==
|