feat(whatsapp): QWEN primary AI backend, Hermes fallback, conversation history, vehicle persistence, demo prompts

- Add QWEN (qwen3.6) as primary AI backend with short system prompt
- Hermes remains as fallback with 45s timeout
- Increase QWEN timeout to 35s, max_tokens to 4000
- Add conversation history loading from whatsapp_messages (last 4 msgs)
- Persist detected vehicle in whatsapp_sessions table
- Add 'limpiar chat' / 'nuevo chat' / 'reset' commands to clear history
- Fix CSS conflict: rename whatsapp chat-panel classes to wa-chat-panel
- Fix JS ID conflicts with chat.js widget (waChatPanel, waChatMessages, etc.)
- Improve no-stock response: conversational with alternatives
- Split search_query by | for multi-part lookups
- Add DEMO_PROMPTS.md and DEMO_PROMPTS_V2.md
This commit is contained in:
2026-05-06 20:27:14 +00:00
parent 371d72887e
commit ff45905b49
33 changed files with 3040 additions and 445 deletions

View File

@@ -82,8 +82,10 @@ const POS = (() => {
console.warn('Could not parse token:', e);
}
// Load cart from localStorage (from catalog)
// Load cart from localStorage (from catalog or quotation edit/convert)
const catalogCart = localStorage.getItem('pos_cart');
const editQuoteId = localStorage.getItem('pos_edit_quote_id');
const convertQuoteId = localStorage.getItem('pos_convert_quote_id');
if (catalogCart) {
try {
const items = JSON.parse(catalogCart);
@@ -93,6 +95,12 @@ const POS = (() => {
localStorage.removeItem('pos_cart');
} catch (e) { console.warn('Could not load catalog cart:', e); }
}
if (editQuoteId) {
showToast(`Modo edicion: Cotizacion #${editQuoteId}. Guardar actualizara la cotizacion.`);
}
if (convertQuoteId) {
showToast(`Modo conversion: Cotizacion #${convertQuoteId}. El pago convertira la cotizacion en venta.`);
}
// Load current register
await loadRegister();
@@ -702,10 +710,29 @@ const POS = (() => {
confirmBtn.textContent = 'Procesando...';
try {
const sale = await api('/pos/api/sales', {
method: 'POST',
body: JSON.stringify(saleData),
});
const convertQuoteId = localStorage.getItem('pos_convert_quote_id');
let sale;
if (convertQuoteId) {
const convertData = {
register_id: currentRegister ? currentRegister.id : null,
payment_method: paymentMethod,
sale_type: 'cash',
amount_paid: amountPaid,
payment_details: paymentDetails,
};
sale = await api('/pos/api/quotations/' + convertQuoteId + '/convert', {
method: 'POST',
body: JSON.stringify(convertData),
});
localStorage.removeItem('pos_convert_quote_id');
showToast(`Cotizacion #${convertQuoteId} convertida a venta #${sale.id}`);
} else {
sale = await api('/pos/api/sales', {
method: 'POST',
body: JSON.stringify(saleData),
});
showToast(`Venta #${sale.id} completada`);
}
lastSaleId = sale.id;
lastSaleData = sale;
@@ -717,8 +744,6 @@ const POS = (() => {
selectedRow = -1;
clearCustomer();
renderCart();
showToast(`Venta #${sale.id} completada`);
} catch (e) {
alert('Error al procesar venta: ' + e.message);
} finally {
@@ -790,12 +815,25 @@ const POS = (() => {
customer_id: currentCustomer ? currentCustomer.id : null,
};
const editQuoteId = localStorage.getItem('pos_edit_quote_id');
try {
const result = await api('/pos/api/quotations', {
method: 'POST',
body: JSON.stringify(body),
});
showToast(`Cotizacion #${result.id} guardada. Total: ${fmt(result.total)}`);
if (editQuoteId) {
const result = await api('/pos/api/quotations/' + editQuoteId, {
method: 'PUT',
body: JSON.stringify(body),
});
localStorage.removeItem('pos_edit_quote_id');
localStorage.removeItem('pos_edit_quote_customer_id');
localStorage.removeItem('pos_edit_quote_notes');
showToast(`Cotizacion #${editQuoteId} actualizada. Total: ${fmt(result.total)}`);
} else {
const result = await api('/pos/api/quotations', {
method: 'POST',
body: JSON.stringify(body),
});
showToast(`Cotizacion #${result.id} guardada. Total: ${fmt(result.total)}`);
}
} catch (e) {
alert('Error: ' + e.message);
}