feat: Add automatic anticipo notes to tickets and optimize for 58mm printers

- Add automatic notes for anticipo tickets with commitment and cancellation policy
- Optimize ticket layout for 58mm thermal printers (reduced from 72mm)
- Improve ticket spacing with better margins (0.1cm/0.05cm)
- Add proper capitalization for service titles (e.g. "anticipo" → "Anticipo")
- Enhance total amount formatting with larger font size (15px) and better spacing
- Add visual separation between payment method and staff information

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Marco Gallegos
2025-09-14 08:36:50 -06:00
parent 2b1438a064
commit 9559652f40
4 changed files with 40 additions and 11 deletions

View File

@@ -10,7 +10,7 @@ Este es un sistema de Punto de Venta (POS) simple y eficiente, diseñado para ge
- **Sistema de descuentos**: Descuentos por porcentaje o monto fijo con motivo - **Sistema de descuentos**: Descuentos por porcentaje o monto fijo con motivo
- **Cálculo automático de totales**: Subtotal, descuento y total final en tiempo real - **Cálculo automático de totales**: Subtotal, descuento y total final en tiempo real
- **Programación de citas**: Fecha y hora integradas en el flujo de ventas - **Programación de citas**: Fecha y hora integradas en el flujo de ventas
- **Generación de tickets**: Recibos optimizados para impresión térmica de 58mm - **Generación de tickets**: Recibos optimizados para impresión térmica de 58mm con notas automáticas para anticipos
- **Exportación a CSV**: Historial completo de ventas exportable - **Exportación a CSV**: Historial completo de ventas exportable
- **Gestión de Clientes:** Registro y consulta de clientes, con expediente completo incluyendo historial de servicios y cursos. - **Gestión de Clientes:** Registro y consulta de clientes, con expediente completo incluyendo historial de servicios y cursos.
- **Gestión de Productos:** Administración completa de servicios y cursos con precios actualizables. - **Gestión de Productos:** Administración completa de servicios y cursos con precios actualizables.
@@ -128,6 +128,15 @@ El sistema está diseñado para ser desplegado fácilmente utilizando Docker y D
--- ---
## Historial - Versión 1.5.5
### 🎫 **Mejoras en Tickets de Anticipo**
- **Notas automáticas**: Los tickets de anticipo incluyen automáticamente notas sobre compromiso de tiempo y política de cancelación
- **Optimización 58mm**: Tickets optimizados específicamente para impresoras térmicas de 58mm con mejor aprovechamiento del espacio
- **Formato mejorado**: Capitalización automática de títulos de servicios y mejor espaciado visual
---
## Historial - Versión 1.5.0 ## Historial - Versión 1.5.0
### 🎫 **Reorganización de Interface** ### 🎫 **Reorganización de Interface**

View File

