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
- Wraps decimal parts in a CSS class for custom styling
- Works across all numeric field types (float, monetary, percentage, etc.)
- **Fixed**: Proper decimal formatting in inventory detailed operations
- Enhanced support for stock moves and inventory operations
- Handles escaped HTML markup properly in list views
- **Visual Enhancement**: Decimal parts are styled to be smaller (80% font size) and slightly transparent (0.7 opacity), making large numbers easier to read.
- **Universal Application**: Works across **all** views (List, Form, Kanban, Pivot, etc.) and all fields (Float, Monetary, Quantity).
- **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.
- **Localization Aware**: Correctly handles decimal separators (dot or comma) based on the user's language settings.
## Recent Fixes
## Technical Details
### Inventory Operations Fix
- **Issue**: Decimal formatting was not working in inventory detailed operations
- **Root Cause**: Previous implementation stripped decimal formatting from stock moves to avoid HTML escaping issues
- **Solution**:
- Improved markup handling to preserve decimal formatting while avoiding escaping issues
- Added specific patches for stock move line renderers
- Enhanced the `wrapDecimal` function to handle inventory-specific cases
- Added proper view inheritance for stock move line views
### Architecture
The module has been refactored to use a **MutationObserver** approach:
1. **Global Observer**: A `MutationObserver` watches the document body for changes.
2. **Safe Text Scanning**: It scans text nodes using a `TreeWalker` to find numbers.
3. **Regex Matching**: It uses a strict Regular Expression to identify decimal parts.
- It dynamically builds the regex using Odoo's `localization.decimalPoint` setting.
- Example (English): Matches `123.45` (.45 is styled).
- 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
1. **Enhanced List View Patch**: Instead of stripping formatting from stock moves, now properly handles escaped HTML
2. **New Stock Move Patch**: Added specific handling for stock move line renderers and enhanced formatter registry
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
### Advantages
- **No Conflicts**: Does not override `formatFloat` or `formatMonetary` functions, avoiding conflicts with other modules that modify formatting.
- **Robust**: Works even on custom widgets or non-standard views, as long as the number is rendered as text.
## 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
### Console Debugging
1. Open browser developer tools (F12)
2. Go to Console tab
3. Look for messages starting with:
- "Web Decimal Style module loaded successfully"
- "Universal Decimal Patch: Formatting"
- "Stock Operations: Applying decimal formatting"
### Console
You can verify the module is running by checking the browser console:
- Look for: `"Web Decimal Style: Loading DOM Decorator observer..."`
### Manual Testing
In the browser console, you can test the decimal formatting function:
```javascript
// Test the wrapDecimal function
testDecimalStyle('123.45')
```
### 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
### Troubleshooting
If styling is not applied:
1. **Check Localization**: Ensure your user's language setting has the correct "Decimal Separator" configured.
2. **Browser Cache**: Clear your browser cache or force refresh (Ctrl+Shift+R) to ensure the new JS is loaded.
3. **Inspect Element**: Right-click a number. You should see the decimal part wrapped in:
```html
<span class="o_decimal">.00</span>
```
## Installation
1. Install the module
2. Restart Odoo
3. Update the module list
4. Install "Web Decimal Style"
The formatting will be applied automatically to all numeric fields across the system, including inventory operations.
1. Install the module.
2. Restart Odoo.
3. Upgrade the module `web_decimal_style`.
4. Refresh your browser.

View File

@ -14,6 +14,7 @@
'web.assets_backend': [
'web_decimal_style/static/src/css/decimal_style.css',
'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/tax_totals_patch.js',
'web_decimal_style/static/src/views/fields/float_field.xml',

View File

@ -1,56 +1,4 @@
import { localization } from "@web/core/l10n/localization";
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}
*/
// Web Decimal Style: wrapDecimal deprecated, now identity function.
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;
}

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 {
font-size: 0.8em !important;
opacity: 0.85 !important;
display: inline-block !important;
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;
font-size: 0.80em !important;
opacity: 0.7 !important;
display: inline-block;
}

View File

