diff --git a/static/src/views/decimal_observer.js b/static/src/views/decimal_observer.js index ab92fac..f97066b 100644 --- a/static/src/views/decimal_observer.js +++ b/static/src/views/decimal_observer.js @@ -5,15 +5,15 @@ console.log('Loading ultra-safe decimal observer...'); // Function to safely apply decimal styling to any element function safelyApplyDecimalStyling(element) { if (!element || !element.textContent || !document.contains(element)) return false; - + // Skip if already processed or if Owl is updating this element - if (element.querySelector('.o_decimal') || + if (element.querySelector('.o_decimal') || element.classList.contains('o_decimal') || 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 || + if (element.isContentEditable || element.closest('.o_field_widget:not(.o_readonly_modifier)') || element.closest('[contenteditable="true"]') || element.closest('input') || @@ -24,88 +24,96 @@ function safelyApplyDecimalStyling(element) { element.hasAttribute('contenteditable')) { return false; } - + try { const text = element.textContent.trim(); if (!text) return false; - + // Mark as processed to avoid conflicts element.setAttribute('data-decimal-processed', 'true'); - + // Improved patterns that properly distinguish thousands separators from decimal separators const patterns = [ // Currency with decimal: Rp 6,210,000.00 (comma for thousands, dot for decimal) - /^(.*Rp\s*)(\d{1,3}(?:,\d{3})*)\.(\d{2})(.*)$/, - // Standard decimal with thousands: 1,234.56 (comma for thousands, dot for decimal) - /^(.*)(\d{1,3}(?:,\d{3})*)\.(\d+)(.*)$/, - // Simple decimal without thousands: 240.000, 123.45 - /^(.*)(\d+)\.(\d+)(.*)$/ + /^(.*Rp[\s\u00A0]*)(\d[\d,\s\u00A0]*)\.(\d{2})(.*)$/, + // Generic decimal with thousand separators (dot as decimal) + /^(.*?)([\d,\s\u00A0]*\d)\.(\d+)(.*)$/, + // Simple decimal + /^(.*?)(\d+)\.(\d+)(.*)$/ ]; - + for (const pattern of patterns) { const match = text.match(pattern); if (match) { const [, prefix, integerPart, decimalPart, suffix] = match; - - console.log('Ultra-Safe Decimal Observer: Styling decimal number', text); - + + // console.log('Ultra-Safe Decimal Observer: MATCH FOUND', {text, prefix, integerPart, decimalPart, suffix}); + // 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)')) { + // For tfoot, we are more permissive because they are always readonly + if (element.textContent.trim() === text && + document.contains(element) && + (!element.closest('.o_field_widget:not(.o_readonly_modifier)') || element.closest('tfoot'))) { + + console.log('Ultra-Safe Decimal Observer: STYLING', text, element.tagName, element.className); const newHTML = `${prefix || ''}${integerPart}.${decimalPart}${suffix || ''}`; element.innerHTML = newHTML; return true; } } } - + // 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 && + 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"]') - )) { + element.closest('[name="quantity"]') || + element.closest('[name="quantity_done"]') || + element.closest('[name="reserved_availability"]') + )) { console.log('Ultra-Safe Decimal Observer: Adding decimals to readonly quantity', text); - + // Triple-check before updating - if (element.textContent.trim() === text && - document.contains(element) && + if (element.textContent.trim() === text && + document.contains(element) && element.closest('.o_readonly_modifier')) { element.innerHTML = `${text}.000`; return true; } } - + // 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('Ultra-Safe Decimal Observer: Skipping thousands separator number', text); return false; } - + } catch (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'); } - + return false; } // Ultra-safe processing of containers function ultraSafelyProcessContainer(container) { if (!container || !document.contains(container)) return; - + try { - // 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])'); - + // Select elements likely to contain formatted decimals in readonly contexts or footers + const elements = container.querySelectorAll('.o_readonly_modifier td:not([data-decimal-processed]), .o_readonly_modifier span:not([data-decimal-processed]), tfoot td:not([data-decimal-processed]), tfoot span:not([data-decimal-processed]), .o_list_number:not([data-decimal-processed])'); + elements.forEach(element => { - // Only process leaf elements in readonly contexts - if ((element.children.length === 0 || + // Optimization: If a TD has a SPAN, we prefer to style the SPAN to preserve tooltips + if (element.tagName === 'TD' && element.querySelector('span')) { + return; + } + + // Only process leaf elements or elements with very simple structures + if ((element.children.length === 0 || (element.children.length === 1 && element.children[0].tagName === 'SPAN')) && !element.hasAttribute('data-owl-updating') && !element.closest('.o_field_widget:not(.o_readonly_modifier)')) { @@ -144,18 +152,18 @@ const ultraSafeObserver = new MutationObserver((mutations) => { // Start ultra-safe observing function startUltraSafeObserver() { console.log('Ultra-Safe Decimal Observer: Starting ultra-conservative observation'); - + // Process existing content safely with delay setTimeout(() => { ultraSafelyProcessContainer(document.body); }, 1000); // Initial delay to let everything load - + // Start observing for new content with very reduced frequency ultraSafeObserver.observe(document.body, { childList: true, subtree: true }); - + // Much less frequent periodic processing to reduce conflicts setInterval(() => { ultraSafelyProcessContainer(document.body);