feat(config): agrega personalización de ticket con logo y opciones
Some checks failed
CI / lint-and-test (3.11) (push) Has been cancelled
CI / lint-and-test (3.13) (push) Has been cancelled

- Nuevos endpoints /pos/api/config/receipt (GET/PUT)
- Sección en config.html para subir logo, datos del negocio y toggles visuales
- pos.js carga la config y renderiza el ticket usando logo, RFC, dirección,
  teléfono, mensajes y opciones de desglose/empleado/pago configurables
- Logo se redimensiona a 300px de ancho y se guarda como base64 JPEG en tenant_config

Tests: 35 passed
This commit is contained in:
2026-06-30 08:35:10 +00:00
parent 70a600cc66
commit c1e481fb79
4 changed files with 350 additions and 12 deletions

View File

@@ -752,3 +752,99 @@ def set_onboarding_status():
cur.close()
conn.close()
return jsonify({'completed': completed == 'true'})
# ─── Receipt / Ticket Customization ────────────────────────────────────────
RECEIPT_CONFIG_KEYS = [
'receipt_logo',
'receipt_store_name',
'receipt_tagline',
'receipt_rfc',
'receipt_address',
'receipt_phone',
'receipt_footer',
'receipt_thanks_message',
'receipt_show_logo',
'receipt_show_rfc',
'receipt_show_address',
'receipt_show_phone',
'receipt_show_iva_breakdown',
'receipt_show_payment_details',
'receipt_show_employee',
]
@config_bp.route('/receipt', methods=['GET'])
@require_auth('pos.view')
def get_receipt_config():
"""Get receipt customization settings."""
conn = get_tenant_conn(g.tenant_id)
cur = conn.cursor()
cur.execute(
"SELECT key, value FROM tenant_config WHERE key = ANY(%s)",
(RECEIPT_CONFIG_KEYS,)
)
rows = {row[0]: row[1] for row in cur.fetchall()}
cur.close()
conn.close()
def _bool(key, default=False):
v = rows.get(key, 'true' if default else 'false')
return str(v).lower() == 'true'
return jsonify({
'logo': rows.get('receipt_logo', ''),
'store_name': rows.get('receipt_store_name', ''),
'tagline': rows.get('receipt_tagline', ''),
'rfc': rows.get('receipt_rfc', ''),
'address': rows.get('receipt_address', ''),
'phone': rows.get('receipt_phone', ''),
'footer': rows.get('receipt_footer', ''),
'thanks_message': rows.get('receipt_thanks_message', 'Gracias por su compra!'),
'show_logo': _bool('receipt_show_logo', True),
'show_rfc': _bool('receipt_show_rfc', True),
'show_address': _bool('receipt_show_address', False),
'show_phone': _bool('receipt_show_phone', False),
'show_iva_breakdown': _bool('receipt_show_iva_breakdown', True),
'show_payment_details': _bool('receipt_show_payment_details', True),
'show_employee': _bool('receipt_show_employee', False),
})
@config_bp.route('/receipt', methods=['PUT'])
@require_auth('config.edit')
def update_receipt_config():
"""Update receipt customization settings."""
data = request.get_json() or {}
conn = get_tenant_conn(g.tenant_id)
cur = conn.cursor()
settings = {
'receipt_logo': data.get('logo', ''),
'receipt_store_name': data.get('store_name', ''),
'receipt_tagline': data.get('tagline', ''),
'receipt_rfc': data.get('rfc', ''),
'receipt_address': data.get('address', ''),
'receipt_phone': data.get('phone', ''),
'receipt_footer': data.get('footer', ''),
'receipt_thanks_message': data.get('thanks_message', 'Gracias por su compra!'),
'receipt_show_logo': 'true' if data.get('show_logo') else 'false',
'receipt_show_rfc': 'true' if data.get('show_rfc') else 'false',
'receipt_show_address': 'true' if data.get('show_address') else 'false',
'receipt_show_phone': 'true' if data.get('show_phone') else 'false',
'receipt_show_iva_breakdown': 'true' if data.get('show_iva_breakdown') else 'false',
'receipt_show_payment_details': 'true' if data.get('show_payment_details') else 'false',
'receipt_show_employee': 'true' if data.get('show_employee') else 'false',
}
for key, value in settings.items():
cur.execute("""
INSERT INTO tenant_config (key, value) VALUES (%s, %s)
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value
""", (key, value))
conn.commit()
cur.close()
conn.close()
return jsonify({'message': 'Receipt configuration updated'})