From 9f06f87fab128aa016a68110ad7a08e86d785ca3 Mon Sep 17 00:00:00 2001 From: Jon Bendtsen Date: Mon, 8 Jun 2026 01:09:58 +0200 Subject: [PATCH 1/3] Qual remove several uncommented presend lines (#38623) Co-authored-by: Jon Bendtsen --- htdocs/comm/propal/messaging.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/htdocs/comm/propal/messaging.php b/htdocs/comm/propal/messaging.php index 5fe4b9c0ca7..f8f44ae8338 100644 --- a/htdocs/comm/propal/messaging.php +++ b/htdocs/comm/propal/messaging.php @@ -215,17 +215,6 @@ if (!empty($object->id)) { $messagingUrl = DOL_URL_ROOT . '/comm/propal/agenda.php?id=' . $object->id; // Changed from projet $morehtmlright .= dolGetButtonTitle($langs->trans('MessageListViewType'), '', 'fa fa-bars imgforviewmode', $messagingUrl, '', 1); - - // // Show link to send an email (if read and not closed) - // $btnstatus = $object->status < Ticket::STATUS_CLOSED && $action != "presend" && $action != "presend_addmessage"; - // $url = 'card.php?track_id='.$object->track_id.'&action=presend_addmessage&mode=init&private_message=0&send_email=1&backtopage='.urlencode($_SERVER["PHP_SELF"].'?track_id='.$object->track_id).'#formmailbeforetitle'; - // $morehtmlright .= dolGetButtonTitle($langs->trans('SendMail'), '', 'fa fa-paper-plane', $url, 'email-title-button', $btnstatus); - - // // Show link to add a private message (if read and not closed) - // $btnstatus = $object->status < Ticket::STATUS_CLOSED && $action != "presend" && $action != "presend_addmessage"; - // $url = 'card.php?track_id='.$object->track_id.'&action=presend_addmessage&mode=init&backtopage='.urlencode($_SERVER["PHP_SELF"].'?track_id='.$object->track_id).'#formmailbeforetitle'; - // $morehtmlright .= dolGetButtonTitle($langs->trans('TicketAddMessage'), '', 'fa fa-comment-dots', $url, 'add-new-ticket-title-button', $btnstatus); - // Show link to add event if (isModEnabled('agenda')) { $addActionBtnRight = $user->hasRight('agenda', 'myactions', 'create') || $user->hasRight('agenda', 'allactions', 'create'); From 588fa88a8c4c15d20d4c8755be8a79a038c14731 Mon Sep 17 00:00:00 2001 From: sonikf <93765174+sonikf@users.noreply.github.com> Date: Sun, 7 Jun 2026 23:13:30 +0000 Subject: [PATCH 2/3] Enhance confirmation handling and error messages (#38625) --- htdocs/ai/js/ai_assistant.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/htdocs/ai/js/ai_assistant.js b/htdocs/ai/js/ai_assistant.js index ab175c42f2b..1b620f16f9c 100644 --- a/htdocs/ai/js/ai_assistant.js +++ b/htdocs/ai/js/ai_assistant.js @@ -944,8 +944,16 @@ document.addEventListener('DOMContentLoaded', () => { } function handleConfirmation(action, details, originalIntent) { - pendingIntent = originalIntent.arguments.original_intent || originalIntent; - const toolName = pendingIntent.tool || 'unknown tool'; + // original_intent must be provided by parse_intent.php. + // If missing, treat as a malformed confirmation and abort. + if (!originalIntent.arguments || !originalIntent.arguments.original_intent) { + appendMsg('error', t('AIError') + ': malformed confirmation response (missing original_intent). Please try again.'); + input.disabled = false; + input.focus(); + return; + } + pendingIntent = originalIntent.arguments.original_intent; + const toolName = pendingIntent.tool || 'unknown tool'; let template = t('ConfirmAiAction'); let messageHtml = template.replace('%1$s', `${action}`).replace('%2$s', `${toolName}`); let html = `
${t('confirmation')}

${messageHtml}

${details ? `

${details}

` : ''}
`; @@ -1022,6 +1030,17 @@ document.addEventListener('DOMContentLoaded', () => { appendMsg('system', ` ${t('ExecutingTool')} ${pendingIntent.tool || ''}...`); const loadingMsg = chat.lastElementChild; input.disabled = true; + // PendingIntent must be a real action tool, never a system tool. + // This catches the edge case where handleConfirmation stored the wrong intent. + const systemTools = ['ask_for_confirmation', 'ask_for_clarification', 'respond_to_user', 'reject_general_question']; + if (!pendingIntent || !pendingIntent.tool || systemTools.includes(pendingIntent.tool)) { + loadingMsg.remove(); + appendMsg('error', t('AIError') + ': cannot execute system tool "' + (pendingIntent && pendingIntent.tool || 'unknown') + '" as an action. Please try again.'); + pendingIntent = null; + input.disabled = false; + input.focus(); + return; + } try { const toolRes = await fetch('execute_tool.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, From fc48006717f3cf4a8d788d4aefd3006af07b2d97 Mon Sep 17 00:00:00 2001 From: MDW Date: Mon, 8 Jun 2026 01:18:21 +0200 Subject: [PATCH 3/3] # FIX #36258: Do not format qty from barcode (#38624) # FIX #36258: Do not format qty from barcode When a barcode contains a quantity (e.g., 0.275 from qu=0 and qd=275), the quantity is already in international format (using '.' as decimal separator). However, when this qty is passed to invoice.php, it uses GETPOSTFLOAT('qty') which defaults to option=2, meaning it treats the value as local user format. In Spanish/Argentinian locale (thousand separator=., decimal separator=,), price2num with option=2 incorrectly treats 0.275 as a number with thousand separator and converts it to 275. ## Solution: 1. Add a 'qty_std' parameter to the URL when calling invoice.php from barcode 2. In invoice.php, use GETPOSTFLOAT('qty', '', 1) when qty_std is set 3. Option=1 tells price2num that the value is NOT in local user format This way, qty from barcode (already in international format) is not converted, while qty from user input (in local format) continues to be converted correctly. Changes: - htdocs/takepos/index.php: Add qty_std parameter to ClickProduct and pass it through to invoice.php - htdocs/takepos/invoice.php: Use GETPOSTFLOAT with option=1 when qty_std is set Co-authored-by: Laurent Destailleur --- htdocs/takepos/index.php | 34 +++++++++++++++++----------------- htdocs/takepos/invoice.php | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/htdocs/takepos/index.php b/htdocs/takepos/index.php index 4f8338fe24e..23821db4d3b 100644 --- a/htdocs/takepos/index.php +++ b/htdocs/takepos/index.php @@ -2,7 +2,7 @@ /* Copyright (C) 2018 Andreu Bisquerra * Copyright (C) 2019 Josep Lluís Amador * Copyright (C) 2020 Thibault FOUCART - * Copyright (C) 2024-2025 MDW + * Copyright (C) 2024-2026 MDW * Copyright (C) 2024-2025 Frédéric France * * This program is free software; you can redistribute it and/or modify @@ -499,7 +499,7 @@ function MoreProducts(moreorless) { } var nb_cat_shown = $('.div5 div.wrapper2[data-iscat=1]').length; - var offset = * pageproducts - nb_cat_shown; + var offset = * pageproducts - nb_cat_shown; // Get socid let socid = jQuery('#thirdpartyid').val(); @@ -566,7 +566,7 @@ function MoreProducts(moreorless) { ClearSearch(false); } -function ClickProduct(position, qty = 1) { +function ClickProduct(position, qty = 1, qty_std = 0) { console.log("ClickProduct at position"+position); if ($('#invoiceid').val() == "") { invoiceid = $('#invoiceid').val(); @@ -585,9 +585,9 @@ function ClickProduct(position, qty = 1) { if (idproduct == "") { return; } - addInvoiceLine = function(qty) { + addInvoiceLine = function(qty, qty_std_inner = 0) { // Call page invoice.php to generate the section with product lines - $("#poslines").load("invoice.php?action=addline&token=&place="+place+"&idproduct="+idproduct+"&qty="+qty+"&invoiceid="+invoiceid, function() { + $("#poslines").load("invoice.php?action=addline&token=&place="+place+"&idproduct="+idproduct+"&qty="+qty+"&invoiceid="+invoiceid+(qty_std_inner ? '&qty_std=1' : ''), function() { idproduct = ""; - addInvoiceLine(qty); + addInvoiceLine(qty, qty_std); } @@ -893,7 +893,7 @@ function Search2(keyCodeForEnter, moreorless) { } else if ('product' == data[0]['object'] && $('#search').val() == data[0]['barcode']) { console.log("There is only 1 answer and we found search on a barcode, so we add the product in basket, qty="+data[0]['qty']); - ClickProduct(0, data[0]['qty']); + ClickProduct(0, data[0]['qty'], 1); } } if (eventKeyCode == keyCodeForEnter){ @@ -1607,7 +1607,7 @@ if ($reshook == 0) { //add buttons } } elseif ($reshook == 1) { $r = 0; //replace buttons - if (is_array($hookmanager->resArray) ) { + if (is_array($hookmanager->resArray)) { foreach ($hookmanager->resArray as $resArray) { foreach ($resArray as $butmenu) { $menus[$r++] = $butmenu; @@ -1720,14 +1720,14 @@ if ($reshook == 0) { //add buttons while ($count < $MAXPRODUCT) { print '
- onclick="MoreProducts('less')" onclick="MoreProducts('more')" > + onclick="MoreProducts('less')" onclick="MoreProducts('more')" > '; diff --git a/htdocs/takepos/invoice.php b/htdocs/takepos/invoice.php index 56fdbf23277..db81c959614 100644 --- a/htdocs/takepos/invoice.php +++ b/htdocs/takepos/invoice.php @@ -761,7 +761,7 @@ if (empty($reshook)) { $datapriceofproduct = $prod->getSellPrice($mysoc, $customer, 0); - $qty = GETPOSTISSET('qty') ? GETPOSTFLOAT('qty') : 1; + $qty = GETPOSTISSET('qty') ? GETPOSTFLOAT('qty', '', GETPOSTINT('qty_std') ? 1 : 2) : 1; $price = $datapriceofproduct['pu_ht']; $price_ttc = $datapriceofproduct['pu_ttc']; //$price_min = $datapriceofproduct['price_min'];