refactor: rewrite decimal observer to use Odoo localization for robust and accurate styling of decimal parts.

This commit is contained in:
Suherdy Yacob 2026-02-03 13:23:55 +07:00
parent 19e19de89a
commit f2ac13c446
9 changed files with 144 additions and 553 deletions

View File

@ -4,71 +4,50 @@ This module enhances the display of decimal numbers in Odoo by styling the decim
## Features ## Features
- Wraps decimal parts in a CSS class for custom styling - **Visual Enhancement**: Decimal parts are styled to be smaller (80% font size) and slightly transparent (0.7 opacity), making large numbers easier to read.
- Works across all numeric field types (float, monetary, percentage, etc.) - **Universal Application**: Works across **all** views (List, Form, Kanban, Pivot, etc.) and all fields (Float, Monetary, Quantity).
- **Fixed**: Proper decimal formatting in inventory detailed operations - **Non-Invasive Architecture**: Uses a safe DOM Observer approach instead of patching Odoo's internal JS formatters. This ensures maximum compatibility with other modules and Odoo updates.
- Enhanced support for stock moves and inventory operations - **Localization Aware**: Correctly handles decimal separators (dot or comma) based on the user's language settings.
- Handles escaped HTML markup properly in list views
## Recent Fixes ## Technical Details
### Inventory Operations Fix ### Architecture
- **Issue**: Decimal formatting was not working in inventory detailed operations The module has been refactored to use a **MutationObserver** approach:
- **Root Cause**: Previous implementation stripped decimal formatting from stock moves to avoid HTML escaping issues 1. **Global Observer**: A `MutationObserver` watches the document body for changes.
- **Solution**: 2. **Safe Text Scanning**: It scans text nodes using a `TreeWalker` to find numbers.
- Improved markup handling to preserve decimal formatting while avoiding escaping issues 3. **Regex Matching**: It uses a strict Regular Expression to identify decimal parts.
- Added specific patches for stock move line renderers - It dynamically builds the regex using Odoo's `localization.decimalPoint` setting.
- Enhanced the `wrapDecimal` function to handle inventory-specific cases - Example (English): Matches `123.45` (.45 is styled).
- Added proper view inheritance for stock move line views - Example (Indonesian): Matches `123,45` (,45 is styled).
- **Smart Exclusion**: It intelligently ignores thousands separators (e.g., `1,200` is **not** matched in English/US locale).
4. **DOM Wrapping**: Matches are wrapped in a `<span class="o_decimal">` element for styling.
### Technical Changes ### Advantages
1. **Enhanced List View Patch**: Instead of stripping formatting from stock moves, now properly handles escaped HTML - **No Conflicts**: Does not override `formatFloat` or `formatMonetary` functions, avoiding conflicts with other modules that modify formatting.
2. **New Stock Move Patch**: Added specific handling for stock move line renderers and enhanced formatter registry - **Robust**: Works even on custom widgets or non-standard views, as long as the number is rendered as text.
3. **Improved wrapDecimal Function**: Better regex handling for various number formats
4. **Enhanced Formatter Registry**: Comprehensive patching of all numeric formatters used in inventory
5. **Added Stock Dependency**: Module now depends on stock module for proper integration
## Usage ## Usage
The module automatically applies decimal styling to all numeric fields. The decimal part will appear smaller and slightly transparent compared to the integer part. The module automatically applies decimal styling to all numeric fields. No configuration is required.
## Debugging ## Debugging
### Console Debugging ### Console
1. Open browser developer tools (F12) You can verify the module is running by checking the browser console:
2. Go to Console tab - Look for: `"Web Decimal Style: Loading DOM Decorator observer..."`
3. Look for messages starting with:
- "Web Decimal Style module loaded successfully"
- "Universal Decimal Patch: Formatting"
- "Stock Operations: Applying decimal formatting"
### Manual Testing ### Troubleshooting
In the browser console, you can test the decimal formatting function: If styling is not applied:
```javascript 1. **Check Localization**: Ensure your user's language setting has the correct "Decimal Separator" configured.
// Test the wrapDecimal function 2. **Browser Cache**: Clear your browser cache or force refresh (Ctrl+Shift+R) to ensure the new JS is loaded.
testDecimalStyle('123.45') 3. **Inspect Element**: Right-click a number. You should see the decimal part wrapped in:
``` ```html
<span class="o_decimal">.00</span>
### Visual Debugging ```
The CSS includes a debug indicator (🔸) that appears before each decimal part. This is currently enabled to help identify when decimal formatting is applied.
### Troubleshooting Steps
1. **Check module loading**: Look for "Web Decimal Style module loaded successfully" in console
2. **Check field detection**: Look for logging messages when viewing inventory operations
3. **Verify decimal detection**: The module should detect values with decimal points (e.g., "123.45")
4. **Check CSS application**: Look for `<span class="o_decimal">` elements in the HTML
If decimal formatting is not working:
1. Refresh the page after module update
2. Clear browser cache
3. Check browser console for JavaScript errors
4. Verify the module is properly installed and upgraded
## Installation ## Installation
1. Install the module 1. Install the module.
2. Restart Odoo 2. Restart Odoo.
3. Update the module list 3. Upgrade the module `web_decimal_style`.
4. Install "Web Decimal Style" 4. Refresh your browser.
The formatting will be applied automatically to all numeric fields across the system, including inventory operations.

