17.0 vanilla

This commit is contained in:
Ernad Husremovic 2025-10-03 18:05:14 +02:00
parent 2e65bf056a
commit df627a6bba
328 changed files with 578149 additions and 759311 deletions

View file

@ -16,8 +16,8 @@ __path__ = [
]
import sys
MIN_PY_VERSION = (3, 7)
MAX_PY_VERSION = (3, 12)
MIN_PY_VERSION = (3, 10)
MAX_PY_VERSION = (3, 13)
assert sys.version_info > MIN_PY_VERSION, f"Outdated python version detected, Odoo requires Python >= {'.'.join(map(str, MIN_PY_VERSION))} to run."
#----------------------------------------------------------
@ -68,26 +68,6 @@ import time
if hasattr(time, 'tzset'):
time.tzset()
#----------------------------------------------------------
# PyPDF2 hack
# ensure that zlib does not throw error -5 when decompressing
# because some pdf won't fit into allocated memory
# https://docs.python.org/3/library/zlib.html#zlib.decompressobj
# ----------------------------------------------------------
import PyPDF2
try:
import zlib
def _decompress(data):
zobj = zlib.decompressobj()
return zobj.decompress(data)
import PyPDF2.filters # needed after PyPDF2 2.0.0 and before 2.11.0
PyPDF2.filters.decompress = _decompress
except ImportError:
pass # no fix required
# ---------------------------------------------------------
# some charset are known by Python under a different name
# ---------------------------------------------------------

View file

@ -8,9 +8,6 @@ from . import report
from . import wizard
def post_init(cr, registry):
def post_init(env):
"""Rewrite ICP's to force groups"""
from odoo import api, SUPERUSER_ID
env = api.Environment(cr, SUPERUSER_ID, {})
env['ir.config_parameter'].init(force=True)

View file

