From 61439c2a1d9bf94924828bb83e5aca3748f161a0 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Fri, 9 Jan 2026 15:28:25 +0700 Subject: [PATCH] fix the OWL error at Approval app --- static/src/views/decimal_observer.js | 115 ++++++++++++++++----------- 1 file changed, 67 insertions(+), 48 deletions(-) diff --git a/static/src/views/decimal_observer.js b/static/src/views/decimal_observer.js index f583948..ab92fac 100644 --- a/static/src/views/decimal_observer.js +++ b/static/src/views/decimal_observer.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -console.log('Loading safe decimal observer...'); +console.log('Loading ultra-safe decimal observer...'); // Function to safely apply decimal styling to any element function safelyApplyDecimalStyling(element) { @@ -12,6 +12,19 @@ function safelyApplyDecimalStyling(element) { element.hasAttribute('data-decimal-processed') || element.hasAttribute('data-owl-updating')) return false; + // CRITICAL: Skip any editable elements or elements that might become editable + if (element.isContentEditable || + element.closest('.o_field_widget:not(.o_readonly_modifier)') || + element.closest('[contenteditable="true"]') || + element.closest('input') || + element.closest('textarea') || + element.closest('.o_input') || + element.closest('.o_field_float:not(.o_readonly_modifier)') || + element.closest('.o_field_monetary:not(.o_readonly_modifier)') || + element.hasAttribute('contenteditable')) { + return false; + } + try { const text = element.textContent.trim(); if (!text) return false; @@ -34,10 +47,12 @@ function safelyApplyDecimalStyling(element) { if (match) { const [, prefix, integerPart, decimalPart, suffix] = match; - console.log('Safe Decimal Observer: Styling', text); + console.log('Ultra-Safe Decimal Observer: Styling decimal number', text); - // Double-check the element hasn't changed before updating - if (element.textContent.trim() === text && document.contains(element)) { + // Triple-check the element is safe to modify + if (element.textContent.trim() === text && + document.contains(element) && + !element.closest('.o_field_widget:not(.o_readonly_modifier)')) { const newHTML = `${prefix || ''}${integerPart}.${decimalPart}${suffix || ''}`; element.innerHTML = newHTML; return true; @@ -45,17 +60,20 @@ function safelyApplyDecimalStyling(element) { } } - // Handle whole numbers in quantity contexts (be more selective) - // Only add decimals to numbers that are clearly quantities, not prices with thousands separators - if (text.match(/^\d+$/) && text.length > 0 && text.length <= 4 && ( + // Handle whole numbers in quantity contexts (be even more selective) + // Only add decimals to numbers that are clearly quantities in readonly contexts + if (text.match(/^\d+$/) && text.length > 0 && text.length <= 4 && + element.closest('.o_readonly_modifier') && ( element.closest('[name="quantity"]') || element.closest('[name="quantity_done"]') || element.closest('[name="reserved_availability"]') )) { - console.log('Safe Decimal Observer: Adding decimals to quantity', text); + console.log('Ultra-Safe Decimal Observer: Adding decimals to readonly quantity', text); - // Double-check before updating - if (element.textContent.trim() === text && document.contains(element)) { + // Triple-check before updating + if (element.textContent.trim() === text && + document.contains(element) && + element.closest('.o_readonly_modifier')) { element.innerHTML = `${text}.000`; return true; } @@ -64,12 +82,12 @@ function safelyApplyDecimalStyling(element) { // Do NOT process numbers with commas that don't have decimal points // These are likely thousands separators (e.g., 8,500 should stay as 8,500) if (text.match(/^\d{1,3}(?:,\d{3})+$/) && !text.includes('.')) { - console.log('Safe Decimal Observer: Skipping thousands separator number', text); + console.log('Ultra-Safe Decimal Observer: Skipping thousands separator number', text); return false; } } catch (error) { - console.warn('Safe decimal styling error (non-critical):', error); + console.warn('Ultra-safe decimal styling error (non-critical):', error); // Remove the processing flag if there was an error element.removeAttribute('data-decimal-processed'); } @@ -77,79 +95,80 @@ function safelyApplyDecimalStyling(element) { return false; } -// Safe processing of containers -function safelyProcessContainer(container) { +// Ultra-safe processing of containers +function ultraSafelyProcessContainer(container) { if (!container || !document.contains(container)) return; try { - // Find all text-containing elements, but be more selective - const elements = container.querySelectorAll('td:not([data-decimal-processed]), span:not([data-decimal-processed]), .o_field_monetary:not([data-decimal-processed]), .o_field_float:not([data-decimal-processed])'); + // Only process elements in readonly contexts to avoid editable field conflicts + const elements = container.querySelectorAll('.o_readonly_modifier td:not([data-decimal-processed]), .o_readonly_modifier span:not([data-decimal-processed]), .o_field_monetary.o_readonly_modifier:not([data-decimal-processed]), .o_field_float.o_readonly_modifier:not([data-decimal-processed])'); elements.forEach(element => { - // Only process leaf elements and skip if Owl is managing them + // Only process leaf elements in readonly contexts if ((element.children.length === 0 || (element.children.length === 1 && element.children[0].tagName === 'SPAN')) && - !element.hasAttribute('data-owl-updating')) { + !element.hasAttribute('data-owl-updating') && + !element.closest('.o_field_widget:not(.o_readonly_modifier)')) { safelyApplyDecimalStyling(element); } }); } catch (error) { - console.warn('Safe container processing error (non-critical):', error); + console.warn('Ultra-safe container processing error (non-critical):', error); } } -// More conservative observer -const safeObserver = new MutationObserver((mutations) => { - // Debounce the processing to avoid conflicts - clearTimeout(safeObserver.timeout); - safeObserver.timeout = setTimeout(() => { +// Ultra-conservative observer with longer debounce +const ultraSafeObserver = new MutationObserver((mutations) => { + // Longer debounce to avoid conflicts with user interactions + clearTimeout(ultraSafeObserver.timeout); + ultraSafeObserver.timeout = setTimeout(() => { try { mutations.forEach((mutation) => { if (mutation.type === 'childList') { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE && document.contains(node)) { - // Use requestAnimationFrame to ensure we run after Owl updates - requestAnimationFrame(() => { - safelyProcessContainer(node); - }); + // Use longer delay to ensure Owl has finished all updates + setTimeout(() => { + ultraSafelyProcessContainer(node); + }, 500); // Much longer delay } }); } }); } catch (error) { - console.warn('Safe observer error (non-critical):', error); + console.warn('Ultra-safe observer error (non-critical):', error); } - }, 200); // Debounce for 200ms + }, 500); // Longer debounce for 500ms }); -// Start observing safely -function startSafeObserver() { - console.log('Safe Decimal Observer: Starting conservative observation'); +// Start ultra-safe observing +function startUltraSafeObserver() { + console.log('Ultra-Safe Decimal Observer: Starting ultra-conservative observation'); - // Process existing content safely - requestAnimationFrame(() => { - safelyProcessContainer(document.body); - }); + // Process existing content safely with delay + setTimeout(() => { + ultraSafelyProcessContainer(document.body); + }, 1000); // Initial delay to let everything load - // Start observing for new content with reduced frequency - safeObserver.observe(document.body, { + // Start observing for new content with very reduced frequency + ultraSafeObserver.observe(document.body, { childList: true, subtree: true }); - // Periodic processing with longer intervals to reduce conflicts + // Much less frequent periodic processing to reduce conflicts setInterval(() => { - requestAnimationFrame(() => { - safelyProcessContainer(document.body); - }); - }, 5000); // Every 5 seconds instead of 2 + ultraSafelyProcessContainer(document.body); + }, 10000); // Every 10 seconds instead of 5 } -// Start safely +// Start ultra-safely if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', startSafeObserver); + document.addEventListener('DOMContentLoaded', () => { + setTimeout(startUltraSafeObserver, 2000); // Extra delay + }); } else { - startSafeObserver(); + setTimeout(startUltraSafeObserver, 2000); // Extra delay } -console.log('Safe decimal observer loaded'); \ No newline at end of file +console.log('Ultra-safe decimal observer loaded'); \ No newline at end of file