View File

@ -14,6 +14,7 @@
'web.assets_backend': [ 'web.assets_backend': [
'web_decimal_style/static/src/css/decimal_style.css', 'web_decimal_style/static/src/css/decimal_style.css',
'web_decimal_style/static/src/core/utils/numbers_patch.js', 'web_decimal_style/static/src/core/utils/numbers_patch.js',
'web_decimal_style/static/src/views/decimal_observer.js',
'web_decimal_style/static/src/views/fields/formatters_patch.js', 'web_decimal_style/static/src/views/fields/formatters_patch.js',
'web_decimal_style/static/src/views/tax_totals_patch.js', 'web_decimal_style/static/src/views/tax_totals_patch.js',
'web_decimal_style/static/src/views/fields/float_field.xml', 'web_decimal_style/static/src/views/fields/float_field.xml',

View File

@ -1,56 +1,4 @@
import { localization } from "@web/core/l10n/localization"; // Web Decimal Style: wrapDecimal deprecated, now identity function.
import { markup } from "@odoo/owl";
/**
* Wraps the decimal part of a formatted number string in a span for styling.
* Simplified version without inline styles to avoid HTML escaping.
* @param {string} formattedValue
* @returns {import("@odoo/owl").Markup|string}
*/
export function wrapDecimal(formattedValue) { export function wrapDecimal(formattedValue) {
// Basic validation
if (!formattedValue || typeof formattedValue !== "string") {
return formattedValue;
}
// If it's already wrapped, don't double wrap
if (formattedValue.includes('class="o_decimal"')) {
return formattedValue;
}
const decimalPoint = localization.decimalPoint || '.';
// Handle numbers with decimal points
if (formattedValue.includes(decimalPoint)) {
const parts = formattedValue.split(decimalPoint);
if (parts.length === 2) {
const integerPart = parts[0];
const decimalPart = parts[1];
// Simple regex to separate digits from symbols
const match = decimalPart.match(/^(\d+)(.*)$/);
if (match) {
const digits = match[1];
const symbols = match[2];
// Use simple class without inline styles
const result = markup(`${integerPart}<span class="o_decimal">${decimalPoint}${digits}</span>${symbols}`);
return result;
}
}
}
// Handle whole numbers - force decimal formatting for inventory
if (formattedValue.match(/^\d+$/)) {
const result = markup(`${formattedValue}<span class="o_decimal">.000</span>`);
return result;
}
return formattedValue; return formattedValue;
} }

View File

@ -1,66 +1,8 @@
/* Web Decimal Style - Improved readability for better accessibility */ /* Web Decimal Style - CSS Override Approach */
/* Base decimal styling - Better contrast for 40+ users */ /* Base decimal styling */
.o_decimal { .o_decimal {
font-size: 0.8em !important; font-size: 0.80em !important;
opacity: 0.85 !important; opacity: 0.7 !important;
display: inline-block !important; display: inline-block;
color: #666 !important;
font-weight: 400 !important;
vertical-align: baseline !important;
position: relative !important;
}
/* Ultra-specific selectors to ensure styling applies */
html body .o_web_client .o_action_manager .o_action .o_view_controller .o_renderer .o_list_renderer .o_list_table tbody tr td .o_decimal,
html body .o_web_client .o_action_manager .o_action .o_view_controller .o_renderer .o_list_renderer .o_list_table .o_data_row .o_data_cell .o_decimal,
html body .o_web_client .o_action_manager .o_decimal,
html body .o_web_client .o_decimal,
html body .o_decimal,
.o_web_client .o_decimal,
.o_action_manager .o_decimal,
.o_list_table .o_decimal,
.o_field_float .o_decimal,
.o_field_monetary .o_decimal,
.o_list_renderer .o_decimal,
.o_data_row .o_decimal,
.o_data_cell .o_decimal,
td .o_decimal,
div .o_decimal,
span .o_decimal,
table .o_decimal {
font-size: 0.8em !important;
opacity: 0.85 !important;
display: inline-block !important;
color: #666 !important;
font-weight: 400 !important;
vertical-align: baseline !important;
background: none !important;
border: none !important;
margin: 0 !important;
padding: 0 !important;
}
/* Stock-specific targeting */
.o_stock_move_line .o_decimal,
.o_stock_move .o_decimal,
[data-model="stock.move.line"] .o_decimal,
[data-model="stock.move"] .o_decimal {
font-size: 0.8em !important;
opacity: 0.85 !important;
color: #666 !important;
font-weight: 400 !important;
}
/* Force styling on any element with o_decimal class */
*[class*="o_decimal"] {
font-size: 0.8em !important;
opacity: 0.85 !important;
color: #666 !important;
font-weight: 400 !important;
}
/* No debug indicator */
.o_decimal::before {
content: none !important;
} }

View File

@ -1,184 +1,117 @@
/** @odoo-module **/ /** @odoo-module **/
console.log('Loading ultra-safe decimal observer...'); import { localization } from "@web/core/l10n/localization";
// Function to safely apply decimal styling to any element console.log('Web Decimal Style: Loading DOM Decorator observer...');
function safelyApplyDecimalStyling(element) {
if (!element || !element.textContent || !document.contains(element)) return false;
// Skip if already processed or if Owl is updating this element let timeout;
if (element.querySelector('.o_decimal') || const processedNodes = new WeakSet();
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 function processNode(textNode) {
if (element.isContentEditable || if (processedNodes.has(textNode)) return;
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 { // Safety check: is it still in doc?
const text = element.textContent.trim(); if (!document.contains(textNode)) return;
if (!text) return false;
// Mark as processed to avoid conflicts // Check parent
element.setAttribute('data-decimal-processed', 'true'); const parent = textNode.parentNode;
if (!parent) return;
// Improved patterns that properly distinguish thousands separators from decimal separators // Skip unsafe parents
const patterns = [ const tagName = parent.tagName;
// Currency with decimal: Rp 6,210,000.00 (comma for thousands, dot for decimal) if (['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'OPTION', 'TITLE'].includes(tagName)) return;
/^(.*Rp[\s\u00A0]*)(\d[\d,\s\u00A0]*)\.(\d{2})(.*)$/, if (parent.isContentEditable) return;
// Generic decimal with thousand separators (dot as decimal) if (parent.closest && parent.closest('[contenteditable="true"]')) return;
/^(.*?)([\d,\s\u00A0]*\d)\.(\d+)(.*)$/, if (parent.classList.contains('o_decimal')) return; // Already wrapped
// Simple decimal
/^(.*?)(\d+)\.(\d+)(.*)$/
];
for (const pattern of patterns) { const text = textNode.nodeValue;
const match = text.match(pattern); if (!text || text.length < 3) return;
if (match) {
const [, prefix, integerPart, decimalPart, suffix] = match;
// console.log('Ultra-Safe Decimal Observer: MATCH FOUND', {text, prefix, integerPart, decimalPart, suffix}); // Get the correct decimal point from Odoo localization
const decimalPoint = localization.decimalPoint || '.';
// Escape special regex characters (like dot)
const escapedPoint = decimalPoint.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
// Triple-check the element is safe to modify // STRICT REGEX: (\d)(decimal_point)(\d+)
// For tfoot, we are more permissive because they are always readonly // We only create a new range if we find a digit, followed by the specific decimal point, followed by digits.
if (element.textContent.trim() === text && // This strictly avoids matching thousands separators (unless thousands separator == decimal point, which shouldn't happen).
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); // We match 'g' to find it anywhere, but typically we take the first one.
const newHTML = `${prefix || ''}${integerPart}<span class="o_decimal">.${decimalPart}</span>${suffix || ''}`; const regex = new RegExp(`(\\d)(${escapedPoint})(\\d+)`, 'g');
element.innerHTML = newHTML;
return true;
}
}
}
// Handle whole numbers in quantity contexts (be even more selective) const match = regex.exec(text);
// Only add decimals to numbers that are clearly quantities in readonly contexts if (match) {
if (text.match(/^\d+$/) && text.length > 0 && text.length <= 4 && // match[0] = "0.00"
element.closest('.o_readonly_modifier') && ( // match[1] = "0" (last int digit)
element.closest('[name="quantity"]') || // match[2] = "." (decimal point)
element.closest('[name="quantity_done"]') || // match[3] = "00" (decimals)
element.closest('[name="reserved_availability"]')
)) {
console.log('Ultra-Safe Decimal Observer: Adding decimals to readonly quantity', text);
// Triple-check before updating const lastIntDigit = match[1];
if (element.textContent.trim() === text && const decimals = match[3];
document.contains(element) &&
element.closest('.o_readonly_modifier')) {
element.innerHTML = `${text}<span class="o_decimal">.000</span>`;
return true;
}
}
// Do NOT process numbers with commas that don't have decimal points // Split at match start + length of lastIntDigit
// These are likely thousands separators (e.g., 8,500 should stay as 8,500) const splitIndex = match.index + lastIntDigit.length;
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) { const middleNode = textNode.splitText(splitIndex); // Starts with decimal point
console.warn('Ultra-safe decimal styling error (non-critical):', error); const endNode = middleNode.splitText(1 + decimals.length); // Rest
// Remove the processing flag if there was an error
element.removeAttribute('data-decimal-processed');
}
return false; // middleNode contains ".00"
}
// Ultra-safe processing of containers const span = document.createElement('span');
function ultraSafelyProcessContainer(container) { span.className = 'o_decimal';
if (!container || !document.contains(container)) return; span.textContent = middleNode.nodeValue;
try {
// 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 => {
// 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)')) {
safelyApplyDecimalStyling(element);
}
});
} catch (error) {
console.warn('Ultra-safe container processing error (non-critical):', error);
}
}
// 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 { try {
mutations.forEach((mutation) => { parent.replaceChild(span, middleNode);
if (mutation.type === 'childList') { processedNodes.add(textNode);
mutation.addedNodes.forEach((node) => { processedNodes.add(endNode);
if (node.nodeType === Node.ELEMENT_NODE && document.contains(node)) { processedNodes.add(span.firstChild);
// Use longer delay to ensure Owl has finished all updates } catch (e) {
setTimeout(() => { console.warn('Web Decimal Style: Failed to replace node', e);
ultraSafelyProcessContainer(node);
}, 500); // Much longer delay
}
});
}
});
} catch (error) {
console.warn('Ultra-safe observer error (non-critical):', error);
} }
}, 500); // Longer debounce for 500ms }
}
function scanDocument() {
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null);
const nodesToProcess = [];
while (walker.nextNode()) {
nodesToProcess.push(walker.currentNode);
}
for (const node of nodesToProcess) {
processNode(node);
}
}
function scheduleUpdate() {
if (timeout) cancelAnimationFrame(timeout);
timeout = requestAnimationFrame(scanDocument);
}
const observer = new MutationObserver((mutations) => {
let shouldUpdate = false;
for (const mutation of mutations) {
if (mutation.type === 'childList' || mutation.type === 'characterData') {
// Avoid reacting to our own changes
if (mutation.target && mutation.target.classList && mutation.target.classList.contains('o_decimal')) continue;
if (mutation.target.parentElement && mutation.target.parentElement.classList.contains('o_decimal')) continue;
shouldUpdate = true;
}
}
if (shouldUpdate) {
scheduleUpdate();
}
}); });
// Start ultra-safe observing if (document.body) {
function startUltraSafeObserver() { observer.observe(document.body, { childList: true, subtree: true, characterData: true });
// console.log('Ultra-Safe Decimal Observer: Starting ultra-conservative observation'); scheduleUpdate();
// 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);
// }, 10000); // Every 10 seconds instead of 5
}
// Start ultra-safely
/*
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
setTimeout(startUltraSafeObserver, 2000); // Extra delay
});
} else { } else {
setTimeout(startUltraSafeObserver, 2000); // Extra delay document.addEventListener('DOMContentLoaded', () => {
} observer.observe(document.body, { childList: true, subtree: true, characterData: true });
*/ scheduleUpdate();
});
console.log('Ultra-safe decimal observer disabled'); }