@ -21,7 +21,6 @@ The kernel of Odoo, needed for all installation.
'data/report_paperformat_data.xml',
'data/res_country_data.xml',
'data/ir_demo_data.xml',
'data/ir_config_parameter_data.xml',
'security/base_groups.xml',
'security/base_security.xml',
'views/base_menus.xml',
@ -59,7 +58,6 @@ The kernel of Odoo, needed for all installation.
'wizard/base_module_uninstall_views.xml',
'wizard/base_export_language_views.xml',
'wizard/base_partner_merge_views.xml',
'data/ir_actions_data.xml',
'data/ir_demo_failure_data.xml',
'views/ir_profile_views.xml',
'views/res_company_views.xml',
@ -69,16 +67,16 @@ The kernel of Odoo, needed for all installation.
'views/res_country_views.xml',
'views/res_currency_views.xml',
'views/res_users_views.xml',
'views/res_users_identitycheck_views.xml',
'views/ir_property_views.xml',
'views/res_config_settings_views.xml',
'views/report_paperformat_views.xml',
'views/onboarding_views.xml',
'security/ir.model.access.csv',
],
'demo': [
'data/res_company_demo.xml',
'data/res_users_demo.xml',
'data/res_partner_bank_demo.xml',
'data/res_currency_demo.xml',
'data/res_currency_rate_demo.xml',
'data/res_bank_demo.xml',
'data/res_partner_demo.xml',

View file

@ -119,8 +119,8 @@ CREATE TABLE res_partner (
---------------------------------
-- Default data
---------------------------------
insert into res_currency (id, name, symbol) VALUES (1, 'EUR', '');
insert into ir_model_data (name, module, model, noupdate, res_id) VALUES ('EUR', 'base', 'res.currency', true, 1);
insert into res_currency (id, name, symbol) VALUES (1, 'USD', '$');
insert into ir_model_data (name, module, model, noupdate, res_id) VALUES ('USD', 'base', 'res.currency', true, 1);
select setval('res_currency_id_seq', 1);
insert into res_company (id, name, partner_id, currency_id, create_date) VALUES (1, 'My Company', 1, 1, now() at time zone 'UTC');

View file

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<record id="action_server_module_immediate_install" model="ir.actions.server">
<field name="name">Activate Modules</field>
<field name="type">ir.actions.server</field>
<field name="model_id" ref="model_ir_module_module" />
<field name="binding_model_id" ref="model_ir_module_module" />
<field name="state">code</field>
<field name="code">records.button_immediate_install()</field>
</record>
</data>
</odoo>

View file

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- Catchall Email Alias -->
<record id="icp_mail_catchall_alias" model="ir.config_parameter">
<field name="key">mail.catchall.alias</field>
<field name="value">catchall</field>
</record>
<!-- Bounce Email Alias -->
<record id="icp_mail_bounce_alias" model="ir.config_parameter">
<field name="key">mail.bounce.alias</field>
<field name="value">bounce</field>
</record>
<!-- Notifications -->
<record id="icp_mail_default_from" model="ir.config_parameter">
<field name="key">mail.default.from</field>
<field name="value">notifications</field>
</record>
</data>
</odoo>

View file

@ -8,5 +8,16 @@
<field name='interval_number'>1</field>
<field name='interval_type'>days</field>
<field name="numbercall">-1</field>
<field name="priority">3</field>
</record>
<record id="ir_cron_res_users_deletion" model="ir.cron">
<field name="name">Base: Portal Users Deletion</field>
<field name="model_id" ref="base.model_res_users_deletion"/>
<field name="state">code</field>
<field name="code">model._gc_portal_users()</field>
<field name='interval_number'>1</field>
<field name='interval_type'>days</field>
<field name="numbercall">-1</field>
</record>
</odoo>

View file

@ -28,7 +28,7 @@
</div>
<footer>
<button special="cancel" data-hotkey="z" string="Oops, no!" class="btn-primary"/>
<button special="cancel" data-hotkey="x" string="Oops, no!" class="btn-primary"/>
<button name="install_demo" string="Yes, I understand the risks" type="object" class="btn-secondary" data-hotkey="q"/>
</footer>
</form>

View file

@ -101,6 +101,11 @@
<field name="sequence">14</field>
</record>
<record model="ir.module.category" id="module_category_services_appointment">
<field name="name">Appointment</field>
<field name="parent_id" ref="module_category_services"/>
</record>
<record model="ir.module.category" id="module_category_services_field_service">
<field name="name">Field Service</field>
<field name="parent_id" ref="module_category_services"/>

View file

@ -142,6 +142,20 @@
<field name="website">https://www.odoo.com/app/quality?utm_source=db&amp;utm_medium=module</field>
</record>
<record model="ir.module.module" id="base.module_sale_amazon">
<field name="name">sale_amazon</field>
<field name="shortdesc">Amazon Connector</field>
<field name="sequence">320</field>
<field name="category_id" ref="base.module_category_sales_sales"/>
<field name="application" eval="True"/>
<field name="summary">Import Amazon orders and sync deliveries</field>
<field name="license">OEEL-1</field>
<field name="author">Odoo S.A.</field>
<field name="to_buy" eval="True"/>
<field name="icon">/base/static/img/icons/sale_amazon.png</field>
<field name="website">https://www.odoo.com/app/amazon-connector?utm_source=db&amp;utm_medium=module</field>
</record>
<record model="ir.module.module" id="base.module_sale_ebay">
<field name="name">sale_ebay</field>
<field name="shortdesc">eBay Connector</field>

View file

@ -22,3 +22,8 @@ INSERT INTO ir_config_parameter (key, value)
VALUES ('database.is_neutralized', true)
ON CONFLICT (key) DO
UPDATE SET value = true;
-- deactivate webhooks
UPDATE ir_act_server
SET webhook_url = 'neutralization - disable webhook'
WHERE state = 'webhook';

View file

@ -467,22 +467,22 @@ state_es_va,es,"Valladolid","VA"
state_es_bi,es,"Bizkaia (Vizcaya)","BI"
state_es_za,es,"Zamora","ZA"
state_es_z,es,"Zaragoza","Z"
state_my_jhr,my,"Johor","JHR"
state_my_kdh,my,"Kedah","KDH"
state_my_ktn,my,"Kelantan","KTN"
state_my_kul,my,"Kuala Lumpur","KUL"
state_my_lbn,my,"Labuan","LBN"
state_my_mlk,my,"Melaka","MLK"
state_my_nsn,my,"Negeri Sembilan","NSN"
state_my_phg,my,"Pahang","PHG"
state_my_prk,my,"Perak","PRK"
state_my_pls,my,"Perlis","PLS"
state_my_png,my,"Pulau Pinang","PNG"
state_my_pjy,my,"Putrajaya","PJY"
state_my_sbh,my,"Sabah","SBH"
state_my_swk,my,"Sarawak","SWK"
state_my_sgr,my,"Selangor","SGR"
state_my_trg,my,"Terengganu","TRG"
state_my_jhr,my,"Johor","MY-01"
state_my_kdh,my,"Kedah","MY-02"
state_my_ktn,my,"Kelantan","MY-03"
state_my_kul,my,"Kuala Lumpur","MY-14"
state_my_lbn,my,"Labuan","MY-15"
state_my_mlk,my,"Melaka","MY-04"
state_my_nsn,my,"Negeri Sembilan","MY-05"
state_my_phg,my,"Pahang","MY-06"
state_my_prk,my,"Perak","MY-08"
state_my_pls,my,"Perlis","MY-09"
state_my_png,my,"Pulau Pinang","MY-07"
state_my_pjy,my,"Putrajaya","MY-16"
state_my_sbh,my,"Sabah","MY-12"
state_my_swk,my,"Sarawak","MY-13"
state_my_sgr,my,"Selangor","MY-10"
state_my_trg,my,"Terengganu","MY-11"
state_mx_ags,mx,"Aguascalientes","AGU"
state_mx_bc,mx,"Baja California","BCN"
state_mx_bcs,mx,"Baja California Sur","BCS"
@ -1667,3 +1667,84 @@ state_sa_90,sa,"Yanbu commercial city","YNB"
state_sa_91,sa,"Yanbu Industrial City","YBI"
state_sa_92,sa,"Zilfi","ZUL"
state_sa_93,sa,"Zulayfayn","ZUY"
state_uy_01,uy,"Artigas","AR"
state_uy_02,uy,"Canelones","CA"
state_uy_03,uy,"Cerro Largo","CL"
state_uy_04,uy,"Colonia","CO"
state_uy_05,uy,"Durazno","DU"
state_uy_06,uy,"Flores","FS"
state_uy_07,uy,"Florida","FD"
state_uy_08,uy,"Lavalleja","LA"
state_uy_09,uy,"Maldonado","MA"
state_uy_10,uy,"Montevideo","MO"
state_uy_11,uy,"Paysandú","PA"
state_uy_12,uy,"Río Negro","RN"
state_uy_13,uy,"Rivera","RV"
state_uy_14,uy,"Rocha","RO"
state_uy_15,uy,"Salto","SA"
state_uy_16,uy,"San José","SJ"
state_uy_17,uy,"Soriano","SO"
state_uy_18,uy,"Tacuarembó","TA"
state_uy_19,uy,"Treinta y Tres","TT"
state_hk_hk,base.hk,Hong Kong Island,HK
state_hk_kln,base.hk,Kowloon,KLN
state_hk_nt,base.hk,New Territories,NT
state_ke_01,ke,"Baringo",KE-01
state_ke_02,ke,"Bomet",KE-02
state_ke_03,ke,"Bungoma",KE-03
state_ke_04,ke,"Busia",KE-04
state_ke_05,ke,"Elgeyo/Marakwet",KE-05
state_ke_06,ke,"Embu",KE-06
state_ke_07,ke,"Garissa",KE-07
state_ke_08,ke,"Homa Bay",KE-08
state_ke_09,ke,"Isiolo",KE-09
state_ke_10,ke,"Kajiado",KE-10
state_ke_11,ke,"Kakamega",KE-11
state_ke_12,ke,"Kericho",KE-12
state_ke_13,ke,"Kiambu",KE-13
state_ke_14,ke,"Kilifi",KE-14
state_ke_15,ke,"Kirinyaga",KE-15
state_ke_16,ke,"Kisii",KE-16
state_ke_17,ke,"Kisumu",KE-17
state_ke_18,ke,"Kitui",KE-18
state_ke_19,ke,"Kwale",KE-19
state_ke_20,ke,"Laikipia",KE-20
state_ke_21,ke,"Lamu",KE-21
state_ke_22,ke,"Machakos",KE-22
state_ke_23,ke,"Makueni",KE-23
state_ke_24,ke,"Mandera",KE-24
state_ke_25,ke,"Marsabit",KE-25
state_ke_26,ke,"Meru",KE-26
state_ke_27,ke,"Migori",KE-27
state_ke_28,ke,"Mombasa",KE-28
state_ke_29,ke,"Murang'a",KE-29
state_ke_30,ke,"Nairobi City",KE-30
state_ke_31,ke,"Nakuru",KE-31
state_ke_32,ke,"Nandi",KE-32
state_ke_33,ke,"Narok",KE-33
state_ke_34,ke,"Nyamira",KE-34
state_ke_35,ke,"Nyandarua",KE-35
state_ke_36,ke,"Nyeri",KE-36
state_ke_37,ke,"Samburu",KE-37
state_ke_38,ke,"Siaya",KE-38
state_ke_39,ke,"Taita/Taveta",KE-39
state_ke_40,ke,"Tana River",KE-40
state_ke_41,ke,"Tharaka-Nithi",KE-41
state_ke_42,ke,"Trans Nzoia",KE-42
state_ke_43,ke,"Turkana",KE-43
state_ke_44,ke,"Uasin Gishu",KE-44
state_ke_45,ke,"Vihiga",KE-45
state_ke_46,ke,"Wajir",KE-46
state_ke_47,ke,"West Pokot",KE-47
state_jo_aj,jo,"Ajloun",JO-AJ
state_jo_am,jo,"Amman",JO-AM
state_jo_aq,jo,"Aqaba",JO-AQ
state_jo_at,jo,"Tafileh",JO-AT
state_jo_az,jo,"Zarqa",JO-AZ
state_jo_ba,jo,"Balqa",JO-BA
state_jo_ir,jo,"Irbid",JO-IR
state_jo_ja,jo,"Jerash",JO-JA
state_jo_ka,jo,"Karak",JO-KA
state_jo_ma,jo,"Mafraq",JO-MA
state_jo_md,jo,"Madaba",JO-MD
state_jo_mn,jo,"Maan",JO-MN

1 id country_id:id name code
467 state_es_bi es Bizkaia (Vizcaya) BI
468 state_es_za es Zamora ZA
469 state_es_z es Zaragoza Z
470 state_my_jhr my Johor JHR MY-01
471 state_my_kdh my Kedah KDH MY-02
472 state_my_ktn my Kelantan KTN MY-03
473 state_my_kul my Kuala Lumpur KUL MY-14
474 state_my_lbn my Labuan LBN MY-15
475 state_my_mlk my Melaka MLK MY-04
476 state_my_nsn my Negeri Sembilan NSN MY-05
477 state_my_phg my Pahang PHG MY-06
478 state_my_prk my Perak PRK MY-08
479 state_my_pls my Perlis PLS MY-09
480 state_my_png my Pulau Pinang PNG MY-07
481 state_my_pjy my Putrajaya PJY MY-16
482 state_my_sbh my Sabah SBH MY-12
483 state_my_swk my Sarawak SWK MY-13
484 state_my_sgr my Selangor SGR MY-10
485 state_my_trg my Terengganu TRG MY-11
486 state_mx_ags mx Aguascalientes AGU
487 state_mx_bc mx Baja California BCN
488 state_mx_bcs mx Baja California Sur BCS
1667 state_sa_91 sa Yanbu Industrial City YBI
1668 state_sa_92 sa Zilfi ZUL
1669 state_sa_93 sa Zulayfayn ZUY
1670 state_uy_01 uy Artigas AR
1671 state_uy_02 uy Canelones CA
1672 state_uy_03 uy Cerro Largo CL
1673 state_uy_04 uy Colonia CO
1674 state_uy_05 uy Durazno DU
1675 state_uy_06 uy Flores FS
1676 state_uy_07 uy Florida FD
1677 state_uy_08 uy Lavalleja LA
1678 state_uy_09 uy Maldonado MA
1679 state_uy_10 uy Montevideo MO
1680 state_uy_11 uy Paysandú PA
1681 state_uy_12 uy Río Negro RN
1682 state_uy_13 uy Rivera RV
1683 state_uy_14 uy Rocha RO
1684 state_uy_15 uy Salto SA
1685 state_uy_16 uy San José SJ
1686 state_uy_17 uy Soriano SO
1687 state_uy_18 uy Tacuarembó TA
1688 state_uy_19 uy Treinta y Tres TT
1689 state_hk_hk base.hk Hong Kong Island HK
1690 state_hk_kln base.hk Kowloon KLN
1691 state_hk_nt base.hk New Territories NT
1692 state_ke_01 ke Baringo KE-01
1693 state_ke_02 ke Bomet KE-02
1694 state_ke_03 ke Bungoma KE-03
1695 state_ke_04 ke Busia KE-04
1696 state_ke_05 ke Elgeyo/Marakwet KE-05
1697 state_ke_06 ke Embu KE-06
1698 state_ke_07 ke Garissa KE-07
1699 state_ke_08 ke Homa Bay KE-08
1700 state_ke_09 ke Isiolo KE-09
1701 state_ke_10 ke Kajiado KE-10
1702 state_ke_11 ke Kakamega KE-11
1703 state_ke_12 ke Kericho KE-12
1704 state_ke_13 ke Kiambu KE-13
1705 state_ke_14 ke Kilifi KE-14
1706 state_ke_15 ke Kirinyaga KE-15
1707 state_ke_16 ke Kisii KE-16
1708 state_ke_17 ke Kisumu KE-17
1709 state_ke_18 ke Kitui KE-18
1710 state_ke_19 ke Kwale KE-19
1711 state_ke_20 ke Laikipia KE-20
1712 state_ke_21 ke Lamu KE-21
1713 state_ke_22 ke Machakos KE-22
1714 state_ke_23 ke Makueni KE-23
1715 state_ke_24 ke Mandera KE-24
1716 state_ke_25 ke Marsabit KE-25
1717 state_ke_26 ke Meru KE-26
1718 state_ke_27 ke Migori KE-27
1719 state_ke_28 ke Mombasa KE-28
1720 state_ke_29 ke Murang'a KE-29
1721 state_ke_30 ke Nairobi City KE-30
1722 state_ke_31 ke Nakuru KE-31
1723 state_ke_32 ke Nandi KE-32
1724 state_ke_33 ke Narok KE-33
1725 state_ke_34 ke Nyamira KE-34
1726 state_ke_35 ke Nyandarua KE-35
1727 state_ke_36 ke Nyeri KE-36
1728 state_ke_37 ke Samburu KE-37
1729 state_ke_38 ke Siaya KE-38
1730 state_ke_39 ke Taita/Taveta KE-39
1731 state_ke_40 ke Tana River KE-40
1732 state_ke_41 ke Tharaka-Nithi KE-41
1733 state_ke_42 ke Trans Nzoia KE-42
1734 state_ke_43 ke Turkana KE-43
1735 state_ke_44 ke Uasin Gishu KE-44
1736 state_ke_45 ke Vihiga KE-45
1737 state_ke_46 ke Wajir KE-46
1738 state_ke_47 ke West Pokot KE-47
1739 state_jo_aj jo Ajloun JO-AJ
1740 state_jo_am jo Amman JO-AM
1741 state_jo_aq jo Aqaba JO-AQ
1742 state_jo_at jo Tafileh JO-AT
1743 state_jo_az jo Zarqa JO-AZ
1744 state_jo_ba jo Balqa JO-BA
1745 state_jo_ir jo Irbid JO-IR
1746 state_jo_ja jo Jerash JO-JA
1747 state_jo_ka jo Karak JO-KA
1748 state_jo_ma jo Mafraq JO-MA
1749 state_jo_md jo Madaba JO-MD
1750 state_jo_mn jo Maan JO-MN

View file

@ -1,8 +1,8 @@
"id","name","code","iso_code","direction","grouping","decimal_point","thousands_sep","date_format","time_format","week_start"
"base.lang_en","English (US)","en_US","en","Left-to-Right","[3,0]",".",",","%m/%d/%Y","%H:%M:%S","7"
"base.lang_am_ET","Amharic / አምሃርኛ","am_ET","am_ET","Left-to-Right","[3,0]",".",",","%d/%m/%Y","%I:%M:%S","7"
"base.lang_ar","Arabic / الْعَرَبيّة","ar_001","ar","Right-to-Left","[3,0]",".",",","%d %b, %Y","%I:%M:%S","6"
"base.lang_ar_SY","Arabic (Syria) / الْعَرَبيّة","ar_SY","ar_SY","Right-to-Left","[3,0]",".",",","%d %b, %Y","%I:%M:%S","6"
"base.lang_ar","Arabic / الْعَرَبيّة","ar_001","ar","Right-to-Left","[3,0]",".",",","%d %b, %Y","%I:%M:%S %p","6"
"base.lang_ar_SY","Arabic (Syria) / الْعَرَبيّة","ar_SY","ar_SY","Right-to-Left","[3,0]",".",",","%d %b, %Y","%I:%M:%S %p","6"
"base.lang_az","Azerbaijani / Azərbaycanca","az_AZ","az","Left-to-Right","[3,0]",","," ","%d.%m.%Y","%H:%M:%S","1"
"base.lang_eu_ES","Basque / Euskara","eu_ES","eu_ES","Left-to-Right","[]",",",,"%a, %Y.eko %bren %da","%H:%M:%S","1"
"base.lang_bn_IN","Bengali / বাংলা","bn_IN","bn_IN","Left-to-Right","[]",",",,"%A %d %b %Y","%I:%M:%S","1"
@ -21,6 +21,7 @@
"base.lang_en_CA","English (CA)","en_CA","en_CA","Left-to-Right","[3,0]",".",",","%Y-%m-%d","%H:%M:%S","7"
"base.lang_en_GB","English (UK)","en_GB","en_GB","Left-to-Right","[3,0]",".",",","%d/%m/%Y","%H:%M:%S","1"
"base.lang_en_IN","English (IN)","en_IN","en_IN","Left-to-Right","[3,2,0]",".",",","%d/%m/%Y","%H:%M:%S","7"
"base.lang_en_NZ","English (NZ)","en_NZ","en_NZ","Left-to-Right","[3,0]",".",",","%d/%m/%Y","%H:%M:%S","7"
"base.lang_et_EE","Estonian / Eesti keel","et_EE","et","Left-to-Right","[3,0]",","," ","%d.%m.%Y","%H:%M:%S","1"
"base.lang_fi","Finnish / Suomi","fi_FI","fi","Left-to-Right","[3,0]",","," ","%d.%m.%Y","%H.%M.%S","1"
"base.lang_fr_BE","French (BE) / Français (BE)","fr_BE","fr_BE","Left-to-Right","[3,0]",",",".","%d/%m/%Y","%H:%M:%S","1"
@ -43,7 +44,7 @@
"base.lang_km","Khmer / ភាសាខ្មែរ","km_KH","km","Left-to-Right","[3,0]",".",",","%d %B %Y","%H:%M:%S","7"
"base.lang_ko_KP","Korean (KP) / 한국어 (KP)","ko_KP","ko_KP","Left-to-Right","[3,0]",".",",","%m/%d/%Y","%I:%M:%S %p","1"
"base.lang_ko_KR","Korean (KR) / 한국어 (KR)","ko_KR","ko_KR","Left-to-Right","[3,0]",".",",","%Y년 %m월 %d일","%H시 %M분 %S초","7"
"base.lang_lo_LA","Lao / ພາສາລາວ","lo_LA","lo","Left-to-Right","[3,0]",".",",","%d/%m/y","%H:%M:%S","7"
"base.lang_lo_LA","Lao / ພາສາລາວ","lo_LA","lo","Left-to-Right","[3,0]",".",",","%d/%m/%Y","%H:%M:%S","7"
"base.lang_lv","Latvian / latviešu valoda","lv_LV","lv","Left-to-Right","[3,0]",","," ","%Y.%m.%d.","%H:%M:%S","1"
"base.lang_lt","Lithuanian / Lietuvių kalba","lt_LT","lt","Left-to-Right","[3,0]",",",".","%Y-%m-%d","%H:%M:%S","1"
"base.lang_lb","Luxembourgish","lb_LU","lb","Left-to-Right","[3,0]",","," ","%d/%m/%Y","%H:%M:%S","1"
@ -64,6 +65,7 @@ base.lang_my,"Burmese / ဗမာစာ",my_MM,my,"Left-to-Right","[3,3]",".",",
"base.lang_sr@latin","Serbian (Latin) / srpski","sr@latin","sr@latin","Left-to-Right","[]",".",",","%m/%d/%Y","%I:%M:%S %p","7"
"base.lang_sk","Slovak / Slovenský jazyk","sk_SK","sk","Left-to-Right","[3,0]",","," ","%d.%m.%Y","%H:%M:%S","1"
"base.lang_sl_SI","Slovenian / slovenščina","sl_SI","sl","Left-to-Right","[]",","," ","%d. %m. %Y","%H:%M:%S","1"
"base.lang_es_419","Spanish (Latin America) / Español (América Latina)","es_419","es_419","Left-to-Right","[3,0]",",",".","%d/%m/%Y","%H:%M:%S","1"
"base.lang_es_AR","Spanish (AR) / Español (AR)","es_AR","es_AR","Left-to-Right","[3,0]",",",".","%d/%m/%Y","%H:%M:%S","7"
"base.lang_es_BO","Spanish (BO) / Español (BO)","es_BO","es_BO","Left-to-Right","[3,0]",",",".","%d/%m/%Y","%H:%M:%S","1"
"base.lang_es_CL","Spanish (CL) / Español (CL)","es_CL","es_CL","Left-to-Right","[3,0]",",",".","%d/%m/%Y","%H:%M:%S","1"
@ -79,6 +81,7 @@ base.lang_my,"Burmese / ဗမာစာ",my_MM,my,"Left-to-Right","[3,3]",".",",
"base.lang_es_UY","Spanish (UY) / Español (UY)","es_UY","es_UY","Left-to-Right","[3,0]",",",".","%d/%m/%Y","%H:%M:%S","1"
"base.lang_es_VE","Spanish (VE) / Español (VE)","es_VE","es_VE","Left-to-Right","[3,0]",",",".","%d/%m/%Y","%H:%M:%S","7"
"base.lang_es","Spanish / Español","es_ES","es","Left-to-Right","[3,0]",",",".","%d/%m/%Y","%H:%M:%S","1"
"base.lang_sw","Swahili / Kiswahili","sw","sw","Left-to-Right","[3,0]",".",",","%d/%m/%Y","%H:%M:%S","1"
"base.lang_sv_SE","Swedish / Svenska","sv_SE","sv","Left-to-Right","[3,0]",","," ","%Y-%m-%d","%H:%M:%S","1"
"base.lang_th","Thai / ภาษาไทย","th_TH","th","Left-to-Right","[3,0]",".",",","%d/%m/%Y","%H:%M:%S","7"
"base.lang_tl","Tagalog / Filipino","tl_PH","tl","Left-to-Right","[3,0]",".",",","%m/%d/%y","%H:%M:%S","1"

1 id name code iso_code direction grouping decimal_point thousands_sep date_format time_format week_start
2 base.lang_en English (US) en_US en Left-to-Right [3,0] . , %m/%d/%Y %H:%M:%S 7
3 base.lang_am_ET Amharic / አምሃርኛ am_ET am_ET Left-to-Right [3,0] . , %d/%m/%Y %I:%M:%S 7
4 base.lang_ar Arabic / الْعَرَبيّة ar_001 ar Right-to-Left [3,0] . , %d %b, %Y %I:%M:%S %I:%M:%S %p 6
5 base.lang_ar_SY Arabic (Syria) / الْعَرَبيّة ar_SY ar_SY Right-to-Left [3,0] . , %d %b, %Y %I:%M:%S %I:%M:%S %p 6
6 base.lang_az Azerbaijani / Azərbaycanca az_AZ az Left-to-Right [3,0] ,   %d.%m.%Y %H:%M:%S 1
7 base.lang_eu_ES Basque / Euskara eu_ES eu_ES Left-to-Right [] , %a, %Y.eko %bren %da %H:%M:%S 1
8 base.lang_bn_IN Bengali / বাংলা bn_IN bn_IN Left-to-Right [] , %A %d %b %Y %I:%M:%S 1
21 base.lang_en_CA English (CA) en_CA en_CA Left-to-Right [3,0] . , %Y-%m-%d %H:%M:%S 7
22 base.lang_en_GB English (UK) en_GB en_GB Left-to-Right [3,0] . , %d/%m/%Y %H:%M:%S 1
23 base.lang_en_IN English (IN) en_IN en_IN Left-to-Right [3,2,0] . , %d/%m/%Y %H:%M:%S 7
24 base.lang_en_NZ English (NZ) en_NZ en_NZ Left-to-Right [3,0] . , %d/%m/%Y %H:%M:%S 7
25 base.lang_et_EE Estonian / Eesti keel et_EE et Left-to-Right [3,0] ,   %d.%m.%Y %H:%M:%S 1
26 base.lang_fi Finnish / Suomi fi_FI fi Left-to-Right [3,0] ,   %d.%m.%Y %H.%M.%S 1
27 base.lang_fr_BE French (BE) / Français (BE) fr_BE fr_BE Left-to-Right [3,0] , . %d/%m/%Y %H:%M:%S 1
44 base.lang_km Khmer / ភាសាខ្មែរ km_KH km Left-to-Right [3,0] . , %d %B %Y %H:%M:%S 7
45 base.lang_ko_KP Korean (KP) / 한국어 (KP) ko_KP ko_KP Left-to-Right [3,0] . , %m/%d/%Y %I:%M:%S %p 1
46 base.lang_ko_KR Korean (KR) / 한국어 (KR) ko_KR ko_KR Left-to-Right [3,0] . , %Y년 %m월 %d일 %H시 %M분 %S초 7
47 base.lang_lo_LA Lao / ພາສາລາວ lo_LA lo Left-to-Right [3,0] . , %d/%m/y %d/%m/%Y %H:%M:%S 7
48 base.lang_lv Latvian / latviešu valoda lv_LV lv Left-to-Right [3,0] ,   %Y.%m.%d. %H:%M:%S 1
49 base.lang_lt Lithuanian / Lietuvių kalba lt_LT lt Left-to-Right [3,0] , . %Y-%m-%d %H:%M:%S 1
50 base.lang_lb Luxembourgish lb_LU lb Left-to-Right [3,0] ,   %d/%m/%Y %H:%M:%S 1
65 base.lang_sr@latin Serbian (Latin) / srpski sr@latin sr@latin Left-to-Right [] . , %m/%d/%Y %I:%M:%S %p 7
66 base.lang_sk Slovak / Slovenský jazyk sk_SK sk Left-to-Right [3,0] ,   %d.%m.%Y %H:%M:%S 1
67 base.lang_sl_SI Slovenian / slovenščina sl_SI sl Left-to-Right [] ,   %d. %m. %Y %H:%M:%S 1
68 base.lang_es_419 Spanish (Latin America) / Español (América Latina) es_419 es_419 Left-to-Right [3,0] , . %d/%m/%Y %H:%M:%S 1
69 base.lang_es_AR Spanish (AR) / Español (AR) es_AR es_AR Left-to-Right [3,0] , . %d/%m/%Y %H:%M:%S 7
70 base.lang_es_BO Spanish (BO) / Español (BO) es_BO es_BO Left-to-Right [3,0] , . %d/%m/%Y %H:%M:%S 1
71 base.lang_es_CL Spanish (CL) / Español (CL) es_CL es_CL Left-to-Right [3,0] , . %d/%m/%Y %H:%M:%S 1
81 base.lang_es_UY Spanish (UY) / Español (UY) es_UY es_UY Left-to-Right [3,0] , . %d/%m/%Y %H:%M:%S 1
82 base.lang_es_VE Spanish (VE) / Español (VE) es_VE es_VE Left-to-Right [3,0] , . %d/%m/%Y %H:%M:%S 7
83 base.lang_es Spanish / Español es_ES es Left-to-Right [3,0] , . %d/%m/%Y %H:%M:%S 1
84 base.lang_sw Swahili / Kiswahili sw sw Left-to-Right [3,0] . , %d/%m/%Y %H:%M:%S 1
85 base.lang_sv_SE Swedish / Svenska sv_SE sv Left-to-Right [3,0] ,   %Y-%m-%d %H:%M:%S 1
86 base.lang_th Thai / ภาษาไทย th_TH th Left-to-Right [3,0] . , %d/%m/%Y %H:%M:%S 7
87 base.lang_tl Tagalog / Filipino tl_PH tl Left-to-Right [3,0] . , %m/%d/%y %H:%M:%S 1

View file

@ -4,8 +4,7 @@
<record id="main_company" model="res.company">
<field name="name">My Company</field>
<field name="partner_id" ref="main_partner"/>
<field name="currency_id" ref="base.EUR"/>
<field name="favicon" model="res.company" eval="obj()._get_default_favicon(original=True)"/>
<field name="currency_id" ref="base.USD"/>
</record>
</data>
</odoo>

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="main_company" model="res.company">
<field name="name">ProBike Inc</field>
</record>
</data>
</odoo>

View file

@ -246,7 +246,7 @@
<field eval="'%(street)s\n%(street2)s\n%(city)s %(state_code)s %(zip)s\n%(country_name)s'" name="address_format" />
<field name="currency_id" ref="CAD" />
<field eval="1" name="phone_code" />
<field name="vat_label">HST</field>
<field name="vat_label">GST/HST number</field>
</record>
<record id="cc" model="res.country">
<field name="name">Cocos (Keeling) Islands</field>
@ -412,6 +412,8 @@
<field name="currency_id" ref="USD" />
<field eval="593" name="phone_code" />
<field name="vat_label">RUC</field>
<field name='zip_required'>0</field>
<field name="address_format" eval="'%(street)s\n%(street2)s\n%(city)s\n%(country_name)s'" />
</record>
<record id="ee" model="res.country">
<field name="name">Estonia</field>
@ -441,7 +443,7 @@
<record id="es" model="res.country">
<field name="name">Spain</field>
<field name="code">es</field>
<field eval="'%(street)s\n%(street2)s\n%(zip)s %(city)s (%(state_name)s)\n%(country_name)s'" name="address_format"/>
<field eval="'%(street)s\n%(street2)s\n%(zip)s %(city)s\n%(state_name)s\n%(country_name)s'" name="address_format"/>
<field name="currency_id" ref="EUR" />
<field eval="34" name="phone_code" />
<field name="vat_label">VAT</field>
@ -1095,7 +1097,7 @@
<field name="code">nz</field>
<field name="currency_id" ref="NZD" />
<field eval="64" name="phone_code" />
<field name="vat_label">IRD/GST</field>
<field name="vat_label">GST</field>
</record>
<record id="om" model="res.country">
<field name="name">Oman</field>
@ -1298,7 +1300,7 @@
<record id="sl" model="res.country">
<field name="name">Sierra Leone</field>
<field name="code">sl</field>
<field name="currency_id" ref="SLL" />
<field name="currency_id" ref="SLE" />
<field eval="232" name="phone_code" />
</record>
<record id="sm" model="res.country">
@ -1470,6 +1472,7 @@
<field name="code">ug</field>
<field name="currency_id" ref="UGX" />
<field eval="256" name="phone_code" />
<field name="vat_label">TIN</field>
</record>
<record id="uk" model="res.country">
<field name="name">United Kingdom</field>
@ -1499,6 +1502,7 @@
<field name="code">uy</field>
<field name="currency_id" ref="UYU" />
<field eval="598" name="phone_code" />
<field name="vat_label">RUT</field>
</record>
<record id="uz" model="res.country">
<field name="name">Uzbekistan</field>
@ -1585,6 +1589,7 @@
<field name="code">zm</field>
<field name="currency_id" ref="ZMW" />
<field eval="260" name="phone_code" />
<field name="vat_label">TPIN</field>
</record>
<record id="zw" model="res.country">
<field name="name">Zimbabwe</field>
@ -1600,7 +1605,7 @@
</record>
<record id="europe" model="res.country.group">
<field name="name">Europe</field>
<field name="name">European Union</field>
<field name="country_ids" eval="[Command.set([
ref('at'),ref('be'),ref('bg'),ref('hr'),ref('cy'),
ref('cz'),ref('dk'),ref('ee'),ref('fi'),ref('fr'),
@ -1637,6 +1642,12 @@
<field name="name">Gulf Cooperation Council (GCC)</field>
<field name="country_ids" eval="[(6,0, [ref('base.sa'), ref('base.ae'), ref('base.bh'), ref('base.om'), ref('base.qa'), ref('base.kw')])]"/>
</record>
<record id="eurasian_economic_union" model="res.country.group">
<field name="name">Eurasian Economic Union</field>
<field name="country_ids" eval="[(6, 0, [ref('ru'),ref('by'),ref('am'),ref('kg'),ref('kz')])]"/>
</record>
<record id="ch_and_li" model="res.country.group">
<field name="name">Switzerland and Liechtenstein</field>
<field name="country_ids" eval="[Command.set([ref('ch'), ref('li')])]"/>

View file

@ -931,7 +931,7 @@
<record id="KZT" model="res.currency">
<field name="name">KZT</field>
<field name="full_name">Kazakhstani tenge</field>
<field name="symbol">лв</field>
<field name="symbol"></field>
<field name="rounding">0.01</field>
<field name="active" eval="False"/>
<field name="currency_unit_label">Tenge</field>
@ -1064,6 +1064,7 @@
<field name="symbol"></field>
<field name="rounding">0.01</field>
<field name="active" eval="False"/>
<field name="position">before</field>
<field name="currency_unit_label">Rufiyaa</field>
<field name="currency_subunit_label">Laari</field>
</record>
@ -1495,6 +1496,16 @@
<field name="currency_subunit_label">Cents</field>
</record>
<record id="SLE" model="res.currency">
<field name="name">SLE</field>
<field name="full_name">Sierra Leonean leone</field>
<field name="symbol">Le</field>
<field name="rounding">0.01</field>
<field name="active" eval="False"/>
<field name="currency_unit_label">Leone</field>
<field name="currency_subunit_label">Cents</field>
</record>
<record id="SCR" model="res.currency">
<field name="name">SCR</field>
<field name="full_name">Seychellois rupee</field>
@ -1699,7 +1710,7 @@
<field name="name">UYI</field>
<field name="full_name">Uruguay Peso en Unidades Indexadas</field>
<field name="symbol">$</field>
<field name="rounding">1</field>
<field name="rounding">0.0001</field>
<field name="active" eval="False"/>
<field name="currency_unit_label">Peso</field>
<field name="currency_subunit_label">centésimo</field>

View file

@ -1,19 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- The Following currency rates are considered as on 1st Jan,2010 against EUR. -->
<record forcecreate="0" id="rateUSD" model="res.currency.rate">
<field name="rate">1.2834</field>
<field name="rate">1.0</field>
<field name="currency_id" ref="USD"/>
<field name="name">2010-01-01</field>
</record>
<record id="rateUSDbis" model="res.currency.rate">
<field name="rate">1.5289</field>
<field name="currency_id" ref="USD"/>
<field eval="time.strftime('%Y-01-01')" name="name"/>
</record>
<record forcecreate="0" id="rateVEF" model="res.currency.rate">
<field name="rate">5.864</field>
<field name="currency_id" ref="VEF"/>
@ -283,6 +276,12 @@
<field name="rate">28.36</field>
</record>
<record forcecreate="0" id="rateUYI" model="res.currency.rate">
<field name="currency_id" ref="UYI"/>
<field name="name">2023-09-13</field>
<field name="rate">5.7778</field>
</record>
<record forcecreate="0" id="rateAFN" model="res.currency.rate">
<field name="currency_id" ref="AFN" />
<field name="name">2010-01-01</field>
@ -754,7 +753,7 @@
<record forcecreate="0" id="rateEUR" model="res.currency.rate">
<field name="currency_id" ref="EUR" />
<field name="name">2010-01-01</field>
<field name="rate">1.0</field>
<field name="rate">1.2834</field>
</record>
<record forcecreate="0" id="rateVUV" model="res.currency.rate">
@ -883,6 +882,12 @@
<field name="rate">5320.43478</field>
</record>
<record forcecreate="0" id="rateSLE" model="res.currency.rate">
<field name="currency_id" ref="SLE" />
<field name="name">2023-06-08</field>
<field name="rate">22.5847</field>
</record>
<record forcecreate="0" id="rateSCR" model="res.currency.rate">
<field name="currency_id" ref="SCR" />
<field name="name">2010-01-01</field>

View file

@ -38,10 +38,6 @@
<!--
Resource: res.partner
-->
<record id="main_partner" model="res.partner">
<field name="email">info@yourcompany.com</field>
<field name="website">www.example.com</field>
</record>
<record id="res_partner_1" model="res.partner">
<field name="name">Wood Corner</field>
<field eval="[Command.set([ref('res_partner_category_14'), ref('res_partner_category_12')])]" name="category_id"/>
@ -55,6 +51,7 @@
<field name="phone">(623)-853-7197</field>
<field name="website">http://www.wood-corner.com</field>
<field name="image_1920" type="base64" file="base/static/img/res_partner_1-image.png"/>
<field name="vat">US12345672</field>
</record>
<record id="res_partner_2" model="res.partner">
<field name="name">Deco Addict</field>
@ -65,10 +62,11 @@
<field name="state_id" ref='state_us_5'/>
<field name="zip">94523</field>
<field name="country_id" ref="base.us"/>
<field name="email">deco.addict82@example.com</field>
<field name="email">deco_addict@yourcompany.example.com</field>
<field name="phone">(603)-996-3829</field>
<field name="website">http://www.deco-addict.com</field>
<field name="image_1920" type="base64" file="base/static/img/res_partner_2-image.png"/>
<field name="vat">US12345673</field>
</record>
<record id="res_partner_3" model="res.partner">
<field name="name">Gemini Furniture</field>
@ -79,10 +77,11 @@
<field name="state_id" ref='state_us_5'/>
<field name="zip">94535</field>
<field name="country_id" ref="base.us"/>
<field name="email">gemini.furniture39@example.com</field>
<field name="email">gemini_furniture@fake.geminifurniture.com</field>
<field name="phone">(941)-284-4875</field>
<field name="website">http://www.gemini-furniture.com/</field>
<field name="image_1920" type="base64" file="base/static/img/res_partner_3-image.png"/>
<field name="vat">US12345674</field>
</record>
<record id="res_partner_4" model="res.partner">
@ -98,6 +97,7 @@
<field name="phone">(803)-873-6126</field>
<field name="website">http://www.ready-mat.com/</field>
<field name="image_1920" type="base64" file="base/static/img/res_partner_4-image.png"/>
<field name="vat">US12345675</field>
</record>
<record id="res_partner_10" model="res.partner">
@ -111,6 +111,7 @@
<field name="email">jackson.group82@example.com</field>
<field name="phone">(334)-502-1024</field>
<field name="image_1920" type="base64" file="base/static/img/res_partner_10-image.jpg"/>
<field name="vat">US12345676</field>
</record>
<record id="res_partner_12" model="res.partner">
@ -126,6 +127,7 @@
<field name="email">azure.Interior24@example.com</field>
<field name="website">http://www.azure-interior.com</field>
<field name="image_1920" type="base64" file="base/static/img/res_partner_12-image.png"/>
<field name="vat">US12345677</field>
</record>
<record id="res_partner_18" model="res.partner">
@ -140,6 +142,7 @@
<field name="country_id" ref="base.us"/>
<field name="website">http://www.lumber-inc.com</field>
<field name="image_1920" type="base64" file="base/static/img/res_partner_18-image.png"/>
<field name="vat">US12345678</field>
</record>
<record id="res_partner_address_1" model="res.partner">

View file

@ -8,7 +8,7 @@
<field name="company_name">YourCompany</field>
<field name="street">3575 Buena Vista Avenue</field>
<field name="city">Eugene</field>
<field name="state_id" model="res.country.state" search="[('code','ilike','OR')]"/>
<field name="state_id" model="res.country.state" search="[('code','=','OR')]"/>
<field name="zip">97401</field>
<field name="country_id" ref="us"/>
<field name="tz">Europe/Brussels</field>
@ -16,18 +16,21 @@
<field name="phone">(441)-695-2334</field>
</record>
<record id="main_partner" model="res.partner">
<field name="name">YourCompany</field>
<field name="company_name">YourCompany</field>
<field name="street">250 Executive Park Blvd, Suite 3400</field>
<field name="city">San Francisco</field>
<field name="zip">94134</field>
<field name='country_id' ref='base.us'/>
<field name='state_id' ref='state_us_5'/>
<field name="phone">+1 555-555-5556</field>
<field name="email">info@yourcompany.example.com</field>
<field name="website">www.example.com</field>
</record>
<!-- Only update if we don't have information coming from the database manager -->
<function model="res.partner" name="write">
<value eval="[ref('base.main_partner')]"/>
<value eval="{
'name': 'YourCompany',
'street': '250 Executive Park Blvd, Suite 3400',
'city': 'San Francisco',
'zip': '94134',
'country_id': ref('base.us'),
'state_id': ref('base.state_us_5'),
'phone': '+1 555-555-5556',
'website': 'www.example.com',
'email': 'info@yourcompany.com',
} if obj(ref('base.main_partner')).name == 'My Company' else {}" model="res.partner"/>
</function>
<record id="user_demo" model="res.users">
<field name="partner_id" ref="base.partner_demo"/>
@ -36,7 +39,7 @@
<field name="signature" type="html"><span>-- <br/>+Mr Demo</span></field>
<field name="company_id" ref="main_company"/>
<field name="groups_id" eval="[Command.set([ref('base.group_user'), ref('base.group_partner_manager'), ref('base.group_allow_export')])]"/>
<field name="image_1920" type="base64" file="base/static/img/user_demo-image.jpg"/>
<field name="image_1920" type="base64" file="base/static/img/user_demo-image.png"/>
</record>
<record model="res.partner" id="base.partner_root">
@ -54,7 +57,7 @@
<field name="phone">+1 555-555-5555</field>
<field name="email">admin@yourcompany.example.com</field>
<field name="tz">Europe/Brussels</field>
<field name="image_1920" type="base64" file="base/static/img/partner_root-image.jpg"/>
<field name="image_1920" type="base64" file="base/static/img/partner_root-image.png"/>
</record>
<record id="base.user_admin" model="res.users">
@ -71,7 +74,7 @@
<field name="zip">07002</field>
<field name="country_id" ref="base.us"/>
<field name="company_name">YourCompany</field>
<field name="image_1920" type="base64" file="base/static/img/partner_demo_portal.jpg"/>
<field name="image_1920" type="base64" file="base/static/img/partner_demo_portal.png"/>
<field name="phone">(683)-556-5104</field>
</record>
<record id="demo_user0" model="res.users" context="{'no_reset_password': True}">

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -43,6 +43,7 @@ from . import res_config
from . import res_currency
from . import res_company
from . import res_users
from . import res_users_settings
from . import res_users_deletion
from . import decimal_precision

View file

@ -24,80 +24,23 @@ except ImportError:
# `sassc` executable in the path.
libsass = None
from rjsmin import jsmin as rjsmin
from odoo import release, SUPERUSER_ID, _
from odoo.http import request
from odoo.modules.module import get_resource_path
from odoo.tools import (func, misc, transpile_javascript,
is_odoo_module, SourceMapGenerator, profiler,
apply_inheritance_specs)
from odoo.tools.misc import file_open, file_path, html_escape as escape
from odoo.tools.constants import SCRIPT_EXTENSIONS, STYLE_EXTENSIONS
from odoo.tools.misc import file_open, file_path
from odoo.tools.pycompat import to_text
_logger = logging.getLogger(__name__)
ANY_UNIQUE = '_' * 7
EXTENSIONS = (".js", ".css", ".scss", ".sass", ".less", ".xml")
class CompileError(RuntimeError): pass
def rjsmin(script):
""" Minify js with a clever regex.
Taken from http://opensource.perlig.de/rjsmin (version 1.1.0)
Apache License, Version 2.0 """
def subber(match):
""" Substitution callback """
groups = match.groups()
return (
groups[0] or
groups[1] or
(groups[3] and (groups[2] + '\n')) or
groups[2] or
(groups[5] and "%s%s%s" % (
groups[4] and '\n' or '',
groups[5],
groups[6] and '\n' or '',
)) or
(groups[7] and '\n') or
(groups[8] and ' ') or
(groups[9] and ' ') or
(groups[10] and ' ') or
''
)
result = re.sub(
r'([^\047"\140/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^'
r'\r\n]|\r?\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^'
r'\r\n]|\r?\n|\r)[^"\\\r\n]*)*")|(?:\140[^\140\\]*(?:\\(?:[^\r\n'
r']|\r?\n|\r)[^\140\\]*)*\140))[^\047"\140/\000-\040]*)|(?<=[(,='
r':\[!&|?{};\r\n+*-])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*'
r'\*+(?:[^/*][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-'
r'\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*('
r'(?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*'
r'(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/))((?:[\000-\011'
r'\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:(?:('
r'?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*'
r']*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040&)+,.:;=?\]|}-]))?|'
r'(?<=[\000-#%-,./:-@\[-^\140{-~-]return)(?:[\000-\011\013\014\0'
r'16-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:((?:(?://[^\r'
r'\n]*)?[\r\n]))(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?'
r':[^/*][^*]*\*+)*/))*)*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^'
r'\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r'
r'\n]*)*/))((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/'
r'*][^*]*\*+)*/))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013'
r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000'
r'-\040&)+,.:;=?\]|}-]))?|(?<=[^\000-!#%&(*,./:-@\[\\^{|~])(?:['
r'\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'
r')*(?:((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011\013\014\016-\040'
r']|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#%-\047'
r')*,./:-@\\-^\140|-~])|(?<=[^\000-#%-,./:-@\[-^\140{-~-])((?:['
r'\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'
r'))+(?=[^\000-#%-,./:-@\[-^\140{-~-])|(?<=\+)((?:[\000-\011\013'
r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<'
r'=-)((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]'
r'*\*+)*/)))+(?=-)|(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*'
r'+(?:[^/*][^*]*\*+)*/))+|(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-'
r'\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+', subber, '\n%s\n' % script
).strip()
return result
class AssetError(Exception):
pass
@ -112,9 +55,9 @@ class AssetsBundle(object):
rx_preprocess_imports = re.compile(r"""(@import\s?['"]([^'"]+)['"](;?))""")
rx_css_split = re.compile(r"\/\*\! ([a-f0-9-]+) \*\/")
TRACKED_BUNDLES = ['web.assets_common', 'web.assets_backend']
TRACKED_BUNDLES = ['web.assets_web']
def __init__(self, name, files, env=None, css=True, js=True):
def __init__(self, name, files, external_assets=(), env=None, css=True, js=True, debug_assets=False, rtl=False, assets_params=None):
"""
:param name: bundle name
:param files: files to be added to the bundle
@ -128,117 +71,89 @@ class AssetsBundle(object):
self.stylesheets = []
self.css_errors = []
self.files = files
self.user_direction = self.env['res.lang']._lang_get(
self.env.context.get('lang') or self.env.user.lang
).direction
self.rtl = rtl
self.assets_params = assets_params or {}
self.has_css = css
self.has_js = js
self._checksum_cache = {}
self.is_debug_assets = debug_assets
self.external_assets = [
url
for url in external_assets
if (css and url.rpartition('.')[2] in STYLE_EXTENSIONS) or (js and url.rpartition('.')[2] in SCRIPT_EXTENSIONS)
]
# asset-wide html "media" attribute
for f in files:
extension = f['url'].rpartition('.')[2]
params = {
'url': f['url'],
'filename': f['filename'],
'inline': f['content'],
'last_modified': None if self.is_debug_assets else f.get('last_modified'),
}
if css:
if f['atype'] == 'text/sass':
self.stylesheets.append(SassStylesheetAsset(self, url=f['url'], filename=f['filename'], inline=f['content'], media=f['media'], direction=self.user_direction))
elif f['atype'] == 'text/scss':
self.stylesheets.append(ScssStylesheetAsset(self, url=f['url'], filename=f['filename'], inline=f['content'], media=f['media'], direction=self.user_direction))
elif f['atype'] == 'text/less':
self.stylesheets.append(LessStylesheetAsset(self, url=f['url'], filename=f['filename'], inline=f['content'], media=f['media'], direction=self.user_direction))
elif f['atype'] == 'text/css':
self.stylesheets.append(StylesheetAsset(self, url=f['url'], filename=f['filename'], inline=f['content'], media=f['media'], direction=self.user_direction))
css_params = {
'rtl': self.rtl,
}
if extension == 'sass':
self.stylesheets.append(SassStylesheetAsset(self, **params, **css_params))
elif extension == 'scss':
self.stylesheets.append(ScssStylesheetAsset(self, **params, **css_params))
elif extension == 'less':
self.stylesheets.append(LessStylesheetAsset(self, **params, **css_params))
elif extension == 'css':
self.stylesheets.append(StylesheetAsset(self, **params, **css_params))
if js:
if f['atype'] == 'text/javascript':
self.javascripts.append(JavascriptAsset(self, url=f['url'], filename=f['filename'], inline=f['content']))
elif f['atype'] == 'text/xml':
self.templates.append(XMLAsset(self, url=f['url'], filename=f['filename'], inline=f['content']))
if extension == 'js':
self.javascripts.append(JavascriptAsset(self, **params))
elif extension == 'xml':
self.templates.append(XMLAsset(self, **params))
def to_node(self, css=True, js=True, debug=False, async_load=False, defer_load=False, lazy_load=False):
def get_links(self):
"""
:returns [(tagName, attributes, content)] if the tag is auto close
:returns a list of tuple. a tuple can be (url, None) or (None, inlineContent)
"""
response = []
is_debug_assets = debug and 'assets' in debug
if css and self.stylesheets:
css_attachments = self.css(is_minified=not is_debug_assets) or []
for attachment in css_attachments:
if is_debug_assets:
href = self.get_debug_asset_url(extra='rtl/' if self.user_direction == 'rtl' else '',
name=css_attachments.name,
extension='')
else:
href = attachment.url
attr = dict([
["type", "text/css"],
["rel", "stylesheet"],
["href", href],
['data-asset-bundle', self.name],
['data-asset-version', self.version],
])
response.append(("link", attr, None))
if self.css_errors:
msg = '\n'.join(self.css_errors)
response.append(JavascriptAsset(self, inline=self.dialog_message(msg)).to_node())
response.append(StylesheetAsset(self, url="/web/static/lib/bootstrap/dist/css/bootstrap.css").to_node())
if js and self.javascripts:
js_attachment = self.js(is_minified=not is_debug_assets)
src = self.get_debug_asset_url(name=js_attachment.name, extension='') if is_debug_assets else js_attachment[0].url
attr = dict([
["async", "async" if async_load else None],
["defer", "defer" if defer_load or lazy_load else None],
["type", "text/javascript"],
["data-src" if lazy_load else "src", src],
['data-asset-bundle', self.name],
['data-asset-version', self.version],
])
response.append(("script", attr, None))
if self.has_css and self.stylesheets:
response.append(self.get_link('css'))
return response
if self.has_js and self.javascripts:
response.append(self.get_link('js'))
@func.lazy_property
def last_modified_combined(self):
"""Returns last modified date of linked files"""
# WebAsset are recreate here when a better solution would be to use self.stylesheets and self.javascripts
# We currently have no garanty that they are present since it will depends on js and css parameters
# last_modified is actually only usefull for the checksum and checksum should be extension specific since
# they are differents bundles. This will be a future work.
return self.external_assets + response
# changing the logic from max date to combined date to fix bundle invalidation issues.
assets = [WebAsset(self, url=f['url'], filename=f['filename'], inline=f['content'])
for f in self.files
if f['atype'] in ['text/sass', "text/scss", "text/less", "text/css", "text/javascript", "text/xml"]]
return ','.join(str(asset.last_modified) for asset in assets)
def get_link(self, asset_type):
unique = self.get_version(asset_type) if not self.is_debug_assets else 'debug'
extension = asset_type if self.is_debug_assets else f'min.{asset_type}'
return self.get_asset_url(unique=unique, extension=extension)
@func.lazy_property
def version(self):
return self.checksum[0:7]
def get_version(self, asset_type):
return self.get_checksum(asset_type)[0:7]
@func.lazy_property
def checksum(self):
def get_checksum(self, asset_type):
"""
Not really a full checksum.
We compute a SHA512/256 on the rendered bundle + combined linked files last_modified date
"""
check = u"%s%s" % (json.dumps(self.files, sort_keys=True), self.last_modified_combined)
return hashlib.sha512(check.encode('utf-8')).hexdigest()[:64]
if asset_type not in self._checksum_cache:
if asset_type == 'css':
assets = self.stylesheets
elif asset_type == 'js':
assets = self.javascripts + self.templates
else:
raise ValueError(f'Asset type {asset_type} not known')
def _get_asset_template_url(self):
return "/web/assets/{id}-{unique}/{extra}{name}{sep}{extension}"
unique_descriptor = ','.join(asset.unique_descriptor for asset in assets)
def _get_asset_url_values(self, id, unique, extra, name, sep, extension): # extra can contain direction or/and website
return {
'id': id,
'unique': unique,
'extra': extra,
'name': name,
'sep': sep,
'extension': extension,
}
self._checksum_cache[asset_type] = hashlib.sha512(unique_descriptor.encode()).hexdigest()[:64]
return self._checksum_cache[asset_type]
def get_asset_url(self, id='%', unique='%', extra='', name='%', sep="%", extension='%'):
return self._get_asset_template_url().format(
**self._get_asset_url_values(id=id, unique=unique, extra=extra, name=name, sep=sep, extension=extension)
)
def get_debug_asset_url(self, extra='', name='%', extension='%'):
return f"/web/assets/debug/{extra}{name}{extension}"
def get_asset_url(self, unique=ANY_UNIQUE, extension='%', ignore_params=False):
direction = '.rtl' if self.is_css(extension) and self.rtl else ''
bundle_name = f"{self.name}{direction}.{extension}"
return self.env['ir.asset']._get_asset_bundle_url(bundle_name, unique, self.assets_params, ignore_params)
def _unlink_attachments(self, attachments):
""" Unlinks attachments without actually calling unlink, so that the ORM cache is not cleared.
@ -251,10 +166,13 @@ class AssetsBundle(object):
self.env.cr.execute(f"""DELETE FROM {attachments._table} WHERE id IN (
SELECT id FROM {attachments._table} WHERE id in %s FOR NO KEY UPDATE SKIP LOCKED
)""", [tuple(attachments.ids)])
for file_path in to_delete:
attachments._file_delete(file_path)
for fpath in to_delete:
attachments._file_delete(fpath)
def clean_attachments(self, extension):
def is_css(self, extension):
return extension in ['css', 'min.css', 'css.map']
def _clean_attachments(self, extension, keep_url):
""" Takes care of deleting any outdated ir.attachment records associated to a bundle before
saving a fresh one.
@ -265,25 +183,23 @@ class AssetsBundle(object):
must exclude the current bundle.
"""
ira = self.env['ir.attachment']
url = self.get_asset_url(
extra='%s' % ('rtl/' if extension in ['css', 'min.css'] and self.user_direction == 'rtl' else ''),
name=self.name,
sep='',
extension='.%s' % extension
to_clean_pattern = self.get_asset_url(
unique=ANY_UNIQUE,
extension=extension,
)
domain = [
('url', '=like', url),
'!', ('url', '=like', self.get_asset_url(unique=self.version))
('url', '=like', to_clean_pattern),
('url', '!=', keep_url),
('public', '=', True),
]
attachments = ira.sudo().search(domain)
# avoid to invalidate cache if it's already empty (mainly useful for test)
if attachments:
_logger.info('Deleting ir.attachment %s (from bundle %s)', attachments.ids, self.name)
_logger.info('Deleting attachments %s (matching %s) because it was replaced with %s', attachments.ids, to_clean_pattern, keep_url)
self._unlink_attachments(attachments)
# force bundle invalidation on other workers
self.env['ir.qweb'].clear_caches()
# clear_cache was removed
return True
@ -296,21 +212,17 @@ class AssetsBundle(object):
by file name and only return the one with the max id for each group.
:param extension: file extension (js, min.js, css)
:param ignore_version: if ignore_version, the url contains a version => web/assets/%-%/name.extension
:param ignore_version: if ignore_version, the url contains a version => web/assets/%/name.extension
(the second '%' corresponds to the version),
else: the url contains a version equal to that of the self.version
=> web/assets/%-self.version/name.extension.
else: the url contains a version equal to that of the self.get_version(type)
=> web/assets/self.get_version(type)/name.extension.
"""
unique = "%" if ignore_version else self.version
unique = ANY_UNIQUE if ignore_version else self.get_version('css' if self.is_css(extension) else 'js')
url_pattern = self.get_asset_url(
unique=unique,
extra='%s' % ('rtl/' if extension in ['css', 'min.css'] and self.user_direction == 'rtl' else ''),
name=self.name,
sep='',
extension='.%s' % extension
extension=extension,
)
self.env.cr.execute("""
query = """
SELECT max(id)
FROM ir_attachment
WHERE create_uid = %s
@ -320,22 +232,37 @@ class AssetsBundle(object):
AND public = true
GROUP BY name
ORDER BY name
""", [SUPERUSER_ID, url_pattern])
attachment_ids = [r[0] for r in self.env.cr.fetchall()]
if not attachment_ids:
_logger.info('Failed to find attachment for assets %s', url_pattern)
return self.env['ir.attachment'].sudo().browse(attachment_ids)
def add_post_rollback(self):
"""
In some rare cases it is possible that an attachment is created
during a transaction, added to the ormcache but the transaction
is rolled back, leading to 404 when getting the attachments.
This postrollback hook will help fix this issue by clearing the
cache if it is not committed.
"""
self.env.cr.postrollback.add(self.env.registry._Registry__cache.clear)
self.env.cr.execute(query, [SUPERUSER_ID, url_pattern])
attachment_id = [r[0] for r in self.env.cr.fetchall()]
if not attachment_id and not ignore_version:
fallback_url_pattern = self.get_asset_url(
unique=unique,
extension=extension,
ignore_params=True,
)
self.env.cr.execute(query, [SUPERUSER_ID, fallback_url_pattern])
similar_attachment_ids = [r[0] for r in self.env.cr.fetchall()]
if similar_attachment_ids:
similar = self.env['ir.attachment'].sudo().browse(similar_attachment_ids)
_logger.info('Found a similar attachment for %s, copying from %s', url_pattern, similar.url)
url = url_pattern
values = {
'name': similar.name,
'mimetype': similar.mimetype,
'res_model': 'ir.ui.view',
'res_id': False,
'type': 'binary',
'public': True,
'raw': similar.raw,
'url': url,
}
attachment = self.env['ir.attachment'].with_user(SUPERUSER_ID).create(values)
attachment_id = attachment.id
self._clean_attachments(extension, url)
return self.env['ir.attachment'].sudo().browse(attachment_id)
def save_attachment(self, extension, content):
"""Record the given bundle in an ir.attachment and delete
@ -360,6 +287,11 @@ class AssetsBundle(object):
'application/json' if extension in ['js.map', 'css.map'] else
'application/javascript'
)
unique = self.get_version('css' if self.is_css(extension) else 'js')
url = self.get_asset_url(
unique=unique,
extension=extension,
)
values = {
'name': fname,
'mimetype': mimetype,
@ -368,26 +300,13 @@ class AssetsBundle(object):
'type': 'binary',
'public': True,
'raw': content.encode('utf8'),
}
self.add_post_rollback()
attachment = ira.with_user(SUPERUSER_ID).create(values)
url = self.get_asset_url(
id=attachment.id,
unique=self.version,
extra='%s' % ('rtl/' if extension in ['css', 'min.css'] and self.user_direction == 'rtl' else ''),
name=fname,
sep='', # included in fname
extension=''
)
values = {
'url': url,
}
attachment.write(values)
attachment = ira.with_user(SUPERUSER_ID).create(values)
if self.env.context.get('commit_assetsbundle') is True:
self.env.cr.commit()
_logger.info('Generating a new asset bundle attachment %s (id:%s)', attachment.url, attachment.id)
self.clean_attachments(extension)
self._clean_attachments(extension, url)
# For end-user assets (common and backend), send a message on the bus
# to invite the user to refresh their browser
@ -395,11 +314,12 @@ class AssetsBundle(object):
self.env['bus.bus']._sendone('broadcast', 'bundle_changed', {
'server_version': release.version # Needs to be dynamically imported
})
_logger.debug('Asset Changed: bundle: %s -- version: %s', self.name, self.version)
_logger.debug('Asset Changed: bundle: %s -- version: %s', self.name, unique)
return attachment
def js(self, is_minified=True):
def js(self):
is_minified = not self.is_debug_assets
extension = 'min.js' if is_minified else 'js'
js_attachment = self.get_attachments(extension)
@ -417,11 +337,10 @@ class AssetsBundle(object):
* Templates *
*******************************************/
odoo.define('{self.name}.bundle.xml', function(require){{
odoo.define('{self.name}.bundle.xml', ['@web/core/registry'], function(require){{
'use strict';
const {{ loadXML }} = require('@web/core/assets');
const templates = `{templates}`;
return loadXML(templates);
const {{ registry }} = require('@web/core/registry');
registry.category(`xml_templates`).add(`{self.name}`, `{templates}`);
}});""")
if is_minified:
@ -443,7 +362,7 @@ class AssetsBundle(object):
or self.save_attachment('js.map', '')
generator = SourceMapGenerator(
source_root="/".join(
[".." for i in range(0, len(self.get_debug_asset_url(name=self.name).split("/")) - 2)]
[".." for i in range(0, len(self.get_asset_url().split("/")) - 2)]
) + "/",
)
content_bundle_list = []
@ -535,7 +454,7 @@ class AssetsBundle(object):
else:
raise ValueError(_("Module %r not loaded or inexistent (try to inherit %r), or templates of addon being loaded %r are misordered (template %r)", parent_addon, parent_name, addon, template_name))
if parent_name not in template_dict[parent_addon]:
raise ValueError(_("Cannot create %r because the template to inherit %r is not found.") % (f'{addon}.{template_name}', f'{parent_addon}.{parent_name}'))
raise ValueError(_("Cannot create %r because the template to inherit %r is not found.", '%s.%s' % (addon, template_name), '%s.%s' % (parent_addon, parent_name)))
# After several performance tests, we found out that deepcopy is the most efficient
# solution in this case (compared with copy, xpath with '.' and stringifying).
@ -607,28 +526,51 @@ class AssetsBundle(object):
# Returns the string by removing the <root> tag.
return etree.tostring(root, encoding='unicode')[6:-7]
def css(self, is_minified=True):
def css(self):
is_minified = not self.is_debug_assets
extension = 'min.css' if is_minified else 'css'
attachments = self.get_attachments(extension)
if not attachments:
# get css content
css = self.preprocess_css()
if self.css_errors:
return self.get_attachments(extension, ignore_version=True)
if attachments:
return attachments
matches = []
css = re.sub(self.rx_css_import, lambda matchobj: matches.append(matchobj.group(0)) and '', css)
css = self.preprocess_css()
if self.css_errors:
error_message = '\n'.join(self.css_errors).replace('"', r'\"').replace('\n', r'\A').replace('*', r'\*')
previous_attachment = self.get_attachments(extension, ignore_version=True)
previous_css = previous_attachment.raw.decode() if previous_attachment else ''
css_error_message_header = '\n\n/* ## CSS error message ##*/'
previous_css = previous_css.split(css_error_message_header)[0]
css = css_error_message_header.join([
previous_css, """
body::before {
font-weight: bold;
content: "A css error occured, using an old style to render this page";
position: fixed;
left: 0;
bottom: 0;
z-index: 100000000000;
background-color: #C00;
color: #DDD;
}
if is_minified:
# move up all @import rules to the top
matches.append(css)
css = u'\n'.join(matches)
css_error_message {
content: "%s";
}
""" % error_message
])
return self.save_attachment(extension, css)
self.save_attachment(extension, css)
attachments = self.get_attachments(extension)
else:
return self.css_with_sourcemap(u'\n'.join(matches))
return attachments
matches = []
css = re.sub(self.rx_css_import, lambda matchobj: matches.append(matchobj.group(0)) and '', css)
if is_minified:
# move up all @import rules to the top
matches.append(css)
css = u'\n'.join(matches)
return self.save_attachment(extension, css)
else:
return self.css_with_sourcemap(u'\n'.join(matches))
def css_with_sourcemap(self, content_import_rules):
"""Create the ir.attachment representing the not-minified content of the bundleCSS
@ -639,8 +581,7 @@ class AssetsBundle(object):
"""
sourcemap_attachment = self.get_attachments('css.map') \
or self.save_attachment('css.map', '')
debug_asset_url = self.get_debug_asset_url(name=self.name,
extra='rtl/' if self.user_direction == 'rtl' else '')
debug_asset_url = self.get_asset_url(unique='debug')
generator = SourceMapGenerator(
source_root="/".join(
[".." for i in range(0, len(debug_asset_url.split("/")) - 2)]
@ -660,7 +601,7 @@ class AssetsBundle(object):
content_bundle_list.append(content)
content_line_count += len(content.split("\n"))
content_bundle = '\n'.join(content_bundle_list) + f"\n//*# sourceMappingURL={sourcemap_attachment.url} */"
content_bundle = '\n'.join(content_bundle_list) + f"\n/*# sourceMappingURL={sourcemap_attachment.url} */"
css_attachment = self.save_attachment('css', content_bundle)
generator._file = css_attachment.url
@ -670,111 +611,6 @@ class AssetsBundle(object):
return css_attachment
def dialog_message(self, message):
"""
Returns a JS script which shows a warning to the user on page load.
TODO: should be refactored to be a base js file whose code is extended
by related apps (web/website).
"""
return """
(function (message) {
'use strict';
if (window.__assetsBundleErrorSeen) {
return;
}
window.__assetsBundleErrorSeen = true;
if (document.readyState !== 'loading') {
onDOMContentLoaded();
} else {
window.addEventListener('DOMContentLoaded', () => onDOMContentLoaded());
}
async function onDOMContentLoaded() {
var odoo = window.top.odoo;
if (!odoo || !odoo.define) {
useAlert();
return;
}
// Wait for potential JS loading
await new Promise(resolve => {
const noLazyTimeout = setTimeout(() => resolve(), 10); // 10 since need to wait for promise resolutions of odoo.define
odoo.define('AssetsBundle.PotentialLazyLoading', function (require) {
'use strict';
const lazyloader = require('web.public.lazyloader');
clearTimeout(noLazyTimeout);
lazyloader.allScriptsLoaded.then(() => resolve());
});
});
var alertTimeout = setTimeout(useAlert, 10); // 10 since need to wait for promise resolutions of odoo.define
odoo.define('AssetsBundle.ErrorMessage', function (require) {
'use strict';
require('web.dom_ready');
var core = require('web.core');
var Dialog = require('web.Dialog');
var _t = core._t;
clearTimeout(alertTimeout);
new Dialog(null, {
title: _t("Style error"),
$content: $('<div/>')
.append($('<p/>', {text: _t("The style compilation failed, see the error below. Your recent actions may be the cause, please try reverting the changes you made.")}))
.append($('<pre/>', {html: message})),
}).open();
});
}
function useAlert() {
window.alert(message);
}
})("%s");
""" % message.replace('"', '\\"').replace('\n', '&NewLine;')
def _get_assets_domain_for_already_processed_css(self, assets):
""" Method to compute the attachments' domain to search the already process assets (css).
This method was created to be overridden.
"""
return [('url', 'in', list(assets.keys()))]
def is_css_preprocessed(self):
preprocessed = True
old_attachments = self.env['ir.attachment'].sudo()
asset_types = [SassStylesheetAsset, ScssStylesheetAsset, LessStylesheetAsset]
if self.user_direction == 'rtl':
asset_types.append(StylesheetAsset)
for atype in asset_types:
outdated = False
assets = dict((asset.html_url, asset) for asset in self.stylesheets if isinstance(asset, atype))
if assets:
assets_domain = self._get_assets_domain_for_already_processed_css(assets)
attachments = self.env['ir.attachment'].sudo().search(assets_domain)
old_attachments += attachments
for attachment in attachments:
asset = assets[attachment.url]
if asset.last_modified > attachment['__last_update']:
outdated = True
break
if asset._content is None:
asset._content = (attachment.raw or b'').decode('utf8')
if not asset._content and attachment.file_size > 0:
asset._content = None # file missing, force recompile
if any(asset._content is None for asset in assets.values()):
outdated = True
if outdated:
preprocessed = False
return preprocessed, old_attachments
def preprocess_css(self, debug=False, old_attachments=None):
"""
Checks if the bundle contains any sass/less content, then compiles it to css.
@ -791,7 +627,7 @@ class AssetsBundle(object):
compiled += self.compile_css(assets[0].compile, source)
# We want to run rtlcss on normal css, so merge it in compiled
if self.user_direction == 'rtl':
if self.rtl:
stylesheet_assets = [asset for asset in self.stylesheets if not isinstance(asset, (SassStylesheetAsset, ScssStylesheetAsset, LessStylesheetAsset))]
compiled += '\n'.join([asset.get_source() for asset in stylesheet_assets])
compiled = self.run_rtlcss(compiled)
@ -861,7 +697,7 @@ class AssetsBundle(object):
except IOError:
rtlcss = 'rtlcss'
cmd = [rtlcss, '-c', get_resource_path("base", "data/rtlcss.json"), '-']
cmd = [rtlcss, '-c', file_path("base/data/rtlcss.json"), '-']
try:
rtlcss = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
@ -915,18 +751,17 @@ class AssetsBundle(object):
class WebAsset(object):
html_url_format = '%s'
_content = None
_filename = None
_ir_attach = None
_id = None
def __init__(self, bundle, inline=None, url=None, filename=None):
def __init__(self, bundle, inline=None, url=None, filename=None, last_modified=None):
self.bundle = bundle
self.inline = inline
self._filename = filename
self.url = url
self.html_url_args = url
self._last_modified = last_modified
if not inline and not url:
raise Exception("An asset should either be inlined or url linked, defined in bundle '%s'" % bundle.name)
@ -935,20 +770,16 @@ class WebAsset(object):
if self._id is None: self._id = str(uuid.uuid4())
return self._id
@func.lazy_property
def unique_descriptor(self):
return f'{self.url or self.inline},{self.last_modified}'
@func.lazy_property
def name(self):
return '<inline asset>' if self.inline else self.url
@property
def html_url(self):
return self.html_url_format % self.html_url_args
def stat(self):
if not (self.inline or self._filename or self._ir_attach):
path = (segment for segment in self.url.split('/') if segment)
self._filename = get_resource_path(*path)
if self._filename:
return
try:
# Test url against ir.attachments
self._ir_attach = self.bundle.env['ir.attachment'].sudo()._get_serve_attachment(self.url)
@ -956,20 +787,20 @@ class WebAsset(object):
except ValueError:
raise AssetNotFound("Could not find %s" % self.name)
def to_node(self):
raise NotImplementedError()
@func.lazy_property
@property
def last_modified(self):
try:
self.stat()
if self._filename:
return datetime.fromtimestamp(os.path.getmtime(self._filename))
if self._last_modified is None:
try:
self.stat()
except Exception: # most likely nor a file or an attachment, skip it
pass
if self._filename and self.bundle.is_debug_assets: # usually _last_modified should be set exept in debug=assets
self._last_modified = os.path.getmtime(self._filename)
elif self._ir_attach:
return self._ir_attach['__last_update']
except Exception:
pass
return datetime(1970, 1, 1)
self._last_modified = self._ir_attach.write_date.timestamp()
if not self._last_modified:
self._last_modified = -1
return self._last_modified
@property
def content(self):
@ -1004,11 +835,15 @@ class WebAsset(object):
class JavascriptAsset(WebAsset):
def __init__(self, bundle, inline=None, url=None, filename=None):
super().__init__(bundle, inline, url, filename)
def __init__(self, bundle, **kwargs):
super().__init__(bundle, **kwargs)
self._is_transpiled = None
self._converted_content = None
@property
def bundle_version(self):
return self.bundle.get_version('js')
@property
def is_transpiled(self):
if self._is_transpiled is None:
@ -1033,21 +868,6 @@ class JavascriptAsset(WebAsset):
except AssetError as e:
return u"console.error(%s);" % json.dumps(to_text(e))
def to_node(self):
if self.url:
return ("script", dict([
["type", "text/javascript"],
["src", self.html_url],
['data-asset-bundle', self.bundle.name],
['data-asset-version', self.bundle.version],
]), None)
else:
return ("script", dict([
["type", "text/javascript"],
["charset", "utf-8"],
['data-asset-bundle', self.bundle.name],
['data-asset-version', self.bundle.version],
]), self.with_header())
def with_header(self, content=None, minimal=True):
if minimal:
@ -1078,24 +898,20 @@ class XMLAsset(WebAsset):
try:
content = super()._fetch_content()
except AssetError as e:
return f'<error data-asset-bundle={self.bundle.name!r} data-asset-version={self.bundle.version!r}>{json.dumps(to_text(e))}</error>'
return u"console.error(%s);" % json.dumps(to_text(e))
parser = etree.XMLParser(ns_clean=True, recover=True, remove_comments=True)
root = etree.parse(io.BytesIO(content.encode('utf-8')), parser=parser).getroot()
parser = etree.XMLParser(ns_clean=True, remove_comments=True, resolve_entities=False)
try:
root = etree.fromstring(content.encode('utf-8'), parser=parser)
except etree.XMLSyntaxError as e:
return f'<t t-name="parsing_error{self.url.replace("/","_")}"><parsererror>Invalid XML template: {self.url} \n {e.msg} </parsererror></t>'
if root.tag in ('templates', 'template'):
return ''.join(etree.tostring(el, encoding='unicode') for el in root)
return etree.tostring(root, encoding='unicode')
def to_node(self):
attributes = {
'async': 'async',
'defer': 'defer',
'type': 'text/xml',
'data-src': self.html_url,
'data-asset-bundle': self.bundle.name,
'data-asset-version': self.bundle.version,
}
return ("script", attributes, None)
@property
def bundle_version(self):
return self.bundle.get_version('js')
def with_header(self, content=None):
if content is None:
@ -1128,21 +944,18 @@ class StylesheetAsset(WebAsset):
rx_sourceMap = re.compile(r'(/\*# sourceMappingURL=.*)', re.U)
rx_charset = re.compile(r'(@charset "[^"]+";)', re.U)
def __init__(self, *args, **kw):
self.media = kw.pop('media', None)
self.direction = kw.pop('direction', None)
def __init__(self, *args, rtl=False, **kw):
self.rtl = rtl
super().__init__(*args, **kw)
if self.direction == 'rtl' and self.url:
self.html_url_args = self.url.rsplit('.', 1)
self.html_url_format = '%%s/%s/%s.%%s' % ('rtl', self.bundle.name)
self.html_url_args = tuple(self.html_url_args)
@property
def content(self):
content = super().content
if self.media:
content = '@media %s { %s }' % (self.media, content)
return content
def bundle_version(self):
return self.bundle.get_version('css')
@func.lazy_property
def unique_descriptor(self):
direction = (self.rtl and 'rtl') or 'ltr'
return f'{self.url or self.inline},{self.last_modified},{direction}'
def _fetch_content(self):
try:
@ -1184,35 +997,10 @@ class StylesheetAsset(WebAsset):
content = re.sub(r' *([{}]) *', r'\1', content)
return self.with_header(content)
def to_node(self):
if self.url:
attr = dict([
["type", "text/css"],
["rel", "stylesheet"],
["href", self.html_url],
["media", escape(to_text(self.media)) if self.media else None],
['data-asset-bundle', self.bundle.name],
['data-asset-version', self.bundle.version],
])
return ("link", attr, None)
else:
attr = dict([
["type", "text/css"],
["media", escape(to_text(self.media)) if self.media else None],
['data-asset-bundle', self.bundle.name],
['data-asset-version', self.bundle.version],
])
return ("style", attr, self.with_header())
class PreprocessedCSS(StylesheetAsset):
rx_import = None
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
self.html_url_args = tuple(self.url.rsplit('/', 1))
self.html_url_format = '%%s/%s%s/%%s.css' % ('rtl/' if self.direction == 'rtl' else '', self.bundle.name)
def get_command(self):
raise NotImplementedError
@ -1271,7 +1059,7 @@ class SassStylesheetAsset(PreprocessedCSS):
class ScssStylesheetAsset(PreprocessedCSS):
@property
def bootstrap_path(self):
return get_resource_path('web', 'static', 'lib', 'bootstrap', 'scss')
return file_path('web/static/lib/bootstrap/scss')
precision = 8
output_style = 'expanded'

Some files were not shown because too many files have changed in this diff Show more