176 lines
7.6 KiB
Python
176 lines
7.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
import babel.dates
|
|
import pytz
|
|
import logging
|
|
import odoo
|
|
from odoo import models, api
|
|
from odoo.osv import expression
|
|
from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
|
|
from odoo.tools.misc import posix_to_ldml, file_open
|
|
from odoo import http
|
|
from odoo.http import request
|
|
import werkzeug
|
|
from datetime import datetime
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
def format_date(env, value, lang_code=False, date_format=False):
|
|
if not value:
|
|
return ''
|
|
if isinstance(value, str):
|
|
try:
|
|
# Convert string to datetime object for Odoo 18
|
|
if len(value) > 10: # datetime
|
|
value = datetime.fromisoformat(value.replace('Z', '+00:00'))
|
|
value = odoo.fields.Datetime.context_timestamp(env['res.lang'], value)
|
|
else: # date
|
|
value = datetime.fromisoformat(value)
|
|
except ValueError:
|
|
return ''
|
|
|
|
lang = env['res.lang']._lang_get(lang_code or env.context.get('lang') or 'en_US')
|
|
locale = babel.Locale.parse(lang.code)
|
|
locale = str(locale)
|
|
if locale.lower().startswith('ar'):
|
|
locale = "ar"
|
|
if not date_format:
|
|
date_format = posix_to_ldml(lang.date_format, locale=locale)
|
|
|
|
return babel.dates.format_date(value, format=date_format, locale=locale)
|
|
|
|
|
|
# Only override if not already overridden
|
|
if not hasattr(odoo.tools.misc, '_original_format_date'):
|
|
odoo.tools.misc._original_format_date = odoo.tools.misc.format_date
|
|
odoo.tools.misc.format_date = format_date
|
|
|
|
|
|
class WebClientInherit(http.Controller):
|
|
@http.route('/web/webclient/locale/<string:lang>', type='http', auth="none")
|
|
def load_locale(self, lang):
|
|
magic_file_finding = [lang.replace("_", '-').lower(), lang.split('_')[0]]
|
|
for code in magic_file_finding:
|
|
if code.lower().startswith('ar'):
|
|
code = 'ar-sa'
|
|
try:
|
|
# Updated for Odoo 18 file structure
|
|
file_path = f'web/static/lib/moment/locale/{code}.js'
|
|
file_content = file_open(file_path, 'rb').read()
|
|
|
|
return request.make_response(
|
|
file_content,
|
|
headers=[
|
|
('Content-Type', 'application/javascript; charset=utf-8'),
|
|
('Cache-Control', 'max-age=36000'),
|
|
]
|
|
)
|
|
except (IOError, FileNotFoundError):
|
|
_logger.debug("No moment locale for code %s", code)
|
|
|
|
return request.make_response("", headers=[
|
|
('Content-Type', 'application/javascript'),
|
|
('Cache-Control', 'max-age=36000'),
|
|
])
|
|
|
|
|
|
class BaseModelExtend(models.BaseModel):
|
|
_name = 'basemodel.extend_custom_months'
|
|
_description = 'Base Model Extension for Custom Months'
|
|
|
|
def _register_hook(self):
|
|
# Store original method
|
|
original_method = models.BaseModel._read_group_format_result
|
|
|
|
@api.model
|
|
def _read_group_format_result_custom(self, rows_dict, lazy_groupby):
|
|
"""
|
|
Enhanced version of _read_group_format_result with Arabic locale support.
|
|
Overrides Odoo 18's method to use 'ar' locale instead of 'ar_SY' for Arabic.
|
|
"""
|
|
from odoo.models import READ_GROUP_TIME_GRANULARITY, READ_GROUP_DISPLAY_FORMAT
|
|
from odoo.tools.misc import get_lang
|
|
import datetime
|
|
|
|
for group in lazy_groupby:
|
|
field_name = group.split(':')[0].split('.')[0]
|
|
field = self._fields.get(field_name)
|
|
if not field:
|
|
continue
|
|
|
|
if field.type in ('date', 'datetime'):
|
|
granularity = group.split(':')[1] if ':' in group else 'month'
|
|
if granularity in READ_GROUP_TIME_GRANULARITY:
|
|
# Get locale and modify for Arabic
|
|
lang = get_lang(self.env)
|
|
locale = lang.code
|
|
if locale.lower().startswith('ar'):
|
|
locale = "ar"
|
|
|
|
fmt = DEFAULT_SERVER_DATETIME_FORMAT if field.type == 'datetime' else DEFAULT_SERVER_DATE_FORMAT
|
|
interval = READ_GROUP_TIME_GRANULARITY[granularity]
|
|
elif field.type == "properties":
|
|
self._read_group_format_result_properties(rows_dict, group)
|
|
continue
|
|
|
|
for row in rows_dict:
|
|
value = row.get(group)
|
|
|
|
if isinstance(value, models.BaseModel):
|
|
row[group] = (value.id, value.sudo().display_name) if value else False
|
|
value = value.id
|
|
|
|
if not value and field.type == 'many2many':
|
|
additional_domain = [(field_name, 'not any', [])]
|
|
else:
|
|
additional_domain = [(field_name, '=', value)]
|
|
|
|
if field.type in ('date', 'datetime'):
|
|
if value and isinstance(value, (datetime.date, datetime.datetime)):
|
|
range_start = value
|
|
range_end = value + interval
|
|
if field.type == 'datetime':
|
|
tzinfo = None
|
|
if self._context.get('tz') in pytz.all_timezones_set:
|
|
tzinfo = pytz.timezone(self._context['tz'])
|
|
range_start = tzinfo.localize(range_start).astimezone(pytz.utc)
|
|
range_end = tzinfo.localize(range_end).astimezone(pytz.utc)
|
|
|
|
label = babel.dates.format_datetime(
|
|
range_start, format=READ_GROUP_DISPLAY_FORMAT[granularity],
|
|
tzinfo=tzinfo, locale=locale
|
|
)
|
|
else:
|
|
label = babel.dates.format_date(
|
|
value, format=READ_GROUP_DISPLAY_FORMAT[granularity],
|
|
locale=locale
|
|
)
|
|
# Special case for weeks
|
|
if granularity == 'week':
|
|
from odoo.tools import date_utils
|
|
year, week = date_utils.weeknumber(
|
|
babel.Locale.parse(locale),
|
|
value,
|
|
)
|
|
label = f"W{week} {year:04}"
|
|
|
|
range_start = range_start.strftime(fmt)
|
|
range_end = range_end.strftime(fmt)
|
|
row[group] = label
|
|
row.setdefault('__range', {})[group] = {'from': range_start, 'to': range_end}
|
|
additional_domain = [
|
|
'&',
|
|
(field_name, '>=', range_start),
|
|
(field_name, '<', range_end),
|
|
]
|
|
elif not value:
|
|
row.setdefault('__range', {})[group] = False
|
|
|
|
row['__domain'] = expression.AND([row.get('__domain', []), additional_domain])
|
|
|
|
# Only override if not already overridden
|
|
if not hasattr(models.BaseModel, '_months_custom_override'):
|
|
models.BaseModel._months_custom_override = True
|
|
models.BaseModel._read_group_format_result = _read_group_format_result_custom
|
|
|
|
return super(BaseModelExtend, self)._register_hook() |