View File

@ -1,108 +1,6 @@
import { registry } from "@web/core/registry"; /** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { wrapDecimal } from "@web_decimal_style/core/utils/numbers_patch"; // Web Decimal Style: Formatters patching disabled in favor of CSS Custom Highlight API.
import { MonetaryField } from "@web/views/fields/monetary/monetary_field"; console.log('Web Decimal Style: Formatters patch disabled.');
import { FloatField } from "@web/views/fields/float/float_field";
import { formatMonetary, formatFloat } from "@web/views/fields/formatters";
// Safe field patching with error handling
try {
// Patch Components for Form Views
patch(MonetaryField.prototype, {
get formattedValue() {
if (this.props.inputType === "number" && !this.props.readonly && this.value) {
return this.value;
}
const res = formatMonetary(this.value, {
digits: this.currencyDigits,
currencyId: this.currencyId,
noSymbol: !this.props.readonly || this.props.hideSymbol,
});
// Start of Owl Safe Decimal Styling
if (this.props.readonly) {
return wrapDecimal(res);
}
// End of Owl Safe Decimal Styling
return res;
}
});
patch(FloatField.prototype, {
get formattedValue() {
if (
!this.props.formatNumber ||
(this.props.inputType === "number" && !this.props.readonly && this.value)
) {
return this.value;
}
const options = {
digits: this.props.digits,
field: this.props.record.fields[this.props.name],
};
let res;
if (this.props.humanReadable && !this.state.hasFocus) {
res = formatFloat(this.value, {
...options,
humanReadable: true,
decimals: this.props.decimals,
});
} else {
res = formatFloat(this.value, { ...options, humanReadable: false });
}
// Start of Owl Safe Decimal Styling
if (this.props.readonly) {
return wrapDecimal(res);
}
// End of Owl Safe Decimal Styling
return res;
}
});
} catch (error) {
console.warn('Field patching error (non-critical):', error);
}
// Safe Registry Patching for List Views
// Safe Registry Patching for List Views
try {
const formattersRegistry = registry.category("formatters");
// Store original formatters
const originalFormatters = new Map();
function safelyPatchFormatter(name) {
if (formattersRegistry.contains(name) && !originalFormatters.has(name)) {
try {
const original = formattersRegistry.get(name);
originalFormatters.set(name, original);
formattersRegistry.add(name, (...args) => {
try {
const res = original(...args);
// Safely wrap with Owl Markup
return wrapDecimal(res);
} catch (error) {
console.warn(`Formatter ${name} error (non-critical):`, error);
return original(...args); // Fallback to original
}
}, { force: true });
} catch (error) {
console.warn(`Error patching formatter ${name} (non-critical):`, error);
}
}
}
// Patch all numeric formatters safely
['float', 'monetary', 'percentage', 'float_factor', 'float_time'].forEach(safelyPatchFormatter);
} catch (error) {
console.warn('Registry patching error (non-critical):', error);
}

View File

@ -1,81 +1,4 @@
/** @odoo-module **/ /** @odoo-module **/
import { patch } from "@web/core/utils/patch"; // Web Decimal Style: Inventory decimal patch disabled in favor of CSS Custom Highlight API.
import { ListRenderer } from "@web/views/list/list_renderer"; console.log('Web Decimal Style: Inventory decimal patch disabled.');
console.log('Loading inventory decimal patch...');
// Safe DOM manipulation approach to avoid Owl lifecycle conflicts
// Safe DOM manipulation approach to avoid Owl lifecycle conflicts
// patch(ListRenderer.prototype, {
// async render() {
// const result = await super.render();
// // Schedule decimal styling after Owl completes its rendering cycle
// this.scheduleDecimalStyling();
// return result;
// },
// scheduleDecimalStyling() {
// // Use requestAnimationFrame to ensure we run after Owl's DOM updates
// requestAnimationFrame(() => {
// try {
// this.applyDecimalStyling();
// } catch (error) {
// console.warn('Decimal styling error (non-critical):', error);
// }
// });
// },
// applyDecimalStyling() {
// if (!this.el || !document.contains(this.el)) return;
// // Find all cells in the list table, but be more careful about DOM manipulation
// const cells = this.el.querySelectorAll('td:not([data-decimal-processed]), .o_data_cell:not([data-decimal-processed])');
// cells.forEach(cell => {
// try {
// // Skip if cell is being updated by Owl
// if (cell.hasAttribute('data-owl-updating')) return;
// // Mark as processed to avoid double processing
// cell.setAttribute('data-decimal-processed', 'true');
// const text = cell.textContent?.trim();
// if (!text) return;
// // Check for decimal numbers (including currency)
// const decimalMatch = text.match(/^(.*)(\d+)([.,])(\d+)(.*)$/);
// if (decimalMatch) {
// const [, prefix, integerPart, decimalPoint, decimalPart, suffix] = decimalMatch;
// console.log('Applying safe DOM decimal styling to:', text);
// // Create new HTML structure safely
// const newHTML = `${prefix}${integerPart}<span class="o_decimal">${decimalPoint}${decimalPart}</span>${suffix}`;
// // Only update if the content hasn't changed
// if (cell.textContent.trim() === text) {
// // cell.innerHTML = newHTML;
// }
// }
// // Handle whole numbers by adding .000
// else if (text.match(/^\d+$/) && text.length > 0) {
// console.log('Adding decimals to whole number safely:', text);
// // Only update if the content hasn't changed
// if (cell.textContent.trim() === text) {
// // cell.innerHTML = `${text}<span class="o_decimal">.000</span>`;
// }
// }
// } catch (error) {
// console.warn('Error processing cell (non-critical):', error);
// }
// });
// }
// });
console.log('Inventory decimal patch loaded');

