fix the decimal style at certain area
This commit is contained in:
parent
0642f7ca84
commit
086bd6a944
74
README.md
Normal file
74
README.md
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# Web Decimal Style
|
||||||
|
|
||||||
|
This module enhances the display of decimal numbers in Odoo by styling the decimal part differently from the integer part.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## Recent Fixes
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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"
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
## 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.
|
||||||
@ -0,0 +1 @@
|
|||||||
|
from . import models
|
||||||
@ -8,18 +8,17 @@
|
|||||||
to wrap the decimal part in a CSS class for custom styling.
|
to wrap the decimal part in a CSS class for custom styling.
|
||||||
""",
|
""",
|
||||||
'author': 'Suherdy Yacob',
|
'author': 'Suherdy Yacob',
|
||||||
'depends': ['web', 'account'],
|
'depends': ['web', 'account', 'stock'],
|
||||||
'data': [],
|
'data': [],
|
||||||
'assets': {
|
'assets': {
|
||||||
'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/fields/formatters_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/float_field.xml',
|
'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/fields/monetary_field.xml',
|
||||||
'web_decimal_style/static/src/views/purchase_dashboard.xml',
|
|
||||||
'web_decimal_style/static/src/views/purchase_dashboard_patch.js',
|
|
||||||
'web_decimal_style/static/src/views/list_view_patch.xml',
|
|
||||||
],
|
],
|
||||||
'web.assets_backend_lazy': [
|
'web.assets_backend_lazy': [
|
||||||
'web_decimal_style/static/src/views/pivot_view_patch.xml',
|
'web_decimal_style/static/src/views/pivot_view_patch.xml',
|
||||||
|
|||||||
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
# No models in this module
|
||||||
Binary file not shown.
@ -1,37 +1,56 @@
|
|||||||
import { localization } from "@web/core/l10n/localization";
|
import { localization } from "@web/core/l10n/localization";
|
||||||
import { markup } from "@odoo/owl";
|
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.
|
* 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
|
* @param {string} formattedValue
|
||||||
* @returns {import("@odoo/owl").Markup|string}
|
* @returns {import("@odoo/owl").Markup|string}
|
||||||
*/
|
*/
|
||||||
export function wrapDecimal(formattedValue) {
|
export function wrapDecimal(formattedValue) {
|
||||||
if (typeof formattedValue !== "string" || !formattedValue) {
|
// Basic validation
|
||||||
return formattedValue;
|
if (!formattedValue || typeof formattedValue !== "string") {
|
||||||
}
|
|
||||||
// If it's already a markup object or contains our span, don't double wrap
|
|
||||||
if (typeof formattedValue === "object" || formattedValue.includes('class="o_decimal"')) {
|
|
||||||
return formattedValue;
|
return formattedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const decimalPoint = localization.decimalPoint;
|
// 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)) {
|
if (formattedValue.includes(decimalPoint)) {
|
||||||
const parts = formattedValue.split(decimalPoint);
|
const parts = formattedValue.split(decimalPoint);
|
||||||
const integerPart = parts.slice(0, -1).join(decimalPoint);
|
if (parts.length === 2) {
|
||||||
const decimalPartWithSymbol = parts[parts.length - 1];
|
const integerPart = parts[0];
|
||||||
|
const decimalPart = parts[1];
|
||||||
|
|
||||||
// Try to separate digits from trailing symbols/spaces
|
// Simple regex to separate digits from symbols
|
||||||
const match = decimalPartWithSymbol.match(/^(\d+)(.*)$/);
|
const match = decimalPart.match(/^(\d+)(.*)$/);
|
||||||
if (match) {
|
if (match) {
|
||||||
const digits = match[1];
|
const digits = match[1];
|
||||||
const rest = match[2];
|
const symbols = match[2];
|
||||||
return markup(`${integerPart}<span class="o_decimal">${decimalPoint}${digits}</span>${rest}`);
|
// Use simple class without inline styles
|
||||||
} else {
|
const result = markup(`${integerPart}<span class="o_decimal">${decimalPoint}${digits}</span>${symbols}`);
|
||||||
// Fallback: wrap the whole decimal part if it doesn't start with digits
|
console.log('wrapDecimal: Wrapped', formattedValue);
|
||||||
return markup(`${integerPart}<span class="o_decimal">${decimalPoint}${decimalPartWithSymbol}</span>`);
|
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;
|
return formattedValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,66 @@
|
|||||||
|
/* Web Decimal Style - Improved readability for better accessibility */
|
||||||
|
|
||||||
|
/* Base decimal styling - Better contrast for 40+ users */
|
||||||
.o_decimal {
|
.o_decimal {
|
||||||
font-size: 0.85em !important;
|
font-size: 0.8em !important;
|
||||||
opacity: 0.8 !important;
|
opacity: 0.85 !important;
|
||||||
display: inline-block;
|
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;
|
||||||
}
|
}
|
||||||
109
static/src/views/decimal_observer.js
Normal file
109
static/src/views/decimal_observer.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
console.log('Loading comprehensive decimal observer...');
|
||||||
|
|
||||||
|
// Function to apply decimal styling to any element
|
||||||
|
function applyDecimalStyling(element) {
|
||||||
|
if (!element || !element.textContent) return;
|
||||||
|
|
||||||
|
// Skip if already processed
|
||||||
|
if (element.querySelector('.o_decimal') || element.classList.contains('o_decimal')) return;
|
||||||
|
|
||||||
|
const text = element.textContent.trim();
|
||||||
|
if (!text) return;
|
||||||
|
|
||||||
|
// Pattern for decimal numbers (including currency formats)
|
||||||
|
const patterns = [
|
||||||
|
// Standard decimal: 123.45, 1,234.56
|
||||||
|
/^(.*)(\d{1,3}(?:[.,]\d{3})*)[.,](\d+)(.*)$/,
|
||||||
|
// Currency with Rp: Rp 6,210,000.00
|
||||||
|
/^(.*Rp\s*)(\d{1,3}(?:[.,]\d{3})*)[.,](\d+)(.*)$/,
|
||||||
|
// Simple decimal: 240.000
|
||||||
|
/^(.*)(\d+)[.](\d+)(.*)$/
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const pattern of patterns) {
|
||||||
|
const match = text.match(pattern);
|
||||||
|
if (match) {
|
||||||
|
const [, prefix, integerPart, decimalPart, suffix] = match;
|
||||||
|
|
||||||
|
console.log('Decimal Observer: Styling', text);
|
||||||
|
|
||||||
|
// Create new HTML with decimal styling
|
||||||
|
const newHTML = `${prefix || ''}${integerPart}<span class="o_decimal">.${decimalPart}</span>${suffix || ''}`;
|
||||||
|
element.innerHTML = newHTML;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle whole numbers in quantity contexts
|
||||||
|
if (text.match(/^\d+$/) && (
|
||||||
|
element.closest('[name="quantity"]') ||
|
||||||
|
element.closest('.o_field_float') ||
|
||||||
|
element.closest('td') && element.closest('table')
|
||||||
|
)) {
|
||||||
|
console.log('Decimal Observer: Adding decimals to whole number', text);
|
||||||
|
element.innerHTML = `${text}<span class="o_decimal">.000</span>`;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process all elements in a container
|
||||||
|
function processContainer(container) {
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
// Find all text-containing elements
|
||||||
|
const elements = container.querySelectorAll('td, span, div, .o_field_monetary, .o_field_float');
|
||||||
|
|
||||||
|
elements.forEach(element => {
|
||||||
|
// Only process leaf elements (no child elements with text)
|
||||||
|
if (element.children.length === 0 ||
|
||||||
|
(element.children.length === 1 && element.children[0].tagName === 'SPAN')) {
|
||||||
|
applyDecimalStyling(element);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observer for dynamic content
|
||||||
|
const observer = new MutationObserver((mutations) => {
|
||||||
|
mutations.forEach((mutation) => {
|
||||||
|
if (mutation.type === 'childList') {
|
||||||
|
mutation.addedNodes.forEach((node) => {
|
||||||
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
// Process the new node and its children
|
||||||
|
processContainer(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start observing when DOM is ready
|
||||||
|
function startObserver() {
|
||||||
|
console.log('Decimal Observer: Starting comprehensive observation');
|
||||||
|
|
||||||
|
// Process existing content
|
||||||
|
processContainer(document.body);
|
||||||
|
|
||||||
|
// Start observing for new content
|
||||||
|
observer.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also process content periodically for dynamic updates
|
||||||
|
setInterval(() => {
|
||||||
|
processContainer(document.body);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start immediately or when DOM is ready
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', startObserver);
|
||||||
|
} else {
|
||||||
|
startObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Comprehensive decimal observer loaded');
|
||||||
@ -5,6 +5,8 @@ import { MonetaryField } from "@web/views/fields/monetary/monetary_field";
|
|||||||
import { FloatField } from "@web/views/fields/float/float_field";
|
import { FloatField } from "@web/views/fields/float/float_field";
|
||||||
import { formatMonetary, formatFloat } from "@web/views/fields/formatters";
|
import { formatMonetary, formatFloat } from "@web/views/fields/formatters";
|
||||||
|
|
||||||
|
console.log('Loading Web Decimal Style formatters patch...');
|
||||||
|
|
||||||
// Patch Components for Form Views
|
// Patch Components for Form Views
|
||||||
patch(MonetaryField.prototype, {
|
patch(MonetaryField.prototype, {
|
||||||
get formattedValue() {
|
get formattedValue() {
|
||||||
@ -16,7 +18,23 @@ patch(MonetaryField.prototype, {
|
|||||||
currencyId: this.currencyId,
|
currencyId: this.currencyId,
|
||||||
noSymbol: !this.props.readonly || this.props.hideSymbol,
|
noSymbol: !this.props.readonly || this.props.hideSymbol,
|
||||||
});
|
});
|
||||||
return this.props.readonly ? wrapDecimal(res) : res;
|
|
||||||
|
// For readonly fields, apply decimal styling via DOM manipulation
|
||||||
|
if (this.props.readonly) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const element = this.el?.querySelector('.o_field_monetary');
|
||||||
|
if (element && !element.querySelector('.o_decimal')) {
|
||||||
|
const text = element.textContent;
|
||||||
|
const match = text?.match(/^(.*)(\d{1,3}(?:[.,]\d{3})*)[.,](\d+)(.*)$/);
|
||||||
|
if (match) {
|
||||||
|
const [, prefix, integerPart, decimalPart, suffix] = match;
|
||||||
|
element.innerHTML = `${prefix}${integerPart}<span class="o_decimal">.${decimalPart}</span>${suffix}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -42,48 +60,50 @@ patch(FloatField.prototype, {
|
|||||||
} else {
|
} else {
|
||||||
res = formatFloat(this.value, { ...options, humanReadable: false });
|
res = formatFloat(this.value, { ...options, humanReadable: false });
|
||||||
}
|
}
|
||||||
return this.props.readonly ? wrapDecimal(res) : res;
|
|
||||||
|
// For readonly fields, apply decimal styling via DOM manipulation
|
||||||
|
if (this.props.readonly) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const element = this.el?.querySelector('.o_field_float');
|
||||||
|
if (element && !element.querySelector('.o_decimal')) {
|
||||||
|
const text = element.textContent;
|
||||||
|
const match = text?.match(/^(.*)(\d+)[.](\d+)(.*)$/);
|
||||||
|
if (match) {
|
||||||
|
const [, prefix, integerPart, decimalPart, suffix] = match;
|
||||||
|
element.innerHTML = `${prefix}${integerPart}<span class="o_decimal">.${decimalPart}</span>${suffix}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Patch Tax Totals (Account module)
|
// Enhanced Registry Patching for List Views
|
||||||
try {
|
|
||||||
const { TaxTotalsComponent, TaxGroupComponent } = require("@account/components/tax_totals/tax_totals");
|
|
||||||
if (TaxTotalsComponent) {
|
|
||||||
patch(TaxTotalsComponent.prototype, {
|
|
||||||
formatMonetary(value) {
|
|
||||||
const res = formatMonetary(value, { currencyId: this.totals.currency_id });
|
|
||||||
return wrapDecimal(res);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (TaxGroupComponent) {
|
|
||||||
patch(TaxGroupComponent.prototype, {
|
|
||||||
formatMonetary(value) {
|
|
||||||
const res = formatMonetary(value, { currencyId: this.props.totals.currency_id });
|
|
||||||
return wrapDecimal(res);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log("Account module not loaded or could not be patched, skipping TaxTotals patching");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also patch Registry for List Views
|
|
||||||
const formattersRegistry = registry.category("formatters");
|
const formattersRegistry = registry.category("formatters");
|
||||||
|
|
||||||
function patchRegistryFormatter(name) {
|
// Store original formatters
|
||||||
if (formattersRegistry.contains(name)) {
|
const originalFormatters = new Map();
|
||||||
|
|
||||||
|
function patchFormatter(name) {
|
||||||
|
if (formattersRegistry.contains(name) && !originalFormatters.has(name)) {
|
||||||
const original = formattersRegistry.get(name);
|
const original = formattersRegistry.get(name);
|
||||||
|
originalFormatters.set(name, original);
|
||||||
|
|
||||||
formattersRegistry.add(name, (...args) => {
|
formattersRegistry.add(name, (...args) => {
|
||||||
const res = original(...args);
|
const res = original(...args);
|
||||||
return wrapDecimal(res);
|
|
||||||
|
// Return the result as-is, let DOM observer handle styling
|
||||||
|
// This avoids markup escaping issues
|
||||||
|
return res;
|
||||||
}, { force: true });
|
}, { force: true });
|
||||||
|
|
||||||
|
console.log('Web Decimal Style: Patched formatter', name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
patchRegistryFormatter("float");
|
// Patch all numeric formatters
|
||||||
patchRegistryFormatter("monetary");
|
['float', 'monetary', 'percentage', 'float_factor', 'float_time'].forEach(patchFormatter);
|
||||||
patchRegistryFormatter("percentage");
|
|
||||||
patchRegistryFormatter("float_factor");
|
console.log('Web Decimal Style formatters patch loaded successfully');
|
||||||
patchRegistryFormatter("float_time");
|
|
||||||
|
|||||||
54
static/src/views/inventory_decimal_patch.js
Normal file
54
static/src/views/inventory_decimal_patch.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import { patch } from "@web/core/utils/patch";
|
||||||
|
import { ListRenderer } from "@web/views/list/list_renderer";
|
||||||
|
|
||||||
|
console.log('Loading inventory decimal patch...');
|
||||||
|
|
||||||
|
// Post-render DOM manipulation approach to avoid markup escaping issues
|
||||||
|
patch(ListRenderer.prototype, {
|
||||||
|
async render() {
|
||||||
|
const result = await super.render();
|
||||||
|
|
||||||
|
// After rendering, find and modify decimal numbers in the DOM
|
||||||
|
setTimeout(() => {
|
||||||
|
this.applyDecimalStyling();
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
applyDecimalStyling() {
|
||||||
|
if (!this.el) return;
|
||||||
|
|
||||||
|
// Find all cells in the list table
|
||||||
|
const cells = this.el.querySelectorAll('td, .o_data_cell');
|
||||||
|
|
||||||
|
cells.forEach(cell => {
|
||||||
|
// Skip if already processed
|
||||||
|
if (cell.querySelector('.o_decimal')) return;
|
||||||
|
|
||||||
|
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 DOM decimal styling to:', text);
|
||||||
|
|
||||||
|
// Create new HTML structure
|
||||||
|
const newHTML = `${prefix}${integerPart}<span class="o_decimal">${decimalPoint}${decimalPart}</span>${suffix}`;
|
||||||
|
cell.innerHTML = newHTML;
|
||||||
|
}
|
||||||
|
// Handle whole numbers by adding .000
|
||||||
|
else if (text.match(/^\d+$/)) {
|
||||||
|
console.log('Adding decimals to whole number:', text);
|
||||||
|
cell.innerHTML = `${text}<span class="o_decimal">.000</span>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Inventory decimal patch loaded');
|
||||||
@ -6,7 +6,7 @@
|
|||||||
<attribute name="t-esc"></attribute>
|
<attribute name="t-esc"></attribute>
|
||||||
<attribute name="t-out">getFormattedVariation(cell)</attribute>
|
<attribute name="t-out">getFormattedVariation(cell)</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//div[hasclass('o_value')]" position="attributes">
|
<xpath expr="//div[hasclass('o_value') and @t-else='1']" position="attributes">
|
||||||
<attribute name="t-esc"></attribute>
|
<attribute name="t-esc"></attribute>
|
||||||
<attribute name="t-out">getFormattedValue(cell)</attribute>
|
<attribute name="t-out">getFormattedValue(cell)</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user