@ -1,184 +1,117 @@
/** @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
function safelyApplyDecimalStyling(element) {
if (!element || !element.textContent || !document.contains(element)) return false;
console.log('Web Decimal Style: Loading DOM Decorator observer...');
// Skip if already processed or if Owl is updating this element
if (element.querySelector('.o_decimal') ||
element.classList.contains('o_decimal') ||
element.hasAttribute('data-decimal-processed') ||
element.hasAttribute('data-owl-updating')) return false;
let timeout;
const processedNodes = new WeakSet();
// 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;
}
function processNode(textNode) {
if (processedNodes.has(textNode)) return;
try {
const text = element.textContent.trim();
if (!text) return false;
// Safety check: is it still in doc?
if (!document.contains(textNode)) return;
// Mark as processed to avoid conflicts
element.setAttribute('data-decimal-processed', 'true');
// Check parent
const parent = textNode.parentNode;
if (!parent) return;
// 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\u00A0]*)(\d[\d,\s\u00A0]*)\.(\d{2})(.*)$/,
// Generic decimal with thousand separators (dot as decimal)
/^(.*?)([\d,\s\u00A0]*\d)\.(\d+)(.*)$/,
// Simple decimal
/^(.*?)(\d+)\.(\d+)(.*)$/
];
// Skip unsafe parents
const tagName = parent.tagName;
if (['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'OPTION', 'TITLE'].includes(tagName)) return;
if (parent.isContentEditable) return;
if (parent.closest && parent.closest('[contenteditable="true"]')) return;
if (parent.classList.contains('o_decimal')) return; // Already wrapped
for (const pattern of patterns) {
const match = text.match(pattern);
if (match) {
const [, prefix, integerPart, decimalPart, suffix] = match;
const text = textNode.nodeValue;
if (!text || text.length < 3) return;
// 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
// 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'))) {
// STRICT REGEX: (\d)(decimal_point)(\d+)
// We only create a new range if we find a digit, followed by the specific decimal point, followed by digits.
// This strictly avoids matching thousands separators (unless thousands separator == decimal point, which shouldn't happen).
console.log('Ultra-Safe Decimal Observer: STYLING', text, element.tagName, element.className);
const newHTML = `${prefix || ''}${integerPart}<span class="o_decimal">.${decimalPart}</span>${suffix || ''}`;
element.innerHTML = newHTML;
return true;
}
}
}
// We match 'g' to find it anywhere, but typically we take the first one.
const regex = new RegExp(`(\\d)(${escapedPoint})(\\d+)`, 'g');
// 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('Ultra-Safe Decimal Observer: Adding decimals to readonly quantity', text);
const match = regex.exec(text);
if (match) {
// match[0] = "0.00"
// match[1] = "0" (last int digit)
// match[2] = "." (decimal point)
// match[3] = "00" (decimals)
// Triple-check before updating
if (element.textContent.trim() === text &&
document.contains(element) &&
element.closest('.o_readonly_modifier')) {
element.innerHTML = `${text}<span class="o_decimal">.000</span>`;
return true;
}
}
const lastIntDigit = match[1];
const decimals = match[3];
// 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;
}
// Split at match start + length of lastIntDigit
const splitIndex = match.index + lastIntDigit.length;
} 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');
}
const middleNode = textNode.splitText(splitIndex); // Starts with decimal point
const endNode = middleNode.splitText(1 + decimals.length); // Rest
return false;
}
// middleNode contains ".00"
// Ultra-safe processing of containers
function ultraSafelyProcessContainer(container) {
if (!container || !document.contains(container)) return;
const span = document.createElement('span');
span.className = 'o_decimal';
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 {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE && document.contains(node)) {
// Use longer delay to ensure Owl has finished all updates
setTimeout(() => {
ultraSafelyProcessContainer(node);
}, 500); // Much longer delay
}
});
}
});
} catch (error) {
console.warn('Ultra-safe observer error (non-critical):', error);
parent.replaceChild(span, middleNode);
processedNodes.add(textNode);
processedNodes.add(endNode);
processedNodes.add(span.firstChild);
} catch (e) {
console.warn('Web Decimal Style: Failed to replace node', e);
}
}, 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
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);
// }, 10000); // Every 10 seconds instead of 5
}
// Start ultra-safely
/*
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
setTimeout(startUltraSafeObserver, 2000); // Extra delay
});
if (document.body) {
observer.observe(document.body, { childList: true, subtree: true, characterData: true });
scheduleUpdate();
} 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";
import { patch } from "@web/core/utils/patch";
import { wrapDecimal } from "@web_decimal_style/core/utils/numbers_patch";
import { MonetaryField } from "@web/views/fields/monetary/monetary_field";
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);
}
/** @odoo-module **/
// Web Decimal Style: Formatters patching disabled in favor of CSS Custom Highlight API.
console.log('Web Decimal Style: Formatters patch disabled.');

View File

@ -1,81 +1,4 @@
/** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { ListRenderer } from "@web/views/list/list_renderer";
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');
// Web Decimal Style: Inventory decimal patch disabled in favor of CSS Custom Highlight API.
console.log('Web Decimal Style: Inventory decimal patch disabled.');

View File

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

View File

@ -1,32 +1,6 @@
/** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { wrapDecimal } from "@web_decimal_style/core/utils/numbers_patch";
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');
}
// Web Decimal Style: Tax totals patch disabled in favor of CSS Custom Highlight API.
console.log('Web Decimal Style: Tax totals patch disabled.');