View File

@ -1,11 +1,4 @@
/** @odoo-module **/ /** @odoo-module **/
import { PurchaseDashBoard } from "@purchase/views/purchase_dashboard"; // Web Decimal Style: Purchase dashboard patch disabled in favor of CSS Custom Highlight API.
import { patch } from "@web/core/utils/patch"; console.log('Web Decimal Style: Purchase dashboard patch disabled.');
import { wrapDecimal } from "@web_decimal_style/core/utils/numbers_patch";
patch(PurchaseDashBoard.prototype, {
formatDecimal(value) {
return wrapDecimal(value);
}
});

View File

@ -1,32 +1,6 @@
/** @odoo-module **/ /** @odoo-module **/
import { patch } from "@web/core/utils/patch"; // Web Decimal Style: Tax totals patch disabled in favor of CSS Custom Highlight API.
import { wrapDecimal } from "@web_decimal_style/core/utils/numbers_patch"; console.log('Web Decimal Style: Tax totals patch disabled.');
import { TaxTotalsComponent } from "@account/components/tax_totals/tax_totals";
// Patch TaxTotalsComponent (the main widget)
patch(TaxTotalsComponent.prototype, {
formatMonetary(value) {
const result = super.formatMonetary(value);
return wrapDecimal(result);
}
});
// We also need to patch TaxGroupComponent which is a sub-component defined in the same file
// However, it's not exported directly in Odoo 16/17/18/19 usually, but accessible via TaxTotalsComponent.components
if (TaxTotalsComponent.components && TaxTotalsComponent.components.TaxGroupComponent) {
const TaxGroupComponent = TaxTotalsComponent.components.TaxGroupComponent;
patch(TaxGroupComponent.prototype, {
formatMonetary(value) {
const result = super.formatMonetary(value);
return wrapDecimal(result);
}
});
} else {
console.warn('Web Decimal Style: Could not find TaxGroupComponent to patch');
}