@@ -1,6 +1,6 @@
{ {
"name": "ap-pos", "name": "ap-pos",
"version": "1.5.4", "version": "1.5.5",
"main": "app.js", "main": "app.js",
"scripts": { "scripts": {
"start": "node server.js" "start": "node server.js"

View File

@@ -40,7 +40,8 @@ function templateTicket(mov, settings) {
} }
const montoFormateado = Number(mov.monto).toFixed(2); const montoFormateado = Number(mov.monto).toFixed(2);
const tipoServicio = mov.subtipo === 'Retoque' ? `Retoque de ${mov.tipo}` : mov.tipo; const tipoServicio = mov.subtipo === 'Retoque' ? `Retoque de ${mov.tipo}` :
(mov.tipo.charAt(0).toUpperCase() + mov.tipo.slice(1).toLowerCase());
const lines = []; const lines = [];
lines.push('<div class="ticket">'); lines.push('<div class="ticket">');
@@ -144,6 +145,16 @@ function templateTicket(mov, settings) {
} }
} }
// Add special notes for anticipo tickets
const isAnticipo = mov.tipo === 'Anticipo' || (mov.concepto && mov.concepto.toLowerCase().includes('anticipo'));
if (isAnticipo) {
lines.push('<div class="t-spacer"></div>');
lines.push('<div class="t-left t-small">Al dejar tu anticipo, te agradecemos tu compromiso con nuestro tiempo, de la misma forma en que nosotros respetamos el tuyo.</div>');
lines.push('<div class="t-spacer"></div>');
lines.push('<div class="t-left t-small">Las cancelaciones con menos de 24 horas no son reembolsables.</div>');
lines.push('<div class="t-spacer"></div>');
}
if (mov.notas) lines.push(`<div class="t-left t-small t-service-detail"><b>Notas:</b> ${esc(mov.notas)}</div>`); if (mov.notas) lines.push(`<div class="t-left t-small t-service-detail"><b>Notas:</b> ${esc(mov.notas)}</div>`);
lines.push('<div class="t-divider"></div>'); lines.push('<div class="t-divider"></div>');
@@ -154,6 +165,9 @@ function templateTicket(mov, settings) {
// MÉTODO DE PAGO DEBAJO DEL TOTAL - ALINEADO A LA IZQUIERDA // MÉTODO DE PAGO DEBAJO DEL TOTAL - ALINEADO A LA IZQUIERDA
if (mov.metodo) lines.push(`<div class="t-left t-small"><b>Método:</b> ${esc(mov.metodo)}</div>`); if (mov.metodo) lines.push(`<div class="t-left t-small"><b>Método:</b> ${esc(mov.metodo)}</div>`);
// PEQUEÑO ESPACIO ANTES DE TE ATENDIÓ
if (mov.staff) lines.push('<div class="t-spacer"></div>');
// TE ATENDIÓ DEBAJO DEL MÉTODO DE PAGO // TE ATENDIÓ DEBAJO DEL MÉTODO DE PAGO
if (mov.staff) lines.push(`<div class="t-left t-small"><b>Te atendió:</b> ${esc(mov.staff)}</div>`); if (mov.staff) lines.push(`<div class="t-left t-small"><b>Te atendió:</b> ${esc(mov.staff)}</div>`);

View File

@@ -870,11 +870,11 @@ button.action-btn {
/* Estilos del Ticket */ /* Estilos del Ticket */
.ticket { .ticket {
width: 72mm; width: 58mm;
max-width: 72mm; max-width: 58mm;
background: #fff; background: #fff;
color: #000; color: #000;
padding: 8px 2px; padding: 6px 1mm;
box-sizing: border-box; box-sizing: border-box;
font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
border: 1px solid #ccc; /* Visible en pantalla, no en impresión */ border: 1px solid #ccc; /* Visible en pantalla, no en impresión */
@@ -895,12 +895,18 @@ button.action-btn {
.t-small { font-size: 11px; line-height: 1.3; } .t-small { font-size: 11px; line-height: 1.3; }
.t-service-title { margin-bottom: 6px; font-size: 12px; } .t-service-title { margin-bottom: 6px; font-size: 12px; }
.t-service-detail { margin-bottom: 3px; } .t-service-detail { margin-bottom: 3px; }
.t-anticipo-note {
word-wrap: break-word;
width: 100%;
text-align: left;
line-height: 1.4;
}
.t-divider { border-top: 1px dashed #000; margin: 8px 0; } .t-divider { border-top: 1px dashed #000; margin: 8px 0; }
.t-row { display: flex; justify-content: space-between; margin-bottom: 2px; } .t-row { display: flex; justify-content: space-between; margin-bottom: 2px; }
.t-row-label { font-weight: bold; } .t-row-label { font-weight: bold; }
.t-service-detail-label { font-weight: bold; } .t-service-detail-label { font-weight: bold; }
.t-footer { margin-top: 10px; } .t-footer { margin-top: 10px; }
.t-total-large { font-size: 13px; } .t-total-large { font-size: 15px; margin: 6px 0; }
.t-qr-section { .t-qr-section {
margin-top: 12px; margin-top: 12px;
@@ -962,15 +968,15 @@ button.action-btn {
} }
@page { @page {
size: 72mm auto; size: 58mm auto;
margin: 0.3cm 0.1cm; margin: 0.1cm 0.05cm;
} }
/* Configuración adicional para impresión PDF */ /* Configuración adicional para impresión PDF */
@media print { @media print {
body { body {
width: 72mm !important; width: 58mm !important;
max-width: 72mm !important; max-width: 58mm !important;
} }
} }