Compare commits

...

7 Commits
main ... 19.0

12 changed files with 183 additions and 563 deletions

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
# Python
*.py[cod]
__pycache__/
*.so
# Odoo
*.po~
*.pot~
# Editor / System
.DS_Store
.vscode/
*.swp
*.swo
*~

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,11 +14,12 @@
'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/fields/formatters_patch.js',
'web_decimal_style/static/src/views/inventory_decimal_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',
'web_decimal_style/static/src/views/fields/monetary_field.xml',
'web_decimal_style/static/src/views/list_view_patch.xml',
],
'web.assets_backend_lazy': [
'web_decimal_style/static/src/views/pivot_view_patch.xml',

View File

@ -1,56 +1,4 @@
import { localization } from "@web/core/l10n/localization";
import { markup } from "@odoo/owl";
console.log('Web Decimal Style: numbers_patch.js loaded');
/**
* 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 || '.';
console.log('wrapDecimal: Processing', formattedValue);
// 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}`);
console.log('wrapDecimal: Wrapped', formattedValue);
return result;
}
}
}
// Handle whole numbers - force decimal formatting for inventory
if (formattedValue.match(/^\d+$/)) {
const result = markup(`${formattedValue}<span class="o_decimal">.000</span>`);
console.log('wrapDecimal: Added decimals to whole number', formattedValue);
return result;
}
console.log('wrapDecimal: No formatting applied to', 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 {
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,174 +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*)(\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+)(.*)$/
];
// 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: Styling decimal number', text);
// 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
if (element.textContent.trim() === text &&
document.contains(element) &&
!element.closest('.o_field_widget:not(.o_readonly_modifier)')) {
const newHTML = `${prefix || ''}${integerPart}<span class="o_decimal">.${decimalPart}</span>${suffix || ''}`;
element.innerHTML = newHTML;
return true;
}
}
}
// 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).
// 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);
// We match 'g' to find it anywhere, but typically we take the first one.
const regex = new RegExp(`(\\d)(${escapedPoint})(\\d+)`, 'g');
// 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 match = regex.exec(text);
if (match) {
// match[0] = "0.00"
// match[1] = "0" (last int digit)
// match[2] = "." (decimal point)
// match[3] = "00" (decimals)
// 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;
}
const lastIntDigit = match[1];
const decimals = match[3];
} 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');
}
// Split at match start + length of lastIntDigit
const splitIndex = match.index + lastIntDigit.length;
return false;
}
const middleNode = textNode.splitText(splitIndex); // Starts with decimal point
const endNode = middleNode.splitText(1 + decimals.length); // Rest
// Ultra-safe processing of containers
function ultraSafelyProcessContainer(container) {
if (!container || !document.contains(container)) return;
// middleNode contains ".00"
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])');
const span = document.createElement('span');
span.className = 'o_decimal';
span.textContent = middleNode.nodeValue;
elements.forEach(element => {
// 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.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 loaded');

View File

@ -1,135 +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";
/** @odoo-module **/
console.log('Loading Web Decimal Style formatters patch...');
// Web Decimal Style: Formatters patching disabled in favor of CSS Custom Highlight API.
console.log('Web Decimal Style: Formatters patch disabled.');
// 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,
});
// For readonly fields, apply decimal styling via safe DOM manipulation
if (this.props.readonly) {
// Use a longer timeout to avoid Owl lifecycle conflicts
setTimeout(() => {
try {
const element = this.el?.querySelector('.o_field_monetary');
if (element && !element.querySelector('.o_decimal') && document.contains(element)) {
const text = element.textContent;
const match = text?.match(/^(.*)(\d{1,3}(?:[.,]\d{3})*)[.,](\d+)(.*)$/);
if (match && element.textContent === text) { // Double-check content hasn't changed
const [, prefix, integerPart, decimalPart, suffix] = match;
element.innerHTML = `${prefix}${integerPart}<span class="o_decimal">.${decimalPart}</span>${suffix}`;
}
}
} catch (error) {
console.warn('Monetary field decimal styling error (non-critical):', error);
}
}, 150); // Longer delay to avoid Owl conflicts
}
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 });
}
// For readonly fields, apply decimal styling via safe DOM manipulation
if (this.props.readonly) {
setTimeout(() => {
try {
const element = this.el?.querySelector('.o_field_float');
if (element && !element.querySelector('.o_decimal') && document.contains(element)) {
const text = element.textContent;
const match = text?.match(/^(.*)(\d+)[.](\d+)(.*)$/);
if (match && element.textContent === text) { // Double-check content hasn't changed
const [, prefix, integerPart, decimalPart, suffix] = match;
element.innerHTML = `${prefix}${integerPart}<span class="o_decimal">.${decimalPart}</span>${suffix}`;
}
}
} catch (error) {
console.warn('Float field decimal styling error (non-critical):', error);
}
}, 150);
}
return res;
}
});
} catch (error) {
console.warn('Field patching error (non-critical):', error);
}
// 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);
// Return the result as-is, let DOM observer handle styling
// This avoids markup escaping issues and Owl conflicts
return res;
} catch (error) {
console.warn(`Formatter ${name} error (non-critical):`, error);
return original(...args); // Fallback to original
}
}, { force: true });
console.log('Web Decimal Style: Safely patched formatter', name);
} 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);
}
console.log('Web Decimal Style formatters patch loaded successfully');

View File

@ -1,78 +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
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

@ -2,15 +2,15 @@
<templates xml:space="preserve">
<!-- Patch List View Footer Aggregates -->
<t t-inherit="web.ListRenderer" t-inherit-mode="extension">
<xpath expr="//tfoot//span[@t-esc='aggregate.value']" position="replace">
<span t-out="aggregate.value" t-att-data-tooltip="aggregate.help"/>
<xpath expr="//tfoot//t[@t-esc='aggregates[column.name].value']" position="replace">
<t t-out="aggregates[column.name].value"/>
</xpath>
</t>
<!-- Patch List View Group Row Aggregates -->
<t t-inherit="web.ListRenderer.GroupRow" t-inherit-mode="extension">
<xpath expr="//t[@t-esc='formatAggregateValue(group, column)']" position="replace">
<t t-if="column.type === 'field'" t-out="formatAggregateValue(group, column)"/>
<xpath expr="//t[@t-esc='groupAggregate.value']" position="replace">
<t t-out="groupAggregate.value"/>
</xpath>
</t>
</templates>

View File

@ -2,11 +2,7 @@
<templates xml:space="preserve">
<!-- Patch Pivot View Cells -->
<t t-inherit="web.PivotRenderer" t-inherit-mode="extension">
<xpath expr="//div[hasclass('o_variation')]" position="attributes">
<attribute name="t-esc"></attribute>
<attribute name="t-out">getFormattedVariation(cell)</attribute>
</xpath>
<xpath expr="//div[hasclass('o_value') and @t-else='1']" position="attributes">
<xpath expr="//div[hasclass('o_value')]" position="attributes">
<attribute name="t-esc"></attribute>
<attribute name="t-out">getFormattedValue(cell)</attribute>
</xpath>

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

@ -0,0 +1,6 @@
/** @odoo-module **/
// Web Decimal Style: Tax totals patch disabled in favor of CSS Custom Highlight API.
console.log('Web Decimal Style: Tax totals patch disabled.');