summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.tx/config434
-rw-r--r--addons/account/__manifest__.py1
-rw-r--r--addons/account/models/account_bank_statement.py2
-rw-r--r--addons/account/static/tests/reconciliation_tests.js1
-rw-r--r--addons/account/tests/account_test_classes.py2
-rw-r--r--addons/account/tests/test_account_journal_dashboard.py18
-rw-r--r--addons/account/tests/test_fiscal_position.py2
-rw-r--r--addons/account/tests/test_payment.py6
-rw-r--r--addons/account/tests/test_reconciliation.py60
-rw-r--r--addons/base_import/models/base_import.py6
-rw-r--r--addons/base_import_module/models/base_import_module.py2
-rw-r--r--addons/calendar/tests/test_calendar.py2
-rw-r--r--addons/crm/tests/test_crm_pls.py4
-rw-r--r--addons/delivery/tests/test_packing_delivery.py4
-rw-r--r--addons/google_calendar/__manifest__.py1
-rw-r--r--addons/hr_contract/tests/test_auto_status.py22
-rw-r--r--addons/hr_expense/tests/test_expenses.py86
-rw-r--r--addons/hr_expense/tests/test_expenses_mail_subjects.py52
-rw-r--r--addons/hr_recruitment/tests/test_recruitment_process.py6
-rw-r--r--addons/hr_timesheet/tests/test_timesheet.py38
-rw-r--r--addons/http_routing/models/ir_http.py2
-rw-r--r--addons/l10n_be_edi/tests/test_ubl.py2
-rw-r--r--addons/l10n_cl/data/res_partner.xml3
-rw-r--r--addons/l10n_cl/models/__init__.py3
-rw-r--r--addons/l10n_cl/models/ir_sequence.py1
-rw-r--r--addons/l10n_fr_fec/wizard/account_fr_fec.py2
-rw-r--r--addons/l10n_it_edi/models/account_invoice.py2
-rw-r--r--addons/l10n_it_edi/models/ir_mail_server.py7
-rw-r--r--addons/l10n_pe/README.rst14
-rw-r--r--addons/l10n_pe/data/l10n_pe.res.city.district.csv2
-rw-r--r--addons/l10n_pe/models/__init__.py1
-rw-r--r--addons/mail/models/mail_cc_mixin.py3
-rw-r--r--addons/mail/models/mail_channel.py3
-rw-r--r--addons/mail/models/mail_mail.py3
-rw-r--r--addons/mail/models/mail_message.py4
-rw-r--r--addons/mail/models/mail_thread.py47
-rw-r--r--addons/mail/wizard/mail_compose_message.py4
-rw-r--r--addons/maintenance/tests/test_maintenance.py4
-rw-r--r--addons/maintenance/tests/test_maintenance_multicompany.py12
-rw-r--r--addons/mass_mailing/models/mail_thread.py3
-rw-r--r--addons/mass_mailing/views/mailing_mailing_views.xml8
-rw-r--r--addons/mass_mailing/views/utm_campaign_views.xml4
-rw-r--r--addons/mass_mailing_sms/views/mailing_mailing_views.xml6
-rw-r--r--addons/mass_mailing_sms/views/mailing_sms_menus.xml16
-rw-r--r--addons/mass_mailing_sms/wizard/mailing_sms_test_views.xml2
-rw-r--r--addons/mrp/models/mrp_bom.py1
-rw-r--r--addons/mrp/models/mrp_production.py1
-rw-r--r--addons/mrp/models/mrp_routing.py1
-rw-r--r--addons/mrp/models/mrp_unbuild.py1
-rw-r--r--addons/mrp/models/mrp_workcenter.py1
-rw-r--r--addons/mrp/models/res_company.py1
-rw-r--r--addons/mrp/tests/test_multicompany.py2
-rw-r--r--addons/mrp/tests/test_procurement.py2
-rw-r--r--addons/mrp/tests/test_stock.py8
-rw-r--r--addons/mrp/tests/test_warehouse_multistep_manufacturing.py2
-rw-r--r--addons/mrp/tests/test_workorder_operation.py12
-rw-r--r--addons/mrp_account/tests/test_bom_price.py4
-rw-r--r--addons/mrp_subcontracting/models/mrp_bom.py1
-rw-r--r--addons/mrp_subcontracting/models/stock_move.py1
-rw-r--r--addons/mrp_subcontracting/tests/test_subcontracting.py68
-rw-r--r--addons/mrp_subcontracting/wizard/mrp_product_produce.py1
-rw-r--r--addons/payment_stripe/models/payment.py2
-rw-r--r--addons/point_of_sale/static/src/js/printers.js4
-rw-r--r--addons/pos_cache/models/pos_cache.py4
-rw-r--r--addons/pos_restaurant/static/src/js/floors.js2
-rw-r--r--addons/pos_restaurant/static/src/js/tours/pos_restaurant.js2
-rw-r--r--addons/project_timesheet_holidays/tests/test_timesheet_holidays.py6
-rw-r--r--addons/purchase/tests/test_purchase_order_report.py8
-rw-r--r--addons/purchase_mrp/tests/test_purchase_mrp_flow.py38
-rw-r--r--addons/purchase_stock/tests/test_create_picking.py4
-rw-r--r--addons/purchase_stock/tests/test_fifo_price.py14
-rw-r--r--addons/purchase_stock/tests/test_move_cancel_propagation.py2
-rw-r--r--addons/purchase_stock/tests/test_onchange_product.py8
-rw-r--r--addons/purchase_stock/tests/test_stockvaluation.py56
-rw-r--r--addons/sale/tests/test_access_rights.py2
-rw-r--r--addons/sale/tests/test_onchange.py6
-rw-r--r--addons/sale/tests/test_reinvoice.py50
-rw-r--r--addons/sale/tests/test_sale_order.py10
-rw-r--r--addons/sale/tests/test_sale_pricelist.py18
-rw-r--r--addons/sale/tests/test_sale_refund.py222
-rw-r--r--addons/sale/tests/test_sale_to_invoice.py76
-rw-r--r--addons/sale_expense/tests/test_reinvoice.py54
-rw-r--r--addons/sale_mrp/tests/test_sale_mrp_flow.py120
-rw-r--r--addons/sale_purchase/tests/test_sale_purchase.py1
-rw-r--r--addons/sale_stock/tests/test_sale_order_dates.py6
-rw-r--r--addons/sale_stock/tests/test_sale_stock.py12
-rw-r--r--addons/sale_stock/tests/test_sale_stock_lead_time.py8
-rw-r--r--addons/sale_timesheet/tests/test_project_billing.py2
-rw-r--r--addons/sale_timesheet/tests/test_reinvoice.py58
-rw-r--r--addons/sms/views/sms_template_views.xml2
-rw-r--r--addons/stock/models/product.py1
-rw-r--r--addons/stock/models/product_strategy.py1
-rw-r--r--addons/stock/models/res_company.py1
-rw-r--r--addons/stock/models/res_partner.py1
-rw-r--r--addons/stock/models/stock_inventory.py1
-rw-r--r--addons/stock/models/stock_move.py1
-rw-r--r--addons/stock/models/stock_move_line.py1
-rw-r--r--addons/stock/models/stock_picking.py1
-rw-r--r--addons/stock/models/stock_production_lot.py1
-rw-r--r--addons/stock/models/stock_quant.py1
-rw-r--r--addons/stock/models/stock_scrap.py1
-rw-r--r--addons/stock/models/stock_warehouse.py1
-rw-r--r--addons/stock/tests/test_move2.py40
-rw-r--r--addons/stock/tests/test_packing.py28
-rw-r--r--addons/stock/tests/test_proc_rule.py14
-rw-r--r--addons/stock/tests/test_stock_flow.py36
-rw-r--r--addons/stock/tests/test_stock_location_search.py4
-rw-r--r--addons/stock_dropshipping/tests/test_dropship.py6
-rw-r--r--addons/stock_picking_batch/models/stock_picking_batch.py1
-rw-r--r--addons/survey/tests/test_certification_badge.py38
-rw-r--r--addons/survey/tests/test_survey_invite.py4
-rw-r--r--addons/survey/views/survey_menus.xml30
-rw-r--r--addons/survey/wizard/survey_invite.py4
-rw-r--r--addons/test_mail/data/test_mail_data.py34
-rw-r--r--addons/test_mail/tests/common.py4
-rw-r--r--addons/test_mail/tests/test_discuss.py2
-rw-r--r--addons/test_mail/tests/test_mail_channel.py4
-rw-r--r--addons/test_mail/tests/test_mail_gateway.py50
-rw-r--r--addons/test_mail/tests/test_mail_message.py5
-rw-r--r--addons/test_mail/tests/test_message_management.py314
-rw-r--r--addons/test_mail/tests/test_message_post.py3
-rw-r--r--addons/test_mail/tests/test_message_track.py4
-rw-r--r--addons/test_mail/tests/test_performance.py4
-rw-r--r--addons/test_mail_full/tests/test_sms_composer.py8
-rw-r--r--addons/test_mass_mailing/tests/test_mail_channel.py2
-rw-r--r--addons/test_website/tests/test_controller_args.py24
-rw-r--r--addons/test_website/tests/test_views_during_module_operation.py4
-rw-r--r--addons/uom/tests/test_uom.py10
-rw-r--r--addons/web/controllers/main.py6
-rw-r--r--addons/web/models/ir_http.py4
-rw-r--r--addons/web/models/ir_qweb.py2
-rw-r--r--addons/web_diagram/__init__.py4
-rw-r--r--addons/web_diagram/__manifest__.py21
-rw-r--r--addons/web_diagram/controllers/__init__.py4
-rw-r--r--addons/web_diagram/controllers/main.py116
-rw-r--r--addons/web_diagram/i18n/af.po93
-rw-r--r--addons/web_diagram/i18n/am.po92
-rw-r--r--addons/web_diagram/i18n/ar.po84
-rw-r--r--addons/web_diagram/i18n/az.po78
-rw-r--r--addons/web_diagram/i18n/bg.po100
-rw-r--r--addons/web_diagram/i18n/bn.po99
-rw-r--r--addons/web_diagram/i18n/bs.po81
-rw-r--r--addons/web_diagram/i18n/ca.po84
-rw-r--r--addons/web_diagram/i18n/cs.po85
-rw-r--r--addons/web_diagram/i18n/da.po82
-rw-r--r--addons/web_diagram/i18n/de.po87
-rw-r--r--addons/web_diagram/i18n/el.po84
-rw-r--r--addons/web_diagram/i18n/en_AU.po100
-rw-r--r--addons/web_diagram/i18n/en_GB.po100
-rw-r--r--addons/web_diagram/i18n/es.po86
-rw-r--r--addons/web_diagram/i18n/es_AR.po100
-rw-r--r--addons/web_diagram/i18n/es_BO.po93
-rw-r--r--addons/web_diagram/i18n/es_CL.po97
-rw-r--r--addons/web_diagram/i18n/es_CO.po101
-rw-r--r--addons/web_diagram/i18n/es_CR.po93
-rw-r--r--addons/web_diagram/i18n/es_DO.po100
-rw-r--r--addons/web_diagram/i18n/es_EC.po100
-rw-r--r--addons/web_diagram/i18n/es_PE.po101
-rw-r--r--addons/web_diagram/i18n/es_PY.po93
-rw-r--r--addons/web_diagram/i18n/es_VE.po93
-rw-r--r--addons/web_diagram/i18n/et.po83
-rw-r--r--addons/web_diagram/i18n/eu.po92
-rw-r--r--addons/web_diagram/i18n/fa.po81
-rw-r--r--addons/web_diagram/i18n/fi.po82
-rw-r--r--addons/web_diagram/i18n/fr.po86
-rw-r--r--addons/web_diagram/i18n/fr_BE.po93
-rw-r--r--addons/web_diagram/i18n/fr_CA.po93
-rw-r--r--addons/web_diagram/i18n/gl.po99
-rw-r--r--addons/web_diagram/i18n/gu.po81
-rw-r--r--addons/web_diagram/i18n/he.po82
-rw-r--r--addons/web_diagram/i18n/hi.po92
-rw-r--r--addons/web_diagram/i18n/hr.po85
-rw-r--r--addons/web_diagram/i18n/hu.po83
-rw-r--r--addons/web_diagram/i18n/hy.po92
-rw-r--r--addons/web_diagram/i18n/id.po82
-rw-r--r--addons/web_diagram/i18n/is.po82
-rw-r--r--addons/web_diagram/i18n/it.po84
-rw-r--r--addons/web_diagram/i18n/ja.po82
-rw-r--r--addons/web_diagram/i18n/ka.po99
-rw-r--r--addons/web_diagram/i18n/kab.po92
-rw-r--r--addons/web_diagram/i18n/kk.po92
-rw-r--r--addons/web_diagram/i18n/km.po81
-rw-r--r--addons/web_diagram/i18n/ko.po83
-rw-r--r--addons/web_diagram/i18n/lt.po85
-rw-r--r--addons/web_diagram/i18n/lv.po78
-rw-r--r--addons/web_diagram/i18n/mk.po100
-rw-r--r--addons/web_diagram/i18n/mn.po82
-rw-r--r--addons/web_diagram/i18n/nb.po85
-rw-r--r--addons/web_diagram/i18n/nl.po86
-rw-r--r--addons/web_diagram/i18n/nl_BE.po100
-rw-r--r--addons/web_diagram/i18n/pl.po88
-rw-r--r--addons/web_diagram/i18n/pt.po83
-rw-r--r--addons/web_diagram/i18n/pt_BR.po83
-rw-r--r--addons/web_diagram/i18n/ro.po81
-rw-r--r--addons/web_diagram/i18n/ru.po82
-rw-r--r--addons/web_diagram/i18n/sk.po82
-rw-r--r--addons/web_diagram/i18n/sl.po78
-rw-r--r--addons/web_diagram/i18n/sr.po82
-rw-r--r--addons/web_diagram/i18n/sr@latin.po81
-rw-r--r--addons/web_diagram/i18n/sv.po81
-rw-r--r--addons/web_diagram/i18n/th.po83
-rw-r--r--addons/web_diagram/i18n/tr.po86
-rw-r--r--addons/web_diagram/i18n/uk.po86
-rw-r--r--addons/web_diagram/i18n/vi.po85
-rw-r--r--addons/web_diagram/i18n/web_diagram.pot78
-rw-r--r--addons/web_diagram/i18n/zh_CN.po86
-rw-r--r--addons/web_diagram/i18n/zh_HK.po93
-rw-r--r--addons/web_diagram/i18n/zh_TW.po83
-rw-r--r--addons/web_diagram/static/lib/js/jquery.mousewheel.js84
-rw-r--r--addons/web_diagram/static/lib/js/raphael.js5501
-rw-r--r--addons/web_diagram/static/src/js/diagram_controller.js209
-rw-r--r--addons/web_diagram/static/src/js/diagram_model.js92
-rw-r--r--addons/web_diagram/static/src/js/diagram_renderer.js143
-rw-r--r--addons/web_diagram/static/src/js/diagram_view.js123
-rw-r--r--addons/web_diagram/static/src/js/graph.js998
-rw-r--r--addons/web_diagram/static/src/js/vec2.js358
-rw-r--r--addons/web_diagram/static/src/js/view_registry.js10
-rw-r--r--addons/web_diagram/static/src/scss/diagram_view.scss12
-rw-r--r--addons/web_diagram/static/src/xml/base_diagram.xml16
-rw-r--r--addons/web_diagram/static/tests/diagram_tests.js308
-rw-r--r--addons/web_diagram/views/web_diagram_templates.xml23
-rw-r--r--addons/web_editor/models/ir_qweb.py2
-rw-r--r--addons/website/models/ir_http.py4
-rw-r--r--addons/website/models/website.py15
-rw-r--r--addons/website_event_questions/views/event_templates.xml2
-rw-r--r--addons/website_form/controllers/main.py2
-rw-r--r--addons/website_mail_channel/tests/test_unsubscribe.py2
-rw-r--r--addons/website_sale/tests/test_website_sale_pricelist.py6
-rw-r--r--addons/website_sale_comparison/tests/test_website_sale_comparison.py6
-rw-r--r--addons/website_slides/__manifest__.py1
-rw-r--r--addons/website_slides/static/src/scss/rating_rating_views.scss10
-rw-r--r--addons/website_slides/static/src/scss/slide_views.scss2
-rw-r--r--addons/website_slides/wizard/slide_channel_invite.py3
-rw-r--r--addons/website_theme_install/tests/test_views.py12
-rw-r--r--debian/copyright9
-rw-r--r--doc/_extensions/odoo_ext/pygments_override.py4
-rw-r--r--doc/cla/individual/bikibi.md9
-rw-r--r--doc/cla/individual/niyasraphy.md12
-rw-r--r--doc/reference/views.rst44
-rw-r--r--odoo/addons/base/models/assetsbundle.py4
-rw-r--r--odoo/addons/base/models/ir_actions_report.py4
-rw-r--r--odoo/addons/base/models/ir_attachment.py19
-rw-r--r--odoo/addons/base/models/ir_http.py2
-rw-r--r--odoo/addons/base/models/ir_mail_server.py161
-rw-r--r--odoo/addons/base/models/ir_model.py2
-rw-r--r--odoo/addons/base/models/ir_ui_menu.py2
-rw-r--r--odoo/addons/base/models/ir_ui_view.py36
-rw-r--r--odoo/addons/base/models/res_partner.py4
-rw-r--r--odoo/addons/base/models/res_users.py6
-rw-r--r--odoo/addons/base/rng/diagram_view.rng50
-rw-r--r--odoo/addons/base/tests/test_acl.py14
-rw-r--r--odoo/addons/base/tests/test_base.py18
-rw-r--r--odoo/addons/base/tests/test_basecase.py2
-rw-r--r--odoo/addons/base/tests/test_mail.py2
-rw-r--r--odoo/addons/base/tests/test_mimetypes.py2
-rw-r--r--odoo/addons/base/tests/test_osv.py12
-rw-r--r--odoo/addons/base/tests/test_qweb.py4
-rw-r--r--odoo/addons/base/tests/test_res_partner_bank.py22
-rw-r--r--odoo/addons/base/tests/test_translate.py40
-rw-r--r--odoo/addons/base/wizard/base_export_language.py2
-rw-r--r--odoo/addons/base/wizard/base_import_language.py2
-rw-r--r--odoo/addons/test_assetsbundle/tests/test_assetsbundle.py124
-rw-r--r--odoo/addons/test_lint/tests/test_pylint.py3
-rw-r--r--odoo/addons/test_new_api/tests/test_company_checks.py1
-rw-r--r--odoo/addons/test_performance/tests/test_performance.py2
-rw-r--r--odoo/addons/test_testing_utilities/tests/test_form_impl.py2
-rw-r--r--odoo/addons/test_translation_import/tests/test_term_count.py6
-rw-r--r--odoo/api.py4
-rw-r--r--odoo/exceptions.py2
-rw-r--r--odoo/http.py25
-rw-r--r--odoo/models.py2
-rw-r--r--odoo/modules/module.py96
-rw-r--r--odoo/release.py2
-rw-r--r--odoo/service/server.py2
-rw-r--r--odoo/service/wsgi_server.py16
-rw-r--r--odoo/tools/cache.py18
-rw-r--r--odoo/tools/convert.py4
-rw-r--r--odoo/tools/mail.py56
-rw-r--r--odoo/tools/view_validation.py28
279 files changed, 1599 insertions, 16307 deletions
diff --git a/.tx/config b/.tx/config
index 9594153564e..f3d592ef24e 100644
--- a/.tx/config
+++ b/.tx/config
@@ -2,1087 +2,1087 @@
host = https://www.transifex.com
type = PO
-[odoo-13.base]
+[odoo-master.base]
file_filter = odoo/addons/base/i18n/<lang>.po
source_file = odoo/addons/base/i18n/base.pot
source_lang = en
-[odoo-13.account]
+[odoo-master.account]
file_filter = addons/account/i18n/<lang>.po
source_file = addons/account/i18n/account.pot
source_lang = en
-[odoo-13.account_analytic_default]
+[odoo-master.account_analytic_default]
file_filter = addons/account_analytic_default/i18n/<lang>.po
source_file = addons/account_analytic_default/i18n/account_analytic_default.pot
source_lang = en
-[odoo-13.account_bank_statement_import]
+[odoo-master.account_bank_statement_import]
file_filter = addons/account_bank_statement_import/i18n/<lang>.po
source_file = addons/account_bank_statement_import/i18n/account_bank_statement_import.pot
source_lang = en
-[odoo-13.account_cancel]
+[odoo-master.account_cancel]
file_filter = addons/account_cancel/i18n/<lang>.po
source_file = addons/account_cancel/i18n/account_cancel.pot
source_lang = en
-[odoo-13.account_check_printing]
+[odoo-master.account_check_printing]
file_filter = addons/account_check_printing/i18n/<lang>.po
source_file = addons/account_check_printing/i18n/account_check_printing.pot
source_lang = en
-[odoo-13.account_facturx]
+[odoo-master.account_facturx]
file_filter = addons/account_facturx/i18n/<lang>.po
source_file = addons/account_facturx/i18n/account_facturx.pot
source_lang = en
-[odoo-13.account_payment]
+[odoo-master.account_payment]
file_filter = addons/account_payment/i18n/<lang>.po
source_file = addons/account_payment/i18n/account_payment.pot
source_lang = en
-[odoo-13.account_tax_python]
+[odoo-master.account_tax_python]
file_filter = addons/account_tax_python/i18n/<lang>.po
source_file = addons/account_tax_python/i18n/account_tax_python.pot
source_lang = en
-[odoo-13.analytic]
+[odoo-master.analytic]
file_filter = addons/analytic/i18n/<lang>.po
source_file = addons/analytic/i18n/analytic.pot
source_lang = en
-[odoo-13.association]
+[odoo-master.association]
file_filter = addons/association/i18n/<lang>.po
source_file = addons/association/i18n/association.pot
source_lang = en
-[odoo-13.attachment_indexation]
+[odoo-master.attachment_indexation]
file_filter = addons/attachment_indexation/i18n/<lang>.po
source_file = addons/attachment_indexation/i18n/attachment_indexation.pot
source_lang = en
-[odoo-13.auth_ldap]
+[odoo-master.auth_ldap]
file_filter = addons/auth_ldap/i18n/<lang>.po
source_file = addons/auth_ldap/i18n/auth_ldap.pot
source_lang = en
-[odoo-13.auth_oauth]
+[odoo-master.auth_oauth]
file_filter = addons/auth_oauth/i18n/<lang>.po
source_file = addons/auth_oauth/i18n/auth_oauth.pot
source_lang = en
-[odoo-13.auth_password_policy]
+[odoo-master.auth_password_policy]
file_filter = addons/auth_password_policy/i18n/<lang>.po
source_file = addons/auth_password_policy/i18n/auth_password_policy.pot
source_lang = en
-[odoo-13.auth_signup]
+[odoo-master.auth_signup]
file_filter = addons/auth_signup/i18n/<lang>.po
source_file = addons/auth_signup/i18n/auth_signup.pot
source_lang = en
-[odoo-13.barcodes]
+[odoo-master.barcodes]
file_filter = addons/barcodes/i18n/<lang>.po
source_file = addons/barcodes/i18n/barcodes.pot
source_lang = en
-[odoo-13.base_address_city]
+[odoo-master.base_address_city]
file_filter = addons/base_address_city/i18n/<lang>.po
source_file = addons/base_address_city/i18n/base_address_city.pot
source_lang = en
-[odoo-13.base_address_extended]
+[odoo-master.base_address_extended]
file_filter = addons/base_address_extended/i18n/<lang>.po
source_file = addons/base_address_extended/i18n/base_address_extended.pot
source_lang = en
-[odoo-13.base_automation]
+[odoo-master.base_automation]
file_filter = addons/base_automation/i18n/<lang>.po
source_file = addons/base_automation/i18n/base_automation.pot
source_lang = en
-[odoo-13.base_gengo]
+[odoo-master.base_gengo]
file_filter = addons/base_gengo/i18n/<lang>.po
source_file = addons/base_gengo/i18n/base_gengo.pot
source_lang = en
-[odoo-13.base_geolocalize]
+[odoo-master.base_geolocalize]
file_filter = addons/base_geolocalize/i18n/<lang>.po
source_file = addons/base_geolocalize/i18n/base_geolocalize.pot
source_lang = en
-[odoo-13.base_iban]
+[odoo-master.base_iban]
file_filter = addons/base_iban/i18n/<lang>.po
source_file = addons/base_iban/i18n/base_iban.pot
source_lang = en
-[odoo-13.base_import]
+[odoo-master.base_import]
file_filter = addons/base_import/i18n/<lang>.po
source_file = addons/base_import/i18n/base_import.pot
source_lang = en
-[odoo-13.base_import_module]
+[odoo-master.base_import_module]
file_filter = addons/base_import_module/i18n/<lang>.po
source_file = addons/base_import_module/i18n/base_import_module.pot
source_lang = en
-[odoo-13.base_setup]
+[odoo-master.base_setup]
file_filter = addons/base_setup/i18n/<lang>.po
source_file = addons/base_setup/i18n/base_setup.pot
source_lang = en
-[odoo-13.base_sparse_field]
+[odoo-master.base_sparse_field]
file_filter = addons/base_sparse_field/i18n/<lang>.po
source_file = addons/base_sparse_field/i18n/base_sparse_field.pot
source_lang = en
-[odoo-13.base_vat]
+[odoo-master.base_vat]
file_filter = addons/base_vat/i18n/<lang>.po
source_file = addons/base_vat/i18n/base_vat.pot
source_lang = en
-[odoo-13.board]
+[odoo-master.board]
file_filter = addons/board/i18n/<lang>.po
source_file = addons/board/i18n/board.pot
source_lang = en
-[odoo-13.bus]
+[odoo-master.bus]
file_filter = addons/bus/i18n/<lang>.po
source_file = addons/bus/i18n/bus.pot
source_lang = en
-[odoo-13.calendar]
+[odoo-master.calendar]
file_filter = addons/calendar/i18n/<lang>.po
source_file = addons/calendar/i18n/calendar.pot
source_lang = en
-[odoo-13.calendar_sms]
+[odoo-master.calendar_sms]
file_filter = addons/calendar_sms/i18n/<lang>.po
source_file = addons/calendar_sms/i18n/calendar_sms.pot
source_lang = en
-[odoo-13.contacts]
+[odoo-master.contacts]
file_filter = addons/contacts/i18n/<lang>.po
source_file = addons/contacts/i18n/contacts.pot
source_lang = en
-[odoo-13.crm]
+[odoo-master.crm]
file_filter = addons/crm/i18n/<lang>.po
source_file = addons/crm/i18n/crm.pot
source_lang = en
-[odoo-13.crm_iap_lead]
+[odoo-master.crm_iap_lead]
file_filter = addons/crm_iap_lead/i18n/<lang>.po
source_file = addons/crm_iap_lead/i18n/crm_iap_lead.pot
source_lang = en
-[odoo-13.crm_iap_lead_enrich]
+[odoo-master.crm_iap_lead_enrich]
file_filter = addons/crm_iap_lead_enrich/i18n/<lang>.po
source_file = addons/crm_iap_lead_enrich/i18n/crm_iap_lead_enrich.pot
source_lang = en
-[odoo-13.crm_iap_lead_website]
+[odoo-master.crm_iap_lead_website]
file_filter = addons/crm_iap_lead_website/i18n/<lang>.po
source_file = addons/crm_iap_lead_website/i18n/crm_iap_lead_website.pot
source_lang = en
-[odoo-13.crm_livechat]
+[odoo-master.crm_livechat]
file_filter = addons/crm_livechat/i18n/<lang>.po
source_file = addons/crm_livechat/i18n/crm_livechat.pot
source_lang = en
-[odoo-13.crm_sms]
+[odoo-master.crm_sms]
file_filter = addons/crm_sms/i18n/<lang>.po
source_file = addons/crm_sms/i18n/crm_sms.pot
source_lang = en
-[odoo-13.delivery]
+[odoo-master.delivery]
file_filter = addons/delivery/i18n/<lang>.po
source_file = addons/delivery/i18n/delivery.pot
source_lang = en
-[odoo-13.digest]
+[odoo-master.digest]
file_filter = addons/digest/i18n/<lang>.po
source_file = addons/digest/i18n/digest.pot
source_lang = en
-[odoo-13.event]
+[odoo-master.event]
file_filter = addons/event/i18n/<lang>.po
source_file = addons/event/i18n/event.pot
source_lang = en
-[odoo-13.event_sale]
+[odoo-master.event_sale]
file_filter = addons/event_sale/i18n/<lang>.po
source_file = addons/event_sale/i18n/event_sale.pot
source_lang = en
-[odoo-13.event_sms]
+[odoo-master.event_sms]
file_filter = addons/event_sms/i18n/<lang>.po
source_file = addons/event_sms/i18n/event_sms.pot
source_lang = en
-[odoo-13.fetchmail]
+[odoo-master.fetchmail]
file_filter = addons/fetchmail/i18n/<lang>.po
source_file = addons/fetchmail/i18n/fetchmail.pot
source_lang = en
-[odoo-13.fleet]
+[odoo-master.fleet]
file_filter = addons/fleet/i18n/<lang>.po
source_file = addons/fleet/i18n/fleet.pot
source_lang = en
-[odoo-13.gamification]
+[odoo-master.gamification]
file_filter = addons/gamification/i18n/<lang>.po
source_file = addons/gamification/i18n/gamification.pot
source_lang = en
-[odoo-13.gamification_sale_crm]
+[odoo-master.gamification_sale_crm]
file_filter = addons/gamification_sale_crm/i18n/<lang>.po
source_file = addons/gamification_sale_crm/i18n/gamification_sale_crm.pot
source_lang = en
-[odoo-13.google_account]
+[odoo-master.google_account]
file_filter = addons/google_account/i18n/<lang>.po
source_file = addons/google_account/i18n/google_account.pot
source_lang = en
-[odoo-13.google_calendar]
+[odoo-master.google_calendar]
file_filter = addons/google_calendar/i18n/<lang>.po
source_file = addons/google_calendar/i18n/google_calendar.pot
source_lang = en
-[odoo-13.google_drive]
+[odoo-master.google_drive]
file_filter = addons/google_drive/i18n/<lang>.po
source_file = addons/google_drive/i18n/google_drive.pot
source_lang = en
-[odoo-13.google_spreadsheet]
+[odoo-master.google_spreadsheet]
file_filter = addons/google_spreadsheet/i18n/<lang>.po
source_file = addons/google_spreadsheet/i18n/google_spreadsheet.pot
source_lang = en
-[odoo-13.hr]
+[odoo-master.hr]
file_filter = addons/hr/i18n/<lang>.po
source_file = addons/hr/i18n/hr.pot
source_lang = en
-[odoo-13.hr_attendance]
+[odoo-master.hr_attendance]
file_filter = addons/hr_attendance/i18n/<lang>.po
source_file = addons/hr_attendance/i18n/hr_attendance.pot
source_lang = en
-[odoo-13.hr_contract]
+[odoo-master.hr_contract]
file_filter = addons/hr_contract/i18n/<lang>.po
source_file = addons/hr_contract/i18n/hr_contract.pot
source_lang = en
-[odoo-13.hr_expense]
+[odoo-master.hr_expense]
file_filter = addons/hr_expense/i18n/<lang>.po
source_file = addons/hr_expense/i18n/hr_expense.pot
source_lang = en
-[odoo-13.hr_expense_check]
+[odoo-master.hr_expense_check]
file_filter = addons/hr_expense_check/i18n/<lang>.po
source_file = addons/hr_expense_check/i18n/hr_expense_check.pot
source_lang = en
-[odoo-13.hr_fleet]
+[odoo-master.hr_fleet]
file_filter = addons/hr_fleet/i18n/<lang>.po
source_file = addons/hr_fleet/i18n/hr_fleet.pot
source_lang = en
-[odoo-13.hr_gamification]
+[odoo-master.hr_gamification]
file_filter = addons/hr_gamification/i18n/<lang>.po
source_file = addons/hr_gamification/i18n/hr_gamification.pot
source_lang = en
-[odoo-13.hr_holidays]
+[odoo-master.hr_holidays]
file_filter = addons/hr_holidays/i18n/<lang>.po
source_file = addons/hr_holidays/i18n/hr_holidays.pot
source_lang = en
-[odoo-13.hr_maintenance]
+[odoo-master.hr_maintenance]
file_filter = addons/hr_maintenance/i18n/<lang>.po
source_file = addons/hr_maintenance/i18n/hr_maintenance.pot
source_lang = en
-[odoo-13.hr_org_chart]
+[odoo-master.hr_org_chart]
file_filter = addons/hr_org_chart/i18n/<lang>.po
source_file = addons/hr_org_chart/i18n/hr_org_chart.pot
source_lang = en
-[odoo-13.hr_presence]
+[odoo-master.hr_presence]
file_filter = addons/hr_presence/i18n/<lang>.po
source_file = addons/hr_presence/i18n/hr_presence.pot
source_lang = en
-[odoo-13.hr_recruitment]
+[odoo-master.hr_recruitment]
file_filter = addons/hr_recruitment/i18n/<lang>.po
source_file = addons/hr_recruitment/i18n/hr_recruitment.pot
source_lang = en
-[odoo-13.hr_recruitment_survey]
+[odoo-master.hr_recruitment_survey]
file_filter = addons/hr_recruitment_survey/i18n/<lang>.po
source_file = addons/hr_recruitment_survey/i18n/hr_recruitment_survey.pot
source_lang = en
-[odoo-13.hr_skills]
+[odoo-master.hr_skills]
file_filter = addons/hr_skills/i18n/<lang>.po
source_file = addons/hr_skills/i18n/hr_skills.pot
source_lang = en
-[odoo-13.hr_skills_slides]
+[odoo-master.hr_skills_slides]
file_filter = addons/hr_skills_slides/i18n/<lang>.po
source_file = addons/hr_skills_slides/i18n/hr_skills_slides.pot
source_lang = en
-[odoo-13.hr_skills_survey]
+[odoo-master.hr_skills_survey]
file_filter = addons/hr_skills_survey/i18n/<lang>.po
source_file = addons/hr_skills_survey/i18n/hr_skills_survey.pot
source_lang = en
-[odoo-13.hr_timesheet]
+[odoo-master.hr_timesheet]
file_filter = addons/hr_timesheet/i18n/<lang>.po
source_file = addons/hr_timesheet/i18n/hr_timesheet.pot
source_lang = en
-[odoo-13.hr_timesheet_attendance]
+[odoo-master.hr_timesheet_attendance]
file_filter = addons/hr_timesheet_attendance/i18n/<lang>.po
source_file = addons/hr_timesheet_attendance/i18n/hr_timesheet_attendance.pot
source_lang = en
-[odoo-13.hr_work_entry]
+[odoo-master.hr_work_entry]
file_filter = addons/hr_work_entry/i18n/<lang>.po
source_file = addons/hr_work_entry/i18n/hr_work_entry.pot
source_lang = en
-[odoo-13.http_routing]
+[odoo-master.http_routing]
file_filter = addons/http_routing/i18n/<lang>.po
source_file = addons/http_routing/i18n/http_routing.pot
source_lang = en
-[odoo-13.iap]
+[odoo-master.iap]
file_filter = addons/iap/i18n/<lang>.po
source_file = addons/iap/i18n/iap.pot
source_lang = en
-[odoo-13.im_livechat]
+[odoo-master.im_livechat]
file_filter = addons/im_livechat/i18n/<lang>.po
source_file = addons/im_livechat/i18n/im_livechat.pot
source_lang = en
-[odoo-13.im_livechat_mail_bot]
+[odoo-master.im_livechat_mail_bot]
file_filter = addons/im_livechat_mail_bot/i18n/<lang>.po
source_file = addons/im_livechat_mail_bot/i18n/im_livechat_mail_bot.pot
source_lang = en
-[odoo-13.im_support]
+[odoo-master.im_support]
file_filter = addons/im_support/i18n/<lang>.po
source_file = addons/im_support/i18n/im_support.pot
source_lang = en
-[odoo-13.l10n_multilang]
+[odoo-master.l10n_multilang]
file_filter = addons/l10n_multilang/i18n/<lang>.po
source_file = addons/l10n_multilang/i18n/l10n_multilang.pot
source_lang = en
-[odoo-13.link_tracker]
+[odoo-master.link_tracker]
file_filter = addons/link_tracker/i18n/<lang>.po
source_file = addons/link_tracker/i18n/link_tracker.pot
source_lang = en
-[odoo-13.lunch]
+[odoo-master.lunch]
file_filter = addons/lunch/i18n/<lang>.po
source_file = addons/lunch/i18n/lunch.pot
source_lang = en
-[odoo-13.mail]
+[odoo-master.mail]
file_filter = addons/mail/i18n/<lang>.po
source_file = addons/mail/i18n/mail.pot
source_lang = en
-[odoo-13.mail_bot]
+[odoo-master.mail_bot]
file_filter = addons/mail_bot/i18n/<lang>.po
source_file = addons/mail_bot/i18n/mail_bot.pot
source_lang = en
-[odoo-13.maintenance]
+[odoo-master.maintenance]
file_filter = addons/maintenance/i18n/<lang>.po
source_file = addons/maintenance/i18n/maintenance.pot
source_lang = en
-[odoo-13.mass_mailing]
+[odoo-master.mass_mailing]
file_filter = addons/mass_mailing/i18n/<lang>.po
source_file = addons/mass_mailing/i18n/mass_mailing.pot
source_lang = en
-[odoo-13.mass_mailing_crm]
+[odoo-master.mass_mailing_crm]
file_filter = addons/mass_mailing_crm/i18n/<lang>.po
source_file = addons/mass_mailing_crm/i18n/mass_mailing_crm.pot
source_lang = en
-[odoo-13.mass_mailing_event]
+[odoo-master.mass_mailing_event]
file_filter = addons/mass_mailing_event/i18n/<lang>.po
source_file = addons/mass_mailing_event/i18n/mass_mailing_event.pot
source_lang = en
-[odoo-13.mass_mailing_event_track]
+[odoo-master.mass_mailing_event_track]
file_filter = addons/mass_mailing_event_track/i18n/<lang>.po
source_file = addons/mass_mailing_event_track/i18n/mass_mailing_event_track.pot
source_lang = en
-[odoo-13.mass_mailing_sale]
+[odoo-master.mass_mailing_sale]
file_filter = addons/mass_mailing_sale/i18n/<lang>.po
source_file = addons/mass_mailing_sale/i18n/mass_mailing_sale.pot
source_lang = en
-[odoo-13.mass_mailing_slides]
+[odoo-master.mass_mailing_slides]
file_filter = addons/mass_mailing_slides/i18n/<lang>.po
source_file = addons/mass_mailing_slides/i18n/mass_mailing_slides.pot
source_lang = en
-[odoo-13.mass_mailing_sms]
+[odoo-master.mass_mailing_sms]
file_filter = addons/mass_mailing_sms/i18n/<lang>.po
source_file = addons/mass_mailing_sms/i18n/mass_mailing_sms.pot
source_lang = en
-[odoo-13.membership]
+[odoo-master.membership]
file_filter = addons/membership/i18n/<lang>.po
source_file = addons/membership/i18n/membership.pot
source_lang = en
-[odoo-13.mrp]
+[odoo-master.mrp]
file_filter = addons/mrp/i18n/<lang>.po
source_file = addons/mrp/i18n/mrp.pot
source_lang = en
-[odoo-13.mrp_account]
+[odoo-master.mrp_account]
file_filter = addons/mrp_account/i18n/<lang>.po
source_file = addons/mrp_account/i18n/mrp_account.pot
source_lang = en
-[odoo-13.mrp_subcontracting]
+[odoo-master.mrp_subcontracting]
file_filter = addons/mrp_subcontracting/i18n/<lang>.po
source_file = addons/mrp_subcontracting/i18n/mrp_subcontracting.pot
source_lang = en
-[odoo-13.note]
+[odoo-master.note]
file_filter = addons/note/i18n/<lang>.po
source_file = addons/note/i18n/note.pot
source_lang = en
-[odoo-13.note_pad]
+[odoo-master.note_pad]
file_filter = addons/note_pad/i18n/<lang>.po
source_file = addons/note_pad/i18n/note_pad.pot
source_lang = en
-[odoo-13.pad]
+[odoo-master.pad]
file_filter = addons/pad/i18n/<lang>.po
source_file = addons/pad/i18n/pad.pot
source_lang = en
-[odoo-13.pad_project]
+[odoo-master.pad_project]
file_filter = addons/pad_project/i18n/<lang>.po
source_file = addons/pad_project/i18n/pad_project.pot
source_lang = en
-[odoo-13.partner_autocomplete]
+[odoo-master.partner_autocomplete]
file_filter = addons/partner_autocomplete/i18n/<lang>.po
source_file = addons/partner_autocomplete/i18n/partner_autocomplete.pot
source_lang = en
-[odoo-13.partner_autocomplete_address_extended]
+[odoo-master.partner_autocomplete_address_extended]
file_filter = addons/partner_autocomplete_address_extended/i18n/<lang>.po
source_file = addons/partner_autocomplete_address_extended/i18n/partner_autocomplete_address_extended.pot
source_lang = en
-[odoo-13.payment]
+[odoo-master.payment]
file_filter = addons/payment/i18n/<lang>.po
source_file = addons/payment/i18n/payment.pot
source_lang = en
-[odoo-13.payment_adyen]
+[odoo-master.payment_adyen]
file_filter = addons/payment_adyen/i18n/<lang>.po
source_file = addons/payment_adyen/i18n/payment_adyen.pot
source_lang = en
-[odoo-13.payment_alipay]
+[odoo-master.payment_alipay]
file_filter = addons/payment_alipay/i18n/<lang>.po
source_file = addons/payment_alipay/i18n/payment_alipay.pot
source_lang = en
-[odoo-13.payment_authorize]
+[odoo-master.payment_authorize]
file_filter = addons/payment_authorize/i18n/<lang>.po
source_file = addons/payment_authorize/i18n/payment_authorize.pot
source_lang = en
-[odoo-13.payment_buckaroo]
+[odoo-master.payment_buckaroo]
file_filter = addons/payment_buckaroo/i18n/<lang>.po
source_file = addons/payment_buckaroo/i18n/payment_buckaroo.pot
source_lang = en
-[odoo-13.payment_ingenico]
+[odoo-master.payment_ingenico]
file_filter = addons/payment_ingenico/i18n/<lang>.po
source_file = addons/payment_ingenico/i18n/payment_ingenico.pot
source_lang = en
-[odoo-13.payment_paypal]
+[odoo-master.payment_paypal]
file_filter = addons/payment_paypal/i18n/<lang>.po
source_file = addons/payment_paypal/i18n/payment_paypal.pot
source_lang = en
-[odoo-13.payment_payulatam]
+[odoo-master.payment_payulatam]
file_filter = addons/payment_payulatam/i18n/<lang>.po
source_file = addons/payment_payulatam/i18n/payment_payulatam.pot
source_lang = en
-[odoo-13.payment_payumoney]
+[odoo-master.payment_payumoney]
file_filter = addons/payment_payumoney/i18n/<lang>.po
source_file = addons/payment_payumoney/i18n/payment_payumoney.pot
source_lang = en
-[odoo-13.payment_sips]
+[odoo-master.payment_sips]
file_filter = addons/payment_sips/i18n/<lang>.po
source_file = addons/payment_sips/i18n/payment_sips.pot
source_lang = en
-[odoo-13.payment_stripe]
+[odoo-master.payment_stripe]
file_filter = addons/payment_stripe/i18n/<lang>.po
source_file = addons/payment_stripe/i18n/payment_stripe.pot
source_lang = en
-[odoo-13.payment_transfer]
+[odoo-master.payment_transfer]
file_filter = addons/payment_transfer/i18n/<lang>.po
source_file = addons/payment_transfer/i18n/payment_transfer.pot
source_lang = en
-[odoo-13.phone_validation]
+[odoo-master.phone_validation]
file_filter = addons/phone_validation/i18n/<lang>.po
source_file = addons/phone_validation/i18n/phone_validation.pot
source_lang = en
-[odoo-13.point_of_sale]
+[odoo-master.point_of_sale]
file_filter = addons/point_of_sale/i18n/<lang>.po
source_file = addons/point_of_sale/i18n/point_of_sale.pot
source_lang = en
-[odoo-13.portal]
+[odoo-master.portal]
file_filter = addons/portal/i18n/<lang>.po
source_file = addons/portal/i18n/portal.pot
source_lang = en
-[odoo-13.pos_adyen]
+[odoo-master.pos_adyen]
file_filter = addons/pos_adyen/i18n/<lang>.po
source_file = addons/pos_adyen/i18n/pos_adyen.pot
source_lang = en
-[odoo-13.pos_cache]
+[odoo-master.pos_cache]
file_filter = addons/pos_cache/i18n/<lang>.po
source_file = addons/pos_cache/i18n/pos_cache.pot
source_lang = en
-[odoo-13.pos_discount]
+[odoo-master.pos_discount]
file_filter = addons/pos_discount/i18n/<lang>.po
source_file = addons/pos_discount/i18n/pos_discount.pot
source_lang = en
-[odoo-13.pos_epson_printer]
+[odoo-master.pos_epson_printer]
file_filter = addons/pos_epson_printer/i18n/<lang>.po
source_file = addons/pos_epson_printer/i18n/pos_epson_printer.pot
source_lang = en
-[odoo-13.pos_epson_printer_restaurant]
+[odoo-master.pos_epson_printer_restaurant]
file_filter = addons/pos_epson_printer_restaurant/i18n/<lang>.po
source_file = addons/pos_epson_printer_restaurant/i18n/pos_epson_printer_restaurant.pot
source_lang = en
-[odoo-13.pos_hr]
+[odoo-master.pos_hr]
file_filter = addons/pos_hr/i18n/<lang>.po
source_file = addons/pos_hr/i18n/pos_hr.pot
source_lang = en
-[odoo-13.pos_mercury]
+[odoo-master.pos_mercury]
file_filter = addons/pos_mercury/i18n/<lang>.po
source_file = addons/pos_mercury/i18n/pos_mercury.pot
source_lang = en
-[odoo-13.pos_reprint]
+[odoo-master.pos_reprint]
file_filter = addons/pos_reprint/i18n/<lang>.po
source_file = addons/pos_reprint/i18n/pos_reprint.pot
source_lang = en
-[odoo-13.pos_restaurant]
+[odoo-master.pos_restaurant]
file_filter = addons/pos_restaurant/i18n/<lang>.po
source_file = addons/pos_restaurant/i18n/pos_restaurant.pot
source_lang = en
-[odoo-13.pos_sale]
+[odoo-master.pos_sale]
file_filter = addons/pos_sale/i18n/<lang>.po
source_file = addons/pos_sale/i18n/pos_sale.pot
source_lang = en
-[odoo-13.procurement_jit]
+[odoo-master.procurement_jit]
file_filter = addons/procurement_jit/i18n/<lang>.po
source_file = addons/procurement_jit/i18n/procurement_jit.pot
source_lang = en
-[odoo-13.product]
+[odoo-master.product]
file_filter = addons/product/i18n/<lang>.po
source_file = addons/product/i18n/product.pot
source_lang = en
-[odoo-13.product_email_template]
+[odoo-master.product_email_template]
file_filter = addons/product_email_template/i18n/<lang>.po
source_file = addons/product_email_template/i18n/product_email_template.pot
source_lang = en
-[odoo-13.product_expiry]
+[odoo-master.product_expiry]
file_filter = addons/product_expiry/i18n/<lang>.po
source_file = addons/product_expiry/i18n/product_expiry.pot
source_lang = en
-[odoo-13.product_margin]
+[odoo-master.product_margin]
file_filter = addons/product_margin/i18n/<lang>.po
source_file = addons/product_margin/i18n/product_margin.pot
source_lang = en
-[odoo-13.product_matrix]
+[odoo-master.product_matrix]
file_filter = addons/product_matrix/i18n/<lang>.po
source_file = addons/product_matrix/i18n/product_matrix.pot
source_lang = en
-[odoo-13.project]
+[odoo-master.project]
file_filter = addons/project/i18n/<lang>.po
source_file = addons/project/i18n/project.pot
source_lang = en
-[odoo-13.project_timesheet_holidays]
+[odoo-master.project_timesheet_holidays]
file_filter = addons/project_timesheet_holidays/i18n/<lang>.po
source_file = addons/project_timesheet_holidays/i18n/project_timesheet_holidays.pot
source_lang = en
-[odoo-13.purchase]
+[odoo-master.purchase]
file_filter = addons/purchase/i18n/<lang>.po
source_file = addons/purchase/i18n/purchase.pot
source_lang = en
-[odoo-13.purchase_mrp]
+[odoo-master.purchase_mrp]
file_filter = addons/purchase_mrp/i18n/<lang>.po
source_file = addons/purchase_mrp/i18n/purchase_mrp.pot
source_lang = en
-[odoo-13.purchase_product_matrix]
+[odoo-master.purchase_product_matrix]
file_filter = addons/purchase_product_matrix/i18n/<lang>.po
source_file = addons/purchase_product_matrix/i18n/purchase_product_matrix.pot
source_lang = en
-[odoo-13.purchase_requisition]
+[odoo-master.purchase_requisition]
file_filter = addons/purchase_requisition/i18n/<lang>.po
source_file = addons/purchase_requisition/i18n/purchase_requisition.pot
source_lang = en
-[odoo-13.purchase_requisition_stock]
+[odoo-master.purchase_requisition_stock]
file_filter = addons/purchase_requisition_stock/i18n/<lang>.po
source_file = addons/purchase_requisition_stock/i18n/purchase_requisition_stock.pot
source_lang = en
-[odoo-13.purchase_stock]
+[odoo-master.purchase_stock]
file_filter = addons/purchase_stock/i18n/<lang>.po
source_file = addons/purchase_stock/i18n/purchase_stock.pot
source_lang = en
-[odoo-13.rating]
+[odoo-master.rating]
file_filter = addons/rating/i18n/<lang>.po
source_file = addons/rating/i18n/rating.pot
source_lang = en
-[odoo-13.repair]
+[odoo-master.repair]
file_filter = addons/repair/i18n/<lang>.po
source_file = addons/repair/i18n/repair.pot
source_lang = en
-[odoo-13.resource]
+[odoo-master.resource]
file_filter = addons/resource/i18n/<lang>.po
source_file = addons/resource/i18n/resource.pot
source_lang = en
-[odoo-13.sale]
+[odoo-master.sale]
file_filter = addons/sale/i18n/<lang>.po
source_file = addons/sale/i18n/sale.pot
source_lang = en
-[odoo-13.sale_coupon]
+[odoo-master.sale_coupon]
file_filter = addons/sale_coupon/i18n/<lang>.po
source_file = addons/sale_coupon/i18n/sale_coupon.pot
source_lang = en
-[odoo-13.sale_coupon_delivery]
+[odoo-master.sale_coupon_delivery]
file_filter = addons/sale_coupon_delivery/i18n/<lang>.po
source_file = addons/sale_coupon_delivery/i18n/sale_coupon_delivery.pot
source_lang = en
-[odoo-13.sale_crm]
+[odoo-master.sale_crm]
file_filter = addons/sale_crm/i18n/<lang>.po
source_file = addons/sale_crm/i18n/sale_crm.pot
source_lang = en
-[odoo-13.sale_expense]
+[odoo-master.sale_expense]
file_filter = addons/sale_expense/i18n/<lang>.po
source_file = addons/sale_expense/i18n/sale_expense.pot
source_lang = en
-[odoo-13.sale_management]
+[odoo-master.sale_management]
file_filter = addons/sale_management/i18n/<lang>.po
source_file = addons/sale_management/i18n/sale_management.pot
source_lang = en
-[odoo-13.sale_margin]
+[odoo-master.sale_margin]
file_filter = addons/sale_margin/i18n/<lang>.po
source_file = addons/sale_margin/i18n/sale_margin.pot
source_lang = en
-[odoo-13.sale_mrp]
+[odoo-master.sale_mrp]
file_filter = addons/sale_mrp/i18n/<lang>.po
source_file = addons/sale_mrp/i18n/sale_mrp.pot
source_lang = en
-[odoo-13.sale_product_configurator]
+[odoo-master.sale_product_configurator]
file_filter = addons/sale_product_configurator/i18n/<lang>.po
source_file = addons/sale_product_configurator/i18n/sale_product_configurator.pot
source_lang = en
-[odoo-13.sale_product_matrix]
+[odoo-master.sale_product_matrix]
file_filter = addons/sale_product_matrix/i18n/<lang>.po
source_file = addons/sale_product_matrix/i18n/sale_product_matrix.pot
source_lang = en
-[odoo-13.sale_purchase]
+[odoo-master.sale_purchase]
file_filter = addons/sale_purchase/i18n/<lang>.po
source_file = addons/sale_purchase/i18n/sale_purchase.pot
source_lang = en
-[odoo-13.sale_quotation_builder]
+[odoo-master.sale_quotation_builder]
file_filter = addons/sale_quotation_builder/i18n/<lang>.po
source_file = addons/sale_quotation_builder/i18n/sale_quotation_builder.pot
source_lang = en
-[odoo-13.sale_stock]
+[odoo-master.sale_stock]
file_filter = addons/sale_stock/i18n/<lang>.po
source_file = addons/sale_stock/i18n/sale_stock.pot
source_lang = en
-[odoo-13.sale_timesheet]
+[odoo-master.sale_timesheet]
file_filter = addons/sale_timesheet/i18n/<lang>.po
source_file = addons/sale_timesheet/i18n/sale_timesheet.pot
source_lang = en
-[odoo-13.sale_timesheet_purchase]
+[odoo-master.sale_timesheet_purchase]
file_filter = addons/sale_timesheet_purchase/i18n/<lang>.po
source_file = addons/sale_timesheet_purchase/i18n/sale_timesheet_purchase.pot
source_lang = en
-[odoo-13.sales_team]
+[odoo-master.sales_team]
file_filter = addons/sales_team/i18n/<lang>.po
source_file = addons/sales_team/i18n/sales_team.pot
source_lang = en
-[odoo-13.sms]
+[odoo-master.sms]
file_filter = addons/sms/i18n/<lang>.po
source_file = addons/sms/i18n/sms.pot
source_lang = en
-[odoo-13.snailmail]
+[odoo-master.snailmail]
file_filter = addons/snailmail/i18n/<lang>.po
source_file = addons/snailmail/i18n/snailmail.pot
source_lang = en
-[odoo-13.snailmail_account]
+[odoo-master.snailmail_account]
file_filter = addons/snailmail_account/i18n/<lang>.po
source_file = addons/snailmail_account/i18n/snailmail_account.pot
source_lang = en
-[odoo-13.social_media]
+[odoo-master.social_media]
file_filter = addons/social_media/i18n/<lang>.po
source_file = addons/social_media/i18n/social_media.pot
source_lang = en
-[odoo-13.stock]
+[odoo-master.stock]
file_filter = addons/stock/i18n/<lang>.po
source_file = addons/stock/i18n/stock.pot
source_lang = en
-[odoo-13.stock_account]
+[odoo-master.stock_account]
file_filter = addons/stock_account/i18n/<lang>.po
source_file = addons/stock_account/i18n/stock_account.pot
source_lang = en
-[odoo-13.stock_dropshipping]
+[odoo-master.stock_dropshipping]
file_filter = addons/stock_dropshipping/i18n/<lang>.po
source_file = addons/stock_dropshipping/i18n/stock_dropshipping.pot
source_lang = en
-[odoo-13.stock_landed_costs]
+[odoo-master.stock_landed_costs]
file_filter = addons/stock_landed_costs/i18n/<lang>.po
source_file = addons/stock_landed_costs/i18n/stock_landed_costs.pot
source_lang = en
-[odoo-13.stock_picking_batch]
+[odoo-master.stock_picking_batch]
file_filter = addons/stock_picking_batch/i18n/<lang>.po
source_file = addons/stock_picking_batch/i18n/stock_picking_batch.pot
source_lang = en
-[odoo-13.stock_sms]
+[odoo-master.stock_sms]
file_filter = addons/stock_sms/i18n/<lang>.po
source_file = addons/stock_sms/i18n/stock_sms.pot
source_lang = en
-[odoo-13.survey]
+[odoo-master.survey]
file_filter = addons/survey/i18n/<lang>.po
source_file = addons/survey/i18n/survey.pot
source_lang = en
-[odoo-13.theme_bootswatch]
+[odoo-master.theme_bootswatch]
file_filter = addons/theme_bootswatch/i18n/<lang>.po
source_file = addons/theme_bootswatch/i18n/theme_bootswatch.pot
source_lang = en
-[odoo-13.transifex]
+[odoo-master.transifex]
file_filter = addons/transifex/i18n/<lang>.po
source_file = addons/transifex/i18n/transifex.pot
source_lang = en
-[odoo-13.uom]
+[odoo-master.uom]
file_filter = addons/uom/i18n/<lang>.po
source_file = addons/uom/i18n/uom.pot
source_lang = en
-[odoo-13.utm]
+[odoo-master.utm]
file_filter = addons/utm/i18n/<lang>.po
source_file = addons/utm/i18n/utm.pot
source_lang = en
-[odoo-13.web]
+[odoo-master.web]
file_filter = addons/web/i18n/<lang>.po
source_file = addons/web/i18n/web.pot
source_lang = en
-[odoo-13.web_diagram]
+[odoo-master.web_diagram]
file_filter = addons/web_diagram/i18n/<lang>.po
source_file = addons/web_diagram/i18n/web_diagram.pot
source_lang = en
-[odoo-13.web_editor]
+[odoo-master.web_editor]
file_filter = addons/web_editor/i18n/<lang>.po
source_file = addons/web_editor/i18n/web_editor.pot
source_lang = en
-[odoo-13.web_kanban_gauge]
+[odoo-master.web_kanban_gauge]
file_filter = addons/web_kanban_gauge/i18n/<lang>.po
source_file = addons/web_kanban_gauge/i18n/web_kanban_gauge.pot
source_lang = en
-[odoo-13.web_tour]
+[odoo-master.web_tour]
file_filter = addons/web_tour/i18n/<lang>.po
source_file = addons/web_tour/i18n/web_tour.pot
source_lang = en
-[odoo-13.web_unsplash]
+[odoo-master.web_unsplash]
file_filter = addons/web_unsplash/i18n/<lang>.po
source_file = addons/web_unsplash/i18n/web_unsplash.pot
source_lang = en
-[odoo-13.website]
+[odoo-master.website]
file_filter = addons/website/i18n/<lang>.po
source_file = addons/website/i18n/website.pot
source_lang = en
-[odoo-13.website_blog]
+[odoo-master.website_blog]
file_filter = addons/website_blog/i18n/<lang>.po
source_file = addons/website_blog/i18n/website_blog.pot
source_lang = en
-[odoo-13.website_crm]
+[odoo-master.website_crm]
file_filter = addons/website_crm/i18n/<lang>.po
source_file = addons/website_crm/i18n/website_crm.pot
source_lang = en
-[odoo-13.website_crm_partner_assign]
+[odoo-master.website_crm_partner_assign]
file_filter = addons/website_crm_partner_assign/i18n/<lang>.po
source_file = addons/website_crm_partner_assign/i18n/website_crm_partner_assign.pot
source_lang = en
-[odoo-13.website_customer]
+[odoo-master.website_customer]
file_filter = addons/website_customer/i18n/<lang>.po
source_file = addons/website_customer/i18n/website_customer.pot
source_lang = en
-[odoo-13.website_event]
+[odoo-master.website_event]
file_filter = addons/website_event/i18n/<lang>.po
source_file = addons/website_event/i18n/website_event.pot
source_lang = en
-[odoo-13.website_event_questions]
+[odoo-master.website_event_questions]
file_filter = addons/website_event_questions/i18n/<lang>.po
source_file = addons/website_event_questions/i18n/website_event_questions.pot
source_lang = en
-[odoo-13.website_event_sale]
+[odoo-master.website_event_sale]
file_filter = addons/website_event_sale/i18n/<lang>.po
source_file = addons/website_event_sale/i18n/website_event_sale.pot
source_lang = en
-[odoo-13.website_event_track]
+[odoo-master.website_event_track]
file_filter = addons/website_event_track/i18n/<lang>.po
source_file = addons/website_event_track/i18n/website_event_track.pot
source_lang = en
-[odoo-13.website_form]
+[odoo-master.website_form]
file_filter = addons/website_form/i18n/<lang>.po
source_file = addons/website_form/i18n/website_form.pot
source_lang = en
-[odoo-13.website_form_project]
+[odoo-master.website_form_project]
file_filter = addons/website_form_project/i18n/<lang>.po
source_file = addons/website_form_project/i18n/website_form_project.pot
source_lang = en
-[odoo-13.website_forum]
+[odoo-master.website_forum]
file_filter = addons/website_forum/i18n/<lang>.po
source_file = addons/website_forum/i18n/website_forum.pot
source_lang = en
-[odoo-13.website_gengo]
+[odoo-master.website_gengo]
file_filter = addons/website_gengo/i18n/<lang>.po
source_file = addons/website_gengo/i18n/website_gengo.pot
source_lang = en
-[odoo-13.website_hr_recruitment]
+[odoo-master.website_hr_recruitment]
file_filter = addons/website_hr_recruitment/i18n/<lang>.po
source_file = addons/website_hr_recruitment/i18n/website_hr_recruitment.pot
source_lang = en
-[odoo-13.website_links]
+[odoo-master.website_links]
file_filter = addons/website_links/i18n/<lang>.po
source_file = addons/website_links/i18n/website_links.pot
source_lang = en
-[odoo-13.website_livechat]
+[odoo-master.website_livechat]
file_filter = addons/website_livechat/i18n/<lang>.po
source_file = addons/website_livechat/i18n/website_livechat.pot
source_lang = en
-[odoo-13.website_mail]
+[odoo-master.website_mail]
file_filter = addons/website_mail/i18n/<lang>.po
source_file = addons/website_mail/i18n/website_mail.pot
source_lang = en
-[odoo-13.website_mail_channel]
+[odoo-master.website_mail_channel]
file_filter = addons/website_mail_channel/i18n/<lang>.po
source_file = addons/website_mail_channel/i18n/website_mail_channel.pot
source_lang = en
-[odoo-13.website_mass_mailing]
+[odoo-master.website_mass_mailing]
file_filter = addons/website_mass_mailing/i18n/<lang>.po
source_file = addons/website_mass_mailing/i18n/website_mass_mailing.pot
source_lang = en
-[odoo-13.website_membership]
+[odoo-master.website_membership]
file_filter = addons/website_membership/i18n/<lang>.po
source_file = addons/website_membership/i18n/website_membership.pot
source_lang = en
-[odoo-13.website_partner]
+[odoo-master.website_partner]
file_filter = addons/website_partner/i18n/<lang>.po
source_file = addons/website_partner/i18n/website_partner.pot
source_lang = en
-[odoo-13.website_payment]
+[odoo-master.website_payment]
file_filter = addons/website_payment/i18n/<lang>.po
source_file = addons/website_payment/i18n/website_payment.pot
source_lang = en
-[odoo-13.website_profile]
+[odoo-master.website_profile]
file_filter = addons/website_profile/i18n/<lang>.po
source_file = addons/website_profile/i18n/website_profile.pot
source_lang = en
-[odoo-13.website_rating]
+[odoo-master.website_rating]
file_filter = addons/website_rating/i18n/<lang>.po
source_file = addons/website_rating/i18n/website_rating.pot
source_lang = en
-[odoo-13.website_sale]
+[odoo-master.website_sale]
file_filter = addons/website_sale/i18n/<lang>.po
source_file = addons/website_sale/i18n/website_sale.pot
source_lang = en
-[odoo-13.website_sale_comparison]
+[odoo-master.website_sale_comparison]
file_filter = addons/website_sale_comparison/i18n/<lang>.po
source_file = addons/website_sale_comparison/i18n/website_sale_comparison.pot
source_lang = en
-[odoo-13.website_sale_coupon]
+[odoo-master.website_sale_coupon]
file_filter = addons/website_sale_coupon/i18n/<lang>.po
source_file = addons/website_sale_coupon/i18n/website_sale_coupon.pot
source_lang = en
-[odoo-13.website_sale_delivery]
+[odoo-master.website_sale_delivery]
file_filter = addons/website_sale_delivery/i18n/<lang>.po
source_file = addons/website_sale_delivery/i18n/website_sale_delivery.pot
source_lang = en
-[odoo-13.website_sale_digital]
+[odoo-master.website_sale_digital]
file_filter = addons/website_sale_digital/i18n/<lang>.po
source_file = addons/website_sale_digital/i18n/website_sale_digital.pot
source_lang = en
-[odoo-13.website_sale_management]
+[odoo-master.website_sale_management]
file_filter = addons/website_sale_management/i18n/<lang>.po
source_file = addons/website_sale_management/i18n/website_sale_management.pot
source_lang = en
-[odoo-13.website_sale_product_configurator]
+[odoo-master.website_sale_product_configurator]
file_filter = addons/website_sale_product_configurator/i18n/<lang>.po
source_file = addons/website_sale_product_configurator/i18n/website_sale_product_configurator.pot
source_lang = en
-[odoo-13.website_sale_slides]
+[odoo-master.website_sale_slides]
file_filter = addons/website_sale_slides/i18n/<lang>.po
source_file = addons/website_sale_slides/i18n/website_sale_slides.pot
source_lang = en
-[odoo-13.website_sale_stock]
+[odoo-master.website_sale_stock]
file_filter = addons/website_sale_stock/i18n/<lang>.po
source_file = addons/website_sale_stock/i18n/website_sale_stock.pot
source_lang = en
-[odoo-13.website_sale_wishlist]
+[odoo-master.website_sale_wishlist]
file_filter = addons/website_sale_wishlist/i18n/<lang>.po
source_file = addons/website_sale_wishlist/i18n/website_sale_wishlist.pot
source_lang = en
-[odoo-13.website_slides]
+[odoo-master.website_slides]
file_filter = addons/website_slides/i18n/<lang>.po
source_file = addons/website_slides/i18n/website_slides.pot
source_lang = en
-[odoo-13.website_slides_forum]
+[odoo-master.website_slides_forum]
file_filter = addons/website_slides_forum/i18n/<lang>.po
source_file = addons/website_slides_forum/i18n/website_slides_forum.pot
source_lang = en
-[odoo-13.website_slides_survey]
+[odoo-master.website_slides_survey]
file_filter = addons/website_slides_survey/i18n/<lang>.po
source_file = addons/website_slides_survey/i18n/website_slides_survey.pot
source_lang = en
-[odoo-13.website_theme_install]
+[odoo-master.website_theme_install]
file_filter = addons/website_theme_install/i18n/<lang>.po
source_file = addons/website_theme_install/i18n/website_theme_install.pot
source_lang = en
-[odoo-13.website_twitter]
+[odoo-master.website_twitter]
file_filter = addons/website_twitter/i18n/<lang>.po
source_file = addons/website_twitter/i18n/website_twitter.pot
source_lang = en
diff --git a/addons/account/__manifest__.py b/addons/account/__manifest__.py
index 1670fd3bc62..c4da9f3878c 100644
--- a/addons/account/__manifest__.py
+++ b/addons/account/__manifest__.py
@@ -61,7 +61,6 @@ You could use this simplified accounting in case you work with an (external) acc
'views/digest_views.xml',
'wizard/account_invoice_send_views.xml',
'views/account_tax_report_views.xml',
- 'views/report_statement.xml'
],
'demo': [
'demo/account_demo.xml',
diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py
index 8ddf1b6000a..e9fab122292 100644
--- a/addons/account/models/account_bank_statement.py
+++ b/addons/account/models/account_bank_statement.py
@@ -300,7 +300,7 @@ class AccountBankStatement(models.Model):
self.env['ir.attachment'].create({
'name': statement.name and _("Bank Statement %s.pdf") % statement.name or _("Bank Statement.pdf"),
'type': 'binary',
- 'datas': base64.encodestring(content),
+ 'datas': base64.encodebytes(content),
'res_model': statement._name,
'res_id': statement.id
})
diff --git a/addons/account/static/tests/reconciliation_tests.js b/addons/account/static/tests/reconciliation_tests.js
index 216074a0073..853e7205f87 100644
--- a/addons/account/static/tests/reconciliation_tests.js
+++ b/addons/account/static/tests/reconciliation_tests.js
@@ -1366,7 +1366,6 @@ QUnit.module('account', {
await testUtils.dom.click(clientAction.$('.o_reconciliation_line:eq(1) .accounting_view'))
await testUtils.dom.click(clientAction.$('.accounting_view:eq(1) thead td:first'));
- // debugger
await testUtils.dom.click(clientAction.$('.o_reconciliation_line:eq(1) [data-line-id="21"] .cell_label'));
await testUtils.dom.click(clientAction.$('.o_reconciliation_line:eq(1) [data-line-id="22"] .cell_label'));
diff --git a/addons/account/tests/account_test_classes.py b/addons/account/tests/account_test_classes.py
index 3bf7d53f970..00267faddd6 100644
--- a/addons/account/tests/account_test_classes.py
+++ b/addons/account/tests/account_test_classes.py
@@ -17,7 +17,7 @@ class AccountingTestCase(HttpCase):
super(AccountingTestCase, self).setUp()
domain = [('company_id', '=', self.env.ref('base.main_company').id)]
if not self.env['account.account'].search_count(domain):
- _logger.warn('Test skipped because there is no chart of account defined ...')
+ _logger.warning('Test skipped because there is no chart of account defined ...')
self.skipTest("No Chart of account found")
def ensure_account_property(self, property_name):
diff --git a/addons/account/tests/test_account_journal_dashboard.py b/addons/account/tests/test_account_journal_dashboard.py
index ef7f8fad3a1..0d6e88e30bc 100644
--- a/addons/account/tests/test_account_journal_dashboard.py
+++ b/addons/account/tests/test_account_journal_dashboard.py
@@ -49,30 +49,30 @@ class TestAccountJournalDashboard(AccountTestUsers):
# Check Draft
dashboard_data = journal.get_journal_dashboard_datas()
- self.assertEquals(dashboard_data['number_draft'], 2)
+ self.assertEqual(dashboard_data['number_draft'], 2)
self.assertIn('68.42', dashboard_data['sum_draft'])
- self.assertEquals(dashboard_data['number_waiting'], 0)
+ self.assertEqual(dashboard_data['number_waiting'], 0)
self.assertIn('0.00', dashboard_data['sum_waiting'])
# Check Both
invoice.post()
dashboard_data = journal.get_journal_dashboard_datas()
- self.assertEquals(dashboard_data['number_draft'], 1)
+ self.assertEqual(dashboard_data['number_draft'], 1)
self.assertIn('-13.30', dashboard_data['sum_draft'])
- self.assertEquals(dashboard_data['number_waiting'], 1)
+ self.assertEqual(dashboard_data['number_waiting'], 1)
self.assertIn('81.72', dashboard_data['sum_waiting'])
# Check waiting payment
refund.post()
dashboard_data = journal.get_journal_dashboard_datas()
- self.assertEquals(dashboard_data['number_draft'], 0)
+ self.assertEqual(dashboard_data['number_draft'], 0)
self.assertIn('0.00', dashboard_data['sum_draft'])
- self.assertEquals(dashboard_data['number_waiting'], 2)
+ self.assertEqual(dashboard_data['number_waiting'], 2)
self.assertIn('68.42', dashboard_data['sum_waiting'])
# Check partial
@@ -96,13 +96,13 @@ class TestAccountJournalDashboard(AccountTestUsers):
refund.js_assign_outstanding_line(payment_move_line.id)
dashboard_data = journal.get_journal_dashboard_datas()
- self.assertEquals(dashboard_data['number_draft'], 0)
+ self.assertEqual(dashboard_data['number_draft'], 0)
self.assertIn('0.00', dashboard_data['sum_draft'])
- self.assertEquals(dashboard_data['number_waiting'], 2)
+ self.assertEqual(dashboard_data['number_waiting'], 2)
self.assertIn('78.42', dashboard_data['sum_waiting'])
with patch('odoo.fields.Date.today', patched_today):
dashboard_data = journal.get_journal_dashboard_datas()
- self.assertEquals(dashboard_data['number_late'], 2)
+ self.assertEqual(dashboard_data['number_late'], 2)
self.assertIn('78.42', dashboard_data['sum_late'])
diff --git a/addons/account/tests/test_fiscal_position.py b/addons/account/tests/test_fiscal_position.py
index f9d5ddd4b27..4b19431806b 100644
--- a/addons/account/tests/test_fiscal_position.py
+++ b/addons/account/tests/test_fiscal_position.py
@@ -58,7 +58,7 @@ class TestFiscalPosition(common.TransactionCase):
def test_10_fp_country(self):
def assert_fp(partner, expected_pos, message):
- self.assertEquals(
+ self.assertEqual(
self.fp.get_fiscal_position(partner.id),
expected_pos.id,
message)
diff --git a/addons/account/tests/test_payment.py b/addons/account/tests/test_payment.py
index 557a482d34f..6a059705694 100644
--- a/addons/account/tests/test_payment.py
+++ b/addons/account/tests/test_payment.py
@@ -90,7 +90,7 @@ class TestPayment(AccountingTestCase):
})
payment = self.payment_model.browse(register_payments.create_payments()['res_id'])
- self.assertAlmostEquals(payment.amount, 300)
+ self.assertAlmostEqual(payment.amount, 300)
self.assertEqual(payment.state, 'posted')
self.assertEqual(payment.state, 'posted')
self.assertEqual(inv_1.invoice_payment_state, 'paid')
@@ -163,7 +163,7 @@ class TestPayment(AccountingTestCase):
self.assertEqual(len(payment), 1)
self.assertEqual(payment.invoice_ids[0].id, inv_1.id)
- self.assertAlmostEquals(payment.amount, 550)
+ self.assertAlmostEqual(payment.amount, 550)
self.assertEqual(payment.payment_type, 'inbound')
self.assertEqual(payment.partner_id, self.partner_agrolait)
self.assertEqual(payment.partner_type, 'customer')
@@ -182,7 +182,7 @@ class TestPayment(AccountingTestCase):
self.assertEqual(len(payment), 1)
self.assertEqual(payment.invoice_ids[0].id, inv_2.id)
- self.assertAlmostEquals(payment.amount, 300)
+ self.assertAlmostEqual(payment.amount, 300)
self.assertEqual(payment.payment_type, 'outbound')
self.assertEqual(payment.partner_id, self.partner_china_exp)
self.assertEqual(payment.partner_type, 'supplier')
diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py
index 94e5e69bee4..c23f4b048da 100644
--- a/addons/account/tests/test_reconciliation.py
+++ b/addons/account/tests/test_reconciliation.py
@@ -370,11 +370,11 @@ class TestReconciliationExec(TestReconciliation):
])
# The invoice should be paid, as the payments totally cover its total
- self.assertEquals(move.invoice_payment_state, 'paid', 'The invoice should be paid by now')
+ self.assertEqual(move.invoice_payment_state, 'paid', 'The invoice should be paid by now')
self.assertTrue(receivable_line.reconciled, 'The invoice should be totally reconciled')
self.assertTrue(receivable_line.full_reconcile_id, 'The invoice should have a full reconcile number')
- self.assertEquals(receivable_line.amount_residual, 0, 'The invoice should be totally reconciled')
- self.assertEquals(receivable_line.amount_residual_currency, 0, 'The invoice should be totally reconciled')
+ self.assertEqual(receivable_line.amount_residual, 0, 'The invoice should be totally reconciled')
+ self.assertEqual(receivable_line.amount_residual_currency, 0, 'The invoice should be totally reconciled')
@unittest.skip('adapt to new accounting')
def test_balanced_exchanges_gain_loss(self):
@@ -440,7 +440,7 @@ class TestReconciliationExec(TestReconciliation):
])
# The invoice should be paid, as the payments totally cover its total
- self.assertEquals(invoice.state, 'paid', 'The invoice should be paid by now')
+ self.assertEqual(invoice.state, 'paid', 'The invoice should be paid by now')
reconcile = None
for payment in invoice.payment_ids:
reconcile = payment.reconcile_model_id
@@ -460,8 +460,8 @@ class TestReconciliationExec(TestReconciliation):
self.assertTrue(exchange_loss_line, 'There should be one move line of 0.01 EUR in credit')
# The journal items of the reconciliation should have their debit and credit total equal
# Besides, the total debit and total credit should be 60.61 EUR (2.00 USD)
- self.assertEquals(sum(res['debit'] for res in result.values()), 60.61)
- self.assertEquals(sum(res['credit'] for res in result.items()), 60.61)
+ self.assertEqual(sum(res['debit'] for res in result.values()), 60.61)
+ self.assertEqual(sum(res['credit'] for res in result.items()), 60.61)
counterpart_exchange_loss_line = None
for line in exchange_loss_line.move_id.line_id:
if line.account_id.id == self.account_fx_expense_id:
@@ -505,8 +505,8 @@ class TestReconciliationExec(TestReconciliation):
aml_recs.reconcile()
for aml in aml_recs:
self.assertTrue(aml.reconciled, 'The journal item should be totally reconciled')
- self.assertEquals(aml.amount_residual, 0, 'The journal item should be totally reconciled')
- self.assertEquals(aml.amount_residual_currency, 0, 'The journal item should be totally reconciled')
+ self.assertEqual(aml.amount_residual, 0, 'The journal item should be totally reconciled')
+ self.assertEqual(aml.amount_residual_currency, 0, 'The journal item should be totally reconciled')
move_list_vals = [
('2', 728.35, 795.05, self.currency_swiss_id),
@@ -522,8 +522,8 @@ class TestReconciliationExec(TestReconciliation):
aml_recs.reconcile(self.account_rsa, self.bank_journal_usd)
for aml in aml_recs:
self.assertTrue(aml.reconciled, 'The journal item should be totally reconciled')
- self.assertEquals(aml.amount_residual, 0, 'The journal item should be totally reconciled')
- self.assertEquals(aml.amount_residual_currency, 0, 'The journal item should be totally reconciled')
+ self.assertEqual(aml.amount_residual, 0, 'The journal item should be totally reconciled')
+ self.assertEqual(aml.amount_residual_currency, 0, 'The journal item should be totally reconciled')
def test_manual_reconcile_wizard_same_account(self):
move_ids = self.env['account.move']
@@ -582,7 +582,7 @@ class TestReconciliationExec(TestReconciliation):
}]
writeoff_line = account_move_line._create_writeoff(writeoff_vals)
(account_move_line + writeoff_line).reconcile()
- self.assertEquals(len(writeoff_line), 1, "The writeoff_line (balance_line) should have only one moves line")
+ self.assertEqual(len(writeoff_line), 1, "The writeoff_line (balance_line) should have only one moves line")
self.assertTrue(all(l.reconciled for l in writeoff_line), 'The balance lines should be totally reconciled')
self.assertTrue(all(l.reconciled for l in account_move_line), 'The move lines should be totally reconciled')
@@ -630,7 +630,7 @@ class TestReconciliationExec(TestReconciliation):
# Check that move lines associated to bank_statement are correct
bank_stmt_aml = self.env['account.move.line'].search([('statement_id', '=', bank_stmt.id)])
bank_stmt_aml |= bank_stmt_aml.mapped('move_id').mapped('line_ids')
- self.assertEquals(len(bank_stmt_aml), 4, "The bank statement should have 4 moves lines")
+ self.assertEqual(len(bank_stmt_aml), 4, "The bank statement should have 4 moves lines")
lines = {
self.account_usd.id: [
{'debit': 3.27, 'credit': 0.0, 'amount_currency': 5, 'currency_id': self.currency_usd_id},
@@ -655,10 +655,10 @@ class TestReconciliationExec(TestReconciliation):
line = line[0]
else:
line = line[1]
- self.assertEquals(round(aml.debit, 2), line['debit'])
- self.assertEquals(round(aml.credit, 2), line['credit'])
- self.assertEquals(round(aml.amount_currency, 2), line['amount_currency'])
- self.assertEquals(aml.currency_id.id, line['currency_id'])
+ self.assertEqual(round(aml.debit, 2), line['debit'])
+ self.assertEqual(round(aml.credit, 2), line['credit'])
+ self.assertEqual(round(aml.amount_currency, 2), line['amount_currency'])
+ self.assertEqual(aml.currency_id.id, line['currency_id'])
def test_partial_reconcile_currencies_01(self):
# client Account (payable, rsa)
@@ -809,19 +809,19 @@ class TestReconciliationExec(TestReconciliation):
credit_aml = payment.move_line_ids.filtered('credit')
# Check residual before assignation
- self.assertAlmostEquals(inv1.amount_residual, 10)
- self.assertAlmostEquals(inv2.amount_residual, 20)
+ self.assertAlmostEqual(inv1.amount_residual, 10)
+ self.assertAlmostEqual(inv2.amount_residual, 20)
# Assign credit and residual
inv1.js_assign_outstanding_line(credit_aml.id)
inv2.js_assign_outstanding_line(credit_aml.id)
- self.assertAlmostEquals(inv1.amount_residual, 0)
- self.assertAlmostEquals(inv2.amount_residual, 0)
+ self.assertAlmostEqual(inv1.amount_residual, 0)
+ self.assertAlmostEqual(inv2.amount_residual, 0)
# Unreconcile one invoice at a time and check residual
credit_aml.remove_move_reconcile()
- self.assertAlmostEquals(inv1.amount_residual, 10)
- self.assertAlmostEquals(inv2.amount_residual, 20)
+ self.assertAlmostEqual(inv1.amount_residual, 10)
+ self.assertAlmostEqual(inv2.amount_residual, 20)
def test_unreconcile_exchange(self):
# Use case:
@@ -860,16 +860,16 @@ class TestReconciliationExec(TestReconciliation):
credit_aml = payment.move_line_ids.filtered('credit')
# Check residual before assignation
- self.assertAlmostEquals(inv.amount_residual, 111)
+ self.assertAlmostEqual(inv.amount_residual, 111)
# Assign credit, check exchange move and residual
inv.js_assign_outstanding_line(credit_aml.id)
self.assertEqual(len(payment.move_line_ids.mapped('full_reconcile_id').exchange_move_id), 1)
- self.assertAlmostEquals(inv.amount_residual, 0)
+ self.assertAlmostEqual(inv.amount_residual, 0)
# Unreconcile invoice and check residual
credit_aml.with_context(invoice_id=inv.id).remove_move_reconcile()
- self.assertAlmostEquals(inv.amount_residual, 111)
+ self.assertAlmostEqual(inv.amount_residual, 111)
def test_revert_payment_and_reconcile(self):
payment = self.env['account.payment'].create({
@@ -1224,14 +1224,14 @@ class TestReconciliationExec(TestReconciliation):
self.env["account.reconciliation.widget"].process_move_lines(data_for_reconciliation)
self.assertTrue(inv1_receivable.full_reconcile_id.exists())
- self.assertEquals(inv1_receivable.full_reconcile_id, inv2_receivable.full_reconcile_id)
- self.assertEquals(inv1_receivable.full_reconcile_id, pay_receivable.full_reconcile_id)
+ self.assertEqual(inv1_receivable.full_reconcile_id, inv2_receivable.full_reconcile_id)
+ self.assertEqual(inv1_receivable.full_reconcile_id, pay_receivable.full_reconcile_id)
self.assertTrue(all(l.reconciled for l in inv1_receivable))
self.assertTrue(all(l.reconciled for l in inv2_receivable))
- self.assertEquals(inv1.invoice_payment_state, 'paid')
- self.assertEquals(inv2.invoice_payment_state, 'paid')
+ self.assertEqual(inv1.invoice_payment_state, 'paid')
+ self.assertEqual(inv2.invoice_payment_state, 'paid')
def test_multiple_term_reconciliation_opw_1906665(self):
'''Test that when registering a payment to an invoice with multiple
@@ -2145,4 +2145,4 @@ class TestReconciliationExec(TestReconciliation):
}])
writeoff_line = self.env['account.move.line'].search([('name', '=', 'writeoff')])
- self.assertEquals(writeoff_line.credit, 15.0)
+ self.assertEqual(writeoff_line.credit, 15.0)
diff --git a/addons/base_import/models/base_import.py b/addons/base_import/models/base_import.py
index 9fc3feff1bd..9094bbabf16 100644
--- a/addons/base_import/models/base_import.py
+++ b/addons/base_import/models/base_import.py
@@ -236,7 +236,7 @@ class Import(models.TransientModel):
try:
return getattr(self, '_read_' + file_extension)(options)
except Exception:
- _logger.warn("Failed to read file '%s' (transient id %d) using guessed mimetype %s", self.file_name or '<unknown>', self.id, mimetype)
+ _logger.warning("Failed to read file '%s' (transient id %d) using guessed mimetype %s", self.file_name or '<unknown>', self.id, mimetype)
# try reading with user-provided mimetype
(file_extension, handler, req) = FILE_TYPE_DICT.get(self.file_type, (None, None, None))
@@ -244,7 +244,7 @@ class Import(models.TransientModel):
try:
return getattr(self, '_read_' + file_extension)(options)
except Exception:
- _logger.warn("Failed to read file '%s' (transient id %d) using user-provided mimetype %s", self.file_name or '<unknown>', self.id, self.file_type)
+ _logger.warning("Failed to read file '%s' (transient id %d) using user-provided mimetype %s", self.file_name or '<unknown>', self.id, self.file_type)
# fallback on file extensions as mime types can be unreliable (e.g.
# software setting incorrect mime types, or non-installed software
@@ -255,7 +255,7 @@ class Import(models.TransientModel):
try:
return getattr(self, '_read_' + ext[1:])(options)
except Exception:
- _logger.warn("Failed to read file '%s' (transient id %s) using file extension", self.file_name, self.id)
+ _logger.warning("Failed to read file '%s' (transient id %s) using file extension", self.file_name, self.id)
if req:
raise ImportError(_("Unable to load \"{extension}\" file: requires Python module \"{modname}\"").format(extension=file_extension, modname=req))
diff --git a/addons/base_import_module/models/base_import_module.py b/addons/base_import_module/models/base_import_module.py
index 84510330ab6..985f3e45a7b 100644
--- a/addons/base_import_module/models/base_import_module.py
+++ b/addons/base_import_module/models/base_import_module.py
@@ -17,7 +17,7 @@ class BaseImportModule(models.TransientModel):
def import_module(self):
self.ensure_one()
IrModule = self.env['ir.module.module']
- zip_data = base64.decodestring(self.module_file)
+ zip_data = base64.decodebytes(self.module_file)
fp = BytesIO()
fp.write(zip_data)
res = IrModule.import_zipfile(fp, force=self.force)
diff --git a/addons/calendar/tests/test_calendar.py b/addons/calendar/tests/test_calendar.py
index 6b7d7f1a550..ba7d3e840a2 100644
--- a/addons/calendar/tests/test_calendar.py
+++ b/addons/calendar/tests/test_calendar.py
@@ -159,7 +159,7 @@ class TestCalendar(TransactionCase):
(str(m.start_datetime), str(m.stop_datetime)),
('2017-07-12 14:30:00', u'2017-07-12 15:00:00'),
)
- self.assertEquals(
+ self.assertEqual(
(str(records.start_datetime), str(records.stop_datetime)),
(u'2017-07-10 15:30:00', u'2017-07-10 16:00:00'),
)
diff --git a/addons/crm/tests/test_crm_pls.py b/addons/crm/tests/test_crm_pls.py
index ef3a7cdba15..304bdce8868 100644
--- a/addons/crm/tests/test_crm_pls.py
+++ b/addons/crm/tests/test_crm_pls.py
@@ -78,5 +78,5 @@ class TestCRMPLS(TransactionCase):
# As the cron is computing and writing in SQL queries, we need to invalidate the cache
leads.invalidate_cache()
- self.assertEquals(tools.float_compare(leads[3].automated_probability, 33.49, 2), 0)
- self.assertEquals(tools.float_compare(leads[7].automated_probability, 7.74, 2), 0)
+ self.assertEqual(tools.float_compare(leads[3].automated_probability, 33.49, 2), 0)
+ self.assertEqual(tools.float_compare(leads[7].automated_probability, 7.74, 2), 0)
diff --git a/addons/delivery/tests/test_packing_delivery.py b/addons/delivery/tests/test_packing_delivery.py
index 84e694a530d..89630390cd4 100644
--- a/addons/delivery/tests/test_packing_delivery.py
+++ b/addons/delivery/tests/test_packing_delivery.py
@@ -66,9 +66,9 @@ class TestPacking(TestPacking):
pack_action_model = pack_action['res_model']
# We make sure the correct action was returned
- self.assertEquals(pack_action_model, 'choose.delivery.package')
+ self.assertEqual(pack_action_model, 'choose.delivery.package')
# We instanciate the wizard with the context of the action and check that the
# default weight was set.
pack_wiz = self.env['choose.delivery.package'].with_context(pack_action_ctx).create({})
- self.assertEquals(pack_wiz.shipping_weight, 13.5)
+ self.assertEqual(pack_wiz.shipping_weight, 13.5)
diff --git a/addons/google_calendar/__manifest__.py b/addons/google_calendar/__manifest__.py
index 34d058bd202..a09d9b3fff4 100644
--- a/addons/google_calendar/__manifest__.py
+++ b/addons/google_calendar/__manifest__.py
@@ -10,7 +10,6 @@
'qweb': ['static/src/xml/*.xml'],
'data': [
'data/google_calendar_data.xml',
- 'data/google_calendar_data.xml',
'security/ir.model.access.csv',
'views/res_config_settings_views.xml',
'views/res_users_views.xml',
diff --git a/addons/hr_contract/tests/test_auto_status.py b/addons/hr_contract/tests/test_auto_status.py
index fcff8621bb9..847e6fb9ca0 100644
--- a/addons/hr_contract/tests/test_auto_status.py
+++ b/addons/hr_contract/tests/test_auto_status.py
@@ -13,7 +13,7 @@ class TestHrContracts(TestContractBase):
self.test_contract = dict(name='Test', wage=1, employee_id=self.employee.id, state='open')
def test_employee_contractwarning(self):
- self.assertEquals(self.employee.contract_warning, True)
+ self.assertEqual(self.employee.contract_warning, True)
def apply_cron(self):
self.env.ref('hr_contract.ir_cron_data_contract_update_state').method_direct_trigger()
@@ -22,15 +22,15 @@ class TestHrContracts(TestContractBase):
self.test_contract.update(dict(date_end=datetime.now() + relativedelta(days=100)))
self.contract = self.contracts.create(self.test_contract)
self.apply_cron()
- self.assertEquals(self.contract.state, 'open')
- self.assertEquals(self.contract.kanban_state, 'normal')
- self.assertEquals(self.employee.contract_warning, False)
+ self.assertEqual(self.contract.state, 'open')
+ self.assertEqual(self.contract.kanban_state, 'normal')
+ self.assertEqual(self.employee.contract_warning, False)
self.test_contract.update(dict(date_end=datetime.now() + relativedelta(days=5)))
self.contract.write(self.test_contract)
self.apply_cron()
- self.assertEquals(self.contract.state, 'open')
- self.assertEquals(self.contract.kanban_state, 'blocked')
+ self.assertEqual(self.contract.state, 'open')
+ self.assertEqual(self.contract.kanban_state, 'blocked')
self.test_contract.update({
'date_start': datetime.now() + relativedelta(days=-50),
@@ -40,15 +40,15 @@ class TestHrContracts(TestContractBase):
})
self.contract.write(self.test_contract)
self.apply_cron()
- self.assertEquals(self.contract.state, 'close')
+ self.assertEqual(self.contract.state, 'close')
def test_contract_pending_visa_expire(self):
self.employee.visa_expire = date.today() + relativedelta(days=30)
self.test_contract.update(dict(date_end=False))
self.contract = self.contracts.create(self.test_contract)
self.apply_cron()
- self.assertEquals(self.contract.state, 'open')
- self.assertEquals(self.contract.kanban_state, 'blocked')
+ self.assertEqual(self.contract.state, 'open')
+ self.assertEqual(self.contract.kanban_state, 'blocked')
self.employee.visa_expire = date.today() + relativedelta(days=-5)
self.test_contract.update({
@@ -58,10 +58,10 @@ class TestHrContracts(TestContractBase):
})
self.contract.write(self.test_contract)
self.apply_cron()
- self.assertEquals(self.contract.state, 'close')
+ self.assertEqual(self.contract.state, 'close')
def test_contract_start_date(self):
self.test_contract.update(dict(date_start=datetime.now(), state='draft', kanban_state='done'))
self.contract = self.contracts.create(self.test_contract)
self.apply_cron()
- self.assertEquals(self.contract.state, 'open')
+ self.assertEqual(self.contract.state, 'open')
diff --git a/addons/hr_expense/tests/test_expenses.py b/addons/hr_expense/tests/test_expenses.py
index c4958f41108..809ed23079c 100644
--- a/addons/hr_expense/tests/test_expenses.py
+++ b/addons/hr_expense/tests/test_expenses.py
@@ -44,39 +44,39 @@ class TestAccountEntry(TestExpenseCommon):
})
expense_line._onchange_product_id()
# State should default to draft
- self.assertEquals(expense.state, 'draft', 'Expense should be created in Draft state')
+ self.assertEqual(expense.state, 'draft', 'Expense should be created in Draft state')
# Submitted to Manager
expense.action_submit_sheet()
- self.assertEquals(expense.state, 'submit', 'Expense is not in Reported state')
+ self.assertEqual(expense.state, 'submit', 'Expense is not in Reported state')
# Approve
expense.approve_expense_sheets()
- self.assertEquals(expense.state, 'approve', 'Expense is not in Approved state')
+ self.assertEqual(expense.state, 'approve', 'Expense is not in Approved state')
# Create Expense Entries
expense.action_sheet_move_create()
- self.assertEquals(expense.state, 'post', 'Expense is not in Waiting Payment state')
+ self.assertEqual(expense.state, 'post', 'Expense is not in Waiting Payment state')
self.assertTrue(expense.account_move_id.id, 'Expense Journal Entry is not created')
# [(line.debit, line.credit, line.tax_line_id.id) for line in self.expense.expense_line_ids.account_move_id.line_ids]
# should git this result [(0.0, 700.0, False), (63.64, 0.0, 179), (636.36, 0.0, False)]
for line in expense.account_move_id.line_ids:
if line.credit:
- self.assertAlmostEquals(line.credit, 700.00)
- self.assertEquals(len(line.analytic_line_ids), 0, "The credit move line should not have analytic lines")
+ self.assertAlmostEqual(line.credit, 700.00)
+ self.assertEqual(len(line.analytic_line_ids), 0, "The credit move line should not have analytic lines")
self.assertFalse(line.product_id, "Product of credit move line should be false")
else:
if not line.tax_line_id == self.tax:
- self.assertAlmostEquals(line.debit, 636.36)
- self.assertEquals(len(line.analytic_line_ids), 1, "The debit move line should have 1 analytic lines")
- self.assertEquals(line.product_id, self.product_expense, "Product of debit move line should be the one from the expense")
+ self.assertAlmostEqual(line.debit, 636.36)
+ self.assertEqual(len(line.analytic_line_ids), 1, "The debit move line should have 1 analytic lines")
+ self.assertEqual(line.product_id, self.product_expense, "Product of debit move line should be the one from the expense")
else:
- self.assertAlmostEquals(line.debit, 63.64)
- self.assertEquals(len(line.analytic_line_ids), 0, "The tax move line should not have analytic lines")
+ self.assertAlmostEqual(line.debit, 63.64)
+ self.assertEqual(len(line.analytic_line_ids), 0, "The tax move line should not have analytic lines")
self.assertFalse(line.product_id, "Product of tax move line should be false")
- self.assertEquals(self.analytic_account.line_ids, expense.account_move_id.mapped('line_ids.analytic_line_ids'))
- self.assertEquals(len(self.analytic_account.line_ids), 1, "Analytic Account should have only one line")
- self.assertAlmostEquals(self.analytic_account.line_ids[0].amount, -636.36, "Amount on the only AAL is wrong")
- self.assertEquals(self.analytic_account.line_ids[0].product_id, self.product_expense, "Product of AAL should be the one from the expense")
+ self.assertEqual(self.analytic_account.line_ids, expense.account_move_id.mapped('line_ids.analytic_line_ids'))
+ self.assertEqual(len(self.analytic_account.line_ids), 1, "Analytic Account should have only one line")
+ self.assertAlmostEqual(self.analytic_account.line_ids[0].amount, -636.36, "Amount on the only AAL is wrong")
+ self.assertEqual(self.analytic_account.line_ids[0].product_id, self.product_expense, "Product of AAL should be the one from the expense")
def test_account_entry_multi_currency(self):
""" Checking accounting move entries and analytic entries when submitting expense. With
@@ -108,42 +108,42 @@ class TestAccountEntry(TestExpenseCommon):
})
expense_line._onchange_product_id()
# State should default to draft
- self.assertEquals(expense.state, 'draft', 'Expense should be created in Draft state')
+ self.assertEqual(expense.state, 'draft', 'Expense should be created in Draft state')
# Submitted to Manager
expense.action_submit_sheet()
- self.assertEquals(expense.state, 'submit', 'Expense is not in Reported state')
+ self.assertEqual(expense.state, 'submit', 'Expense is not in Reported state')
# Approve
expense.approve_expense_sheets()
- self.assertEquals(expense.state, 'approve', 'Expense is not in Approved state')
+ self.assertEqual(expense.state, 'approve', 'Expense is not in Approved state')
# Create Expense Entries
expense.action_sheet_move_create()
- self.assertEquals(expense.state, 'post', 'Expense is not in Waiting Payment state')
+ self.assertEqual(expense.state, 'post', 'Expense is not in Waiting Payment state')
self.assertTrue(expense.account_move_id.id, 'Expense Journal Entry is not created')
# Should get this result [(0.0, 350.0, -700.0), (318.18, 0.0, 636.36), (31.82, 0.0, 63.64)]
for line in expense.account_move_id.line_ids:
if line.credit:
- self.assertAlmostEquals(line.credit, 350.0)
- self.assertAlmostEquals(line.amount_currency, -700.0)
- self.assertEquals(len(line.analytic_line_ids), 0, "The credit move line should not have analytic lines")
+ self.assertAlmostEqual(line.credit, 350.0)
+ self.assertAlmostEqual(line.amount_currency, -700.0)
+ self.assertEqual(len(line.analytic_line_ids), 0, "The credit move line should not have analytic lines")
self.assertFalse(line.product_id, "Product of credit move line should be false")
else:
if not line.tax_line_id == self.tax:
- self.assertAlmostEquals(line.debit, 318.18)
- self.assertAlmostEquals(line.amount_currency, 636.36)
- self.assertEquals(len(line.analytic_line_ids), 1, "The debit move line should have 1 analytic lines")
- self.assertEquals(line.product_id, self.product_expense, "Product of debit move line should be the one from the expense")
+ self.assertAlmostEqual(line.debit, 318.18)
+ self.assertAlmostEqual(line.amount_currency, 636.36)
+ self.assertEqual(len(line.analytic_line_ids), 1, "The debit move line should have 1 analytic lines")
+ self.assertEqual(line.product_id, self.product_expense, "Product of debit move line should be the one from the expense")
else:
- self.assertAlmostEquals(line.debit, 31.82)
- self.assertAlmostEquals(line.amount_currency, 63.64)
- self.assertEquals(len(line.analytic_line_ids), 0, "The tax move line should not have analytic lines")
+ self.assertAlmostEqual(line.debit, 31.82)
+ self.assertAlmostEqual(line.amount_currency, 63.64)
+ self.assertEqual(len(line.analytic_line_ids), 0, "The tax move line should not have analytic lines")
self.assertFalse(line.product_id, "Product of tax move line should be false")
- self.assertEquals(self.analytic_account.line_ids, expense.account_move_id.mapped('line_ids.analytic_line_ids'))
- self.assertEquals(len(self.analytic_account.line_ids), 1, "Analytic Account should have only one line")
- self.assertAlmostEquals(self.analytic_account.line_ids[0].amount, -318.18, "Amount on the only AAL is wrong")
- self.assertAlmostEquals(self.analytic_account.line_ids[0].currency_id, self.env.company.currency_id, "Currency on the only AAL is wrong")
- self.assertEquals(self.analytic_account.line_ids[0].product_id, self.product_expense, "Product of AAL should be the one from the expense")
+ self.assertEqual(self.analytic_account.line_ids, expense.account_move_id.mapped('line_ids.analytic_line_ids'))
+ self.assertEqual(len(self.analytic_account.line_ids), 1, "Analytic Account should have only one line")
+ self.assertAlmostEqual(self.analytic_account.line_ids[0].amount, -318.18, "Amount on the only AAL is wrong")
+ self.assertAlmostEqual(self.analytic_account.line_ids[0].currency_id, self.env.company.currency_id, "Currency on the only AAL is wrong")
+ self.assertEqual(self.analytic_account.line_ids[0].product_id, self.product_expense, "Product of AAL should be the one from the expense")
def test_expense_from_email(self):
user_demo = self.env.ref('base.user_demo')
@@ -161,9 +161,9 @@ class TestAccountEntry(TestExpenseCommon):
expense = self.env['hr.expense'].message_new(message_parsed)
air_ticket = self.env.ref("hr_expense.air_ticket")
- self.assertEquals(expense.product_id, air_ticket)
- self.assertEquals(expense.tax_ids.ids, [])
- self.assertEquals(expense.total_amount, 9876.0)
+ self.assertEqual(expense.product_id, air_ticket)
+ self.assertEqual(expense.tax_ids.ids, [])
+ self.assertEqual(expense.total_amount, 9876.0)
self.assertTrue(expense.employee_id in user_demo.employee_ids)
def test_expense_from_email_without_product(self):
@@ -183,8 +183,8 @@ class TestAccountEntry(TestExpenseCommon):
air_ticket = self.env.ref("hr_expense.air_ticket")
self.assertFalse(expense.product_id, "No product should be linked")
- self.assertEquals(expense.tax_ids.ids, [])
- self.assertEquals(expense.total_amount, 9876.0)
+ self.assertEqual(expense.tax_ids.ids, [])
+ self.assertEqual(expense.total_amount, 9876.0)
self.assertTrue(expense.employee_id in user_demo.employee_ids)
def test_partial_payment_multiexpense(self):
@@ -222,7 +222,7 @@ class TestAccountEntry(TestExpenseCommon):
exp_move_lines = expense.account_move_id.line_ids
payable_move_lines = exp_move_lines.filtered(lambda l: l.account_id.internal_type == 'payable')
- self.assertEquals(len(payable_move_lines), 2)
+ self.assertEqual(len(payable_move_lines), 2)
WizardRegister = self.env["hr.expense.sheet.register.payment.wizard"].with_context(
active_model=expense._name, active_id=expense.id, active_ids=expense.ids
@@ -237,7 +237,7 @@ class TestAccountEntry(TestExpenseCommon):
exp_move_lines = expense.account_move_id.line_ids
payable_move_lines = exp_move_lines.filtered(lambda l: l.account_id.internal_type == 'payable')
- self.assertEquals(len(payable_move_lines.filtered(lambda l: l.reconciled)), 1)
+ self.assertEqual(len(payable_move_lines.filtered(lambda l: l.reconciled)), 1)
register_pay2 = WizardRegister.create({
'journal_id': bank_journal.id,
@@ -247,10 +247,10 @@ class TestAccountEntry(TestExpenseCommon):
register_pay2.expense_post_payment()
exp_move_lines = expense.account_move_id.line_ids
payable_move_lines = exp_move_lines.filtered(lambda l: l.account_id.internal_type == 'payable')
- self.assertEquals(len(payable_move_lines.filtered(lambda l: l.reconciled)), 2)
+ self.assertEqual(len(payable_move_lines.filtered(lambda l: l.reconciled)), 2)
full_reconcile = payable_move_lines.mapped('full_reconcile_id')
- self.assertEquals(len(full_reconcile), 1)
+ self.assertEqual(len(full_reconcile), 1)
class TestExpenseRights(TestExpenseCommon):
diff --git a/addons/hr_expense/tests/test_expenses_mail_subjects.py b/addons/hr_expense/tests/test_expenses_mail_subjects.py
index 134726e32cf..0500050a39e 100644
--- a/addons/hr_expense/tests/test_expenses_mail_subjects.py
+++ b/addons/hr_expense/tests/test_expenses_mail_subjects.py
@@ -31,16 +31,16 @@ class TestExpenseSubject(TestExpenseCommon):
subject = 'EXP-PHONE bar %s1205.91 electro wizard' % (company_currency.symbol,)
product, price, currency_id, expense_description = parse_subject(subject, company_currency)
- self.assertEquals(expense_description, 'bar electro wizard', "Should be remove price and product from subject")
- self.assertAlmostEquals(price, 1205.91, "Price is not fetched correctly")
- self.assertEquals(currency_id, company_currency, "Should fetch currency correctly")
- self.assertEquals(product, self.product_expense, "Should fetch product correctly")
+ self.assertEqual(expense_description, 'bar electro wizard', "Should be remove price and product from subject")
+ self.assertAlmostEqual(price, 1205.91, "Price is not fetched correctly")
+ self.assertEqual(currency_id, company_currency, "Should fetch currency correctly")
+ self.assertEqual(product, self.product_expense, "Should fetch product correctly")
# subject having other currency then company currency, it should ignore other currency then company currency
subject = 'foo bar %s1406.91 royal giant' % (alternate_currency.symbol,)
product, price, currency_id, expense_description = parse_subject(subject, company_currency)
- self.assertEquals(expense_description, 'foo bar %s royal giant' % (alternate_currency.symbol,), "Should be remove price and product from subject but not currency symbol")
- self.assertEquals(currency_id, company_currency, "Should fetch currency correctly")
+ self.assertEqual(expense_description, 'foo bar %s royal giant' % (alternate_currency.symbol,), "Should be remove price and product from subject but not currency symbol")
+ self.assertEqual(currency_id, company_currency, "Should fetch currency correctly")
# With Multi currency access
group_multi_currency = self.env.ref('base.group_multi_currency')
@@ -50,55 +50,55 @@ class TestExpenseSubject(TestExpenseCommon):
subject = 'EXP-PHONE foo bar %s2205.92 elite barbarians' % (company_currency.symbol,) # with product code at start
product, price, currency_id, expense_description = parse_subject(subject, company_currency)
- self.assertEquals(expense_description, 'foo bar elite barbarians', "Should be remove price and product from subject")
- self.assertAlmostEquals(price, 2205.92, "Price is not fetched correctly")
- self.assertEquals(currency_id, company_currency, "Should fetch currency correctly")
- self.assertEquals(product, self.product_expense, "Should fetch product correctly")
+ self.assertEqual(expense_description, 'foo bar elite barbarians', "Should be remove price and product from subject")
+ self.assertAlmostEqual(price, 2205.92, "Price is not fetched correctly")
+ self.assertEqual(currency_id, company_currency, "Should fetch currency correctly")
+ self.assertEqual(product, self.product_expense, "Should fetch product correctly")
# subject having other currency then company currency, it should accept other currency because multi currency is activated
subject = 'EXP-PHONE %s2510.90 chhota bheem' % (alternate_currency.symbol,)
product, price, currency_id, expense_description = parse_subject(subject, company_currency | alternate_currency)
- self.assertEquals(expense_description, 'chhota bheem', "Should be remove price and product from subject but not currency symbol")
- self.assertAlmostEquals(price, 2510.90, "Price is not fetched correctly")
- self.assertEquals(currency_id, alternate_currency, "Should fetch currency correctly")
- self.assertEquals(product, self.product_expense, "Should fetch product correctly")
+ self.assertEqual(expense_description, 'chhota bheem', "Should be remove price and product from subject but not currency symbol")
+ self.assertAlmostEqual(price, 2510.90, "Price is not fetched correctly")
+ self.assertEqual(currency_id, alternate_currency, "Should fetch currency correctly")
+ self.assertEqual(product, self.product_expense, "Should fetch product correctly")
# subject without product and currency, should take company currency and default product
subject = 'foo bar 109.96 spear goblins'
product, price, currency_id, expense_description = parse_subject(subject, company_currency | alternate_currency)
- self.assertEquals(expense_description, 'foo bar spear goblins', "Should remove price")
- self.assertAlmostEquals(price, 109.96, "Price is not fetched correctly")
+ self.assertEqual(expense_description, 'foo bar spear goblins', "Should remove price")
+ self.assertAlmostEqual(price, 109.96, "Price is not fetched correctly")
self.assertIn(currency_id, company_currency | alternate_currency, "Should fetch company currency")
self.assertFalse(product, "Should not have parsed any product")
# subject with currency symbol at end
subject = 'EXP-PHONE foo bar 2910.94%s inferno dragon' % (company_currency.symbol,)
product, price, currency_id, expense_description = parse_subject(subject, company_currency | alternate_currency)
- self.assertEquals(expense_description, 'foo bar inferno dragon', "Should be remove price and product from subject")
- self.assertAlmostEquals(price, 2910.94, "Price is not fetched correctly")
- self.assertEquals(currency_id, company_currency, "Should fetch currency correctly")
- self.assertEquals(product, self.product_expense, "Should fetch product correctly")
+ self.assertEqual(expense_description, 'foo bar inferno dragon', "Should be remove price and product from subject")
+ self.assertAlmostEqual(price, 2910.94, "Price is not fetched correctly")
+ self.assertEqual(currency_id, company_currency, "Should fetch currency correctly")
+ self.assertEqual(product, self.product_expense, "Should fetch product correctly")
# subject with no amount and product
subject = 'foo bar mega knight'
product, price, currency_id, expense_description = parse_subject(subject, company_currency | alternate_currency)
- self.assertEquals(expense_description, 'foo bar mega knight', "Should be same as subject")
- self.assertAlmostEquals(price, 0.0, "Price is not fetched correctly")
+ self.assertEqual(expense_description, 'foo bar mega knight', "Should be same as subject")
+ self.assertAlmostEqual(price, 0.0, "Price is not fetched correctly")
self.assertIn(currency_id, company_currency | alternate_currency, "Should fetch currency correctly")
self.assertFalse(product, "Should fetch product correctly")
# price with a comma
subject = 'foo bar 291,56%s mega knight' % (company_currency.symbol,)
product, price, currency_id, expense_description = parse_subject(subject, company_currency | alternate_currency)
- self.assertAlmostEquals(price, 291.56, "Price is not fetched correctly")
- self.assertEquals(currency_id, company_currency, "Should fetch currency correctly")
+ self.assertAlmostEqual(price, 291.56, "Price is not fetched correctly")
+ self.assertEqual(currency_id, company_currency, "Should fetch currency correctly")
# price without decimals
subject = 'foo bar 291%s mega knight' % (company_currency.symbol,)
product, price, currency_id, expense_description = parse_subject(subject, company_currency | alternate_currency)
- self.assertAlmostEquals(price, 291.0, "Price is not fetched correctly")
+ self.assertAlmostEqual(price, 291.0, "Price is not fetched correctly")
subject = 'EXP-PHONE 2 foo bar 291.5%s mega knight' % (company_currency.symbol,)
product, price, currency_id, expense_description = parse_subject(subject, company_currency | alternate_currency)
print(price)
- self.assertAlmostEquals(price, 291.5, "Price is not fetched correctly")
+ self.assertAlmostEqual(price, 291.5, "Price is not fetched correctly")
diff --git a/addons/hr_recruitment/tests/test_recruitment_process.py b/addons/hr_recruitment/tests/test_recruitment_process.py
index 03f411bb371..0eebc5fe5f8 100644
--- a/addons/hr_recruitment/tests/test_recruitment_process.py
+++ b/addons/hr_recruitment/tests/test_recruitment_process.py
@@ -33,13 +33,13 @@ class TestRecruitmentProcess(common.TransactionCase):
('name', '=', 'resume.pdf'),
('res_model', '=', self.env['hr.applicant']._name),
('res_id', '=', applicant.id)])
- self.assertEquals(applicant.name, 'Application for the post of Jr.application Programmer.', 'Applicant name does not match.')
- self.assertEquals(applicant.stage_id, self.env.ref('hr_recruitment.stage_job1'),
+ self.assertEqual(applicant.name, 'Application for the post of Jr.application Programmer.', 'Applicant name does not match.')
+ self.assertEqual(applicant.stage_id, self.env.ref('hr_recruitment.stage_job1'),
"Stage should be 'Initial qualification' and is '%s'." % (applicant.stage_id.name))
self.assertTrue(resume_ids, 'Resume is not attached.')
# I assign the Job position to the applicant
applicant.write({'job_id': self.env.ref('hr.job_developer').id})
# I schedule meeting with applicant for interview.
applicant_meeting = applicant.action_makeMeeting()
- self.assertEquals(applicant_meeting['context']['default_name'], 'Application for the post of Jr.application Programmer.',
+ self.assertEqual(applicant_meeting['context']['default_name'], 'Application for the post of Jr.application Programmer.',
'Applicant name does not match.')
diff --git a/addons/hr_timesheet/tests/test_timesheet.py b/addons/hr_timesheet/tests/test_timesheet.py
index c2bfab9e8bd..fed0c53b953 100644
--- a/addons/hr_timesheet/tests/test_timesheet.py
+++ b/addons/hr_timesheet/tests/test_timesheet.py
@@ -89,10 +89,10 @@ class TestTimesheet(TestCommonTimesheet):
'name': 'my first timesheet',
'unit_amount': 4,
})
- self.assertEquals(timesheet1.account_id, self.project_customer.analytic_account_id, 'Analytic account should be the same as the project')
- self.assertEquals(timesheet1.employee_id, self.empl_employee, 'Employee should be the one of the current user')
- self.assertEquals(timesheet1.partner_id, self.task1.partner_id, 'Customer of task should be the same of the one set on new timesheet')
- self.assertEquals(timesheet1.product_uom_id, timesheet_uom, "The UoM of the timesheet should be the one set on the company of the analytic account.")
+ self.assertEqual(timesheet1.account_id, self.project_customer.analytic_account_id, 'Analytic account should be the same as the project')
+ self.assertEqual(timesheet1.employee_id, self.empl_employee, 'Employee should be the one of the current user')
+ self.assertEqual(timesheet1.partner_id, self.task1.partner_id, 'Customer of task should be the same of the one set on new timesheet')
+ self.assertEqual(timesheet1.product_uom_id, timesheet_uom, "The UoM of the timesheet should be the one set on the company of the analytic account.")
# employee 1 cannot log timesheet for employee 2
with self.assertRaises(AccessError):
@@ -113,8 +113,8 @@ class TestTimesheet(TestCommonTimesheet):
'employee_id': self.empl_employee2.id,
})
timesheet3._onchange_employee_id()
- self.assertEquals(timesheet3.user_id, self.user_employee2, 'Timesheet user should be the one linked to the given employee')
- self.assertEquals(timesheet3.product_uom_id, timesheet_uom, "The UoM of the timesheet 3 should be the one set on the company of the analytic account.")
+ self.assertEqual(timesheet3.user_id, self.user_employee2, 'Timesheet user should be the one linked to the given employee')
+ self.assertEqual(timesheet3.product_uom_id, timesheet_uom, "The UoM of the timesheet 3 should be the one set on the company of the analytic account.")
# employee 1 log some timesheet on project (no task)
timesheet4 = Timesheet.with_user(self.user_employee).create({
@@ -122,7 +122,7 @@ class TestTimesheet(TestCommonTimesheet):
'name': 'my first timesheet',
'unit_amount': 4,
})
- self.assertEquals(timesheet4.partner_id, self.project_customer.partner_id, 'Customer of new timesheet should be the same of the one set project (since no task on timesheet)')
+ self.assertEqual(timesheet4.partner_id, self.project_customer.partner_id, 'Customer of new timesheet should be the same of the one set project (since no task on timesheet)')
def test_log_access_rights(self):
""" Test access rights : user can update its own timesheets only, and manager can change all """
@@ -145,7 +145,7 @@ class TestTimesheet(TestCommonTimesheet):
'unit_amount': 8,
'employee_id': self.empl_employee2.id,
})
- self.assertEquals(timesheet1.user_id, self.user_employee2, 'Changing timesheet employee should change the related user')
+ self.assertEqual(timesheet1.user_id, self.user_employee2, 'Changing timesheet employee should change the related user')
def test_create_unlink_project(self):
""" Check project creation, and if necessary the analytic account generated when project should track time. """
@@ -165,9 +165,9 @@ class TestTimesheet(TestCommonTimesheet):
})
self.assertTrue(tracked_project.analytic_account_id, "A time-tracked project should generate an analytic account")
self.assertTrue(tracked_project.analytic_account_id.active, "A time-tracked project should generate an active analytic account")
- self.assertEquals(tracked_project.partner_id, tracked_project.analytic_account_id.partner_id, "The generated AA should have the same partner as the project")
- self.assertEquals(tracked_project.name, tracked_project.analytic_account_id.name, "The generated AA should have the same name as the project")
- self.assertEquals(tracked_project.analytic_account_id.project_count, 1, "The generated AA should be linked to the project")
+ self.assertEqual(tracked_project.partner_id, tracked_project.analytic_account_id.partner_id, "The generated AA should have the same partner as the project")
+ self.assertEqual(tracked_project.name, tracked_project.analytic_account_id.name, "The generated AA should have the same name as the project")
+ self.assertEqual(tracked_project.analytic_account_id.project_count, 1, "The generated AA should be linked to the project")
# create a project without tracking time, but with analytic account
analytic_project = self.env['project.project'].create({
@@ -176,8 +176,8 @@ class TestTimesheet(TestCommonTimesheet):
'partner_id': self.partner.id,
'analytic_account_id': tracked_project.analytic_account_id.id,
})
- self.assertNotEquals(analytic_project.name, tracked_project.analytic_account_id.name, "The name of the associated AA can be different from the project")
- self.assertEquals(tracked_project.analytic_account_id.project_count, 2, "The AA should be linked to 2 project")
+ self.assertNotEqual(analytic_project.name, tracked_project.analytic_account_id.name, "The name of the associated AA can be different from the project")
+ self.assertEqual(tracked_project.analytic_account_id.project_count, 2, "The AA should be linked to 2 project")
# analytic linked to projects containing tasks can not be removed
task = self.env['project.task'].create({
@@ -211,9 +211,9 @@ class TestTimesheet(TestCommonTimesheet):
timesheet_count1 = Timesheet.search_count([('project_id', '=', self.project_customer.id)])
timesheet_count2 = Timesheet.search_count([('project_id', '=', self.project_customer2.id)])
- self.assertEquals(timesheet_count1, 1, "One timesheet in project 1")
- self.assertEquals(timesheet_count2, 0, "No timesheet in project 2")
- self.assertEquals(len(self.task1.timesheet_ids), 1, "The timesheet should be linked to task 1")
+ self.assertEqual(timesheet_count1, 1, "One timesheet in project 1")
+ self.assertEqual(timesheet_count2, 0, "No timesheet in project 2")
+ self.assertEqual(len(self.task1.timesheet_ids), 1, "The timesheet should be linked to task 1")
# change project of task 1
self.task1.write({
@@ -222,9 +222,9 @@ class TestTimesheet(TestCommonTimesheet):
timesheet_count1 = Timesheet.search_count([('project_id', '=', self.project_customer.id)])
timesheet_count2 = Timesheet.search_count([('project_id', '=', self.project_customer2.id)])
- self.assertEquals(timesheet_count1, 1, "Still one timesheet in project 1")
- self.assertEquals(timesheet_count2, 0, "No timesheet in project 2")
- self.assertEquals(len(self.task1.timesheet_ids), 1, "The timesheet still should be linked to task 1")
+ self.assertEqual(timesheet_count1, 1, "Still one timesheet in project 1")
+ self.assertEqual(timesheet_count2, 0, "No timesheet in project 2")
+ self.assertEqual(len(self.task1.timesheet_ids), 1, "The timesheet still should be linked to task 1")
# it is forbidden to set a task with timesheet without project
with self.assertRaises(UserError):
diff --git a/addons/http_routing/models/ir_http.py b/addons/http_routing/models/ir_http.py
index 8ed7527232c..ae72d2d04e2 100644
--- a/addons/http_routing/models/ir_http.py
+++ b/addons/http_routing/models/ir_http.py
@@ -265,7 +265,7 @@ class IrHttp(models.AbstractModel):
session_info.update({
'translationURL': '/website/translations/',
'cache_hashes': {
- 'translations': hashlib.sha1(json.dumps(translation_cache, sort_keys=True).encode()).hexdigest(),
+ 'translations': hashlib.sha512(json.dumps(translation_cache, sort_keys=True).encode()).hexdigest()[:64], # sha512/256
},
})
return session_info
diff --git a/addons/l10n_be_edi/tests/test_ubl.py b/addons/l10n_be_edi/tests/test_ubl.py
index 0fa29858416..37ef7bd9984 100644
--- a/addons/l10n_be_edi/tests/test_ubl.py
+++ b/addons/l10n_be_edi/tests/test_ubl.py
@@ -20,7 +20,7 @@ class TestUBL(common.TransactionCase):
attachment_id = self.env['ir.attachment'].create({
'name': 'efff_test.xml',
- 'datas': base64.encodestring(xml_file),
+ 'datas': base64.encodebytes(xml_file),
'res_id': invoice.id,
'res_model': 'account.move',
})
diff --git a/addons/l10n_cl/data/res_partner.xml b/addons/l10n_cl/data/res_partner.xml
index 91c15a2287a..c82f074e1d8 100644
--- a/addons/l10n_cl/data/res_partner.xml
+++ b/addons/l10n_cl/data/res_partner.xml
@@ -7,7 +7,7 @@
<field name='l10n_cl_sii_taxpayer_type'>3</field>
<field name='l10n_latam_identification_type_id' ref='l10n_cl.it_RUT'/>
<field name='vat'>66666666-6</field>
- </record>
+ </record>
<record model='res.partner' id='par_tgr'>
<field name='name'>Tesorería General de la República</field>
@@ -29,4 +29,3 @@
</data>
</odoo>
-
diff --git a/addons/l10n_cl/models/__init__.py b/addons/l10n_cl/models/__init__.py
index 93becf0fbb9..5412089747b 100644
--- a/addons/l10n_cl/models/__init__.py
+++ b/addons/l10n_cl/models/__init__.py
@@ -12,6 +12,3 @@ from . import res_currency
from . import res_partner
from . import res_partner_bank
from . import uom_uom
-
-
-
diff --git a/addons/l10n_cl/models/ir_sequence.py b/addons/l10n_cl/models/ir_sequence.py
index 6a3331a6628..5da1e5c964a 100644
--- a/addons/l10n_cl/models/ir_sequence.py
+++ b/addons/l10n_cl/models/ir_sequence.py
@@ -8,4 +8,3 @@ class IrSequence(models.Model):
l10n_cl_journal_ids = fields.Many2many('account.journal', 'l10n_cl_journal_sequence_rel', 'sequence_id',
'journal_id', 'Journals', readonly=True)
-
diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec.py b/addons/l10n_fr_fec/wizard/account_fr_fec.py
index 8adab17adea..476905a42e2 100644
--- a/addons/l10n_fr_fec/wizard/account_fr_fec.py
+++ b/addons/l10n_fr_fec/wizard/account_fr_fec.py
@@ -357,7 +357,7 @@ class AccountFrFec(models.TransientModel):
suffix = '-NONOFFICIAL'
self.write({
- 'fec_data': base64.encodestring(fecvalue),
+ 'fec_data': base64.encodebytes(fecvalue),
# Filename = <siren>FECYYYYMMDD where YYYMMDD is the closing date
'filename': '%sFEC%s%s.csv' % (company_legal_data['siren'], end_date, suffix),
})
diff --git a/addons/l10n_it_edi/models/account_invoice.py b/addons/l10n_it_edi/models/account_invoice.py
index 2938c6630ff..5c0090165b3 100644
--- a/addons/l10n_it_edi/models/account_invoice.py
+++ b/addons/l10n_it_edi/models/account_invoice.py
@@ -160,7 +160,7 @@ class AccountMove(models.Model):
'name': report_name,
'res_id': invoice.id,
'res_model': invoice._name,
- 'datas': base64.encodestring(data),
+ 'datas': base64.encodebytes(data),
'description': description,
'type': 'binary',
})
diff --git a/addons/l10n_it_edi/models/ir_mail_server.py b/addons/l10n_it_edi/models/ir_mail_server.py
index 51e4a0a23a3..e465a6b7846 100644
--- a/addons/l10n_it_edi/models/ir_mail_server.py
+++ b/addons/l10n_it_edi/models/ir_mail_server.py
@@ -6,6 +6,7 @@ import io
import re
import logging
import email
+import email.policy
import dateutil
import pytz
import base64
@@ -74,7 +75,7 @@ class FetchmailServer(models.Model):
message = bytes(message.data)
if isinstance(message, str):
message = message.encode('utf-8')
- msg_txt = email.message_from_bytes(message)
+ msg_txt = email.message_from_bytes(message, policy=email.policy.SMTP)
try:
self._attachment_invoice(msg_txt)
@@ -98,7 +99,7 @@ class FetchmailServer(models.Model):
def _attachment_invoice(self, msg_txt):
parsed_values = self.env['mail.thread']._message_parse_extract_payload(msg_txt)
body, attachments = parsed_values['body'], parsed_values['attachments']
- from_address = tools.decode_smtp_header(msg_txt.get('from'))
+ from_address = msg_txt.get('from')
for attachment in attachments:
split_attachment = attachment.fname.rpartition('.')
if len(split_attachment) < 3:
@@ -134,7 +135,7 @@ class FetchmailServer(models.Model):
invoice_attachment = self.env['ir.attachment'].create({
'name': att_name,
- 'datas': base64.encodestring(att_content),
+ 'datas': base64.encodebytes(att_content),
'type': 'binary',
})
diff --git a/addons/l10n_pe/README.rst b/addons/l10n_pe/README.rst
index 764a17a4950..b09d1c14f5b 100644
--- a/addons/l10n_pe/README.rst
+++ b/addons/l10n_pe/README.rst
@@ -54,15 +54,15 @@ https://docs.google.com/spreadsheets/d/1f1fxV8uGhA-Qz9-R1L1-dJirZ8xi3Wfg/edit#gi
**RELACIÓN ENTRE EL PCGE Y LA LEGISLACIÓN TRIBUTARIA:**
-Este PCGE ha sido preparado como una herramienta de carácter contable, para acumular información que
-requiere ser expuesta en el cuerpo de los estados financieros o en las notas a dichos estados. Esa acumulación se
-efectúa en los libros o registros contables, cuya denominación y naturaleza depende de las actividades que se
+Este PCGE ha sido preparado como una herramienta de carácter contable, para acumular información que
+requiere ser expuesta en el cuerpo de los estados financieros o en las notas a dichos estados. Esa acumulación se
+efectúa en los libros o registros contables, cuya denominación y naturaleza depende de las actividades que se
efectúen, y que permiten acciones de verificación, control y seguimiento. Las NIIF completas y la NIIF PYMES no
-contienen prescripciones sobre teneduría de libros, y consecuentemente, sobre los libros y otros registros
-de naturaleza contable. Por otro lado, si bien es cierto la contabilidad es también un insumo, dentro de otros, para
-labores de cumplimiento tributario, este PCGE no ha sido elaborado para satisfacer prescripciones tributarias ni su
+contienen prescripciones sobre teneduría de libros, y consecuentemente, sobre los libros y otros registros
+de naturaleza contable. Por otro lado, si bien es cierto la contabilidad es también un insumo, dentro de otros, para
+labores de cumplimiento tributario, este PCGE no ha sido elaborado para satisfacer prescripciones tributarias ni su
verificación. No obstante ello, donde no hubo oposición entre la contabilidad financiera prescrita por las NIIF y
la legislación tributaria, este PCGE ha incluido subcuentas, divisionarias y sub divisionarias, para
distinguir componentes con validez tributaria, dentro del conjunto de componentes que corresponden a una
perspectiva contable íntegramente. Por lo tanto, este PCGE no debe ser considerado en ningún aspecto
-como una guía con propósitos distintos del contable. \ No newline at end of file
+como una guía con propósitos distintos del contable.
diff --git a/addons/l10n_pe/data/l10n_pe.res.city.district.csv b/addons/l10n_pe/data/l10n_pe.res.city.district.csv
index 44de278818a..83d2efba7db 100644
--- a/addons/l10n_pe/data/l10n_pe.res.city.district.csv
+++ b/addons/l10n_pe/data/l10n_pe.res.city.district.csv
@@ -461,7 +461,7 @@ district_pe_050116,city_pe_0501,"Andrés Avelino Cáceres Dorregaray","050116"
district_pe_050201,city_pe_0502,"Cangallo","050201"
district_pe_050202,city_pe_0502,"Chuschi","050202"
district_pe_050203,city_pe_0502,"Los Morochucos","050203"
-district_pe_050204,city_pe_0502,"María Parado de Bellido","050204"
+district_pe_050204,city_pe_0502,"María Parado de Bellido","050204"
district_pe_050205,city_pe_0502,"Paras","050205"
district_pe_050206,city_pe_0502,"Totos","050206"
district_pe_050301,city_pe_0503,"Sancos","050301"
diff --git a/addons/l10n_pe/models/__init__.py b/addons/l10n_pe/models/__init__.py
index 8489b432a52..7be3012e1aa 100644
--- a/addons/l10n_pe/models/__init__.py
+++ b/addons/l10n_pe/models/__init__.py
@@ -5,4 +5,3 @@ from . import l10n_latam_identification_type
from . import res_partner
from . import res_city_district
from . import res_city
-
diff --git a/addons/mail/models/mail_cc_mixin.py b/addons/mail/models/mail_cc_mixin.py
index cc1554fcff5..427eb25a2bb 100644
--- a/addons/mail/models/mail_cc_mixin.py
+++ b/addons/mail/models/mail_cc_mixin.py
@@ -2,7 +2,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, api, fields, models, tools
-from email.utils import formataddr
class MailCCMixin(models.AbstractModel):
@@ -16,7 +15,7 @@ class MailCCMixin(models.AbstractModel):
'''return a dict of sanitize_email:raw_email from a string of cc'''
if not cc_string:
return {}
- return {tools.email_normalize(email): formataddr((name, tools.email_normalize(email)))
+ return {tools.email_normalize(email): tools.formataddr((name, tools.email_normalize(email)))
for (name, email) in tools.email_split_tuples(cc_string)}
@api.model
diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py
index 91d40bed9de..fb27b41830b 100644
--- a/addons/mail/models/mail_channel.py
+++ b/addons/mail/models/mail_channel.py
@@ -5,13 +5,12 @@ import base64
import logging
import re
-from email.utils import formataddr
from uuid import uuid4
from odoo import _, api, fields, models, modules, tools
from odoo.exceptions import UserError, ValidationError
from odoo.osv import expression
-from odoo.tools import ormcache
+from odoo.tools import ormcache, formataddr
MODERATION_FIELDS = ['moderation', 'moderator_ids', 'moderation_ids', 'moderation_notify', 'moderation_notify_msg', 'moderation_guidelines', 'moderation_guidelines_msg']
_logger = logging.getLogger(__name__)
diff --git a/addons/mail/models/mail_mail.py b/addons/mail/models/mail_mail.py
index 59a8e604fb1..c1e3a19fe54 100644
--- a/addons/mail/models/mail_mail.py
+++ b/addons/mail/models/mail_mail.py
@@ -10,7 +10,6 @@ import threading
import re
from collections import defaultdict
-from email.utils import formataddr
from odoo import _, api, fields, models
from odoo import tools
@@ -195,7 +194,7 @@ class MailMail(models.Model):
body = self._send_prepare_body()
body_alternative = tools.html2plaintext(body)
if partner:
- email_to = [formataddr((partner.name or 'False', partner.email or 'False'))]
+ email_to = [tools.formataddr((partner.name or 'False', partner.email or 'False'))]
else:
email_to = tools.email_split_and_format(self.email_to)
res = {
diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py
index 63ac13917e4..4801560be9f 100644
--- a/addons/mail/models/mail_message.py
+++ b/addons/mail/models/mail_message.py
@@ -32,7 +32,7 @@ class Message(models.Model):
@api.model
def _get_default_from(self):
if self.env.user.email:
- return formataddr((self.env.user.name, self.env.user.email))
+ return tools.formataddr((self.env.user.name, self.env.user.email))
raise UserError(_("Unable to post message, please configure the sender's email address."))
@api.model
@@ -1179,7 +1179,7 @@ class Message(models.Model):
if not msg.email_from:
continue
if self.env.user.partner_id.email:
- email_from = formataddr((self.env.user.partner_id.name, self.env.user.partner_id.email))
+ email_from = tools.formataddr((self.env.user.partner_id.name, self.env.user.partner_id.email))
else:
email_from = self.env.company.catchall
diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py
index 94ae2e4b448..c37073245f3 100644
--- a/addons/mail/models/mail_thread.py
+++ b/addons/mail/models/mail_thread.py
@@ -5,6 +5,7 @@ import base64
import datetime
import dateutil
import email
+import email.policy
import hashlib
import hmac
import lxml
@@ -20,8 +21,7 @@ except ImportError:
import xmlrpclib
from collections import namedtuple
-from email.message import Message
-from email.utils import formataddr
+from email.message import EmailMessage
from lxml import etree
from werkzeug import url_encode
from werkzeug import urls
@@ -876,8 +876,8 @@ class MailThread(models.AbstractModel):
:raises: ValueError, TypeError
"""
- if not isinstance(message, Message):
- raise TypeError('message must be an email.message.Message at this point')
+ if not isinstance(message, EmailMessage):
+ raise TypeError('message must be an email.message.EmailMessage at this point')
catchall_alias = self.env['ir.config_parameter'].sudo().get_param("mail.catchall.alias")
bounce_alias = self.env['ir.config_parameter'].sudo().get_param("mail.bounce.alias")
fallback_model = model
@@ -1093,7 +1093,7 @@ class MailThread(models.AbstractModel):
message = bytes(message.data)
if isinstance(message, str):
message = message.encode('utf-8')
- message = email.message_from_bytes(message)
+ message = email.message_from_bytes(message, policy=email.policy.SMTP)
# parse the message, verify we are not in a loop by checking message_id is not duplicated
msg_dict = self.message_parse(message, save_original=save_original)
@@ -1231,7 +1231,7 @@ class MailThread(models.AbstractModel):
# type="text/html"
if message.get_content_maintype() == 'text':
encoding = message.get_content_charset()
- body = message.get_payload(decode=True)
+ body = message.get_content()
body = tools.ustr(body, encoding, errors='replace')
if message.get_content_type() == 'text/plain':
# text/plain -> <pre/>
@@ -1247,40 +1247,29 @@ class MailThread(models.AbstractModel):
mixed = True
if part.get_content_maintype() == 'multipart':
continue # skip container
- # part.get_filename returns decoded value if able to decode, coded otherwise.
- # original get_filename is not able to decode iso-8859-1 (for instance).
- # therefore, iso encoded attachements are not able to be decoded properly with get_filename
- # code here partially copy the original get_filename method, but handle more encoding
- filename = part.get_param('filename', None, 'content-disposition')
- if not filename:
- filename = part.get_param('name', None)
- if filename:
- if isinstance(filename, tuple):
- # RFC2231
- filename = email.utils.collapse_rfc2231_value(filename).strip()
- else:
- filename = tools.decode_smtp_header(filename)
+
+ filename = part.get_filename() # I may not properly handle all charsets
encoding = part.get_content_charset() # None if attachment
# 0) Inline Attachments -> attachments, with a third part in the tuple to match cid / attachment
if filename and part.get('content-id'):
inner_cid = part.get('content-id').strip('><')
- attachments.append(self._Attachment(filename, part.get_payload(decode=True), {'cid': inner_cid}))
+ attachments.append(self._Attachment(filename, part.get_content(), {'cid': inner_cid}))
continue
# 1) Explicit Attachments -> attachments
if filename or part.get('content-disposition', '').strip().startswith('attachment'):
- attachments.append(self._Attachment(filename or 'attachment', part.get_payload(decode=True), {}))
+ attachments.append(self._Attachment(filename or 'attachment', part.get_content(), {}))
continue
# 2) text/plain -> <pre/>
if part.get_content_type() == 'text/plain' and (not alternative or not body):
- body = tools.append_content_to_html(body, tools.ustr(part.get_payload(decode=True),
+ body = tools.append_content_to_html(body, tools.ustr(part.get_content(),
encoding, errors='replace'), preserve=True)
# 3) text/html -> raw
elif part.get_content_type() == 'text/html':
# mutlipart/alternative have one text and a html part, keep only the second
# mixed allows several html parts, append html content
append_content = not alternative or (html and mixed)
- html = tools.ustr(part.get_payload(decode=True), encoding, errors='replace')
+ html = tools.ustr(part.get_content(), encoding, errors='replace')
if not append_content:
body = html
else:
@@ -1289,7 +1278,7 @@ class MailThread(models.AbstractModel):
body = tools.html_sanitize(body, sanitize_tags=False, strip_classes=True)
# 4) Anything else -> attachment
else:
- attachments.append(self._Attachment(filename or 'attachment', part.get_payload(decode=True), {}))
+ attachments.append(self._Attachment(filename or 'attachment', part.get_content(), {}))
return self._message_parse_extract_payload_postprocess(message, {'body': body, 'attachments': attachments})
@@ -1301,8 +1290,8 @@ class MailThread(models.AbstractModel):
:param message_dict: dictionary holding already-parsed values and in
which bounce-related values will be added;
"""
- if not isinstance(email_message, Message):
- raise TypeError('message must be an email.message.Message at this point')
+ if not isinstance(email_message, EmailMessage):
+ raise TypeError('message must be an email.message.EmailMessage at this point')
email_part = next((part for part in email_message.walk() if part.get_content_type() == 'message/rfc822'), None)
dsn_part = next((part for part in email_message.walk() if part.get_content_type() == 'message/delivery-status'), None)
@@ -1361,8 +1350,8 @@ class MailThread(models.AbstractModel):
('file2', 'bytes')}
}
"""
- if not isinstance(message, Message):
- raise ValueError(_('Message should be a valid Message instance'))
+ if not isinstance(message, EmailMessage):
+ raise ValueError(_('Message should be a valid EmailMessage instance'))
msg_dict = {'message_type': 'email'}
message_id = message.get('Message-Id')
@@ -2632,7 +2621,7 @@ class MailThread(models.AbstractModel):
company_name = company.name if company else self.env.company.name
for res_id in result_email.keys():
name = '%s%s%s' % (company_name, ' ' if doc_names.get(res_id) else '', doc_names.get(res_id, ''))
- result[res_id] = formataddr((name, result_email[res_id]))
+ result[res_id] = tools.formataddr((name, result_email[res_id]))
left_ids = set(_res_ids) - set(result_email)
if left_ids:
diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py
index aeeba8c2339..b8b516f878c 100644
--- a/addons/mail/wizard/mail_compose_message.py
+++ b/addons/mail/wizard/mail_compose_message.py
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from email.utils import formataddr
-
import base64
import re
@@ -68,7 +66,7 @@ class MailComposer(models.TransientModel):
elif 'email_from' not in result:
author = self.env['res.partner'].browse(result['author_id'])
if author.email:
- result['email_from'] = formataddr((author.name, author.email))
+ result['email_from'] = tools.formataddr((author.name, author.email))
# v6.1 compatibility mode
result['composition_mode'] = result.get('composition_mode', self._context.get('mail.compose.message.mode', 'comment'))
diff --git a/addons/maintenance/tests/test_maintenance.py b/addons/maintenance/tests/test_maintenance.py
index 82909496691..e4decedb583 100644
--- a/addons/maintenance/tests/test_maintenance.py
+++ b/addons/maintenance/tests/test_maintenance.py
@@ -68,13 +68,13 @@ class TestEquipment(TransactionCase):
assert maintenance_request_01, "Maintenance Request not created"
# I check that Initially maintenance request is in the "New Request" stage
- self.assertEquals(maintenance_request_01.stage_id.id, self.ref('maintenance.stage_0'))
+ self.assertEqual(maintenance_request_01.stage_id.id, self.ref('maintenance.stage_0'))
# I check that change the maintenance_request stage on click statusbar
maintenance_request_01.with_user(self.user).write({'stage_id': self.ref('maintenance.stage_1')})
# I check that maintenance request is in the "In Progress" stage
- self.assertEquals(maintenance_request_01.stage_id.id, self.ref('maintenance.stage_1'))
+ self.assertEqual(maintenance_request_01.stage_id.id, self.ref('maintenance.stage_1'))
def test_20_cron(self):
""" Check the cron creates the necessary preventive maintenance requests"""
diff --git a/addons/maintenance/tests/test_maintenance_multicompany.py b/addons/maintenance/tests/test_maintenance_multicompany.py
index f535e7f4ce3..7c615921f4b 100644
--- a/addons/maintenance/tests/test_maintenance_multicompany.py
+++ b/addons/maintenance/tests/test_maintenance_multicompany.py
@@ -98,8 +98,8 @@ class TestEquipmentMulticompany(TransactionCase):
})
# Check category for user equipment_manager and user
- self.assertEquals(Category.with_user(equipment_manager).with_context(allowed_company_ids=cids).search_count([]), 3)
- self.assertEquals(Category.with_user(user).search_count([]), 2)
+ self.assertEqual(Category.with_user(equipment_manager).with_context(allowed_company_ids=cids).search_count([]), 3)
+ self.assertEqual(Category.with_user(user).search_count([]), 2)
# User should not able to create equipment.
with self.assertRaises(AccessError):
@@ -128,10 +128,10 @@ class TestEquipmentMulticompany(TransactionCase):
'owner_user_id': equipment_manager.id,
})
# Now there are total 2 equipments created and can view by equipment_manager user
- self.assertEquals(Equipment.with_user(equipment_manager).with_context(allowed_company_ids=cids).search_count([]), 2)
+ self.assertEqual(Equipment.with_user(equipment_manager).with_context(allowed_company_ids=cids).search_count([]), 2)
# And there is total 1 equipment can be view by Normal User ( Which user is followers)
- self.assertEquals(Equipment.with_user(user).search_count([]), 1)
+ self.assertEqual(Equipment.with_user(user).search_count([]), 1)
# create an equipment team BY user
with self.assertRaises(AccessError):
@@ -171,5 +171,5 @@ class TestEquipmentMulticompany(TransactionCase):
})
# Now here is total 1 maintenance request can be view by Normal User
- self.assertEquals(MaintenanceRequest.with_user(equipment_manager).with_context(allowed_company_ids=cids).search_count([]), 2)
- self.assertEquals(MaintenanceRequest.with_user(user).search_count([]), 1)
+ self.assertEqual(MaintenanceRequest.with_user(equipment_manager).with_context(allowed_company_ids=cids).search_count([]), 2)
+ self.assertEqual(MaintenanceRequest.with_user(user).search_count([]), 1)
diff --git a/addons/mass_mailing/models/mail_thread.py b/addons/mass_mailing/models/mail_thread.py
index 77ff580d594..86683a7e540 100644
--- a/addons/mass_mailing/models/mail_thread.py
+++ b/addons/mass_mailing/models/mail_thread.py
@@ -4,7 +4,6 @@
import datetime
from odoo import api, models, fields
-from odoo.tools import decode_smtp_header
BLACKLIST_MAX_BOUNCED_LIMIT = 5
@@ -19,7 +18,7 @@ class MailThread(models.AbstractModel):
by using the References header of the incoming message and looking for
matching message_id in mailing.trace. """
if message.get('References') and routes:
- message_ids = [x.strip() for x in decode_smtp_header(message['References']).split()]
+ message_ids = [x.strip() for x in message['References'].split()]
self.env['mailing.trace'].set_opened(mail_message_ids=message_ids)
self.env['mailing.trace'].set_replied(mail_message_ids=message_ids)
return super(MailThread, self)._message_route_process(message, message_dict, routes)
diff --git a/addons/mass_mailing/views/mailing_mailing_views.xml b/addons/mass_mailing/views/mailing_mailing_views.xml
index 133a54ffe27..ad6e43ebf8f 100644
--- a/addons/mass_mailing/views/mailing_mailing_views.xml
+++ b/addons/mass_mailing/views/mailing_mailing_views.xml
@@ -142,7 +142,7 @@
<div>
<div class="row">
<div class="col-xs-12 col-md-3" >
- <field name="mailing_model_id" widget="selection"
+ <field name="mailing_model_id" widget="selection"
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
</div>
<div attrs="{'invisible': [('mailing_model_name', '!=', 'mailing.list')]}" class="col-xs-12 col-md-9 pt-1">
@@ -163,7 +163,7 @@
'invisible': [('mailing_model_name', '=', 'mailing.list')],
'readonly': [('state', 'in', ('sending', 'done'))]
}">
- </field>
+ </field>
</div>
</group>
<notebook>
@@ -197,7 +197,7 @@
}"/>
<field name="reply_to"
attrs="{
- 'required': [('reply_to_mode', '=', 'email')],
+ 'required': [('reply_to_mode', '=', 'email')],
'invisible': [('reply_to_mode', '=', 'thread')],
'readonly': [('state', 'in', ('sending', 'done'))]
}"/>
@@ -229,7 +229,7 @@
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
<label for="contact_ab_pc" groups="mass_mailing.group_mass_mailing_campaign"/>
<div groups="mass_mailing.group_mass_mailing_campaign">
- <field name="contact_ab_pc"
+ <field name="contact_ab_pc"
class="oe_inline"
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/> %
</div>
diff --git a/addons/mass_mailing/views/utm_campaign_views.xml b/addons/mass_mailing/views/utm_campaign_views.xml
index 997841ec679..82f08f4ec4b 100644
--- a/addons/mass_mailing/views/utm_campaign_views.xml
+++ b/addons/mass_mailing/views/utm_campaign_views.xml
@@ -117,10 +117,10 @@
parent="mass_mailing_configuration" sequence="1"
groups="mass_mailing.group_mass_mailing_campaign"
action="utm.action_view_utm_stage"/>
-
+
<menuitem id="mass_mailing_tag_menu"
parent="mass_mailing_configuration"
action="utm.action_view_utm_tag"
sequence="2"
groups="mass_mailing.group_mass_mailing_campaign"/>
-</odoo> \ No newline at end of file
+</odoo>
diff --git a/addons/mass_mailing_sms/views/mailing_mailing_views.xml b/addons/mass_mailing_sms/views/mailing_mailing_views.xml
index 0b8d47637f1..a95361f397f 100644
--- a/addons/mass_mailing_sms/views/mailing_mailing_views.xml
+++ b/addons/mass_mailing_sms/views/mailing_mailing_views.xml
@@ -87,7 +87,7 @@
<!-- Form -->
<xpath expr="//field[@name='subject']" position="attributes">
<attribute name="attrs">{
- 'invisible': [('mailing_type', '!=', 'mail')],
+ 'invisible': [('mailing_type', '!=', 'mail')],
'readonly': [('state', 'in', ('sending', 'done'))]}
</attribute>
</xpath>
@@ -105,7 +105,7 @@
<!-- Option page tweaks -->
<xpath expr="//field[@name='email_from']" position="attributes">
<attribute name="attrs">{
- 'invisible': [('mailing_type', '!=', 'mail')],
+ 'invisible': [('mailing_type', '!=', 'mail')],
'readonly': [('state', 'in', ('sending', 'done'))]}
</attribute>
</xpath>
@@ -123,7 +123,7 @@
</xpath>
<xpath expr="//field[@name='mail_server_id']" position="attributes">
<attribute name="attrs">{
- 'invisible': [('mailing_type', '!=', 'mail')],
+ 'invisible': [('mailing_type', '!=', 'mail')],
'readonly': [('state', 'in', ('sending', 'done'))]}
</attribute>
</xpath>
diff --git a/addons/mass_mailing_sms/views/mailing_sms_menus.xml b/addons/mass_mailing_sms/views/mailing_sms_menus.xml
index f009c9df330..8773241b880 100644
--- a/addons/mass_mailing_sms/views/mailing_sms_menus.xml
+++ b/addons/mass_mailing_sms/views/mailing_sms_menus.xml
@@ -2,10 +2,10 @@
<odoo>
<!-- SMS Marketing -->
<menuitem id="mass_mailing_sms_menu_root"
- name="SMS Marketing"
- sequence="60"
- web_icon="mass_mailing_sms,static/description/icon.png"
- groups="mass_mailing.group_mass_mailing_user"/>
+ name="SMS Marketing"
+ sequence="60"
+ web_icon="mass_mailing_sms,static/description/icon.png"
+ groups="mass_mailing.group_mass_mailing_user"/>
<!-- SMS Marketing / SMS Marketing -->
<menuitem id="mass_mailing_sms_menu_mass_sms"
@@ -48,9 +48,9 @@
groups="mass_mailing.group_mass_mailing_user"/>
<!-- SMS Marketing / Configuration / Blacklist -->
<menuitem id="phone_blacklist_menu"
- name="Phone Blacklist"
- parent="mass_mailing_sms_menu_configuration"
- sequence="1"
+ name="Phone Blacklist"
+ parent="mass_mailing_sms_menu_configuration"
+ sequence="1"
action="phone_validation.phone_blacklist_action"
groups="mass_mailing.group_mass_mailing_user"/>
<!-- SMS Marketing / Configuration / Link Tracker -->
@@ -60,4 +60,4 @@
sequence="2"
action="link_tracker.link_tracker_action"
groups="mass_mailing.group_mass_mailing_user"/>
-</odoo> \ No newline at end of file
+</odoo>
diff --git a/addons/mass_mailing_sms/wizard/mailing_sms_test_views.xml b/addons/mass_mailing_sms/wizard/mailing_sms_test_views.xml
index 8224f8ae1fb..db0d6bf84a7 100644
--- a/addons/mass_mailing_sms/wizard/mailing_sms_test_views.xml
+++ b/addons/mass_mailing_sms/wizard/mailing_sms_test_views.xml
@@ -6,7 +6,7 @@
<field name="arch" type="xml">
<form string="Send a Sample SMS">
<p class="text-muted">
- Send a sample SMS for testing purpose to the numbers below (comma-separated list).
+ Send a sample SMS for testing purpose to the numbers below (comma-separated list).
</p>
<group>
<field name="numbers" placeholder="+32 495 85 85 77, +33 545 55 55 55"/>
diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py
index 0e777b64243..d24ad6d9504 100644
--- a/addons/mrp/models/mrp_bom.py
+++ b/addons/mrp/models/mrp_bom.py
@@ -413,4 +413,3 @@ class MrpByProduct(models.Model):
}
self.product_uom_id = self.product_id.uom_id.id
return res
-
diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py
index d72caa41b24..d0da5edac4b 100644
--- a/addons/mrp/models/mrp_production.py
+++ b/addons/mrp/models/mrp_production.py
@@ -1024,4 +1024,3 @@ class MrpProduction(models.Model):
return self.env.ref('mrp.exception_on_mo').render(values=values)
self.env['stock.picking']._log_activity(_render_note_exception_quantity_mo, documents)
-
diff --git a/addons/mrp/models/mrp_routing.py b/addons/mrp/models/mrp_routing.py
index 43b9965694b..ca3a4645f7a 100644
--- a/addons/mrp/models/mrp_routing.py
+++ b/addons/mrp/models/mrp_routing.py
@@ -97,4 +97,3 @@ class MrpRoutingWorkcenter(models.Model):
count_data = dict((item['operation_id'][0], item['operation_id_count']) for item in data)
for operation in self:
operation.workorder_count = count_data.get(operation.id, 0)
-
diff --git a/addons/mrp/models/mrp_unbuild.py b/addons/mrp/models/mrp_unbuild.py
index 3f8b10588d1..b767e71cf1b 100644
--- a/addons/mrp/models/mrp_unbuild.py
+++ b/addons/mrp/models/mrp_unbuild.py
@@ -267,4 +267,3 @@ class MrpUnbuild(models.Model):
},
'target': 'new'
}
-
diff --git a/addons/mrp/models/mrp_workcenter.py b/addons/mrp/models/mrp_workcenter.py
index b7b9fe0cdb0..8743ff5f3aa 100644
--- a/addons/mrp/models/mrp_workcenter.py
+++ b/addons/mrp/models/mrp_workcenter.py
@@ -285,4 +285,3 @@ class MrpWorkcenterProductivity(models.Model):
def button_block(self):
self.ensure_one()
self.workcenter_id.order_ids.end_all()
-
diff --git a/addons/mrp/models/res_company.py b/addons/mrp/models/res_company.py
index 452d715ab3c..bbfdfd84d14 100644
--- a/addons/mrp/models/res_company.py
+++ b/addons/mrp/models/res_company.py
@@ -36,4 +36,3 @@ class Company(models.Model):
def _create_per_company_sequences(self):
super(Company, self)._create_per_company_sequences()
self._create_unbuild_sequence()
-
diff --git a/addons/mrp/tests/test_multicompany.py b/addons/mrp/tests/test_multicompany.py
index 56227363e58..5b43be07224 100644
--- a/addons/mrp/tests/test_multicompany.py
+++ b/addons/mrp/tests/test_multicompany.py
@@ -113,7 +113,7 @@ class TestMrpMulticompany(common.TransactionCase):
def test_product_produce_1(self):
"""Check that using a finished lot of company b in the produce wizard of a production
of company a is not allowed """
-
+
product = self.env['product.product'].create({
'name': 'p1',
'tracking': 'lot',
diff --git a/addons/mrp/tests/test_procurement.py b/addons/mrp/tests/test_procurement.py
index 789985930ed..ce8c8baf4d6 100644
--- a/addons/mrp/tests/test_procurement.py
+++ b/addons/mrp/tests/test_procurement.py
@@ -116,7 +116,7 @@ class TestProcurement(TestMrpCommon):
# set the product of `self.bom_1` to this child category
for bom_line_id in self.bom_1.bom_line_ids:
# check that no routes are defined on the product
- self.assertEquals(len(bom_line_id.product_id.route_ids), 0)
+ self.assertEqual(len(bom_line_id.product_id.route_ids), 0)
# set the category of the product to a child category
bom_line_id.product_id.categ_id = child_categ_id
diff --git a/addons/mrp/tests/test_stock.py b/addons/mrp/tests/test_stock.py
index 9a58cdc22cb..c80e2bdf420 100644
--- a/addons/mrp/tests/test_stock.py
+++ b/addons/mrp/tests/test_stock.py
@@ -348,9 +348,9 @@ class TestKitPicking(common.TestMrpCommon):
picking.button_validate()
# We check that the picking has the correct quantities after its move were splitted.
- self.assertEquals(len(picking.move_lines), 7)
+ self.assertEqual(len(picking.move_lines), 7)
for move_line in picking.move_lines:
- self.assertEquals(move_line.quantity_done, self.expected_quantities[move_line.product_id])
+ self.assertEqual(move_line.quantity_done, self.expected_quantities[move_line.product_id])
def test_kit_planned_transfer(self):
""" Make sure a kit is split in the corrects product_qty by components in case of a
@@ -376,6 +376,6 @@ class TestKitPicking(common.TestMrpCommon):
picking.action_confirm()
# We check that the picking has the correct quantities after its move were splitted.
- self.assertEquals(len(picking.move_lines), 7)
+ self.assertEqual(len(picking.move_lines), 7)
for move_line in picking.move_lines:
- self.assertEquals(move_line.product_qty, self.expected_quantities[move_line.product_id])
+ self.assertEqual(move_line.product_qty, self.expected_quantities[move_line.product_id])
diff --git a/addons/mrp/tests/test_warehouse_multistep_manufacturing.py b/addons/mrp/tests/test_warehouse_multistep_manufacturing.py
index 000701be638..7402d2495b3 100644
--- a/addons/mrp/tests/test_warehouse_multistep_manufacturing.py
+++ b/addons/mrp/tests/test_warehouse_multistep_manufacturing.py
@@ -252,6 +252,6 @@ class TestMultistepManufacturingWarehouse(TestMrpCommon):
self.assertEqual(move_stock_postprod.state, 'waiting')
move_stock_preprod._action_cancel()
- self.assertEquals(production_order.state, 'confirmed')
+ self.assertEqual(production_order.state, 'confirmed')
production_order.action_cancel()
self.assertTrue(move_stock_postprod.state, 'cancel')
diff --git a/addons/mrp/tests/test_workorder_operation.py b/addons/mrp/tests/test_workorder_operation.py
index 68635a5bb21..e4efa917e7c 100644
--- a/addons/mrp/tests/test_workorder_operation.py
+++ b/addons/mrp/tests/test_workorder_operation.py
@@ -539,8 +539,8 @@ class TestWorkOrderProcess(TestMrpCommon):
charger_done_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_charger.id and x.state == 'done')
keybord_done_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_keybord.id and x.state == 'done')
- self.assertEquals(charger_done_move.product_uom_qty, 12)
- self.assertEquals(keybord_done_move.product_uom_qty, 12)
+ self.assertEqual(charger_done_move.product_uom_qty, 12)
+ self.assertEqual(keybord_done_move.product_uom_qty, 12)
# Produce remaining 4 quantity
# ----------------------------
@@ -552,13 +552,13 @@ class TestWorkOrderProcess(TestMrpCommon):
laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id, 'company_id': self.env.company.id})
produce_form.finished_lot_id = laptop_lot_002
product_consume = produce_form.save()
- self.assertEquals(len(product_consume._workorder_line_ids()), 2)
+ self.assertEqual(len(product_consume._workorder_line_ids()), 2)
product_consume._workorder_line_ids()[0].qty_done = 8
product_consume.do_produce()
charger_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_charger.id and x.state != 'done')
keybord_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_keybord.id and x.state !='done')
- self.assertEquals(charger_move.quantity_done, 8, "Wrong consumed quantity of %s" % charger_move.product_id.name)
- self.assertEquals(keybord_move.quantity_done, 8, "Wrong consumed quantity of %s" % keybord_move.product_id.name)
+ self.assertEqual(charger_move.quantity_done, 8, "Wrong consumed quantity of %s" % charger_move.product_id.name)
+ self.assertEqual(keybord_move.quantity_done, 8, "Wrong consumed quantity of %s" % keybord_move.product_id.name)
# Post Inventory of production order.
mo_custom_laptop.post_inventory()
@@ -690,7 +690,7 @@ class TestWorkOrderProcess(TestMrpCommon):
produce_form.finished_lot_id = lot_a
product_consume = produce_form.save()
# laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id})
- self.assertEquals(len(product_consume._workorder_line_ids()), 2)
+ self.assertEqual(len(product_consume._workorder_line_ids()), 2)
product_consume._workorder_line_ids().filtered(lambda x: x.product_id == product_C).write({'qty_done': 3000})
product_consume._workorder_line_ids().filtered(lambda x: x.product_id == product_B).write({'qty_done': 20})
product_consume.do_produce()
diff --git a/addons/mrp_account/tests/test_bom_price.py b/addons/mrp_account/tests/test_bom_price.py
index c91f6203bca..1273e3ecdba 100644
--- a/addons/mrp_account/tests/test_bom_price.py
+++ b/addons/mrp_account/tests/test_bom_price.py
@@ -178,10 +178,10 @@ class TestBom(common.TransactionCase):
self.assertEqual(self.dining_table.standard_price, 1000, "Initial price of the Product should be 1000")
self.dining_table.button_bom_cost()
# Total cost of Dining Table = (550) + Total cost of operations (125) = 675.0
- self.assertEquals(float_round(self.dining_table.standard_price, precision_digits=2), 675.0, "After computing price from BoM price should be 612.5")
+ self.assertEqual(float_round(self.dining_table.standard_price, precision_digits=2), 675.0, "After computing price from BoM price should be 612.5")
self.Product.browse([self.dining_table.id, self.table_head.id]).action_bom_cost()
# Total cost of Dining Table = (718.75) + Total cost of all operations (125 + 10.42) = 854.17
- self.assertEquals(float_compare(self.dining_table.standard_price, 854.17, precision_digits=2), 0, "After computing price from BoM price should be 786.46")
+ self.assertEqual(float_compare(self.dining_table.standard_price, 854.17, precision_digits=2), 0, "After computing price from BoM price should be 786.46")
def test_01_compute_price_inventory_valuation(self):
"""Test update cost from bom in list view when inventory valuation is real time."""
diff --git a/addons/mrp_subcontracting/models/mrp_bom.py b/addons/mrp_subcontracting/models/mrp_bom.py
index 91271404059..3375653d737 100644
--- a/addons/mrp_subcontracting/models/mrp_bom.py
+++ b/addons/mrp_subcontracting/models/mrp_bom.py
@@ -17,4 +17,3 @@ class MrpBom(models.Model):
return self.search(domain, order='sequence, product_id', limit=1)
else:
return self.env['mrp.bom']
-
diff --git a/addons/mrp_subcontracting/models/stock_move.py b/addons/mrp_subcontracting/models/stock_move.py
index 264f8efe1ea..9cb940781cf 100644
--- a/addons/mrp_subcontracting/models/stock_move.py
+++ b/addons/mrp_subcontracting/models/stock_move.py
@@ -188,4 +188,3 @@ operations.""") % ('\n'.join(overprocessed_moves.mapped('product_id.display_name
'mo_id': production.id,
'product_qty': move.product_uom_qty
}).change_prod_qty()
-
diff --git a/addons/mrp_subcontracting/tests/test_subcontracting.py b/addons/mrp_subcontracting/tests/test_subcontracting.py
index 41ffda20acd..785599cff5c 100644
--- a/addons/mrp_subcontracting/tests/test_subcontracting.py
+++ b/addons/mrp_subcontracting/tests/test_subcontracting.py
@@ -46,7 +46,7 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
self.assertEqual(len(mo), 1)
self.assertEqual(len(mo.picking_ids), 0)
wh = picking_receipt.picking_type_id.warehouse_id
- self.assertEquals(mo.picking_type_id, wh.subcontracting_type_id)
+ self.assertEqual(mo.picking_type_id, wh.subcontracting_type_id)
self.assertFalse(mo.picking_type_id.active)
# Create a RR
@@ -64,18 +64,18 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
self.env['procurement.group'].run_scheduler()
picking = self.env['stock.picking'].search([('group_id', '=', pg1.id)])
self.assertEqual(len(picking), 1)
- self.assertEquals(picking.picking_type_id, wh.out_type_id)
+ self.assertEqual(picking.picking_type_id, wh.out_type_id)
picking_receipt.move_lines.quantity_done = 1
picking_receipt.button_validate()
- self.assertEquals(mo.state, 'done')
+ self.assertEqual(mo.state, 'done')
# Available quantities should be negative at the subcontracting location for each components
avail_qty_comp1 = self.env['stock.quant']._get_available_quantity(self.comp1, self.subcontractor_partner1.property_stock_subcontractor, allow_negative=True)
avail_qty_comp2 = self.env['stock.quant']._get_available_quantity(self.comp2, self.subcontractor_partner1.property_stock_subcontractor, allow_negative=True)
avail_qty_finished = self.env['stock.quant']._get_available_quantity(self.finished, wh.lot_stock_id)
- self.assertEquals(avail_qty_comp1, -1)
- self.assertEquals(avail_qty_comp2, -1)
- self.assertEquals(avail_qty_finished, 1)
+ self.assertEqual(avail_qty_comp1, -1)
+ self.assertEqual(avail_qty_comp2, -1)
+ self.assertEqual(avail_qty_finished, 1)
# Ensure returns to subcontractor location
return_form = Form(self.env['stock.return.picking'].with_context(active_id=picking_receipt.id, active_model='stock.picking'))
@@ -126,16 +126,16 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
# Pickings should directly be created
mo = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)])
self.assertEqual(len(mo.picking_ids), 1)
- self.assertEquals(mo.state, 'confirmed')
+ self.assertEqual(mo.state, 'confirmed')
self.assertEqual(len(mo.picking_ids.move_lines), 2)
picking = mo.picking_ids
wh = picking.picking_type_id.warehouse_id
# The picking should be a delivery order
- self.assertEquals(picking.picking_type_id, wh.out_type_id)
+ self.assertEqual(picking.picking_type_id, wh.out_type_id)
- self.assertEquals(mo.picking_type_id, wh.subcontracting_type_id)
+ self.assertEqual(mo.picking_type_id, wh.subcontracting_type_id)
self.assertFalse(mo.picking_type_id.active)
# No manufacturing order for `self.comp2`
@@ -144,15 +144,15 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
picking_receipt.move_lines.quantity_done = 1
picking_receipt.button_validate()
- self.assertEquals(mo.state, 'done')
+ self.assertEqual(mo.state, 'done')
# Available quantities should be negative at the subcontracting location for each components
avail_qty_comp1 = self.env['stock.quant']._get_available_quantity(self.comp1, self.subcontractor_partner1.property_stock_subcontractor, allow_negative=True)
avail_qty_comp2 = self.env['stock.quant']._get_available_quantity(self.comp2, self.subcontractor_partner1.property_stock_subcontractor, allow_negative=True)
avail_qty_finished = self.env['stock.quant']._get_available_quantity(self.finished, wh.lot_stock_id)
- self.assertEquals(avail_qty_comp1, -1)
- self.assertEquals(avail_qty_comp2, -1)
- self.assertEquals(avail_qty_finished, 1)
+ self.assertEqual(avail_qty_comp1, -1)
+ self.assertEqual(avail_qty_comp2, -1)
+ self.assertEqual(avail_qty_finished, 1)
avail_qty_comp1_in_global_location = self.env['stock.quant']._get_available_quantity(self.comp1, self.env.company.subcontracting_location_id, allow_negative=True)
avail_qty_comp2_in_global_location = self.env['stock.quant']._get_available_quantity(self.comp2, self.env.company.subcontracting_location_id, allow_negative=True)
@@ -190,19 +190,19 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
# Pickings should directly be created
mo = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)])
- self.assertEquals(mo.state, 'confirmed')
+ self.assertEqual(mo.state, 'confirmed')
picking_delivery = mo.picking_ids
self.assertEqual(len(picking_delivery), 1)
self.assertEqual(len(picking_delivery.move_lines), 2)
- self.assertEquals(picking_delivery.origin, picking_receipt.name)
- self.assertEquals(picking_delivery.partner_id, picking_receipt.partner_id)
+ self.assertEqual(picking_delivery.origin, picking_receipt.name)
+ self.assertEqual(picking_delivery.partner_id, picking_receipt.partner_id)
# The picking should be a delivery order
wh = picking_receipt.picking_type_id.warehouse_id
- self.assertEquals(mo.picking_ids.picking_type_id, wh.out_type_id)
+ self.assertEqual(mo.picking_ids.picking_type_id, wh.out_type_id)
- self.assertEquals(mo.picking_type_id, wh.subcontracting_type_id)
+ self.assertEqual(mo.picking_type_id, wh.subcontracting_type_id)
self.assertFalse(mo.picking_type_id.active)
# As well as a manufacturing order for `self.comp2`
@@ -210,15 +210,15 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
self.assertEqual(len(comp2mo), 1)
picking_receipt.move_lines.quantity_done = 1
picking_receipt.button_validate()
- self.assertEquals(mo.state, 'done')
+ self.assertEqual(mo.state, 'done')
# Available quantities should be negative at the subcontracting location for each components
avail_qty_comp1 = self.env['stock.quant']._get_available_quantity(self.comp1, self.subcontractor_partner1.property_stock_subcontractor, allow_negative=True)
avail_qty_comp2 = self.env['stock.quant']._get_available_quantity(self.comp2, self.subcontractor_partner1.property_stock_subcontractor, allow_negative=True)
avail_qty_finished = self.env['stock.quant']._get_available_quantity(self.finished, wh.lot_stock_id)
- self.assertEquals(avail_qty_comp1, -1)
- self.assertEquals(avail_qty_comp2, -1)
- self.assertEquals(avail_qty_finished, 1)
+ self.assertEqual(avail_qty_comp1, -1)
+ self.assertEqual(avail_qty_comp2, -1)
+ self.assertEqual(avail_qty_finished, 1)
def test_flow_4(self):
""" Tick "Manufacture" and "MTO" on the components and trigger the
@@ -254,7 +254,7 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
# Pickings should directly be created
mo = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)])
- self.assertEquals(mo.state, 'confirmed')
+ self.assertEqual(mo.state, 'confirmed')
picking_delivery = mo.picking_ids
self.assertFalse(picking_delivery)
@@ -340,10 +340,10 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon):
mo_pick1 = picking_receipt1.move_lines.mapped('move_orig_ids.production_id')
mo_pick2 = picking_receipt2.move_lines.mapped('move_orig_ids.production_id')
- self.assertEquals(len(mo_pick1), 1)
- self.assertEquals(len(mo_pick2), 1)
- self.assertEquals(mo_pick1.bom_id, self.bom)
- self.assertEquals(mo_pick2.bom_id, bom2)
+ self.assertEqual(len(mo_pick1), 1)
+ self.assertEqual(len(mo_pick2), 1)
+ self.assertEqual(mo_pick1.bom_id, self.bom)
+ self.assertEqual(mo_pick2.bom_id, bom2)
def test_flow_6(self):
""" Extra quantity on the move.
@@ -603,10 +603,10 @@ class TestSubcontractingTracking(TransactionCase):
# Check the created manufacturing order
mo = self.env['mrp.production'].search([('bom_id', '=', self.bom_tracked.id)])
self.assertEqual(len(mo), 1)
- self.assertEquals(mo.state, 'confirmed')
+ self.assertEqual(mo.state, 'confirmed')
self.assertEqual(len(mo.picking_ids), 0)
wh = picking_receipt.picking_type_id.warehouse_id
- self.assertEquals(mo.picking_type_id, wh.subcontracting_type_id)
+ self.assertEqual(mo.picking_type_id, wh.subcontracting_type_id)
self.assertFalse(mo.picking_type_id.active)
# Create a RR
@@ -624,7 +624,7 @@ class TestSubcontractingTracking(TransactionCase):
self.env['procurement.group'].run_scheduler()
picking = self.env['stock.picking'].search([('group_id', '=', pg1.id)])
self.assertEqual(len(picking), 1)
- self.assertEquals(picking.picking_type_id, wh.out_type_id)
+ self.assertEqual(picking.picking_type_id, wh.out_type_id)
lot_id = self.env['stock.production.lot'].create({
'name': 'lot1',
@@ -651,12 +651,12 @@ class TestSubcontractingTracking(TransactionCase):
picking_receipt.move_lines.quantity_done = 1
picking_receipt.move_lines.move_line_ids.lot_id = lot_id.id
picking_receipt.button_validate()
- self.assertEquals(mo.state, 'done')
+ self.assertEqual(mo.state, 'done')
# Available quantities should be negative at the subcontracting location for each components
avail_qty_comp1 = self.env['stock.quant']._get_available_quantity(self.comp1_sn, self.subcontractor_partner1.property_stock_subcontractor, allow_negative=True)
avail_qty_comp2 = self.env['stock.quant']._get_available_quantity(self.comp2, self.subcontractor_partner1.property_stock_subcontractor, allow_negative=True)
avail_qty_finished = self.env['stock.quant']._get_available_quantity(self.finished_lot, wh.lot_stock_id)
- self.assertEquals(avail_qty_comp1, -1)
- self.assertEquals(avail_qty_comp2, -1)
- self.assertEquals(avail_qty_finished, 1)
+ self.assertEqual(avail_qty_comp1, -1)
+ self.assertEqual(avail_qty_comp2, -1)
+ self.assertEqual(avail_qty_finished, 1)
diff --git a/addons/mrp_subcontracting/wizard/mrp_product_produce.py b/addons/mrp_subcontracting/wizard/mrp_product_produce.py
index db386fd0648..368cfa90df7 100644
--- a/addons/mrp_subcontracting/wizard/mrp_product_produce.py
+++ b/addons/mrp_subcontracting/wizard/mrp_product_produce.py
@@ -53,4 +53,3 @@ class MrpProductProduce(models.TransientModel):
ml.product_uom_qty = ml.qty_done
self.subcontract_move_id._recompute_state()
return res
-
diff --git a/addons/payment_stripe/models/payment.py b/addons/payment_stripe/models/payment.py
index 9272f194cbf..4e68d122bc1 100644
--- a/addons/payment_stripe/models/payment.py
+++ b/addons/payment_stripe/models/payment.py
@@ -246,7 +246,7 @@ class PaymentTransactionStripe(models.Model):
return True
else:
error = tree.get('failure_message')
- _logger.warn(error)
+ _logger.warning(error)
vals.update({'state_message': error})
self.write(vals)
self._set_transaction_cancel()
diff --git a/addons/point_of_sale/static/src/js/printers.js b/addons/point_of_sale/static/src/js/printers.js
index 1b322697285..0777e2679b1 100644
--- a/addons/point_of_sale/static/src/js/printers.js
+++ b/addons/point_of_sale/static/src/js/printers.js
@@ -34,7 +34,7 @@ var PrinterMixin = {
/**
* Generate a jpeg image from a canvas
- * @param {DOMElement} canvas
+ * @param {DOMElement} canvas
*/
process_canvas: function (canvas) {
return canvas.toDataURL('image/jpeg').replace('data:image/jpeg;base64,','');
@@ -64,7 +64,7 @@ var PrinterMixin = {
onrendered: function (canvas) {
$('.pos-receipts').empty();
resolve(self.process_canvas(canvas));
- }
+ }
})
});
return promise;
diff --git a/addons/pos_cache/models/pos_cache.py b/addons/pos_cache/models/pos_cache.py
index 8987907f981..74e2a99019b 100644
--- a/addons/pos_cache/models/pos_cache.py
+++ b/addons/pos_cache/models/pos_cache.py
@@ -30,7 +30,7 @@ class pos_cache(models.Model):
display_default_code=False, lang=cache.compute_user_id.lang)
res = prod_ctx.read(cache.get_product_fields())
cache.write({
- 'cache': base64.encodestring(json.dumps(res).encode('utf-8')),
+ 'cache': base64.encodebytes(json.dumps(res).encode('utf-8')),
})
@api.model
@@ -48,7 +48,7 @@ class pos_cache(models.Model):
self.product_fields = str(fields)
self.refresh_cache()
- return json.loads(base64.decodestring(self.cache).decode('utf-8'))
+ return json.loads(base64.decodebytes(self.cache).decode('utf-8'))
class pos_config(models.Model):
diff --git a/addons/pos_restaurant/static/src/js/floors.js b/addons/pos_restaurant/static/src/js/floors.js
index b36a169312f..148e667d624 100644
--- a/addons/pos_restaurant/static/src/js/floors.js
+++ b/addons/pos_restaurant/static/src/js/floors.js
@@ -926,7 +926,7 @@ models.PosModel = models.PosModel.extend({
self.set_table(table);
}
});
- } else {
+ } else {
if (ids_to_remove.length) {
self.remove_from_server_and_set_sync_state(ids_to_remove);
}
diff --git a/addons/pos_restaurant/static/src/js/tours/pos_restaurant.js b/addons/pos_restaurant/static/src/js/tours/pos_restaurant.js
index 37e3cc1adc2..a59f2d6caaf 100644
--- a/addons/pos_restaurant/static/src/js/tours/pos_restaurant.js
+++ b/addons/pos_restaurant/static/src/js/tours/pos_restaurant.js
@@ -180,7 +180,7 @@ odoo.define('pos_reataurant.tour.synchronized_table_management', function (requi
}];
steps = steps.concat(open_table('T5', 1));
steps = steps.concat(verify_order_total('4.40'));
-
+
// Test transfering an order
steps = steps.concat(transfer_order_to_table('T4', '002-0001'));
diff --git a/addons/project_timesheet_holidays/tests/test_timesheet_holidays.py b/addons/project_timesheet_holidays/tests/test_timesheet_holidays.py
index 6aad4f1e51a..84c80cb133a 100644
--- a/addons/project_timesheet_holidays/tests/test_timesheet_holidays.py
+++ b/addons/project_timesheet_holidays/tests/test_timesheet_holidays.py
@@ -98,11 +98,11 @@ class TestTimesheetHolidays(TestCommonTimesheet):
'number_of_days': number_of_days,
})
holiday.with_user(SUPERUSER_ID).action_validate()
- self.assertEquals(len(holiday.timesheet_ids), number_of_days, 'Number of generated timesheets should be the same as the leave duration (1 per day between %s and %s)' % (fields.Datetime.to_string(self.leave_start_datetime), fields.Datetime.to_string(self.leave_end_datetime)))
+ self.assertEqual(len(holiday.timesheet_ids), number_of_days, 'Number of generated timesheets should be the same as the leave duration (1 per day between %s and %s)' % (fields.Datetime.to_string(self.leave_start_datetime), fields.Datetime.to_string(self.leave_end_datetime)))
# manager refuse the leave
holiday.with_user(SUPERUSER_ID).action_refuse()
- self.assertEquals(len(holiday.timesheet_ids), 0, 'Number of linked timesheets should be zero, since the leave is refused.')
+ self.assertEqual(len(holiday.timesheet_ids), 0, 'Number of linked timesheets should be zero, since the leave is refused.')
def test_validate_without_timesheet(self):
# employee creates a leave request
@@ -116,4 +116,4 @@ class TestTimesheetHolidays(TestCommonTimesheet):
'number_of_days': number_of_days,
})
holiday.with_user(SUPERUSER_ID).action_validate()
- self.assertEquals(len(holiday.timesheet_ids), 0, 'Number of generated timesheets should be zero since the leave type does not generate timesheet')
+ self.assertEqual(len(holiday.timesheet_ids), 0, 'Number of generated timesheets should be zero since the leave type does not generate timesheet')
diff --git a/addons/purchase/tests/test_purchase_order_report.py b/addons/purchase/tests/test_purchase_order_report.py
index 9497348e3ab..163df4bc3ec 100644
--- a/addons/purchase/tests/test_purchase_order_report.py
+++ b/addons/purchase/tests/test_purchase_order_report.py
@@ -67,14 +67,14 @@ class TestPurchaseOrderReport(common.TransactionCase):
('order_id', '=', po.id), ('product_id', '=', self.product1.id)])
# check that report will convert dozen to unit or not
- self.assertEquals(res_product1.qty_ordered, 12.0, 'UoM conversion is not working')
+ self.assertEqual(res_product1.qty_ordered, 12.0, 'UoM conversion is not working')
# report should show in company currency (amount/rate) = (100/2)
- self.assertEquals(res_product1.price_total, 50.0, 'Currency conversion is not working')
+ self.assertEqual(res_product1.price_total, 50.0, 'Currency conversion is not working')
res_product2 = self.PurchaseReport.search([
('order_id', '=', po.id), ('product_id', '=', self.product2.id)])
# Check that repost should show 6 unit of product
- self.assertEquals(res_product2.qty_ordered, 12.0, 'UoM conversion is not working')
+ self.assertEqual(res_product2.qty_ordered, 12.0, 'UoM conversion is not working')
# report should show in company currency (amount/rate) = (200/2)
- self.assertEquals(res_product2.price_total, 100.0, 'Currency conversion is not working')
+ self.assertEqual(res_product2.price_total, 100.0, 'Currency conversion is not working')
diff --git a/addons/purchase_mrp/tests/test_purchase_mrp_flow.py b/addons/purchase_mrp/tests/test_purchase_mrp_flow.py
index ea99478a93b..03f17161fe8 100644
--- a/addons/purchase_mrp/tests/test_purchase_mrp_flow.py
+++ b/addons/purchase_mrp/tests/test_purchase_mrp_flow.py
@@ -177,7 +177,7 @@ class TestSaleMrpFlow(TransactionCase):
"""
moves_to_process = moves.filtered(lambda m: m.product_id in quantities_to_process.keys())
for move in moves_to_process:
- self.assertEquals(move.product_uom_qty, quantities_to_process[move.product_id])
+ self.assertEqual(move.product_uom_qty, quantities_to_process[move.product_id])
def _create_move_quantities(self, qty_to_process, components, warehouse):
""" Helper to creates moves in order to update the quantities of components
@@ -237,7 +237,7 @@ class TestSaleMrpFlow(TransactionCase):
# Check picking creation, its move lines should concern
# only components. Also checks that the quantities are corresponding
# to the PO
- self.assertEquals(len(po.picking_ids), 1)
+ self.assertEqual(len(po.picking_ids), 1)
order_line = po.order_line[0]
picking_original = po.picking_ids[0]
move_lines = picking_original.move_lines
@@ -255,7 +255,7 @@ class TestSaleMrpFlow(TransactionCase):
self.component_g: 28.0
}
- self.assertEquals(len(move_lines), 7)
+ self.assertEqual(len(move_lines), 7)
self.assertTrue(not any(kit in products for kit in kits))
self.assertTrue(all(component in products for component in components))
self._assert_quantities(move_lines, expected_quantities)
@@ -269,13 +269,13 @@ class TestSaleMrpFlow(TransactionCase):
backorder_wizard.process()
# Check that a backorded is created
- self.assertEquals(len(po.picking_ids), 2)
+ self.assertEqual(len(po.picking_ids), 2)
backorder_1 = po.picking_ids - picking_original
- self.assertEquals(backorder_1.backorder_id.id, picking_original.id)
+ self.assertEqual(backorder_1.backorder_id.id, picking_original.id)
# Even if some components are received completely,
# no KitParent should be received
- self.assertEquals(order_line.qty_received, 0)
+ self.assertEqual(order_line.qty_received, 0)
# Process just enough components to make 1 kit_parent
qty_to_process = {
@@ -289,12 +289,12 @@ class TestSaleMrpFlow(TransactionCase):
backorder_wizard.process()
# Only 1 kit_parent should be received at this point
- self.assertEquals(order_line.qty_received, 1)
+ self.assertEqual(order_line.qty_received, 1)
# Check that the second backorder is created
- self.assertEquals(len(po.picking_ids), 3)
+ self.assertEqual(len(po.picking_ids), 3)
backorder_2 = po.picking_ids - picking_original - backorder_1
- self.assertEquals(backorder_2.backorder_id.id, backorder_1.id)
+ self.assertEqual(backorder_2.backorder_id.id, backorder_1.id)
# Set the components quantities that backorder_2 should have
expected_quantities = {
@@ -309,7 +309,7 @@ class TestSaleMrpFlow(TransactionCase):
# Check that the computed quantities are matching the theorical ones.
# Since component_e was totally processed, this componenent shouldn't be
# present in backorder_2
- self.assertEquals(len(backorder_2.move_lines), 6)
+ self.assertEqual(len(backorder_2.move_lines), 6)
move_comp_e = backorder_2.move_lines.filtered(lambda m: m.product_id.id == self.component_e.id)
self.assertFalse(move_comp_e)
self._assert_quantities(backorder_2.move_lines, expected_quantities)
@@ -328,12 +328,12 @@ class TestSaleMrpFlow(TransactionCase):
backorder_wizard.process()
# Check that x3 kit_parents are indeed received
- self.assertEquals(order_line.qty_received, 3)
+ self.assertEqual(order_line.qty_received, 3)
# Check that the third backorder is created
- self.assertEquals(len(po.picking_ids), 4)
+ self.assertEqual(len(po.picking_ids), 4)
backorder_3 = po.picking_ids - (picking_original + backorder_1 + backorder_2)
- self.assertEquals(backorder_3.backorder_id.id, backorder_2.id)
+ self.assertEqual(backorder_3.backorder_id.id, backorder_2.id)
# Check the components quantities that backorder_3 should have
expected_quantities = {
@@ -352,7 +352,7 @@ class TestSaleMrpFlow(TransactionCase):
# Validating the last backorder now it's complete.
# All kits should be received
backorder_3.button_validate()
- self.assertEquals(order_line.qty_received, 7.0)
+ self.assertEqual(order_line.qty_received, 7.0)
# Return all components processed by backorder_3
stock_return_picking_form = Form(self.env['stock.return.picking']
@@ -373,7 +373,7 @@ class TestSaleMrpFlow(TransactionCase):
wiz.process()
# Now quantity received should be 3 again
- self.assertEquals(order_line.qty_received, 3)
+ self.assertEqual(order_line.qty_received, 3)
stock_return_picking_form = Form(self.env['stock.return.picking']
.with_context(active_ids=return_pick.ids, active_id=return_pick.ids[0],
@@ -395,14 +395,14 @@ class TestSaleMrpFlow(TransactionCase):
backorder_wizard.process()
# As one of each component is missing, only 6 kit_parents should be received
- self.assertEquals(order_line.qty_received, 6)
+ self.assertEqual(order_line.qty_received, 6)
# Check that the 4th backorder is created.
- self.assertEquals(len(po.picking_ids), 7)
+ self.assertEqual(len(po.picking_ids), 7)
backorder_4 = po.picking_ids - (
picking_original + backorder_1 + backorder_2 + backorder_3 + return_of_return_pick + return_pick)
- self.assertEquals(backorder_4.backorder_id.id, return_of_return_pick.id)
+ self.assertEqual(backorder_4.backorder_id.id, return_of_return_pick.id)
# Check the components quantities that backorder_4 should have
for move in backorder_4.move_lines:
- self.assertEquals(move.product_qty, 1)
+ self.assertEqual(move.product_qty, 1)
diff --git a/addons/purchase_stock/tests/test_create_picking.py b/addons/purchase_stock/tests/test_create_picking.py
index de209d06319..c042167d164 100644
--- a/addons/purchase_stock/tests/test_create_picking.py
+++ b/addons/purchase_stock/tests/test_create_picking.py
@@ -446,7 +446,7 @@ class TestCreatePicking(common.TestProductCommon):
purchase_order_line.write({'date_planned': purchase_order_line.date_planned + timedelta(days=5)})
# Now check scheduled date of delivery order is changed or not.
- self.assertEquals(purchase_order_line.date_planned, delivery_order.scheduled_date,
+ self.assertEqual(purchase_order_line.date_planned, delivery_order.scheduled_date,
'Delivery order schedule date should be changed as we have set date propagate.')
def test_06_no_propagate_date(self):
@@ -466,5 +466,5 @@ class TestCreatePicking(common.TestProductCommon):
purchase_order_line.write({'date_planned': purchase_order_line.date_planned + timedelta(days=5)})
# Now check scheduled date of delivery order is changed or not.
- self.assertNotEquals(purchase_order_line.date_planned, delivery_order.scheduled_date,
+ self.assertNotEqual(purchase_order_line.date_planned, delivery_order.scheduled_date,
'Delivery order schedule date should not changed.')
diff --git a/addons/purchase_stock/tests/test_fifo_price.py b/addons/purchase_stock/tests/test_fifo_price.py
index 7ea86d55720..39f83213cb7 100644
--- a/addons/purchase_stock/tests/test_fifo_price.py
+++ b/addons/purchase_stock/tests/test_fifo_price.py
@@ -47,7 +47,7 @@ class TestFifoPrice(TestPurchase):
purchase_order_1.button_confirm()
# Check the "Purchase" status of purchase order 1
- self.assertEquals(purchase_order_1.state, 'purchase')
+ self.assertEqual(purchase_order_1.state, 'purchase')
# Process the reception of purchase order 1 and set date
picking = purchase_order_1.picking_ids[0]
@@ -55,8 +55,8 @@ class TestFifoPrice(TestPurchase):
# Check the standard price of the product (fifo icecream), that should have not changed
# because the standard price is supposed to be updated only when goods are going out of the stock
- self.assertEquals(product_cable_management_box.standard_price, 70.0, 'Standard price should not have changed')
- self.assertEquals(product_cable_management_box.value_svl, 500.0, 'Wrong stock value')
+ self.assertEqual(product_cable_management_box.standard_price, 70.0, 'Standard price should not have changed')
+ self.assertEqual(product_cable_management_box.value_svl, 500.0, 'Wrong stock value')
# I create a draft Purchase Order for second shipment for 30 kg at 80 euro
purchase_order_2 = self.env['purchase.order'].create({
@@ -79,8 +79,8 @@ class TestFifoPrice(TestPurchase):
# Check the standard price of the product, that should have not changed because the
# standard price is supposed to be updated only when goods are going out of the stock
- self.assertEquals(product_cable_management_box.standard_price, 70.0, 'Standard price as fifo price of second reception incorrect!')
- self.assertEquals(product_cable_management_box.value_svl, 2900.0, 'Stock valuation should be 2900')
+ self.assertEqual(product_cable_management_box.standard_price, 70.0, 'Standard price as fifo price of second reception incorrect!')
+ self.assertEqual(product_cable_management_box.value_svl, 2900.0, 'Stock valuation should be 2900')
# Let us send some goods
outgoing_shipment = self.env['stock.picking'].create({
@@ -313,5 +313,5 @@ class TestFifoPrice(TestPurchase):
self.env['stock.immediate.transfer'].create({'pick_ids': [(4, picking.id)]}).process()
original_out_move = outgoing_shipment_neg.move_lines[0]
- self.assertEquals(original_out_move.product_id.value_svl, 12000.0, 'Value of the move should be 12000')
- self.assertEquals(original_out_move.product_id.qty_available, 150.0, 'Qty available should be 150')
+ self.assertEqual(original_out_move.product_id.value_svl, 12000.0, 'Value of the move should be 12000')
+ self.assertEqual(original_out_move.product_id.qty_available, 150.0, 'Qty available should be 150')
diff --git a/addons/purchase_stock/tests/test_move_cancel_propagation.py b/addons/purchase_stock/tests/test_move_cancel_propagation.py
index 2c7aeda0719..9203927c470 100644
--- a/addons/purchase_stock/tests/test_move_cancel_propagation.py
+++ b/addons/purchase_stock/tests/test_move_cancel_propagation.py
@@ -95,7 +95,7 @@ class TestMoveCancelPropagation(TestPurchase):
purchase_order .button_cancel()
# Check the status of picking after canceling po.
- self.assertEquals(picking_in.state, 'cancel')
+ self.assertEqual(picking_in.state, 'cancel')
self.assertNotEqual(self.picking_out.state, 'cancel')
def test_03_cancel_draft_purchase_order_two_steps(self):
diff --git a/addons/purchase_stock/tests/test_onchange_product.py b/addons/purchase_stock/tests/test_onchange_product.py
index 74361778fd5..3628d75affe 100644
--- a/addons/purchase_stock/tests/test_onchange_product.py
+++ b/addons/purchase_stock/tests/test_onchange_product.py
@@ -72,16 +72,16 @@ class TestOnchangeProductId(TransactionCase):
po_line = po.order_line[0]
po_line.onchange_product_id()
- self.assertEquals(100, po_line.price_unit, "The included tax must be subtracted to the price")
+ self.assertEqual(100, po_line.price_unit, "The included tax must be subtracted to the price")
supplierinfo.write({'min_qty': 24})
po_line.write({'product_qty': 20})
po_line._onchange_quantity()
- self.assertEquals(0, po_line.price_unit, "Unit price should be reset to 0 since the supplier supplies minimum of 24 quantities")
+ self.assertEqual(0, po_line.price_unit, "Unit price should be reset to 0 since the supplier supplies minimum of 24 quantities")
po_line.write({'product_qty': 3, 'product_uom': self.ref("uom.product_uom_dozen")})
po_line._onchange_quantity()
- self.assertEquals(1200, po_line.price_unit, "Unit price should be 1200 for one Dozen")
+ self.assertEqual(1200, po_line.price_unit, "Unit price should be 1200 for one Dozen")
product_ipad = self.env.ref('product.product_product_4')
po_line2 = self.po_line_model.create({
@@ -94,4 +94,4 @@ class TestOnchangeProductId(TransactionCase):
'date_planned': fields.Date().today()
})
po_line2.onchange_product_id()
- self.assertEquals(0, po_line2.price_unit, "No vendor supplies this product, hence unit price should be set to 0")
+ self.assertEqual(0, po_line2.price_unit, "No vendor supplies this product, hence unit price should be set to 0")
diff --git a/addons/purchase_stock/tests/test_stockvaluation.py b/addons/purchase_stock/tests/test_stockvaluation.py
index 9490d218f17..24937f6d1d9 100644
--- a/addons/purchase_stock/tests/test_stockvaluation.py
+++ b/addons/purchase_stock/tests/test_stockvaluation.py
@@ -74,13 +74,13 @@ class TestStockValuation(TransactionCase):
move1 = picking1.move_lines[0]
# the unit price of the purchase order line is copied to the in move
- self.assertEquals(move1.price_unit, 100)
+ self.assertEqual(move1.price_unit, 100)
# update the unit price on the purchase order line
po1.order_line.price_unit = 200
# the unit price on the stock move is not directly updated
- self.assertEquals(move1.price_unit, 100)
+ self.assertEqual(move1.price_unit, 100)
# validate the receipt
res_dict = picking1.button_validate()
@@ -88,9 +88,9 @@ class TestStockValuation(TransactionCase):
wizard.process()
# the unit price of the valuationlayer used the latest value
- self.assertEquals(move1.stock_valuation_layer_ids.unit_cost, 200)
+ self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 200)
- self.assertEquals(self.product1.value_svl, 2000)
+ self.assertEqual(self.product1.value_svl, 2000)
def test_standard_price_change_1(self):
""" Confirm a purchase order and create the associated receipt, change the unit cost of the
@@ -122,13 +122,13 @@ class TestStockValuation(TransactionCase):
# the move's unit price reflects the purchase order line's cost even if it's useless when
# the product's cost method is standard
- self.assertEquals(move1.price_unit, 11)
+ self.assertEqual(move1.price_unit, 11)
# set a new standard price
self.product1.product_tmpl_id.standard_price = 12
# the unit price on the stock move is not directly updated
- self.assertEquals(move1.price_unit, 11)
+ self.assertEqual(move1.price_unit, 11)
# validate the receipt
res_dict = picking1.button_validate()
@@ -136,9 +136,9 @@ class TestStockValuation(TransactionCase):
wizard.process()
# the unit price of the valuation layer used the latest value
- self.assertEquals(move1.stock_valuation_layer_ids.unit_cost, 12)
+ self.assertEqual(move1.stock_valuation_layer_ids.unit_cost, 12)
- self.assertEquals(self.product1.value_svl, 120)
+ self.assertEqual(self.product1.value_svl, 120)
def test_change_currency_rate_average_1(self):
""" Confirm a purchase order in another currency and create the associated receipt, change
@@ -460,17 +460,17 @@ class TestStockValuationWithCOA(AccountingTestCase):
# Check what was posted in the price difference account
price_diff_aml = self.env['account.move.line'].search([('account_id','=', self.price_diff_account.id)])
- self.assertEquals(len(price_diff_aml), 1, "Only one line should have been generated in the price difference account.")
- self.assertAlmostEquals(price_diff_aml.debit, 5, "Price difference should be equal to 5 (15-10)")
+ self.assertEqual(len(price_diff_aml), 1, "Only one line should have been generated in the price difference account.")
+ self.assertAlmostEqual(price_diff_aml.debit, 5, "Price difference should be equal to 5 (15-10)")
# Check what was posted in stock input account
input_aml = self.env['account.move.line'].search([('account_id','=',self.stock_input_account.id)])
- self.assertEquals(len(input_aml), 3, "Only three lines should have been generated in stock input account: one when receiving the product, one when making the invoice.")
+ self.assertEqual(len(input_aml), 3, "Only three lines should have been generated in stock input account: one when receiving the product, one when making the invoice.")
invoice_amls = input_aml.filtered(lambda l: l.move_id == invoice)
picking_aml = input_aml - invoice_amls
- self.assertAlmostEquals(sum(invoice_amls.mapped('debit')), 15, "Total debit value on stock input account should be equal to the original PO price of the product.")
- self.assertAlmostEquals(sum(invoice_amls.mapped('credit')), 5, "Total debit value on stock input account should be equal to the original PO price of the product.")
- self.assertAlmostEquals(sum(picking_aml.mapped('credit')), 10, "Total credit value on stock input account should be equal to the original PO price of the product.")
+ self.assertAlmostEqual(sum(invoice_amls.mapped('debit')), 15, "Total debit value on stock input account should be equal to the original PO price of the product.")
+ self.assertAlmostEqual(sum(invoice_amls.mapped('credit')), 5, "Total debit value on stock input account should be equal to the original PO price of the product.")
+ self.assertAlmostEqual(sum(picking_aml.mapped('credit')), 10, "Total credit value on stock input account should be equal to the original PO price of the product.")
def test_valuation_from_increasing_tax(self):
""" Check that a tax without account will increment the stock value.
@@ -1207,14 +1207,14 @@ class TestStockValuationWithCOA(AccountingTestCase):
# Check what was posted in the price difference account
price_diff_aml = self.env['account.move.line'].search([('account_id','=', self.price_diff_account.id)])
- self.assertEquals(len(price_diff_aml), 1, "Only one line should have been generated in the price difference account.")
- self.assertAlmostEquals(price_diff_aml.credit, 20, "Price difference should be equal to 20 (110-90)")
+ self.assertEqual(len(price_diff_aml), 1, "Only one line should have been generated in the price difference account.")
+ self.assertAlmostEqual(price_diff_aml.credit, 20, "Price difference should be equal to 20 (110-90)")
# Check what was posted in stock input account
input_aml = self.env['account.move.line'].search([('account_id','=', self.stock_input_account.id)])
- self.assertEquals(len(input_aml), 3, "Only two lines should have been generated in stock input account: one when receiving the product, two when making the invoice.")
- self.assertAlmostEquals(sum(input_aml.mapped('debit')), 110, "Total debit value on stock input account should be equal to the original PO price of the product.")
- self.assertAlmostEquals(sum(input_aml.mapped('credit')), 110, "Total credit value on stock input account should be equal to the original PO price of the product.")
+ self.assertEqual(len(input_aml), 3, "Only two lines should have been generated in stock input account: one when receiving the product, two when making the invoice.")
+ self.assertAlmostEqual(sum(input_aml.mapped('debit')), 110, "Total debit value on stock input account should be equal to the original PO price of the product.")
+ self.assertAlmostEqual(sum(input_aml.mapped('credit')), 110, "Total credit value on stock input account should be equal to the original PO price of the product.")
def test_anglosaxon_valuation_discount(self):
"""
@@ -1254,14 +1254,14 @@ class TestStockValuationWithCOA(AccountingTestCase):
# Check what was posted in the price difference account
price_diff_aml = self.env['account.move.line'].search([('account_id', '=', self.price_diff_account.id)])
- self.assertEquals(len(price_diff_aml), 1, "Only one line should have been generated in the price difference account.")
- self.assertAlmostEquals(price_diff_aml.credit, 10, "Price difference should be equal to 10 (100-90)")
+ self.assertEqual(len(price_diff_aml), 1, "Only one line should have been generated in the price difference account.")
+ self.assertAlmostEqual(price_diff_aml.credit, 10, "Price difference should be equal to 10 (100-90)")
# Check what was posted in stock input account
input_aml = self.env['account.move.line'].search([('account_id', '=', self.stock_input_account.id)])
- self.assertEquals(len(input_aml), 3, "Three lines generated in stock input account: one when receiving the product, two when making the invoice.")
- self.assertAlmostEquals(sum(input_aml.mapped('debit')), 100, "Total debit value on stock input account should be equal to the original PO price of the product.")
- self.assertAlmostEquals(sum(input_aml.mapped('credit')), 100, "Total credit value on stock input account should be equal to the original PO price of the product.")
+ self.assertEqual(len(input_aml), 3, "Three lines generated in stock input account: one when receiving the product, two when making the invoice.")
+ self.assertAlmostEqual(sum(input_aml.mapped('debit')), 100, "Total debit value on stock input account should be equal to the original PO price of the product.")
+ self.assertAlmostEqual(sum(input_aml.mapped('credit')), 100, "Total credit value on stock input account should be equal to the original PO price of the product.")
def test_anglosaxon_valuation_price_unit_diff_discount(self):
"""
@@ -1301,10 +1301,10 @@ class TestStockValuationWithCOA(AccountingTestCase):
# Check if something was posted in the price difference account
price_diff_aml = self.env['account.move.line'].search([('account_id','=', self.price_diff_account.id)])
- self.assertEquals(len(price_diff_aml), 0, "No line should have been generated in the price difference account.")
+ self.assertEqual(len(price_diff_aml), 0, "No line should have been generated in the price difference account.")
# Check what was posted in stock input account
input_aml = self.env['account.move.line'].search([('account_id','=', self.stock_input_account.id)])
- self.assertEquals(len(input_aml), 2, "Only two lines should have been generated in stock input account: one when receiving the product, one when making the invoice.")
- self.assertAlmostEquals(sum(input_aml.mapped('debit')), 90, "Total debit value on stock input account should be equal to the original PO price of the product.")
- self.assertAlmostEquals(sum(input_aml.mapped('credit')), 90, "Total credit value on stock input account should be equal to the original PO price of the product.")
+ self.assertEqual(len(input_aml), 2, "Only two lines should have been generated in stock input account: one when receiving the product, one when making the invoice.")
+ self.assertAlmostEqual(sum(input_aml.mapped('debit')), 90, "Total debit value on stock input account should be equal to the original PO price of the product.")
+ self.assertAlmostEqual(sum(input_aml.mapped('credit')), 90, "Total credit value on stock input account should be equal to the original PO price of the product.")
diff --git a/addons/sale/tests/test_access_rights.py b/addons/sale/tests/test_access_rights.py
index 492ff640ce3..cc6f66556b1 100644
--- a/addons/sale/tests/test_access_rights.py
+++ b/addons/sale/tests/test_access_rights.py
@@ -87,7 +87,7 @@ class TestAccessRights(TestCommonSaleNoChart):
self.assertIn(india_channel.id, self.env['crm.team'].search([]).ids, 'Sales manager should be able to create a Sales Team')
# Manager can edit a Sales Team
india_channel.with_user(self.user_manager).write({'name': 'new_india'})
- self.assertEquals(india_channel.name, 'new_india', 'Sales manager should be able to edit a Sales Team')
+ self.assertEqual(india_channel.name, 'new_india', 'Sales manager should be able to edit a Sales Team')
# Manager can delete a Sales Team
india_channel.with_user(self.user_manager).unlink()
self.assertNotIn(india_channel.id, self.env['crm.team'].search([]).ids, 'Sales manager should be able to delete a Sales Team')
diff --git a/addons/sale/tests/test_onchange.py b/addons/sale/tests/test_onchange.py
index 978cde5321a..ffb2dbd09d2 100644
--- a/addons/sale/tests/test_onchange.py
+++ b/addons/sale/tests/test_onchange.py
@@ -63,7 +63,7 @@ class TestOnchangeProductId(TransactionCase):
sale_order = order_form.save()
# Check the unit price of SO line
- self.assertEquals(100, sale_order.order_line[0].price_unit, "The included tax must be subtracted to the price")
+ self.assertEqual(100, sale_order.order_line[0].price_unit, "The included tax must be subtracted to the price")
def test_pricelist_application(self):
""" Test different prices are correctly applied based on dates """
@@ -102,7 +102,7 @@ class TestOnchangeProductId(TransactionCase):
so = order_form.save()
# Check the unit price and subtotal of SO line
self.assertEqual(so.order_line[0].price_unit, 80, "First date pricelist rule not applied")
- self.assertEquals(so.order_line[0].price_subtotal, so.order_line[0].price_unit * so.order_line[0].product_uom_qty, 'Total of SO line should be a multiplication of unit price and ordered quantity')
+ self.assertEqual(so.order_line[0].price_subtotal, so.order_line[0].price_unit * so.order_line[0].product_uom_qty, 'Total of SO line should be a multiplication of unit price and ordered quantity')
# Change order date of the SO and check the unit price and subtotal of SO line
with Form(so) as order:
@@ -111,7 +111,7 @@ class TestOnchangeProductId(TransactionCase):
line.product_id = support_product
self.assertEqual(so.order_line[0].price_unit, 50, "Second date pricelist rule not applied")
- self.assertEquals(so.order_line[0].price_subtotal, so.order_line[0].price_unit * so.order_line[0].product_uom_qty, 'Total of SO line should be a multiplication of unit price and ordered quantity')
+ self.assertEqual(so.order_line[0].price_subtotal, so.order_line[0].price_unit * so.order_line[0].product_uom_qty, 'Total of SO line should be a multiplication of unit price and ordered quantity')
def test_pricelist_uom_discount(self):
""" Test prices and discounts are correctly applied based on date and uom"""
diff --git a/addons/sale/tests/test_reinvoice.py b/addons/sale/tests/test_reinvoice.py
index b7640fd23e9..0fb5747ecde 100644
--- a/addons/sale/tests/test_reinvoice.py
+++ b/addons/sale/tests/test_reinvoice.py
@@ -78,17 +78,17 @@ class TestReInvoice(TestCommonSaleNoChart):
self.assertTrue(sale_order_line3, "A new sale line should have been created with ordered product")
self.assertTrue(sale_order_line4, "A new sale line should have been created with delivered product")
- self.assertEquals(len(self.sale_order.order_line), 4, "There should be 4 lines on the SO (2 vendor bill lines created)")
- self.assertEquals(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 2, "There should be 4 lines on the SO (2 vendor bill lines created)")
+ self.assertEqual(len(self.sale_order.order_line), 4, "There should be 4 lines on the SO (2 vendor bill lines created)")
+ self.assertEqual(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 2, "There should be 4 lines on the SO (2 vendor bill lines created)")
- self.assertEquals(sale_order_line1.qty_delivered, 1, "Exising SO line 1 should not be impacted by reinvoicing product at cost")
- self.assertEquals(sale_order_line2.qty_delivered, 1, "Exising SO line 2 should not be impacted by reinvoicing product at cost")
+ self.assertEqual(sale_order_line1.qty_delivered, 1, "Exising SO line 1 should not be impacted by reinvoicing product at cost")
+ self.assertEqual(sale_order_line2.qty_delivered, 1, "Exising SO line 2 should not be impacted by reinvoicing product at cost")
- self.assertEquals((sale_order_line3.price_unit, sale_order_line3.qty_delivered, sale_order_line3.product_uom_qty, sale_order_line3.qty_invoiced), (self.product_ordered_cost.standard_price, 3, 0, 0), 'Sale line is wrong after confirming vendor invoice')
- self.assertEquals((sale_order_line4.price_unit, sale_order_line4.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line4.qty_invoiced), (self.product_deliver_cost.standard_price, 3, 0, 0), 'Sale line is wrong after confirming vendor invoice')
+ self.assertEqual((sale_order_line3.price_unit, sale_order_line3.qty_delivered, sale_order_line3.product_uom_qty, sale_order_line3.qty_invoiced), (self.product_ordered_cost.standard_price, 3, 0, 0), 'Sale line is wrong after confirming vendor invoice')
+ self.assertEqual((sale_order_line4.price_unit, sale_order_line4.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line4.qty_invoiced), (self.product_deliver_cost.standard_price, 3, 0, 0), 'Sale line is wrong after confirming vendor invoice')
- self.assertEquals(sale_order_line3.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line should be computed by analytic amount")
- self.assertEquals(sale_order_line4.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line should be computed by analytic amount")
+ self.assertEqual(sale_order_line3.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line should be computed by analytic amount")
+ self.assertEqual(sale_order_line4.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line should be computed by analytic amount")
# create second invoice lines and validate it
move_form = Form(self.AccountMove)
@@ -110,11 +110,11 @@ class TestReInvoice(TestCommonSaleNoChart):
self.assertTrue(sale_order_line5, "A new sale line should have been created with ordered product")
self.assertTrue(sale_order_line6, "A new sale line should have been created with delivered product")
- self.assertEquals(len(self.sale_order.order_line), 6, "There should be still 4 lines on the SO, no new created")
- self.assertEquals(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 4, "There should be still 2 expenses lines on the SO")
+ self.assertEqual(len(self.sale_order.order_line), 6, "There should be still 4 lines on the SO, no new created")
+ self.assertEqual(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 4, "There should be still 2 expenses lines on the SO")
- self.assertEquals((sale_order_line5.price_unit, sale_order_line5.qty_delivered, sale_order_line5.product_uom_qty, sale_order_line5.qty_invoiced), (self.product_ordered_cost.standard_price, 2, 0, 0), 'Sale line 5 is wrong after confirming 2e vendor invoice')
- self.assertEquals((sale_order_line6.price_unit, sale_order_line6.qty_delivered, sale_order_line6.product_uom_qty, sale_order_line6.qty_invoiced), (self.product_deliver_cost.standard_price, 2, 0, 0), 'Sale line 6 is wrong after confirming 2e vendor invoice')
+ self.assertEqual((sale_order_line5.price_unit, sale_order_line5.qty_delivered, sale_order_line5.product_uom_qty, sale_order_line5.qty_invoiced), (self.product_ordered_cost.standard_price, 2, 0, 0), 'Sale line 5 is wrong after confirming 2e vendor invoice')
+ self.assertEqual((sale_order_line6.price_unit, sale_order_line6.qty_delivered, sale_order_line6.product_uom_qty, sale_order_line6.qty_invoiced), (self.product_deliver_cost.standard_price, 2, 0, 0), 'Sale line 6 is wrong after confirming 2e vendor invoice')
def test_sales_price(self):
""" Test invoicing vendor bill at sales price for products based on delivered and ordered quantities. Check no existing SO line is incremented, but when invoicing a
@@ -163,17 +163,17 @@ class TestReInvoice(TestCommonSaleNoChart):
self.assertTrue(sale_order_line3, "A new sale line should have been created with ordered product")
self.assertTrue(sale_order_line4, "A new sale line should have been created with delivered product")
- self.assertEquals(len(self.sale_order.order_line), 4, "There should be 4 lines on the SO (2 vendor bill lines created)")
- self.assertEquals(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 2, "There should be 4 lines on the SO (2 vendor bill lines created)")
+ self.assertEqual(len(self.sale_order.order_line), 4, "There should be 4 lines on the SO (2 vendor bill lines created)")
+ self.assertEqual(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 2, "There should be 4 lines on the SO (2 vendor bill lines created)")
- self.assertEquals(sale_order_line1.qty_delivered, 1, "Exising SO line 1 should not be impacted by reinvoicing product at cost")
- self.assertEquals(sale_order_line2.qty_delivered, 1, "Exising SO line 2 should not be impacted by reinvoicing product at cost")
+ self.assertEqual(sale_order_line1.qty_delivered, 1, "Exising SO line 1 should not be impacted by reinvoicing product at cost")
+ self.assertEqual(sale_order_line2.qty_delivered, 1, "Exising SO line 2 should not be impacted by reinvoicing product at cost")
- self.assertEquals((sale_order_line3.price_unit, sale_order_line3.qty_delivered, sale_order_line3.product_uom_qty, sale_order_line3.qty_invoiced), (self.product_deliver_sales_price.list_price, 3, 0, 0), 'Sale line is wrong after confirming vendor invoice')
- self.assertEquals((sale_order_line4.price_unit, sale_order_line4.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line4.qty_invoiced), (self.product_order_sales_price.list_price, 3, 0, 0), 'Sale line is wrong after confirming vendor invoice')
+ self.assertEqual((sale_order_line3.price_unit, sale_order_line3.qty_delivered, sale_order_line3.product_uom_qty, sale_order_line3.qty_invoiced), (self.product_deliver_sales_price.list_price, 3, 0, 0), 'Sale line is wrong after confirming vendor invoice')
+ self.assertEqual((sale_order_line4.price_unit, sale_order_line4.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line4.qty_invoiced), (self.product_order_sales_price.list_price, 3, 0, 0), 'Sale line is wrong after confirming vendor invoice')
- self.assertEquals(sale_order_line3.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line 3 should be computed by analytic amount")
- self.assertEquals(sale_order_line4.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line 4 should be computed by analytic amount")
+ self.assertEqual(sale_order_line3.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line 3 should be computed by analytic amount")
+ self.assertEqual(sale_order_line4.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line 4 should be computed by analytic amount")
# create second invoice lines and validate it
move_form = Form(self.AccountMove)
@@ -195,10 +195,10 @@ class TestReInvoice(TestCommonSaleNoChart):
self.assertFalse(sale_order_line5, "No new sale line should have been created with delivered product !!")
self.assertTrue(sale_order_line6, "A new sale line should have been created with ordered product")
- self.assertEquals(len(self.sale_order.order_line), 5, "There should be 5 lines on the SO, 1 new created and 1 incremented")
- self.assertEquals(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 3, "There should be 3 expenses lines on the SO")
+ self.assertEqual(len(self.sale_order.order_line), 5, "There should be 5 lines on the SO, 1 new created and 1 incremented")
+ self.assertEqual(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 3, "There should be 3 expenses lines on the SO")
- self.assertEquals((sale_order_line6.price_unit, sale_order_line6.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line6.qty_invoiced), (self.product_order_sales_price.list_price, 2, 0, 0), 'Sale line is wrong after confirming 2e vendor invoice')
+ self.assertEqual((sale_order_line6.price_unit, sale_order_line6.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line6.qty_invoiced), (self.product_order_sales_price.list_price, 2, 0, 0), 'Sale line is wrong after confirming 2e vendor invoice')
def test_no_expense(self):
""" Test invoicing vendor bill with no policy. Check nothing happen. """
@@ -229,6 +229,6 @@ class TestReInvoice(TestCommonSaleNoChart):
})
invoice_a.post()
- self.assertEquals(len(self.sale_order.order_line), 1, "No SO line should have been created (or removed) when validating vendor bill")
- self.assertEquals(sale_order_line.qty_delivered, 1, "The delivered quantity of SO line should not have been incremented")
+ self.assertEqual(len(self.sale_order.order_line), 1, "No SO line should have been created (or removed) when validating vendor bill")
+ self.assertEqual(sale_order_line.qty_delivered, 1, "The delivered quantity of SO line should not have been incremented")
self.assertTrue(invoice_a.mapped('line_ids.analytic_line_ids'), "Analytic lines should be generated")
diff --git a/addons/sale/tests/test_sale_order.py b/addons/sale/tests/test_sale_order.py
index 0b30a9c0aa0..e491f7e5c87 100644
--- a/addons/sale/tests/test_sale_order.py
+++ b/addons/sale/tests/test_sale_order.py
@@ -212,7 +212,7 @@ class TestSaleOrder(TestCommonSaleNoChart):
inv.post()
sol = so.order_line.filtered(lambda l: l.product_id == serv_cost)
self.assertTrue(sol, 'Sale: cost invoicing does not add lines when confirming vendor invoice')
- self.assertEquals((sol.price_unit, sol.qty_delivered, sol.product_uom_qty, sol.qty_invoiced), (160, 2, 0, 0), 'Sale: line is wrong after confirming vendor invoice')
+ self.assertEqual((sol.price_unit, sol.qty_delivered, sol.product_uom_qty, sol.qty_invoiced), (160, 2, 0, 0), 'Sale: line is wrong after confirming vendor invoice')
def test_sale_with_taxes(self):
""" Test SO with taxes applied on its lines and check subtotal applied on its lines and total applied on the SO """
@@ -245,9 +245,9 @@ class TestSaleOrder(TestCommonSaleNoChart):
else:
price = line.price_unit * line.product_uom_qty
- self.assertEquals(float_compare(line.price_subtotal, price, precision_digits=2), 0)
+ self.assertEqual(float_compare(line.price_subtotal, price, precision_digits=2), 0)
- self.assertEquals(self.sale_order.amount_total,
+ self.assertEqual(self.sale_order.amount_total,
self.sale_order.amount_untaxed + self.sale_order.amount_tax,
'Taxes should be applied')
@@ -353,8 +353,8 @@ class TestSaleOrder(TestCommonSaleNoChart):
# Call again for st_line_2, it should find sale_order
res = self.env['account.reconciliation.widget'].get_bank_statement_line_data([st_line2.id])
line = res.get('lines', [{}])[0]
- self.assertEquals(line.get('sale_order_ids', []), [so.id])
+ self.assertEqual(line.get('sale_order_ids', []), [so.id])
# Call again for st_line_3, it should find sale_order based on reference
res = self.env['account.reconciliation.widget'].get_bank_statement_line_data([st_line3.id])
line = res.get('lines', [{}])[0]
- self.assertEquals(line.get('sale_order_ids', []), [so.id])
+ self.assertEqual(line.get('sale_order_ids', []), [so.id])
diff --git a/addons/sale/tests/test_sale_pricelist.py b/addons/sale/tests/test_sale_pricelist.py
index 3fc37a5a2a8..6f655db4eb0 100644
--- a/addons/sale/tests/test_sale_pricelist.py
+++ b/addons/sale/tests/test_sale_pricelist.py
@@ -125,7 +125,7 @@ class TestSaleOrder(TestCommonSaleNoChart):
line._onchange_discount()
# Check that pricelist of the SO has been applied on the sale order lines or not
for line in self.sale_order.order_line:
- self.assertEquals(line.price_unit, line.product_id.list_price, 'Pricelist of the SO should not be applied on an order line %s' % (line.name,))
+ self.assertEqual(line.price_unit, line.product_id.list_price, 'Pricelist of the SO should not be applied on an order line %s' % (line.name,))
def test_sale_with_pricelist_discount_included(self):
""" Test SO with the pricelist and check unit price appeared on its lines """
@@ -138,11 +138,11 @@ class TestSaleOrder(TestCommonSaleNoChart):
# Check that pricelist of the SO has been applied on the sale order lines or not
for line in self.sale_order.order_line:
if line.product_id == self.product_order:
- self.assertEquals(line.price_unit, self.pricelist_discount_incl_item3.fixed_price, 'Price of product_order should be %s applied on the order line' % (self.pricelist_discount_incl_item3.fixed_price,))
+ self.assertEqual(line.price_unit, self.pricelist_discount_incl_item3.fixed_price, 'Price of product_order should be %s applied on the order line' % (self.pricelist_discount_incl_item3.fixed_price,))
else: # only services (service_order and service_deliver)
for item in self.sale_order.pricelist_id.item_ids.filtered(lambda l: l.product_tmpl_id == line.product_id.product_tmpl_id):
price = item.percent_price
- self.assertEquals(price, (line.product_id.list_price - line.price_unit) / line.product_id.list_price * 100, 'Pricelist of the SO should be applied on an order line %s' % (line.product_id.name,))
+ self.assertEqual(price, (line.product_id.list_price - line.price_unit) / line.product_id.list_price * 100, 'Pricelist of the SO should be applied on an order line %s' % (line.product_id.name,))
def test_sale_with_pricelist_discount_excluded(self):
""" Test SO with the pricelist 'discount displayed' and check discount and unit price appeared on its lines """
@@ -164,12 +164,12 @@ class TestSaleOrder(TestCommonSaleNoChart):
for line in self.sale_order.order_line:
if line.product_id.categ_id in self.sale_order.pricelist_id.item_ids.mapped('categ_id'): # reduction per category (consummable only)
for item in self.sale_order.pricelist_id.item_ids.filtered(lambda l: l.categ_id == line.product_id.categ_id):
- self.assertEquals(line.discount, item.price_discount, "Discount should be displayed on order line %s since its category get some discount" % (line.name,))
- self.assertEquals(line.price_unit, line.product_id.standard_price, "Price unit should be the cost price for product %s" % (line.name,))
+ self.assertEqual(line.discount, item.price_discount, "Discount should be displayed on order line %s since its category get some discount" % (line.name,))
+ self.assertEqual(line.price_unit, line.product_id.standard_price, "Price unit should be the cost price for product %s" % (line.name,))
else:
if line.product_id == self.service_order: # reduction for this product
- self.assertEquals(line.discount, 20.0, "Discount for product %s should be 20 percent with pricelist %s" % (line.name, self.pricelist_discount_excl.name))
- self.assertEquals(line.price_unit, line.product_id.list_price, 'Unit price of order line should be a sale price as the pricelist not applied on the other category\'s product')
+ self.assertEqual(line.discount, 20.0, "Discount for product %s should be 20 percent with pricelist %s" % (line.name, self.pricelist_discount_excl.name))
+ self.assertEqual(line.price_unit, line.product_id.list_price, 'Unit price of order line should be a sale price as the pricelist not applied on the other category\'s product')
else: # no discount for the rest
- self.assertEquals(line.discount, 0.0, 'Pricelist of SO should not be applied on an order line')
- self.assertEquals(line.price_unit, line.product_id.list_price, 'Unit price of order line should be a sale price as the pricelist not applied on the other category\'s product')
+ self.assertEqual(line.discount, 0.0, 'Pricelist of SO should not be applied on an order line')
+ self.assertEqual(line.price_unit, line.product_id.list_price, 'Unit price of order line should be a sale price as the pricelist not applied on the other category\'s product')
diff --git a/addons/sale/tests/test_sale_refund.py b/addons/sale/tests/test_sale_refund.py
index 07446ec33a1..5788ed2a648 100644
--- a/addons/sale/tests/test_sale_refund.py
+++ b/addons/sale/tests/test_sale_refund.py
@@ -83,21 +83,21 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
# Check quantity to invoice on SO lines
for line in self.sale_order.order_line:
if line.product_id.invoice_policy == 'delivery':
- self.assertEquals(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
- self.assertEquals(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
+ self.assertEqual(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
self.assertFalse(line.invoice_lines, "The line based on delivered qty are not invoiced, so they should not be linked to invoice line")
else:
if line == self.sol_prod_order:
- self.assertEquals(line.qty_to_invoice, 0.0, "The ordered sale line are totally invoiced (qty to invoice is zero)")
- self.assertEquals(line.qty_invoiced, 5.0, "The ordered (prod) sale line are totally invoiced (qty invoiced come from the invoice lines)")
+ self.assertEqual(line.qty_to_invoice, 0.0, "The ordered sale line are totally invoiced (qty to invoice is zero)")
+ self.assertEqual(line.qty_invoiced, 5.0, "The ordered (prod) sale line are totally invoiced (qty invoiced come from the invoice lines)")
else:
- self.assertEquals(line.qty_to_invoice, 0.0, "The ordered sale line are totally invoiced (qty to invoice is zero)")
- self.assertEquals(line.qty_invoiced, 3.0, "The ordered (serv) sale line are totally invoiced (qty invoiced = the invoice lines)")
- self.assertEquals(line.untaxed_amount_to_invoice, line.price_unit * line.qty_to_invoice, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products")
- self.assertEquals(line.untaxed_amount_invoiced, line.price_unit * line.qty_invoiced, "Amount invoiced is now set as qty invoiced * unit price since no price change on invoice, for ordered products")
- self.assertEquals(len(line.invoice_lines), 1, "The lines 'ordered' qty are invoiced, so it should be linked to 1 invoice lines")
+ self.assertEqual(line.qty_to_invoice, 0.0, "The ordered sale line are totally invoiced (qty to invoice is zero)")
+ self.assertEqual(line.qty_invoiced, 3.0, "The ordered (serv) sale line are totally invoiced (qty invoiced = the invoice lines)")
+ self.assertEqual(line.untaxed_amount_to_invoice, line.price_unit * line.qty_to_invoice, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products")
+ self.assertEqual(line.untaxed_amount_invoiced, line.price_unit * line.qty_invoiced, "Amount invoiced is now set as qty invoiced * unit price since no price change on invoice, for ordered products")
+ self.assertEqual(len(line.invoice_lines), 1, "The lines 'ordered' qty are invoiced, so it should be linked to 1 invoice lines")
# Make a credit note
credit_note_wizard = self.env['account.move.reversal'].with_context({'active_ids': [self.invoice.id], 'active_id': self.invoice.id}).create({
@@ -108,58 +108,58 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
invoice_refund = self.sale_order.invoice_ids.sorted(key=lambda inv: inv.id, reverse=False)[-1] # the first invoice, its refund, and the new invoice
# Check invoice's type and number
- self.assertEquals(invoice_refund.type, 'out_refund', 'The last created invoiced should be a refund')
- self.assertEquals(invoice_refund.state, 'draft', 'Last Customer invoices should be in draft')
- self.assertEquals(self.sale_order.invoice_count, 2, "The SO should have 2 related invoices: the original, the new refund")
- self.assertEquals(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_refund')), 1, "The SO should be linked to only one refund")
- self.assertEquals(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_invoice')), 1, "The SO should be linked to only one customer invoices")
+ self.assertEqual(invoice_refund.type, 'out_refund', 'The last created invoiced should be a refund')
+ self.assertEqual(invoice_refund.state, 'draft', 'Last Customer invoices should be in draft')
+ self.assertEqual(self.sale_order.invoice_count, 2, "The SO should have 2 related invoices: the original, the new refund")
+ self.assertEqual(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_refund')), 1, "The SO should be linked to only one refund")
+ self.assertEqual(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_invoice')), 1, "The SO should be linked to only one customer invoices")
# At this time, the invoice 1 is opend (validated) and its refund is in draft, so the amounts invoiced are not zero for
# invoiced sale line. The amounts only take validated invoice/refund into account.
for line in self.sale_order.order_line:
if line.product_id.invoice_policy == 'delivery':
- self.assertEquals(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
- self.assertEquals(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO line based on delivered qty")
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
+ self.assertEqual(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO line based on delivered qty")
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
self.assertFalse(line.invoice_lines, "The line based on delivered are not invoiced, so they should not be linked to invoice line")
else:
if line == self.sol_prod_order:
- self.assertEquals(line.qty_to_invoice, 5.0, "As the refund is created, the invoiced quantity cancel each other (consu ordered)")
- self.assertEquals(line.qty_invoiced, 0.0, "The qty to invoice should have decreased as the refund is created for ordered consu SO line")
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "Amount to invoice is zero as the refund is not validated")
- self.assertEquals(line.untaxed_amount_invoiced, line.price_unit * 5, "Amount invoiced is now set as unit price * ordered qty - refund qty) even if the ")
- self.assertEquals(len(line.invoice_lines), 2, "The line 'ordered consumable' is invoiced, so it should be linked to 2 invoice lines (invoice and refund)")
+ self.assertEqual(line.qty_to_invoice, 5.0, "As the refund is created, the invoiced quantity cancel each other (consu ordered)")
+ self.assertEqual(line.qty_invoiced, 0.0, "The qty to invoice should have decreased as the refund is created for ordered consu SO line")
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "Amount to invoice is zero as the refund is not validated")
+ self.assertEqual(line.untaxed_amount_invoiced, line.price_unit * 5, "Amount invoiced is now set as unit price * ordered qty - refund qty) even if the ")
+ self.assertEqual(len(line.invoice_lines), 2, "The line 'ordered consumable' is invoiced, so it should be linked to 2 invoice lines (invoice and refund)")
else:
- self.assertEquals(line.qty_to_invoice, 3.0, "As the refund is created, the invoiced quantity cancel each other (consu ordered)")
- self.assertEquals(line.qty_invoiced, 0.0, "The qty to invoice should have decreased as the refund is created for ordered service SO line")
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "Amount to invoice is zero as the refund is not validated")
- self.assertEquals(line.untaxed_amount_invoiced, line.price_unit * 3, "Amount invoiced is now set as unit price * ordered qty - refund qty) even if the ")
- self.assertEquals(len(line.invoice_lines), 2, "The line 'ordered service' is invoiced, so it should be linked to 2 invoice lines (invoice and refund)")
+ self.assertEqual(line.qty_to_invoice, 3.0, "As the refund is created, the invoiced quantity cancel each other (consu ordered)")
+ self.assertEqual(line.qty_invoiced, 0.0, "The qty to invoice should have decreased as the refund is created for ordered service SO line")
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "Amount to invoice is zero as the refund is not validated")
+ self.assertEqual(line.untaxed_amount_invoiced, line.price_unit * 3, "Amount invoiced is now set as unit price * ordered qty - refund qty) even if the ")
+ self.assertEqual(len(line.invoice_lines), 2, "The line 'ordered service' is invoiced, so it should be linked to 2 invoice lines (invoice and refund)")
# Validate the refund
invoice_refund.post()
for line in self.sale_order.order_line:
if line.product_id.invoice_policy == 'delivery':
- self.assertEquals(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
- self.assertEquals(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
+ self.assertEqual(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
self.assertFalse(line.invoice_lines, "The line based on delivered are not invoiced, so they should not be linked to invoice line")
else:
if line == self.sol_prod_order:
- self.assertEquals(line.qty_to_invoice, 5.0, "As the refund still exists, the quantity to invoice is the ordered quantity")
- self.assertEquals(line.qty_invoiced, 0.0, "The qty to invoice should be zero as, with the refund, the quantities cancel each other")
- self.assertEquals(line.untaxed_amount_to_invoice, line.price_unit * 5, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, as refund is validated")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "Amount invoiced decreased as the refund is now confirmed")
- self.assertEquals(len(line.invoice_lines), 2, "The line 'ordered consumable' is invoiced, so it should be linked to 2 invoice lines (invoice and refund)")
+ self.assertEqual(line.qty_to_invoice, 5.0, "As the refund still exists, the quantity to invoice is the ordered quantity")
+ self.assertEqual(line.qty_invoiced, 0.0, "The qty to invoice should be zero as, with the refund, the quantities cancel each other")
+ self.assertEqual(line.untaxed_amount_to_invoice, line.price_unit * 5, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, as refund is validated")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "Amount invoiced decreased as the refund is now confirmed")
+ self.assertEqual(len(line.invoice_lines), 2, "The line 'ordered consumable' is invoiced, so it should be linked to 2 invoice lines (invoice and refund)")
else:
- self.assertEquals(line.qty_to_invoice, 3.0, "As the refund still exists, the quantity to invoice is the ordered quantity")
- self.assertEquals(line.qty_invoiced, 0.0, "The qty to invoice should be zero as, with the refund, the quantities cancel each other")
- self.assertEquals(line.untaxed_amount_to_invoice, line.price_unit * 3, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, as refund is validated")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "Amount invoiced decreased as the refund is now confirmed")
- self.assertEquals(len(line.invoice_lines), 2, "The line 'ordered service' is invoiced, so it should be linked to 2 invoice lines (invoice and refund)")
+ self.assertEqual(line.qty_to_invoice, 3.0, "As the refund still exists, the quantity to invoice is the ordered quantity")
+ self.assertEqual(line.qty_invoiced, 0.0, "The qty to invoice should be zero as, with the refund, the quantities cancel each other")
+ self.assertEqual(line.untaxed_amount_to_invoice, line.price_unit * 3, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, as refund is validated")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "Amount invoiced decreased as the refund is now confirmed")
+ self.assertEqual(len(line.invoice_lines), 2, "The line 'ordered service' is invoiced, so it should be linked to 2 invoice lines (invoice and refund)")
def test_refund_cancel(self):
""" Test invoice with a refund in 'cancel' mode, meaning a refund will be created and auto confirm to completely cancel the first
@@ -177,18 +177,18 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
# Check quantity to invoice on SO lines
for line in self.sale_order.order_line:
if line.product_id.invoice_policy == 'delivery':
- self.assertEquals(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
- self.assertEquals(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
+ self.assertEqual(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
self.assertFalse(line.invoice_lines, "The line based on delivered qty are not invoiced, so they should not be linked to invoice line")
else:
- self.assertEquals(line.untaxed_amount_to_invoice, line.price_unit * line.qty_to_invoice, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products")
- self.assertEquals(line.untaxed_amount_invoiced, line.price_unit * line.qty_invoiced, "Amount invoiced is now set as qty invoiced * unit price since no price change on invoice, for ordered products")
- self.assertEquals(len(line.invoice_lines), 1, "The lines 'ordered' qty are invoiced, so it should be linked to 1 invoice lines")
+ self.assertEqual(line.untaxed_amount_to_invoice, line.price_unit * line.qty_to_invoice, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products")
+ self.assertEqual(line.untaxed_amount_invoiced, line.price_unit * line.qty_invoiced, "Amount invoiced is now set as qty invoiced * unit price since no price change on invoice, for ordered products")
+ self.assertEqual(len(line.invoice_lines), 1, "The lines 'ordered' qty are invoiced, so it should be linked to 1 invoice lines")
- self.assertEquals(line.qty_invoiced, line.product_uom_qty + 1, "The quantity invoiced is +1 unit from the one of the sale line, as we modified invoice lines (%s)" % (line.name,))
- self.assertEquals(line.qty_to_invoice, -1, "The quantity to invoice is negative as we invoice more than ordered")
+ self.assertEqual(line.qty_invoiced, line.product_uom_qty + 1, "The quantity invoiced is +1 unit from the one of the sale line, as we modified invoice lines (%s)" % (line.name,))
+ self.assertEqual(line.qty_to_invoice, -1, "The quantity to invoice is negative as we invoice more than ordered")
# Make a credit note
credit_note_wizard = self.env['account.move.reversal'].with_context({'active_ids': self.invoice.ids, 'active_id': self.invoice.id}).create({
@@ -198,28 +198,28 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
invoice_refund = self.env['account.move'].browse(credit_note_wizard.reverse_moves()['res_id'])
# Check invoice's type and number
- self.assertEquals(invoice_refund.type, 'out_refund', 'The last created invoiced should be a customer invoice')
- self.assertEquals(invoice_refund.invoice_payment_state, 'paid', 'Last Customer creadit note should be in paid state')
- self.assertEquals(self.sale_order.invoice_count, 2, "The SO should have 3 related invoices: the original, the refund, and the new one")
- self.assertEquals(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_refund')), 1, "The SO should be linked to only one refund")
- self.assertEquals(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_invoice')), 1, "The SO should be linked to only one customer invoices")
+ self.assertEqual(invoice_refund.type, 'out_refund', 'The last created invoiced should be a customer invoice')
+ self.assertEqual(invoice_refund.invoice_payment_state, 'paid', 'Last Customer creadit note should be in paid state')
+ self.assertEqual(self.sale_order.invoice_count, 2, "The SO should have 3 related invoices: the original, the refund, and the new one")
+ self.assertEqual(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_refund')), 1, "The SO should be linked to only one refund")
+ self.assertEqual(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_invoice')), 1, "The SO should be linked to only one customer invoices")
# At this time, the invoice 1 is opened (validated) and its refund validated too, so the amounts invoiced are zero for
# all sale line. All invoiceable Sale lines have
for line in self.sale_order.order_line:
if line.product_id.invoice_policy == 'delivery':
- self.assertEquals(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
- self.assertEquals(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO line based on delivered qty")
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
+ self.assertEqual(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO line based on delivered qty")
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
self.assertFalse(line.invoice_lines, "The line based on delivered are not invoiced, so they should not be linked to invoice line")
else:
- self.assertEquals(line.qty_to_invoice, line.product_uom_qty, "The quantity to invoice should be the ordered quantity")
- self.assertEquals(line.qty_invoiced, 0, "The quantity invoiced is zero as the refund (paid) completely cancel the first invoice")
+ self.assertEqual(line.qty_to_invoice, line.product_uom_qty, "The quantity to invoice should be the ordered quantity")
+ self.assertEqual(line.qty_invoiced, 0, "The quantity invoiced is zero as the refund (paid) completely cancel the first invoice")
- self.assertEquals(line.untaxed_amount_to_invoice, line.price_unit * line.qty_to_invoice, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products")
- self.assertEquals(line.untaxed_amount_invoiced, line.price_unit * line.qty_invoiced, "Amount invoiced is now set as qty invoiced * unit price since no price change on invoice, for ordered products")
- self.assertEquals(len(line.invoice_lines), 2, "The lines 'ordered' qty are invoiced, so it should be linked to 1 invoice lines")
+ self.assertEqual(line.untaxed_amount_to_invoice, line.price_unit * line.qty_to_invoice, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products")
+ self.assertEqual(line.untaxed_amount_invoiced, line.price_unit * line.qty_invoiced, "Amount invoiced is now set as qty invoiced * unit price since no price change on invoice, for ordered products")
+ self.assertEqual(len(line.invoice_lines), 2, "The lines 'ordered' qty are invoiced, so it should be linked to 1 invoice lines")
def test_refund_modify(self):
""" Test invoice with a refund in 'modify' mode, and check customer invoices credit note is created from respective invoice """
@@ -236,21 +236,21 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
# Check quantity to invoice on SO lines
for line in self.sale_order.order_line:
if line.product_id.invoice_policy == 'delivery':
- self.assertEquals(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
- self.assertEquals(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
+ self.assertEqual(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
self.assertFalse(line.invoice_lines, "The line based on delivered qty are not invoiced, so they should not be linked to invoice line")
else:
if line == self.sol_prod_order:
- self.assertEquals(line.qty_to_invoice, 2.0, "The ordered sale line are totally invoiced (qty to invoice is zero)")
- self.assertEquals(line.qty_invoiced, 3.0, "The ordered (prod) sale line are totally invoiced (qty invoiced come from the invoice lines)")
+ self.assertEqual(line.qty_to_invoice, 2.0, "The ordered sale line are totally invoiced (qty to invoice is zero)")
+ self.assertEqual(line.qty_invoiced, 3.0, "The ordered (prod) sale line are totally invoiced (qty invoiced come from the invoice lines)")
else:
- self.assertEquals(line.qty_to_invoice, 1.0, "The ordered sale line are totally invoiced (qty to invoice is zero)")
- self.assertEquals(line.qty_invoiced, 2.0, "The ordered (serv) sale line are totally invoiced (qty invoiced = the invoice lines)")
- self.assertEquals(line.untaxed_amount_to_invoice, line.price_unit * line.qty_to_invoice, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products")
- self.assertEquals(line.untaxed_amount_invoiced, line.price_unit * line.qty_invoiced, "Amount invoiced is now set as qty invoiced * unit price since no price change on invoice, for ordered products")
- self.assertEquals(len(line.invoice_lines), 1, "The lines 'ordered' qty are invoiced, so it should be linked to 1 invoice lines")
+ self.assertEqual(line.qty_to_invoice, 1.0, "The ordered sale line are totally invoiced (qty to invoice is zero)")
+ self.assertEqual(line.qty_invoiced, 2.0, "The ordered (serv) sale line are totally invoiced (qty invoiced = the invoice lines)")
+ self.assertEqual(line.untaxed_amount_to_invoice, line.price_unit * line.qty_to_invoice, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products")
+ self.assertEqual(line.untaxed_amount_invoiced, line.price_unit * line.qty_invoiced, "Amount invoiced is now set as qty invoiced * unit price since no price change on invoice, for ordered products")
+ self.assertEqual(len(line.invoice_lines), 1, "The lines 'ordered' qty are invoiced, so it should be linked to 1 invoice lines")
# Make a credit note
credit_note_wizard = self.env['account.move.reversal'].with_context({'active_ids': [self.invoice.id], 'active_id': self.invoice.id}).create({
@@ -260,34 +260,34 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
invoice_refund = self.env['account.move'].browse(credit_note_wizard.reverse_moves()['res_id'])
# Check invoice's type and number
- self.assertEquals(invoice_refund.type, 'out_invoice', 'The last created invoiced should be a customer invoice')
- self.assertEquals(invoice_refund.state, 'draft', 'Last Customer invoices should be in draft')
- self.assertEquals(self.sale_order.invoice_count, 3, "The SO should have 3 related invoices: the original, the refund, and the new one")
- self.assertEquals(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_refund')), 1, "The SO should be linked to only one refund")
- self.assertEquals(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_invoice')), 2, "The SO should be linked to two customer invoices")
+ self.assertEqual(invoice_refund.type, 'out_invoice', 'The last created invoiced should be a customer invoice')
+ self.assertEqual(invoice_refund.state, 'draft', 'Last Customer invoices should be in draft')
+ self.assertEqual(self.sale_order.invoice_count, 3, "The SO should have 3 related invoices: the original, the refund, and the new one")
+ self.assertEqual(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_refund')), 1, "The SO should be linked to only one refund")
+ self.assertEqual(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_invoice')), 2, "The SO should be linked to two customer invoices")
# At this time, the invoice 1 and its refund are confirmed, so the amounts invoiced are zero. The third invoice
# (2nd customer inv) is in draft state.
for line in self.sale_order.order_line:
if line.product_id.invoice_policy == 'delivery':
- self.assertEquals(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
- self.assertEquals(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
+ self.assertEqual(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
self.assertFalse(line.invoice_lines, "The line based on delivered are not invoiced, so they should not be linked to invoice line")
else:
if line == self.sol_prod_order:
- self.assertEquals(line.qty_to_invoice, 2.0, "The qty to invoice does not change when confirming the new invoice (2)")
- self.assertEquals(line.qty_invoiced, 3.0, "The ordered (prod) sale line does not change on invoice 2 confirmation")
- self.assertEquals(line.untaxed_amount_to_invoice, line.price_unit * 5, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "Amount invoiced is zero as the invoice 1 and its refund are reconcilied")
- self.assertEquals(len(line.invoice_lines), 3, "The line 'ordered consumable' is invoiced, so it should be linked to 3 invoice lines (invoice and refund)")
+ self.assertEqual(line.qty_to_invoice, 2.0, "The qty to invoice does not change when confirming the new invoice (2)")
+ self.assertEqual(line.qty_invoiced, 3.0, "The ordered (prod) sale line does not change on invoice 2 confirmation")
+ self.assertEqual(line.untaxed_amount_to_invoice, line.price_unit * 5, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "Amount invoiced is zero as the invoice 1 and its refund are reconcilied")
+ self.assertEqual(len(line.invoice_lines), 3, "The line 'ordered consumable' is invoiced, so it should be linked to 3 invoice lines (invoice and refund)")
else:
- self.assertEquals(line.qty_to_invoice, 1.0, "The qty to invoice does not change when confirming the new invoice (2)")
- self.assertEquals(line.qty_invoiced, 2.0, "The ordered (serv) sale line does not change on invoice 2 confirmation")
- self.assertEquals(line.untaxed_amount_to_invoice, line.price_unit * 3, "Amount to invoice is now set as unit price * ordered qty - refund qty) even if the ")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "Amount invoiced is zero as the invoice 1 and its refund are reconcilied")
- self.assertEquals(len(line.invoice_lines), 3, "The line 'ordered service' is invoiced, so it should be linked to 3 invoice lines (invoice and refund)")
+ self.assertEqual(line.qty_to_invoice, 1.0, "The qty to invoice does not change when confirming the new invoice (2)")
+ self.assertEqual(line.qty_invoiced, 2.0, "The ordered (serv) sale line does not change on invoice 2 confirmation")
+ self.assertEqual(line.untaxed_amount_to_invoice, line.price_unit * 3, "Amount to invoice is now set as unit price * ordered qty - refund qty) even if the ")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "Amount invoiced is zero as the invoice 1 and its refund are reconcilied")
+ self.assertEqual(len(line.invoice_lines), 3, "The line 'ordered service' is invoiced, so it should be linked to 3 invoice lines (invoice and refund)")
# Change unit of ordered product on refund lines
move_form = Form(invoice_refund)
@@ -302,21 +302,21 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
for line in self.sale_order.order_line:
if line.product_id.invoice_policy == 'delivery':
- self.assertEquals(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
- self.assertEquals(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
+ self.assertEqual(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
self.assertFalse(line.invoice_lines, "The line based on delivered are not invoiced, so they should not be linked to invoice line, even after validation")
else:
if line == self.sol_prod_order:
- self.assertEquals(line.qty_to_invoice, 2.0, "The qty to invoice does not change when confirming the new invoice (3)")
- self.assertEquals(line.qty_invoiced, 3.0, "The ordered sale line are totally invoiced (qty invoiced = ordered qty)")
- self.assertEquals(line.untaxed_amount_to_invoice, 1100.0, "")
- self.assertEquals(line.untaxed_amount_invoiced, 300.0, "")
- self.assertEquals(len(line.invoice_lines), 3, "The line 'ordered consumable' is invoiced, so it should be linked to 2 invoice lines (invoice and refund), even after validation")
+ self.assertEqual(line.qty_to_invoice, 2.0, "The qty to invoice does not change when confirming the new invoice (3)")
+ self.assertEqual(line.qty_invoiced, 3.0, "The ordered sale line are totally invoiced (qty invoiced = ordered qty)")
+ self.assertEqual(line.untaxed_amount_to_invoice, 1100.0, "")
+ self.assertEqual(line.untaxed_amount_invoiced, 300.0, "")
+ self.assertEqual(len(line.invoice_lines), 3, "The line 'ordered consumable' is invoiced, so it should be linked to 2 invoice lines (invoice and refund), even after validation")
else:
- self.assertEquals(line.qty_to_invoice, 1.0, "The qty to invoice does not change when confirming the new invoice (3)")
- self.assertEquals(line.qty_invoiced, 2.0, "The ordered sale line are totally invoiced (qty invoiced = ordered qty)")
- self.assertEquals(line.untaxed_amount_to_invoice, 170.0, "")
- self.assertEquals(line.untaxed_amount_invoiced, 100.0, "")
- self.assertEquals(len(line.invoice_lines), 3, "The line 'ordered service' is invoiced, so it should be linked to 2 invoice lines (invoice and refund), even after validation")
+ self.assertEqual(line.qty_to_invoice, 1.0, "The qty to invoice does not change when confirming the new invoice (3)")
+ self.assertEqual(line.qty_invoiced, 2.0, "The ordered sale line are totally invoiced (qty invoiced = ordered qty)")
+ self.assertEqual(line.untaxed_amount_to_invoice, 170.0, "")
+ self.assertEqual(line.untaxed_amount_invoiced, 100.0, "")
+ self.assertEqual(len(line.invoice_lines), 3, "The line 'ordered service' is invoiced, so it should be linked to 2 invoice lines (invoice and refund), even after validation")
diff --git a/addons/sale/tests/test_sale_to_invoice.py b/addons/sale/tests/test_sale_to_invoice.py
index 5df778af1fe..a67158a3d11 100644
--- a/addons/sale/tests/test_sale_to_invoice.py
+++ b/addons/sale/tests/test_sale_to_invoice.py
@@ -83,9 +83,9 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
})
payment.create_invoices()
- self.assertEquals(len(self.sale_order.invoice_ids), 1, 'Invoice should be created for the SO')
+ self.assertEqual(len(self.sale_order.invoice_ids), 1, 'Invoice should be created for the SO')
downpayment_line = self.sale_order.order_line.filtered(lambda l: l.is_downpayment)
- self.assertEquals(len(downpayment_line), 1, 'SO line downpayment should be created on SO')
+ self.assertEqual(len(downpayment_line), 1, 'SO line downpayment should be created on SO')
# Update delivered quantity of SO lines
self.sol_serv_deliver.write({'qty_delivered': 4.0})
@@ -97,11 +97,11 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
})
payment.create_invoices()
- self.assertEquals(len(self.sale_order.invoice_ids), 2, 'Invoice should be created for the SO')
+ self.assertEqual(len(self.sale_order.invoice_ids), 2, 'Invoice should be created for the SO')
invoice = self.sale_order.invoice_ids.sorted()[0]
- self.assertEquals(len(invoice.invoice_line_ids), len(self.sale_order.order_line), 'All lines should be invoiced')
- self.assertEquals(invoice.amount_total, self.sale_order.amount_total - downpayment_line.price_unit, 'Downpayment should be applied')
+ self.assertEqual(len(invoice.invoice_line_ids), len(self.sale_order.order_line), 'All lines should be invoiced')
+ self.assertEqual(invoice.amount_total, self.sale_order.amount_total - downpayment_line.price_unit, 'Downpayment should be applied')
def test_invoice_with_discount(self):
""" Test invoice with a discount and check discount applied on both SO lines and an invoice lines """
@@ -113,7 +113,7 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
for line in self.sale_order.order_line.filtered(lambda l: l.discount):
product_price = line.price_unit * line.product_uom_qty
- self.assertEquals(line.discount, (product_price - line.price_subtotal) / product_price * 100, 'Discount should be applied on order line')
+ self.assertEqual(line.discount, (product_price - line.price_subtotal) / product_price * 100, 'Discount should be applied on order line')
# lines are in draft
for line in self.sale_order.order_line:
@@ -125,9 +125,9 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
for line in self.sale_order.order_line:
self.assertTrue(float_is_zero(line.untaxed_amount_invoiced, precision_digits=2), "The invoiced amount should be zero, as the line is in draft state")
- self.assertEquals(self.sol_serv_order.untaxed_amount_to_invoice, 297, "The untaxed amount to invoice is wrong")
- self.assertEquals(self.sol_serv_deliver.untaxed_amount_to_invoice, self.sol_serv_deliver.qty_delivered * self.sol_serv_deliver.price_reduce, "The untaxed amount to invoice should be qty deli * price reduce, so 4 * (180 - 36)")
- self.assertEquals(self.sol_prod_deliver.untaxed_amount_to_invoice, 140, "The untaxed amount to invoice should be qty deli * price reduce, so 4 * (180 - 36)")
+ self.assertEqual(self.sol_serv_order.untaxed_amount_to_invoice, 297, "The untaxed amount to invoice is wrong")
+ self.assertEqual(self.sol_serv_deliver.untaxed_amount_to_invoice, self.sol_serv_deliver.qty_delivered * self.sol_serv_deliver.price_reduce, "The untaxed amount to invoice should be qty deli * price reduce, so 4 * (180 - 36)")
+ self.assertEqual(self.sol_prod_deliver.untaxed_amount_to_invoice, 140, "The untaxed amount to invoice should be qty deli * price reduce, so 4 * (180 - 36)")
# Let's do an invoice with invoiceable lines
payment = self.env['sale.advance.payment.inv'].with_context(self.context).create({
@@ -139,7 +139,7 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
# Check discount appeared on both SO lines and invoice lines
for line, inv_line in zip(self.sale_order.order_line, invoice.invoice_line_ids):
- self.assertEquals(line.discount, inv_line.discount, 'Discount on lines of order and invoice should be same')
+ self.assertEqual(line.discount, inv_line.discount, 'Discount on lines of order and invoice should be same')
def test_invoice(self):
""" Test create and invoice from the SO, and check qty invoice/to invoice, and the related amounts """
@@ -154,15 +154,15 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
# Check ordered quantity, quantity to invoice and invoiced quantity of SO lines
for line in self.sale_order.order_line:
if line.product_id.invoice_policy == 'delivery':
- self.assertEquals(line.qty_to_invoice, 0.0, 'Quantity to invoice should be same as ordered quantity')
- self.assertEquals(line.qty_invoiced, 0.0, 'Invoiced quantity should be zero as no any invoice created for SO')
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.qty_to_invoice, 0.0, 'Quantity to invoice should be same as ordered quantity')
+ self.assertEqual(line.qty_invoiced, 0.0, 'Invoiced quantity should be zero as no any invoice created for SO')
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
else:
- self.assertEquals(line.qty_to_invoice, line.product_uom_qty, 'Quantity to invoice should be same as ordered quantity')
- self.assertEquals(line.qty_invoiced, 0.0, 'Invoiced quantity should be zero as no any invoice created for SO')
- self.assertEquals(line.untaxed_amount_to_invoice, line.product_uom_qty * line.price_unit, "The amount to invoice should the total of the line, as the line is confirmed")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line is confirmed")
+ self.assertEqual(line.qty_to_invoice, line.product_uom_qty, 'Quantity to invoice should be same as ordered quantity')
+ self.assertEqual(line.qty_invoiced, 0.0, 'Invoiced quantity should be zero as no any invoice created for SO')
+ self.assertEqual(line.untaxed_amount_to_invoice, line.product_uom_qty * line.price_unit, "The amount to invoice should the total of the line, as the line is confirmed")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line is confirmed")
# Let's do an invoice with invoiceable lines
payment = self.env['sale.advance.payment.inv'].with_context(self.context).create({
@@ -183,35 +183,35 @@ class TestSaleToInvoice(TestCommonSaleNoChart):
# amount to invoice / invoiced should not have changed (amounts take only confirmed invoice into account)
for line in self.sale_order.order_line:
if line.product_id.invoice_policy == 'delivery':
- self.assertEquals(line.qty_to_invoice, 0.0, "Quantity to invoice should be zero")
- self.assertEquals(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as delivered lines are not delivered yet")
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity (no confirmed invoice)")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as no invoice are validated for now")
+ self.assertEqual(line.qty_to_invoice, 0.0, "Quantity to invoice should be zero")
+ self.assertEqual(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as delivered lines are not delivered yet")
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity (no confirmed invoice)")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as no invoice are validated for now")
else:
if line == self.sol_prod_order:
- self.assertEquals(self.sol_prod_order.qty_to_invoice, 2.0, "Changing the quantity on draft invoice update the qty to invoice on SO lines")
- self.assertEquals(self.sol_prod_order.qty_invoiced, 3.0, "Changing the quantity on draft invoice update the invoiced qty on SO lines")
+ self.assertEqual(self.sol_prod_order.qty_to_invoice, 2.0, "Changing the quantity on draft invoice update the qty to invoice on SO lines")
+ self.assertEqual(self.sol_prod_order.qty_invoiced, 3.0, "Changing the quantity on draft invoice update the invoiced qty on SO lines")
else:
- self.assertEquals(self.sol_serv_order.qty_to_invoice, 1.0, "Changing the quantity on draft invoice update the qty to invoice on SO lines")
- self.assertEquals(self.sol_serv_order.qty_invoiced, 2.0, "Changing the quantity on draft invoice update the invoiced qty on SO lines")
- self.assertEquals(line.untaxed_amount_to_invoice, line.product_uom_qty * line.price_unit, "The amount to invoice should the total of the line, as the line is confirmed (no confirmed invoice)")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as no invoice are validated for now")
+ self.assertEqual(self.sol_serv_order.qty_to_invoice, 1.0, "Changing the quantity on draft invoice update the qty to invoice on SO lines")
+ self.assertEqual(self.sol_serv_order.qty_invoiced, 2.0, "Changing the quantity on draft invoice update the invoiced qty on SO lines")
+ self.assertEqual(line.untaxed_amount_to_invoice, line.product_uom_qty * line.price_unit, "The amount to invoice should the total of the line, as the line is confirmed (no confirmed invoice)")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as no invoice are validated for now")
invoice.post()
# Check quantity to invoice on SO lines
for line in self.sale_order.order_line:
if line.product_id.invoice_policy == 'delivery':
- self.assertEquals(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
- self.assertEquals(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
- self.assertEquals(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
- self.assertEquals(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity")
+ self.assertEqual(line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO")
+ self.assertEqual(line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity")
+ self.assertEqual(line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity")
else:
if line == self.sol_prod_order:
- self.assertEquals(line.qty_to_invoice, 2.0, "The ordered sale line are totally invoiced (qty to invoice is zero)")
- self.assertEquals(line.qty_invoiced, 3.0, "The ordered (prod) sale line are totally invoiced (qty invoiced come from the invoice lines)")
+ self.assertEqual(line.qty_to_invoice, 2.0, "The ordered sale line are totally invoiced (qty to invoice is zero)")
+ self.assertEqual(line.qty_invoiced, 3.0, "The ordered (prod) sale line are totally invoiced (qty invoiced come from the invoice lines)")
else:
- self.assertEquals(line.qty_to_invoice, 1.0, "The ordered sale line are totally invoiced (qty to invoice is zero)")
- self.assertEquals(line.qty_invoiced, 2.0, "The ordered (serv) sale line are totally invoiced (qty invoiced = the invoice lines)")
- self.assertEquals(line.untaxed_amount_to_invoice, line.price_unit * line.qty_to_invoice, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products")
- self.assertEquals(line.untaxed_amount_invoiced, line.price_unit * line.qty_invoiced, "Amount invoiced is now set as qty invoiced * unit price since no price change on invoice, for ordered products")
+ self.assertEqual(line.qty_to_invoice, 1.0, "The ordered sale line are totally invoiced (qty to invoice is zero)")
+ self.assertEqual(line.qty_invoiced, 2.0, "The ordered (serv) sale line are totally invoiced (qty invoiced = the invoice lines)")
+ self.assertEqual(line.untaxed_amount_to_invoice, line.price_unit * line.qty_to_invoice, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products")
+ self.assertEqual(line.untaxed_amount_invoiced, line.price_unit * line.qty_invoiced, "Amount invoiced is now set as qty invoiced * unit price since no price change on invoice, for ordered products")
diff --git a/addons/sale_expense/tests/test_reinvoice.py b/addons/sale_expense/tests/test_reinvoice.py
index 0a7fa507c53..bae15f23017 100644
--- a/addons/sale_expense/tests/test_reinvoice.py
+++ b/addons/sale_expense/tests/test_reinvoice.py
@@ -74,24 +74,24 @@ class TestReInvoice(TestExpenseCommon, TestCommonSaleNoChart):
self.expense_sheet.approve_expense_sheets()
self.expense_sheet.action_sheet_move_create()
- self.assertEquals(len(self.sale_order.order_line), 3, "There should be 3 lines on the SO")
- self.assertEquals(sale_order_line.qty_delivered, 0, "Exising SO line should not be impacted by reinvoicing product at cost")
+ self.assertEqual(len(self.sale_order.order_line), 3, "There should be 3 lines on the SO")
+ self.assertEqual(sale_order_line.qty_delivered, 0, "Exising SO line should not be impacted by reinvoicing product at cost")
sol_ordered = self.sale_order.order_line.filtered(lambda sol: sol.product_id == self.product_ordered_cost and sol != sale_order_line)
self.assertTrue(sol_ordered.is_expense, "The new SOL should be flag as coming from an expense")
- self.assertEquals(sol_ordered.qty_delivered_method, 'analytic', 'Delivered quantity of expense SO line should be computed by analytic amount')
+ self.assertEqual(sol_ordered.qty_delivered_method, 'analytic', 'Delivered quantity of expense SO line should be computed by analytic amount')
self.assertTrue(sol_ordered, "A new line with ordered expense should have been created on expense report posting")
- self.assertEquals(sol_ordered.price_unit, expense1.unit_amount, "The unit price of new SO line should be the one from the expense (at cost)")
- self.assertEquals(sol_ordered.product_uom_qty, 0, "The ordered quantity of new SO line should be zero")
- self.assertEquals(sol_ordered.qty_delivered, expense1.quantity, "The delivered quantity of new SO line should be the one from the expense")
+ self.assertEqual(sol_ordered.price_unit, expense1.unit_amount, "The unit price of new SO line should be the one from the expense (at cost)")
+ self.assertEqual(sol_ordered.product_uom_qty, 0, "The ordered quantity of new SO line should be zero")
+ self.assertEqual(sol_ordered.qty_delivered, expense1.quantity, "The delivered quantity of new SO line should be the one from the expense")
sol_deliver = self.sale_order.order_line.filtered(lambda sol: sol.product_id == self.product_deliver_cost and sol != sale_order_line)
self.assertTrue(sol_deliver.is_expense, "The new SOL should be flag as coming from an expense")
- self.assertEquals(sol_deliver.qty_delivered_method, 'analytic', 'Delivered quantity of expense SO line should be computed by analytic amount')
+ self.assertEqual(sol_deliver.qty_delivered_method, 'analytic', 'Delivered quantity of expense SO line should be computed by analytic amount')
self.assertTrue(sol_deliver, "A new line with delivered expense should have been created on expense report posting")
- self.assertEquals(sol_deliver.price_unit, expense2.unit_amount, "The unit price of new SO line should be the one from the expense (at cost)")
- self.assertEquals(sol_deliver.product_uom_qty, 0, "The ordered quantity of new SO line should be zero")
- self.assertEquals(sol_deliver.qty_delivered, expense2.quantity, "The delivered quantity of new SO line should be the one from the expense")
+ self.assertEqual(sol_deliver.price_unit, expense2.unit_amount, "The unit price of new SO line should be the one from the expense (at cost)")
+ self.assertEqual(sol_deliver.product_uom_qty, 0, "The ordered quantity of new SO line should be zero")
+ self.assertEqual(sol_deliver.qty_delivered, expense2.quantity, "The delivered quantity of new SO line should be the one from the expense")
def test_sales_price_ordered(self):
""" Test invoicing expenses at sales price for product based on ordered quantities. """
@@ -118,15 +118,15 @@ class TestReInvoice(TestExpenseCommon, TestCommonSaleNoChart):
self.expense_sheet.action_sheet_move_create()
self.assertTrue(self.sale_order.analytic_account_id, "Posting expense with an expense product should trigger the analytic account creation on SO")
- self.assertEquals(self.sale_order.analytic_account_id, expense1.analytic_account_id, "SO analytic account should be the same for the expense")
- self.assertEquals(len(self.sale_order.order_line), 1, "A new So line should have been created on expense report posting")
+ self.assertEqual(self.sale_order.analytic_account_id, expense1.analytic_account_id, "SO analytic account should be the same for the expense")
+ self.assertEqual(len(self.sale_order.order_line), 1, "A new So line should have been created on expense report posting")
sol_ordered = self.sale_order.order_line.filtered(lambda sol: sol.product_id == expense1.product_id)
self.assertTrue(sol_ordered.is_expense, "The new SOL should be flag as coming from an expense")
self.assertTrue(sol_ordered, "A new line with ordered expense should have been created on expense report posting")
- self.assertEquals(sol_ordered.price_unit, 10, "The unit price of new SO line should be the one from the expense (at sales price)")
- self.assertEquals(sol_ordered.product_uom_qty, 0, "The ordered quantity of new SO line should be zero")
- self.assertEquals(sol_ordered.qty_delivered, expense1.quantity, "The delivered quantity of new SO line should be the one from the expense")
+ self.assertEqual(sol_ordered.price_unit, 10, "The unit price of new SO line should be the one from the expense (at sales price)")
+ self.assertEqual(sol_ordered.product_uom_qty, 0, "The ordered quantity of new SO line should be zero")
+ self.assertEqual(sol_ordered.qty_delivered, expense1.quantity, "The delivered quantity of new SO line should be the one from the expense")
def test_sales_price_delivered(self):
""" Test invoicing expenses at sales price for product based on delivered quantities. Check the existing SO line is not incremented, but a new
@@ -164,13 +164,13 @@ class TestReInvoice(TestExpenseCommon, TestCommonSaleNoChart):
self.expense_sheet.approve_expense_sheets()
self.expense_sheet.action_sheet_move_create()
- self.assertEquals(len(self.sale_order.order_line), 2, "A new expense SOL should have been created.")
+ self.assertEqual(len(self.sale_order.order_line), 2, "A new expense SOL should have been created.")
sale_order_line2 = self.sale_order.order_line.filtered(lambda sol: sol != sale_order_line1)
self.assertTrue(sale_order_line2.is_expense, "The new SOL should be flag as coming from an expense")
- self.assertEquals(sale_order_line2.price_unit, 10, "The unit price of SO line should be the same")
- self.assertEquals(sale_order_line2.product_uom_qty, 0, "The ordered quantity of new SO line should be zero")
- self.assertEquals(sale_order_line2.qty_delivered, expense1.quantity, "The delivered quantity of SO line should have be the same of the expense")
+ self.assertEqual(sale_order_line2.price_unit, 10, "The unit price of SO line should be the same")
+ self.assertEqual(sale_order_line2.product_uom_qty, 0, "The ordered quantity of new SO line should be zero")
+ self.assertEqual(sale_order_line2.qty_delivered, expense1.quantity, "The delivered quantity of SO line should have be the same of the expense")
# create 2nd expense lines
expense2 = self.env['hr.expense'].create({
@@ -189,11 +189,11 @@ class TestReInvoice(TestExpenseCommon, TestCommonSaleNoChart):
self.expense_sheet2.approve_expense_sheets()
self.expense_sheet2.action_sheet_move_create()
- self.assertEquals(len(self.sale_order.order_line), 2, "No new expense SOL should have been created.")
+ self.assertEqual(len(self.sale_order.order_line), 2, "No new expense SOL should have been created.")
- self.assertEquals(sale_order_line2.price_unit, 10, "The unit price of SO line should be the same")
- self.assertEquals(sale_order_line2.product_uom_qty, 0, "The ordered quantity of new SO line should remains zero")
- self.assertEquals(sale_order_line2.qty_delivered, expense1.quantity + expense2.quantity, "The delivered quantity of SO line should have been incremented")
+ self.assertEqual(sale_order_line2.price_unit, 10, "The unit price of SO line should be the same")
+ self.assertEqual(sale_order_line2.product_uom_qty, 0, "The ordered quantity of new SO line should remains zero")
+ self.assertEqual(sale_order_line2.qty_delivered, expense1.quantity + expense2.quantity, "The delivered quantity of SO line should have been incremented")
def test_no_expense(self):
""" Test invoicing expenses with no policy. Check nothing happen. """
@@ -234,7 +234,7 @@ class TestReInvoice(TestExpenseCommon, TestCommonSaleNoChart):
self.assertFalse(expense1.sale_order_id, "None reinvoicable expense can not be linked to SO")
self.assertFalse(self.sale_order.analytic_account_id, "Posting expense with an non expense product should not trigger analytic Account creation from SO, since non reinvoicable expense can not be linked to SO")
- self.assertEquals(len(self.sale_order.order_line), 1, "No SO line should have been created (or removed) on expense report posting")
- self.assertEquals(sale_order_line.price_unit, self.product_no_expense.list_price, "The unit price of SO line should be the same")
- self.assertEquals(sale_order_line.product_uom_qty, 2, "The ordered quantity of SO line should be two")
- self.assertEquals(sale_order_line.qty_delivered, 0, "The delivered quantity of SO line should have been incremented")
+ self.assertEqual(len(self.sale_order.order_line), 1, "No SO line should have been created (or removed) on expense report posting")
+ self.assertEqual(sale_order_line.price_unit, self.product_no_expense.list_price, "The unit price of SO line should be the same")
+ self.assertEqual(sale_order_line.product_uom_qty, 2, "The ordered quantity of SO line should be two")
+ self.assertEqual(sale_order_line.qty_delivered, 0, "The delivered quantity of SO line should have been incremented")
diff --git a/addons/sale_mrp/tests/test_sale_mrp_flow.py b/addons/sale_mrp/tests/test_sale_mrp_flow.py
index e7ee1afb040..9ebc2b6ed3c 100644
--- a/addons/sale_mrp/tests/test_sale_mrp_flow.py
+++ b/addons/sale_mrp/tests/test_sale_mrp_flow.py
@@ -201,7 +201,7 @@ class TestSaleMrpFlow(common.SavepointCase):
"""
moves_to_process = moves.filtered(lambda m: m.product_id in quantities_to_process.keys())
for move in moves_to_process:
- self.assertEquals(move.product_uom_qty, quantities_to_process[move.product_id])
+ self.assertEqual(move.product_uom_qty, quantities_to_process[move.product_id])
def _create_move_quantities(self, qty_to_process, components, warehouse):
""" Helper to creates moves in order to update the quantities of components
@@ -703,24 +703,24 @@ class TestSaleMrpFlow(common.SavepointCase):
so.action_confirm()
# Check picking creation
- self.assertEquals(len(so.picking_ids), 1)
+ self.assertEqual(len(so.picking_ids), 1)
picking_original = so.picking_ids[0]
move_lines = picking_original.move_lines
# Check if the correct amount of stock.moves are created
- self.assertEquals(len(move_lines), 3)
+ self.assertEqual(len(move_lines), 3)
# Check if BoM is created and is for a 'Kit'
bom_from_k1 = self.env['mrp.bom']._bom_find(product=self.kit_1)
- self.assertEquals(self.bom_kit_1.id, bom_from_k1.id)
- self.assertEquals(bom_from_k1.type, 'phantom')
+ self.assertEqual(self.bom_kit_1.id, bom_from_k1.id)
+ self.assertEqual(bom_from_k1.type, 'phantom')
# Check there's only 1 order line on the SO and it's for x10 'kit_1'
order_lines = so.order_line
- self.assertEquals(len(order_lines), 1)
+ self.assertEqual(len(order_lines), 1)
order_line = order_lines[0]
- self.assertEquals(order_line.product_id.id, self.kit_1.id)
- self.assertEquals(order_line.product_uom_qty, 10.0)
+ self.assertEqual(order_line.product_id.id, self.kit_1.id)
+ self.assertEqual(order_line.product_uom_qty, 10.0)
# Check if correct qty is ordered for each component of the kit
expected_quantities = {
@@ -736,10 +736,10 @@ class TestSaleMrpFlow(common.SavepointCase):
backorder_wizard.process()
# Check that the backorder was created, no kit should be delivered at this point
- self.assertEquals(len(so.picking_ids), 2)
+ self.assertEqual(len(so.picking_ids), 2)
backorder_1 = so.picking_ids - picking_original
- self.assertEquals(backorder_1.backorder_id.id, picking_original.id)
- self.assertEquals(order_line.qty_delivered, 0)
+ self.assertEqual(backorder_1.backorder_id.id, picking_original.id)
+ self.assertEqual(order_line.qty_delivered, 0)
# Process only x6 each componenent in the picking
# Then create a backorder for the missing components
@@ -748,13 +748,13 @@ class TestSaleMrpFlow(common.SavepointCase):
backorder_wizard.process()
# Check that a backorder is created
- self.assertEquals(len(so.picking_ids), 3)
+ self.assertEqual(len(so.picking_ids), 3)
backorder_2 = so.picking_ids - picking_original - backorder_1
- self.assertEquals(backorder_2.backorder_id.id, backorder_1.id)
+ self.assertEqual(backorder_2.backorder_id.id, backorder_1.id)
# With x6 unit of each components, we can only make 2 kits.
# So only 2 kits should be delivered
- self.assertEquals(order_line.qty_delivered, 2)
+ self.assertEqual(order_line.qty_delivered, 2)
# Process x3 more unit of each components :
# - Now only 3 kits should be delivered
@@ -764,10 +764,10 @@ class TestSaleMrpFlow(common.SavepointCase):
backorder_wizard = self.env['stock.backorder.confirmation'].create({'pick_ids': [(4, backorder_2.id)]})
backorder_wizard.process()
- self.assertEquals(len(so.picking_ids), 4)
+ self.assertEqual(len(so.picking_ids), 4)
backorder_3 = so.picking_ids - picking_original - backorder_2 - backorder_1
- self.assertEquals(backorder_3.backorder_id.id, backorder_2.id)
- self.assertEquals(order_line.qty_delivered, 3)
+ self.assertEqual(backorder_3.backorder_id.id, backorder_2.id)
+ self.assertEqual(order_line.qty_delivered, 3)
# Adding missing components
qty_to_process = {
@@ -782,7 +782,7 @@ class TestSaleMrpFlow(common.SavepointCase):
order_line._compute_qty_delivered()
# All kits should be delivered
- self.assertEquals(order_line.qty_delivered, 10)
+ self.assertEqual(order_line.qty_delivered, 10)
def test_04_sale_mrp_kit_qty_delivered(self):
""" Test that the quantities delivered are correct when
@@ -827,7 +827,7 @@ class TestSaleMrpFlow(common.SavepointCase):
# Check picking creation, its move lines should concern
# only components. Also checks that the quantities are corresponding
# to the SO
- self.assertEquals(len(so.picking_ids), 1)
+ self.assertEqual(len(so.picking_ids), 1)
order_line = so.order_line[0]
picking_original = so.picking_ids[0]
move_lines = picking_original.move_lines
@@ -844,7 +844,7 @@ class TestSaleMrpFlow(common.SavepointCase):
self.component_g: 28.0
}
- self.assertEquals(len(move_lines), 7)
+ self.assertEqual(len(move_lines), 7)
self.assertTrue(not any(kit in products for kit in kits))
self.assertTrue(all(component in products for component in components))
self._assert_quantities(move_lines, expected_quantities)
@@ -858,13 +858,13 @@ class TestSaleMrpFlow(common.SavepointCase):
backorder_wizard.process()
# Check that a backorded is created
- self.assertEquals(len(so.picking_ids), 2)
+ self.assertEqual(len(so.picking_ids), 2)
backorder_1 = so.picking_ids - picking_original
- self.assertEquals(backorder_1.backorder_id.id, picking_original.id)
+ self.assertEqual(backorder_1.backorder_id.id, picking_original.id)
# Even if some components are delivered completely,
# no KitParent should be delivered
- self.assertEquals(order_line.qty_delivered, 0)
+ self.assertEqual(order_line.qty_delivered, 0)
# Process just enough components to make 1 kit_parent
qty_to_process = {
@@ -878,12 +878,12 @@ class TestSaleMrpFlow(common.SavepointCase):
backorder_wizard.process()
# Only 1 kit_parent should be delivered at this point
- self.assertEquals(order_line.qty_delivered, 1)
+ self.assertEqual(order_line.qty_delivered, 1)
# Check that the second backorder is created
- self.assertEquals(len(so.picking_ids), 3)
+ self.assertEqual(len(so.picking_ids), 3)
backorder_2 = so.picking_ids - picking_original - backorder_1
- self.assertEquals(backorder_2.backorder_id.id, backorder_1.id)
+ self.assertEqual(backorder_2.backorder_id.id, backorder_1.id)
# Set the components quantities that backorder_2 should have
expected_quantities = {
@@ -898,7 +898,7 @@ class TestSaleMrpFlow(common.SavepointCase):
# Check that the computed quantities are matching the theorical ones.
# Since component_e was totally processed, this componenent shouldn't be
# present in backorder_2
- self.assertEquals(len(backorder_2.move_lines), 6)
+ self.assertEqual(len(backorder_2.move_lines), 6)
move_comp_e = backorder_2.move_lines.filtered(lambda m: m.product_id.id == self.component_e.id)
self.assertFalse(move_comp_e)
self._assert_quantities(backorder_2.move_lines, expected_quantities)
@@ -917,12 +917,12 @@ class TestSaleMrpFlow(common.SavepointCase):
backorder_wizard.process()
# Check that x3 kit_parents are indeed delivered
- self.assertEquals(order_line.qty_delivered, 3)
+ self.assertEqual(order_line.qty_delivered, 3)
# Check that the third backorder is created
- self.assertEquals(len(so.picking_ids), 4)
+ self.assertEqual(len(so.picking_ids), 4)
backorder_3 = so.picking_ids - (picking_original + backorder_1 + backorder_2)
- self.assertEquals(backorder_3.backorder_id.id, backorder_2.id)
+ self.assertEqual(backorder_3.backorder_id.id, backorder_2.id)
# Check the components quantities that backorder_3 should have
expected_quantities = {
@@ -941,7 +941,7 @@ class TestSaleMrpFlow(common.SavepointCase):
# Validating the last backorder now it's complete.
# All kits should be delivered
backorder_3.button_validate()
- self.assertEquals(order_line.qty_delivered, 7.0)
+ self.assertEqual(order_line.qty_delivered, 7.0)
# Return all components processed by backorder_3
stock_return_picking_form = Form(self.env['stock.return.picking']
@@ -962,7 +962,7 @@ class TestSaleMrpFlow(common.SavepointCase):
wiz.process()
# Now quantity delivered should be 3 again
- self.assertEquals(order_line.qty_delivered, 3)
+ self.assertEqual(order_line.qty_delivered, 3)
stock_return_picking_form = Form(self.env['stock.return.picking']
.with_context(active_ids=return_pick.ids, active_id=return_pick.ids[0],
@@ -984,16 +984,16 @@ class TestSaleMrpFlow(common.SavepointCase):
backorder_wizard.process()
# As one of each component is missing, only 6 kit_parents should be delivered
- self.assertEquals(order_line.qty_delivered, 6)
+ self.assertEqual(order_line.qty_delivered, 6)
# Check that the 4th backorder is created.
- self.assertEquals(len(so.picking_ids), 7)
+ self.assertEqual(len(so.picking_ids), 7)
backorder_4 = so.picking_ids - (picking_original + backorder_1 + backorder_2 + backorder_3 + return_of_return_pick + return_pick)
- self.assertEquals(backorder_4.backorder_id.id, return_of_return_pick.id)
+ self.assertEqual(backorder_4.backorder_id.id, return_of_return_pick.id)
# Check the components quantities that backorder_4 should have
for move in backorder_4.move_lines:
- self.assertEquals(move.product_qty, 1)
+ self.assertEqual(move.product_qty, 1)
@mute_logger('odoo.tests.common.onchange')
def test_05_mrp_sale_kit_availability(self):
@@ -1050,10 +1050,10 @@ class TestSaleMrpFlow(common.SavepointCase):
# Check that not enough enough quantities are available in the warehouse set in the SO
# but there are enough quantities in Warehouse 1 for 1 kit_parent
- self.assertEquals(kit_parent_wh_order.virtual_available, 0)
+ self.assertEqual(kit_parent_wh_order.virtual_available, 0)
kit_parent_wh_order.invalidate_cache()
kit_parent_wh1 = self.kit_parent.with_context(warehouse=warehouse_1.id)
- self.assertEquals(kit_parent_wh1.virtual_available, 1)
+ self.assertEqual(kit_parent_wh1.virtual_available, 1)
# Check there arn't enough quantities available for the sale order
self.assertTrue(float_compare(order_line.virtual_available_at_date - order_line.product_uom_qty, 0, precision_rounding=line.product_uom.rounding) == -1)
@@ -1073,10 +1073,10 @@ class TestSaleMrpFlow(common.SavepointCase):
# As 'Warehouse 2' is the warehouse linked to the SO, 3 kits should be available
# But the quantity available in Warehouse 1 should stay 1
kit_parent_wh_order = self.kit_parent.with_context(warehouse=so.warehouse_id.id)
- self.assertEquals(kit_parent_wh_order.virtual_available, 3)
+ self.assertEqual(kit_parent_wh_order.virtual_available, 3)
kit_parent_wh_order.invalidate_cache()
kit_parent_wh1 = self.kit_parent.with_context(warehouse=warehouse_1.id)
- self.assertEquals(kit_parent_wh1.virtual_available, 1)
+ self.assertEqual(kit_parent_wh1.virtual_available, 1)
# Check there arn't enough quantities available for the sale order
self.assertTrue(float_compare(order_line.virtual_available_at_date - order_line.product_uom_qty, 0, precision_rounding=line.product_uom.rounding) == -1)
@@ -1095,7 +1095,7 @@ class TestSaleMrpFlow(common.SavepointCase):
# Enough quantities should be available, no warning message should be displayed
kit_parent_wh_order = self.kit_parent.with_context(warehouse=so.warehouse_id.id)
- self.assertEquals(kit_parent_wh_order.virtual_available, 7)
+ self.assertEqual(kit_parent_wh_order.virtual_available, 7)
def test_06_kit_qty_delivered_mixed_uom(self):
"""
@@ -1164,7 +1164,7 @@ class TestSaleMrpFlow(common.SavepointCase):
for ml in move_lines:
corr_bom_line = bom_kit_uom_1.bom_line_ids.filtered(lambda b: b.product_id.id == ml.product_id.id)
computed_qty = ml.product_uom._compute_quantity(ml.product_uom_qty, corr_bom_line.product_uom_id)
- self.assertEquals(computed_qty, order_line.product_uom_qty * corr_bom_line.product_qty)
+ self.assertEqual(computed_qty, order_line.product_uom_qty * corr_bom_line.product_qty)
# Processe enough componenents in the picking to make 2 kit_uom_1
# Then create a backorder for the missing components
@@ -1179,12 +1179,12 @@ class TestSaleMrpFlow(common.SavepointCase):
backorder_wizard.process()
# Check that a backorder is created
- self.assertEquals(len(so.picking_ids), 2)
+ self.assertEqual(len(so.picking_ids), 2)
backorder_1 = so.picking_ids - picking_original
- self.assertEquals(backorder_1.backorder_id.id, picking_original.id)
+ self.assertEqual(backorder_1.backorder_id.id, picking_original.id)
# Only 2 kits should be delivered
- self.assertEquals(order_line.qty_delivered, 2)
+ self.assertEqual(order_line.qty_delivered, 2)
# Adding missing components
qty_to_process = {
@@ -1198,7 +1198,7 @@ class TestSaleMrpFlow(common.SavepointCase):
backorder_1.button_validate()
order_line._compute_qty_delivered()
# All kits should be delivered
- self.assertEquals(order_line.qty_delivered, 10)
+ self.assertEqual(order_line.qty_delivered, 10)
@mute_logger('odoo.tests.common.onchange')
def test_07_kit_availability_mixed_uom(self):
@@ -1290,7 +1290,7 @@ class TestSaleMrpFlow(common.SavepointCase):
# but there are enough quantities in Warehouse 1 for 1 kit_parent
kit_uom_in_kit.with_context(warehouse=warehouse_1.id)._compute_quantities()
virtual_available_wh_order = kit_uom_in_kit.virtual_available
- self.assertEquals(virtual_available_wh_order, 1)
+ self.assertEqual(virtual_available_wh_order, 1)
# Check there arn't enough quantities available for the sale order
self.assertTrue(float_compare(order_line.virtual_available_at_date - order_line.product_uom_qty, 0, precision_rounding=line.product_uom.rounding) == -1)
@@ -1309,14 +1309,14 @@ class TestSaleMrpFlow(common.SavepointCase):
self.assertTrue(float_compare(order_line.virtual_available_at_date - order_line.product_uom_qty, 0, precision_rounding=line.product_uom.rounding) == -1)
kit_uom_in_kit.with_context(warehouse=warehouse_1.id)._compute_quantities()
virtual_available_wh_order = kit_uom_in_kit.virtual_available
- self.assertEquals(virtual_available_wh_order, 3)
+ self.assertEqual(virtual_available_wh_order, 3)
# We process enough quantities to have enough kit_uom_in_kit available for the sale order.
self._create_move_quantities(qty_to_process, components, warehouse_1)
# We check that enough quantities were processed to sell 5 kit_uom_in_kit
kit_uom_in_kit.with_context(warehouse=warehouse_1.id)._compute_quantities()
- self.assertEquals(kit_uom_in_kit.virtual_available, 5)
+ self.assertEqual(kit_uom_in_kit.virtual_available, 5)
def test_10_sale_mrp_kits_routes(self):
@@ -1390,16 +1390,16 @@ class TestSaleMrpFlow(common.SavepointCase):
# Now we check that the routes of the components were applied, in order to make sure the routes set
# on the kit itself are ignored
- self.assertEquals(len(order.picking_ids), 2)
- self.assertEquals(len(order.picking_ids[0].move_lines), 1)
- self.assertEquals(len(order.picking_ids[1].move_lines), 1)
+ self.assertEqual(len(order.picking_ids), 2)
+ self.assertEqual(len(order.picking_ids[0].move_lines), 1)
+ self.assertEqual(len(order.picking_ids[1].move_lines), 1)
moves = order.picking_ids.mapped('move_lines')
move_shelf1 = moves.filtered(lambda m: m.product_id == component_shelf1)
move_shelf2 = moves.filtered(lambda m: m.product_id == component_shelf2)
- self.assertEquals(move_shelf1.location_id.id, self.ref('stock.stock_location_components'))
- self.assertEquals(move_shelf1.location_dest_id.id, self.ref('stock.stock_location_customers'))
- self.assertEquals(move_shelf2.location_id.id, self.ref('stock.stock_location_14'))
- self.assertEquals(move_shelf2.location_dest_id.id, self.ref('stock.stock_location_customers'))
+ self.assertEqual(move_shelf1.location_id.id, self.ref('stock.stock_location_components'))
+ self.assertEqual(move_shelf1.location_dest_id.id, self.ref('stock.stock_location_customers'))
+ self.assertEqual(move_shelf2.location_id.id, self.ref('stock.stock_location_14'))
+ self.assertEqual(move_shelf2.location_dest_id.id, self.ref('stock.stock_location_customers'))
def test_11_sale_mrp_explode_kits_uom_quantities(self):
@@ -1449,14 +1449,14 @@ class TestSaleMrpFlow(common.SavepointCase):
# Now we check that the routes of the components were applied, in order to make sure the routes set
# on the kit itself are ignored
- self.assertEquals(len(order.picking_ids), 1)
- self.assertEquals(len(order.picking_ids[0].move_lines), 2)
+ self.assertEqual(len(order.picking_ids), 1)
+ self.assertEqual(len(order.picking_ids[0].move_lines), 2)
# Finally, we check the quantities for each component on the picking
move_component_unit = order.picking_ids[0].move_lines.filtered(lambda m: m.product_id == component_unit)
move_component_kg = order.picking_ids[0].move_lines - move_component_unit
- self.assertEquals(move_component_unit.product_uom_qty, 0.5)
- self.assertEquals(move_component_kg.product_uom_qty, 0.583)
+ self.assertEqual(move_component_unit.product_uom_qty, 0.5)
+ self.assertEqual(move_component_kg.product_uom_qty, 0.583)
def test_product_type_service_1(self):
route_manufacture = self.warehouse.manufacture_pull_id.route_id.id
diff --git a/addons/sale_purchase/tests/test_sale_purchase.py b/addons/sale_purchase/tests/test_sale_purchase.py
index 9161b87cbbe..99a37a3ad95 100644
--- a/addons/sale_purchase/tests/test_sale_purchase.py
+++ b/addons/sale_purchase/tests/test_sale_purchase.py
@@ -255,4 +255,3 @@ class TestSalePurchase(TestCommonSalePurchaseNoChart):
self.assertEqual(len(purchase_order2.order_line), 1, "The 2nd PO has only one line")
self.assertEqual(purchase_line2.sale_line_id, self.sol1_service_purchase_1, "The 2nd PO line came from the SO line sol1_service_purchase_1")
self.assertEqual(purchase_line2.product_qty, delta, "The quantity of the new PO line is the quantity added on the Sale Line, after first PO confirmation")
-
diff --git a/addons/sale_stock/tests/test_sale_order_dates.py b/addons/sale_stock/tests/test_sale_order_dates.py
index 4cf0ae8823a..c58e4d2debe 100644
--- a/addons/sale_stock/tests/test_sale_order_dates.py
+++ b/addons/sale_stock/tests/test_sale_order_dates.py
@@ -55,7 +55,7 @@ class TestSaleExpectedDate(common.TransactionCase):
# current date + longest lead time from all of it's order lines
sale_order.write({'picking_policy': 'one'})
expected_date = fields.Datetime.now() + timedelta(days=15)
- self.assertAlmostEquals(expected_date, sale_order.expected_date,
+ self.assertAlmostEqual(expected_date, sale_order.expected_date,
msg="Wrong expected date on sale order!", delta=timedelta(seconds=1))
sale_order.action_confirm()
@@ -83,8 +83,8 @@ class TestSaleExpectedDate(common.TransactionCase):
for ml in picking.move_line_ids:
ml.qty_done = ml.product_uom_qty
picking.action_done()
- self.assertEquals(picking.state, 'done', "Picking not processed correctly!")
- self.assertEquals(fields.Date.today(), sale_order.effective_date, "Wrong effective date on sale order!")
+ self.assertEqual(picking.state, 'done', "Picking not processed correctly!")
+ self.assertEqual(fields.Date.today(), sale_order.effective_date, "Wrong effective date on sale order!")
def test_sale_order_commitment_date(self):
diff --git a/addons/sale_stock/tests/test_sale_stock.py b/addons/sale_stock/tests/test_sale_stock.py
index 2dd0250c33b..686fc854330 100644
--- a/addons/sale_stock/tests/test_sale_stock.py
+++ b/addons/sale_stock/tests/test_sale_stock.py
@@ -291,7 +291,7 @@ class TestSaleStock(TestSale):
# storable product, thus is unavailable. Hitting `button_validate` will first ask to
# process all the reserved quantities and, if the user chose to process, a second wizard
# will ask to create a backorder for the unavailable product.
- self.assertEquals(len(self.so.picking_ids), 1)
+ self.assertEqual(len(self.so.picking_ids), 1)
res_dict = self.so.picking_ids.sorted()[0].button_validate()
wizard = self.env[(res_dict.get('res_model'))].browse(res_dict.get('res_id'))
self.assertEqual(wizard._name, 'stock.immediate.transfer')
@@ -301,7 +301,7 @@ class TestSaleStock(TestSale):
wizard.process()
# Now, the original picking is done and there is a new one (the backorder).
- self.assertEquals(len(self.so.picking_ids), 2)
+ self.assertEqual(len(self.so.picking_ids), 2)
for picking in self.so.picking_ids:
move = picking.move_lines
if picking.backorder_id:
@@ -319,7 +319,7 @@ class TestSaleStock(TestSale):
]
})
# a single picking should be created for the new delivery
- self.assertEquals(len(self.so.picking_ids), 2)
+ self.assertEqual(len(self.so.picking_ids), 2)
backorder = self.so.picking_ids.filtered(lambda p: p.backorder_id)
self.assertEqual(len(backorder.move_lines), 2)
for backorder_move in backorder.move_lines:
@@ -347,11 +347,11 @@ class TestSaleStock(TestSale):
self.so.action_confirm()
# deliver them
- self.assertEquals(len(self.so.picking_ids), 1)
+ self.assertEqual(len(self.so.picking_ids), 1)
res_dict = self.so.picking_ids.sorted()[0].button_validate()
wizard = self.env[(res_dict.get('res_model'))].browse(res_dict.get('res_id'))
wizard.process()
- self.assertEquals(self.so.picking_ids.sorted()[0].state, "done")
+ self.assertEqual(self.so.picking_ids.sorted()[0].state, "done")
# update the two original sale order lines
self.so.write({
@@ -361,7 +361,7 @@ class TestSaleStock(TestSale):
]
})
# a single picking should be created for the new delivery
- self.assertEquals(len(self.so.picking_ids), 2)
+ self.assertEqual(len(self.so.picking_ids), 2)
def test_05_confirm_cancel_confirm(self):
""" Confirm a sale order, cancel it, set to quotation, change the
diff --git a/addons/sale_stock/tests/test_sale_stock_lead_time.py b/addons/sale_stock/tests/test_sale_stock_lead_time.py
index 58d912b0215..fbf6080059d 100644
--- a/addons/sale_stock/tests/test_sale_stock_lead_time.py
+++ b/addons/sale_stock/tests/test_sale_stock_lead_time.py
@@ -178,9 +178,9 @@ class TestSaleStockLeadTime(TestStockCommon):
pick.write({'scheduled_date': pick_min_date + timedelta(days=5)})
# Now check scheduled_date of pack and out are changed or not based on propagate is true on rules?
- self.assertEquals(pack.scheduled_date, (pack_min_date + timedelta(days=5)),
+ self.assertEqual(pack.scheduled_date, (pack_min_date + timedelta(days=5)),
'Schedule date of pack should be changed based on delay.')
- self.assertEquals(out.scheduled_date, (out_min_date + timedelta(days=5)),
+ self.assertEqual(out.scheduled_date, (out_min_date + timedelta(days=5)),
'Schedule date of out should be changed based on delay.')
def test_03_no_propagate_date(self):
@@ -256,5 +256,5 @@ class TestSaleStockLeadTime(TestStockCommon):
pick.write({'scheduled_date': pick_min_date + timedelta(days=5)})
# Now check scheduled_date of pack and out are changed or not based on propagate is false on rules?
- self.assertEquals(pack.scheduled_date, pack_min_date, 'Schedule date of pack should not be changed.')
- self.assertEquals(out.scheduled_date, out_min_date, 'Schedule date of out should not be changed.')
+ self.assertEqual(pack.scheduled_date, pack_min_date, 'Schedule date of pack should not be changed.')
+ self.assertEqual(out.scheduled_date, out_min_date, 'Schedule date of out should not be changed.')
diff --git a/addons/sale_timesheet/tests/test_project_billing.py b/addons/sale_timesheet/tests/test_project_billing.py
index 7f0d533af8c..a7c1514a8d5 100644
--- a/addons/sale_timesheet/tests/test_project_billing.py
+++ b/addons/sale_timesheet/tests/test_project_billing.py
@@ -337,7 +337,7 @@ class TestProjectBilling(TestCommonSaleTimesheetNoChart):
'employee_id': self.employee_tde.id,
})
- self.assertEquals(timesheet4.so_line, self.so1_line_deliver_no_task, "The timesheet should be linked to SOL on the project, as no entry for TDE in project map and no SOL on task")
+ self.assertEqual(timesheet4.so_line, self.so1_line_deliver_no_task, "The timesheet should be linked to SOL on the project, as no entry for TDE in project map and no SOL on task")
def test_billing_task_rate(self):
""" Check task and subtask creation, and timesheeting in a project billed at 'task rate'. Then move the task into a 'employee rate' project then, 'non billable'. """
diff --git a/addons/sale_timesheet/tests/test_reinvoice.py b/addons/sale_timesheet/tests/test_reinvoice.py
index 5b3058ce5ef..2f3c6677207 100644
--- a/addons/sale_timesheet/tests/test_reinvoice.py
+++ b/addons/sale_timesheet/tests/test_reinvoice.py
@@ -73,8 +73,8 @@ class TestReInvoice(TestCommonSaleTimesheetNoChart):
self.sale_order._compute_tax_id()
self.sale_order.action_confirm()
- self.assertEquals(sale_order_line1.qty_delivered_method, 'timesheet', "Delivered quantity of 'service' SO line should be computed by timesheet amount")
- self.assertEquals(sale_order_line2.qty_delivered_method, 'timesheet', "Delivered quantity of 'service' SO line should be computed by timesheet amount")
+ self.assertEqual(sale_order_line1.qty_delivered_method, 'timesheet', "Delivered quantity of 'service' SO line should be computed by timesheet amount")
+ self.assertEqual(sale_order_line2.qty_delivered_method, 'timesheet', "Delivered quantity of 'service' SO line should be computed by timesheet amount")
# let's log some timesheets (on the project created by sale_order_line1)
task_sol1 = sale_order_line1.task_id
@@ -104,21 +104,21 @@ class TestReInvoice(TestCommonSaleTimesheetNoChart):
self.assertTrue(sale_order_line3, "A new sale line should have been created with ordered product")
self.assertTrue(sale_order_line4, "A new sale line should have been created with delivered product")
- self.assertEquals(len(self.sale_order.order_line), 4, "There should be 4 lines on the SO (2 vendor bill lines created)")
- self.assertEquals(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 2, "There should be 4 lines on the SO (2 vendor bill lines created)")
+ self.assertEqual(len(self.sale_order.order_line), 4, "There should be 4 lines on the SO (2 vendor bill lines created)")
+ self.assertEqual(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 2, "There should be 4 lines on the SO (2 vendor bill lines created)")
- self.assertEquals(sale_order_line1.qty_delivered, 1, "Exising SO line 1 should not be impacted by reinvoicing product at cost")
- self.assertEquals(sale_order_line2.qty_delivered, 0, "Exising SO line 2 should not be impacted by reinvoicing product at cost")
+ self.assertEqual(sale_order_line1.qty_delivered, 1, "Exising SO line 1 should not be impacted by reinvoicing product at cost")
+ self.assertEqual(sale_order_line2.qty_delivered, 0, "Exising SO line 2 should not be impacted by reinvoicing product at cost")
self.assertFalse(sale_order_line3.task_id, "Adding a new expense SO line should not create a task (sol3)")
self.assertFalse(sale_order_line4.task_id, "Adding a new expense SO line should not create a task (sol4)")
- self.assertEquals(len(self.sale_order.project_ids), 1, "SO create only one project with its service line. Adding new expense SO line should not impact that")
+ self.assertEqual(len(self.sale_order.project_ids), 1, "SO create only one project with its service line. Adding new expense SO line should not impact that")
- self.assertEquals((sale_order_line3.price_unit, sale_order_line3.qty_delivered, sale_order_line3.product_uom_qty, sale_order_line3.qty_invoiced), (self.product_ordered_cost.standard_price, 3.0, 0, 0), 'Sale line is wrong after confirming vendor invoice')
- self.assertEquals((sale_order_line4.price_unit, sale_order_line4.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line4.qty_invoiced), (self.product_deliver_cost.standard_price, 3.0, 0, 0), 'Sale line is wrong after confirming vendor invoice')
+ self.assertEqual((sale_order_line3.price_unit, sale_order_line3.qty_delivered, sale_order_line3.product_uom_qty, sale_order_line3.qty_invoiced), (self.product_ordered_cost.standard_price, 3.0, 0, 0), 'Sale line is wrong after confirming vendor invoice')
+ self.assertEqual((sale_order_line4.price_unit, sale_order_line4.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line4.qty_invoiced), (self.product_deliver_cost.standard_price, 3.0, 0, 0), 'Sale line is wrong after confirming vendor invoice')
- self.assertEquals(sale_order_line3.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line should be computed by analytic amount")
- self.assertEquals(sale_order_line4.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line should be computed by analytic amount")
+ self.assertEqual(sale_order_line3.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line should be computed by analytic amount")
+ self.assertEqual(sale_order_line4.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line should be computed by analytic amount")
# create second invoice lines and validate it
move_form = Form(self.env['account.move'].with_context(default_type='in_invoice'))
@@ -140,11 +140,11 @@ class TestReInvoice(TestCommonSaleTimesheetNoChart):
self.assertTrue(sale_order_line5, "A new sale line should have been created with ordered product")
self.assertTrue(sale_order_line6, "A new sale line should have been created with delivered product")
- self.assertEquals(len(self.sale_order.order_line), 6, "There should be still 4 lines on the SO, no new created")
- self.assertEquals(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 4, "There should be still 2 expenses lines on the SO")
+ self.assertEqual(len(self.sale_order.order_line), 6, "There should be still 4 lines on the SO, no new created")
+ self.assertEqual(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 4, "There should be still 2 expenses lines on the SO")
- self.assertEquals((sale_order_line5.price_unit, sale_order_line5.qty_delivered, sale_order_line5.product_uom_qty, sale_order_line5.qty_invoiced), (self.product_ordered_cost.standard_price, 2.0, 0, 0), 'Sale line 5 is wrong after confirming 2e vendor invoice')
- self.assertEquals((sale_order_line6.price_unit, sale_order_line6.qty_delivered, sale_order_line6.product_uom_qty, sale_order_line6.qty_invoiced), (self.product_deliver_cost.standard_price, 2.0, 0, 0), 'Sale line 6 is wrong after confirming 2e vendor invoice')
+ self.assertEqual((sale_order_line5.price_unit, sale_order_line5.qty_delivered, sale_order_line5.product_uom_qty, sale_order_line5.qty_invoiced), (self.product_ordered_cost.standard_price, 2.0, 0, 0), 'Sale line 5 is wrong after confirming 2e vendor invoice')
+ self.assertEqual((sale_order_line6.price_unit, sale_order_line6.qty_delivered, sale_order_line6.product_uom_qty, sale_order_line6.qty_invoiced), (self.product_deliver_cost.standard_price, 2.0, 0, 0), 'Sale line 6 is wrong after confirming 2e vendor invoice')
def test_sales_price(self):
""" Test invoicing vendor bill at sales price for products based on delivered and ordered quantities. Check no existing SO line is incremented, but when invoicing a
@@ -203,21 +203,21 @@ class TestReInvoice(TestCommonSaleTimesheetNoChart):
self.assertTrue(sale_order_line3, "A new sale line should have been created with ordered product")
self.assertTrue(sale_order_line4, "A new sale line should have been created with delivered product")
- self.assertEquals(len(self.sale_order.order_line), 4, "There should be 4 lines on the SO (2 vendor bill lines created)")
- self.assertEquals(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 2, "There should be 4 lines on the SO (2 vendor bill lines created)")
+ self.assertEqual(len(self.sale_order.order_line), 4, "There should be 4 lines on the SO (2 vendor bill lines created)")
+ self.assertEqual(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 2, "There should be 4 lines on the SO (2 vendor bill lines created)")
- self.assertEquals(sale_order_line1.qty_delivered, 1, "Exising SO line 1 should not be impacted by reinvoicing product at cost")
- self.assertEquals(sale_order_line2.qty_delivered, 0, "Exising SO line 2 should not be impacted by reinvoicing product at cost")
+ self.assertEqual(sale_order_line1.qty_delivered, 1, "Exising SO line 1 should not be impacted by reinvoicing product at cost")
+ self.assertEqual(sale_order_line2.qty_delivered, 0, "Exising SO line 2 should not be impacted by reinvoicing product at cost")
self.assertFalse(sale_order_line3.task_id, "Adding a new expense SO line should not create a task (sol3)")
self.assertFalse(sale_order_line4.task_id, "Adding a new expense SO line should not create a task (sol4)")
- self.assertEquals(len(self.sale_order.project_ids), 1, "SO create only one project with its service line. Adding new expense SO line should not impact that")
+ self.assertEqual(len(self.sale_order.project_ids), 1, "SO create only one project with its service line. Adding new expense SO line should not impact that")
- self.assertEquals((sale_order_line3.price_unit, sale_order_line3.qty_delivered, sale_order_line3.product_uom_qty, sale_order_line3.qty_invoiced), (self.product_deliver_sales_price.list_price, 3.0, 0, 0), 'Sale line is wrong after confirming vendor invoice')
- self.assertEquals((sale_order_line4.price_unit, sale_order_line4.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line4.qty_invoiced), (self.product_order_sales_price.list_price, 3.0, 0, 0), 'Sale line is wrong after confirming vendor invoice')
+ self.assertEqual((sale_order_line3.price_unit, sale_order_line3.qty_delivered, sale_order_line3.product_uom_qty, sale_order_line3.qty_invoiced), (self.product_deliver_sales_price.list_price, 3.0, 0, 0), 'Sale line is wrong after confirming vendor invoice')
+ self.assertEqual((sale_order_line4.price_unit, sale_order_line4.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line4.qty_invoiced), (self.product_order_sales_price.list_price, 3.0, 0, 0), 'Sale line is wrong after confirming vendor invoice')
- self.assertEquals(sale_order_line3.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line 3 should be computed by analytic amount")
- self.assertEquals(sale_order_line4.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line 4 should be computed by analytic amount")
+ self.assertEqual(sale_order_line3.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line 3 should be computed by analytic amount")
+ self.assertEqual(sale_order_line4.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line 4 should be computed by analytic amount")
# create second invoice lines and validate it
move_form = Form(self.env['account.move'].with_context(default_type='in_invoice'))
@@ -239,10 +239,10 @@ class TestReInvoice(TestCommonSaleTimesheetNoChart):
self.assertFalse(sale_order_line5, "No new sale line should have been created with delivered product !!")
self.assertTrue(sale_order_line6, "A new sale line should have been created with ordered product")
- self.assertEquals(len(self.sale_order.order_line), 5, "There should be 5 lines on the SO, 1 new created and 1 incremented")
- self.assertEquals(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 3, "There should be 3 expenses lines on the SO")
+ self.assertEqual(len(self.sale_order.order_line), 5, "There should be 5 lines on the SO, 1 new created and 1 incremented")
+ self.assertEqual(len(self.sale_order.order_line.filtered(lambda sol: sol.is_expense)), 3, "There should be 3 expenses lines on the SO")
- self.assertEquals((sale_order_line6.price_unit, sale_order_line6.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line6.qty_invoiced), (self.product_order_sales_price.list_price, 2.0, 0, 0), 'Sale line is wrong after confirming 2e vendor invoice')
+ self.assertEqual((sale_order_line6.price_unit, sale_order_line6.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line6.qty_invoiced), (self.product_order_sales_price.list_price, 2.0, 0, 0), 'Sale line is wrong after confirming 2e vendor invoice')
def test_no_expense(self):
""" Test invoicing vendor bill with no policy. Check nothing happen. """
@@ -279,6 +279,6 @@ class TestReInvoice(TestCommonSaleTimesheetNoChart):
'employee_id': self.employee_user.id,
})
- self.assertEquals(len(self.sale_order.order_line), 1, "No SO line should have been created (or removed) when validating vendor bill")
- self.assertEquals(sale_order_line.qty_delivered, 1, "The delivered quantity of SO line should not have been incremented")
+ self.assertEqual(len(self.sale_order.order_line), 1, "No SO line should have been created (or removed) when validating vendor bill")
+ self.assertEqual(sale_order_line.qty_delivered, 1, "The delivered quantity of SO line should not have been incremented")
self.assertTrue(invoice_a.mapped('line_ids.analytic_line_ids'), "Analytic lines should be generated")
diff --git a/addons/sms/views/sms_template_views.xml b/addons/sms/views/sms_template_views.xml
index 28d76d30e13..2c80af0667b 100644
--- a/addons/sms/views/sms_template_views.xml
+++ b/addons/sms/views/sms_template_views.xml
@@ -10,7 +10,7 @@
<field name="sidebar_action_id" invisible="1"/>
<button name="action_create_sidebar_action" type="object"
groups="base.group_system"
- class="oe_stat_button"
+ class="oe_stat_button"
attrs="{'invisible':[('sidebar_action_id','!=',False)]}" icon="fa-plus"
help="Add a contextual action on the related model to open a sms composer with this template">
<div class="o_field_widget o_stat_info">
diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py
index f3da4c34a37..3cc9376855b 100644
--- a/addons/stock/models/product.py
+++ b/addons/stock/models/product.py
@@ -857,4 +857,3 @@ class UoM(models.Model):
else:
computed_qty = self._compute_quantity(qty, procurement_uom, rounding_method='HALF-UP')
return (computed_qty, procurement_uom)
-
diff --git a/addons/stock/models/product_strategy.py b/addons/stock/models/product_strategy.py
index 51a03cbb47c..647c09ec30f 100644
--- a/addons/stock/models/product_strategy.py
+++ b/addons/stock/models/product_strategy.py
@@ -86,4 +86,3 @@ class StockPutawayRule(models.Model):
if rule.company_id.id != vals['company_id']:
raise UserError(_("Changing the company of this record is forbidden at this point, you should rather archive it and create a new one."))
return super(StockPutawayRule, self).write(vals)
-
diff --git a/addons/stock/models/res_company.py b/addons/stock/models/res_company.py
index d18b74eafa2..41eca71e2d8 100644
--- a/addons/stock/models/res_company.py
+++ b/addons/stock/models/res_company.py
@@ -185,4 +185,3 @@ class Company(models.Model):
company.sudo()._create_per_company_rules()
self.env['stock.warehouse'].sudo().create({'name': company.name, 'code': company.name[:5], 'company_id': company.id, 'partner_id': company.partner_id.id})
return company
-
diff --git a/addons/stock/models/res_partner.py b/addons/stock/models/res_partner.py
index faf25801c86..5f2590d1ae8 100644
--- a/addons/stock/models/res_partner.py
+++ b/addons/stock/models/res_partner.py
@@ -19,4 +19,3 @@ class Partner(models.Model):
help="The stock location used as source when receiving goods from this contact.")
picking_warn = fields.Selection(WARNING_MESSAGE, 'Stock Picking', help=WARNING_HELP, default='no-message')
picking_warn_msg = fields.Text('Message for Stock Picking')
-
diff --git a/addons/stock/models/stock_inventory.py b/addons/stock/models/stock_inventory.py
index 556d012d895..06acf3e6e01 100644
--- a/addons/stock/models/stock_inventory.py
+++ b/addons/stock/models/stock_inventory.py
@@ -527,4 +527,3 @@ class InventoryLine(models.Model):
lines = self.search([('inventory_id', '=', self.env.context.get('default_inventory_id'))])
line_ids = lines.filtered(lambda line: line.outdated == value).ids
return [('id', 'in', line_ids)]
-
diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index ec78b56eca9..4088ab96ea3 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -1483,4 +1483,3 @@ class StockMove(models.Model):
move.procure_method = rules.procure_method
else:
move.procure_method = 'make_to_stock'
-
diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py
index 3f48a5644b5..9167479f2ae 100644
--- a/addons/stock/models/stock_move_line.py
+++ b/addons/stock/models/stock_move_line.py
@@ -559,4 +559,3 @@ class StockMoveLine(models.Model):
def _should_bypass_reservation(self, location):
self.ensure_one()
return location.should_bypass_reservation() or self.product_id.type != 'product'
-
diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py
index 931f789cc08..50d1874e562 100644
--- a/addons/stock/models/stock_picking.py
+++ b/addons/stock/models/stock_picking.py
@@ -1221,4 +1221,3 @@ class Picking(models.Model):
action['context'] = self.env.context
action['domain'] = [('picking_id', 'in', self.ids)]
return action
-
diff --git a/addons/stock/models/stock_production_lot.py b/addons/stock/models/stock_production_lot.py
index a6594a55a52..46f020c890d 100644
--- a/addons/stock/models/stock_production_lot.py
+++ b/addons/stock/models/stock_production_lot.py
@@ -94,4 +94,3 @@ class ProductionLot(models.Model):
if self.user_has_groups('stock.group_stock_manager'):
self = self.with_context(inventory_mode=True)
return self.env['stock.quant']._get_quants_action()
-
diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py
index ba8ea9cd119..8c78b5434bf 100644
--- a/addons/stock/models/stock_quant.py
+++ b/addons/stock/models/stock_quant.py
@@ -733,4 +733,3 @@ class QuantPackage(models.Model):
res[quant.product_id] = 0
res[quant.product_id] += quant.quantity
return res
-
diff --git a/addons/stock/models/stock_scrap.py b/addons/stock/models/stock_scrap.py
index 5ad0a297093..ccf5dfeff11 100644
--- a/addons/stock/models/stock_scrap.py
+++ b/addons/stock/models/stock_scrap.py
@@ -181,4 +181,3 @@ class StockScrap(models.Model):
'context': ctx,
'target': 'new'
}
-
diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py
index eaeca84b039..6283ee484f1 100644
--- a/addons/stock/models/stock_warehouse.py
+++ b/addons/stock/models/stock_warehouse.py
@@ -1095,4 +1095,3 @@ class Orderpoint(models.Model):
'orderpoint_id': self,
'group_id': group or self.group_id,
}
-
diff --git a/addons/stock/tests/test_move2.py b/addons/stock/tests/test_move2.py
index e1380a8ec28..934841081ad 100644
--- a/addons/stock/tests/test_move2.py
+++ b/addons/stock/tests/test_move2.py
@@ -902,8 +902,8 @@ class TestSinglePicking(TestStockCommon):
# No backorder should be created and the move corresponding to the missing product should be cancelled
backorder = self.env['stock.picking'].search([('backorder_id', '=', delivery_order.id)])
self.assertFalse(backorder)
- self.assertEquals(delivery_order.state, 'done')
- self.assertEquals(delivery_order.move_lines[1].state, 'cancel')
+ self.assertEqual(delivery_order.state, 'done')
+ self.assertEqual(delivery_order.move_lines[1].state, 'cancel')
def test_extra_move_1(self):
""" Check the good behavior of creating an extra move in a delivery order. This usecase
@@ -2161,16 +2161,16 @@ class TestRoutes(TestStockCommon):
# 3 pickings should be created.
picking_ids = self.env['stock.picking'].search([('group_id', '=', pg.id)])
- self.assertEquals(len(picking_ids), 3)
+ self.assertEqual(len(picking_ids), 3)
for picking in picking_ids:
# Only the picking from Stock to Pack should be MTS
if picking.location_id == warehouse.lot_stock_id:
- self.assertEquals(picking.move_lines.procure_method, 'make_to_stock')
+ self.assertEqual(picking.move_lines.procure_method, 'make_to_stock')
else:
- self.assertEquals(picking.move_lines.procure_method, 'make_to_order')
+ self.assertEqual(picking.move_lines.procure_method, 'make_to_order')
- self.assertEquals(len(picking.move_lines), 1)
- self.assertEquals(picking.move_lines.product_uom_qty, 5, 'The quantity of the move should be the same as on the SO')
+ self.assertEqual(len(picking.move_lines), 1)
+ self.assertEqual(picking.move_lines.product_uom_qty, 5, 'The quantity of the move should be the same as on the SO')
self.assertEqual(qty_available, 4, 'The 4 products should still be available')
def test_mtso_mts(self):
@@ -2213,11 +2213,11 @@ class TestRoutes(TestStockCommon):
# A picking should be created with its move having MTS as procure method.
picking_ids = self.env['stock.picking'].search([('group_id', '=', pg.id)])
- self.assertEquals(len(picking_ids), 1)
+ self.assertEqual(len(picking_ids), 1)
picking = picking_ids
- self.assertEquals(picking.move_lines.procure_method, 'make_to_stock')
- self.assertEquals(len(picking.move_lines), 1)
- self.assertEquals(picking.move_lines.product_uom_qty, 4)
+ self.assertEqual(picking.move_lines.procure_method, 'make_to_stock')
+ self.assertEqual(len(picking.move_lines), 1)
+ self.assertEqual(picking.move_lines.product_uom_qty, 4)
def test_mtso_multi_pg(self):
""" Run 3 procurements for 2 products at the same times when there are 4 in stock then
@@ -2291,23 +2291,23 @@ class TestRoutes(TestStockCommon):
# The 2 first procurements should have create only 1 picking since enough quantities
# are left in the delivery location
- self.assertEquals(len(pickings_pg1), 1)
- self.assertEquals(len(pickings_pg2), 1)
- self.assertEquals(pickings_pg1.move_lines.procure_method, 'make_to_stock')
- self.assertEquals(pickings_pg2.move_lines.procure_method, 'make_to_stock')
+ self.assertEqual(len(pickings_pg1), 1)
+ self.assertEqual(len(pickings_pg2), 1)
+ self.assertEqual(pickings_pg1.move_lines.procure_method, 'make_to_stock')
+ self.assertEqual(pickings_pg2.move_lines.procure_method, 'make_to_stock')
# The last one should have 3 pickings as there's nothing left in the delivery location
- self.assertEquals(len(pickings_pg3), 3)
+ self.assertEqual(len(pickings_pg3), 3)
for picking in pickings_pg3:
# Only the picking from Stock to Pack should be MTS
if picking.location_id == warehouse.lot_stock_id:
- self.assertEquals(picking.move_lines.procure_method, 'make_to_stock')
+ self.assertEqual(picking.move_lines.procure_method, 'make_to_stock')
else:
- self.assertEquals(picking.move_lines.procure_method, 'make_to_order')
+ self.assertEqual(picking.move_lines.procure_method, 'make_to_order')
# All the moves should be should have the same quantity as it is on each procurements
- self.assertEquals(len(picking.move_lines), 1)
- self.assertEquals(picking.move_lines.product_uom_qty, 2)
+ self.assertEqual(len(picking.move_lines), 1)
+ self.assertEqual(picking.move_lines.product_uom_qty, 2)
def test_delay_alert_1(self):
""" On a pick pack ship scenario, enable the delay alert flag on the pack rule. Edit the
diff --git a/addons/stock/tests/test_packing.py b/addons/stock/tests/test_packing.py
index 4f135c70cb7..cc7b9efb895 100644
--- a/addons/stock/tests/test_packing.py
+++ b/addons/stock/tests/test_packing.py
@@ -74,8 +74,8 @@ class TestPacking(SavepointCase):
pick_picking.move_line_ids.filtered(lambda ml: ml.product_id == self.productB).qty_done = 2.0
first_pack = pick_picking.put_in_pack()
- self.assertEquals(len(pick_picking.package_level_ids), 1, 'Put some products in pack should create a package_level')
- self.assertEquals(pick_picking.package_level_ids[0].state, 'new', 'A new pack should be in state "new"')
+ self.assertEqual(len(pick_picking.package_level_ids), 1, 'Put some products in pack should create a package_level')
+ self.assertEqual(pick_picking.package_level_ids[0].state, 'new', 'A new pack should be in state "new"')
pick_picking.move_line_ids.filtered(lambda ml: ml.product_id == self.productA and ml.qty_done == 0.0).qty_done = 4.0
pick_picking.move_line_ids.filtered(lambda ml: ml.product_id == self.productB and ml.qty_done == 0.0).qty_done = 3.0
second_pack = pick_picking.put_in_pack()
@@ -106,42 +106,42 @@ class TestPacking(SavepointCase):
'location_dest_id': self.stock_location.id,
'company_id': picking.company_id.id,
})
- self.assertEquals(package_level.state, 'draft',
+ self.assertEqual(package_level.state, 'draft',
'The package_level should be in draft as it has no moves, move lines and is not confirmed')
picking.action_confirm()
self.assertEqual(len(picking.move_ids_without_package), 0)
self.assertEqual(len(picking.move_lines), 1,
'One move should be created when the package_level has been confirmed')
- self.assertEquals(len(package_level.move_ids), 1,
+ self.assertEqual(len(package_level.move_ids), 1,
'The move should be in the package level')
- self.assertEquals(package_level.state, 'confirmed',
+ self.assertEqual(package_level.state, 'confirmed',
'The package level must be state confirmed when picking is confirmed')
picking.action_assign()
self.assertEqual(len(picking.move_lines), 1,
'You still have only one move when the picking is assigned')
self.assertEqual(len(picking.move_lines.move_line_ids), 1,
'The move should have one move line which is the reservation')
- self.assertEquals(picking.move_line_ids.package_level_id.id, package_level.id,
+ self.assertEqual(picking.move_line_ids.package_level_id.id, package_level.id,
'The move line created should be linked to the package level')
- self.assertEquals(picking.move_line_ids.package_id.id, pack.id,
+ self.assertEqual(picking.move_line_ids.package_id.id, pack.id,
'The move line must have been reserved on the package of the package_level')
- self.assertEquals(picking.move_line_ids.result_package_id.id, pack.id,
+ self.assertEqual(picking.move_line_ids.result_package_id.id, pack.id,
'The move line must have the same package as result package')
- self.assertEquals(package_level.state, 'assigned', 'The package level must be in state assigned')
+ self.assertEqual(package_level.state, 'assigned', 'The package level must be in state assigned')
package_level.write({'is_done': True})
- self.assertEquals(len(package_level.move_line_ids), 1,
+ self.assertEqual(len(package_level.move_line_ids), 1,
'The package level should still keep one move line after have been set to "done"')
- self.assertEquals(package_level.move_line_ids[0].qty_done, 20.0,
+ self.assertEqual(package_level.move_line_ids[0].qty_done, 20.0,
'All quantity in package must be procesed in move line')
picking.button_validate()
self.assertEqual(len(picking.move_lines), 1,
'You still have only one move when the picking is assigned')
self.assertEqual(len(picking.move_lines.move_line_ids), 1,
'The move should have one move line which is the reservation')
- self.assertEquals(package_level.state, 'done', 'The package level must be in state done')
- self.assertEquals(pack.location_id.id, picking.location_dest_id.id,
+ self.assertEqual(package_level.state, 'done', 'The package level must be in state done')
+ self.assertEqual(pack.location_id.id, picking.location_dest_id.id,
'The quant package must be in the destination location')
- self.assertEquals(pack.quant_ids[0].location_id.id, picking.location_dest_id.id,
+ self.assertEqual(pack.quant_ids[0].location_id.id, picking.location_dest_id.id,
'The quant must be in the destination location')
def test_multi_pack_reservation(self):
diff --git a/addons/stock/tests/test_proc_rule.py b/addons/stock/tests/test_proc_rule.py
index 7789563ad21..8d33bfb85fa 100644
--- a/addons/stock/tests/test_proc_rule.py
+++ b/addons/stock/tests/test_proc_rule.py
@@ -101,13 +101,13 @@ class TestProcRule(TransactionCase):
# change above the minimum delta
move_orig.date_expected += timedelta(days=6)
- self.assertAlmostEquals(move_dest.date_expected, move_dest_initial_date + timedelta(days=6), delta=timedelta(seconds=10), msg='date should be propagated as the minimum delta is below')
+ self.assertAlmostEqual(move_dest.date_expected, move_dest_initial_date + timedelta(days=6), delta=timedelta(seconds=10), msg='date should be propagated as the minimum delta is below')
# change below the minimum delta
move_dest_initial_date = move_dest.date_expected
move_orig.date_expected += timedelta(days=4)
- self.assertAlmostEquals(move_dest.date_expected, move_dest_initial_date, delta=timedelta(seconds=10), msg='date should not be propagated as the minimum delta is above')
+ self.assertAlmostEqual(move_dest.date_expected, move_dest_initial_date, delta=timedelta(seconds=10), msg='date should not be propagated as the minimum delta is above')
def test_rule_propagate_2(self):
move_dest = self.env['stock.move'].create({
@@ -133,11 +133,11 @@ class TestProcRule(TransactionCase):
# change below the minimum delta
move_orig.date_expected += timedelta(days=4)
- self.assertAlmostEquals(move_dest.date_expected, move_dest_initial_date, delta=timedelta(seconds=10), msg='date should not be propagated')
+ self.assertAlmostEqual(move_dest.date_expected, move_dest_initial_date, delta=timedelta(seconds=10), msg='date should not be propagated')
# change above the minimum delta
move_orig.date_expected += timedelta(days=2)
- self.assertAlmostEquals(move_dest.date_expected, move_dest_initial_date, delta=timedelta(seconds=10), msg='date should not be propagated')
+ self.assertAlmostEqual(move_dest.date_expected, move_dest_initial_date, delta=timedelta(seconds=10), msg='date should not be propagated')
def test_rule_propagate_3(self):
move_dest = self.env['stock.move'].create({
@@ -163,6 +163,6 @@ class TestProcRule(TransactionCase):
move_dest_initial_date = move_dest.date_expected
move_orig_initial_date = move_orig.date_expected
move_orig._action_done()
- self.assertAlmostEquals(move_orig.date_expected, move_orig_initial_date, delta=timedelta(seconds=10), msg='schedule date should not be impacted by action_done')
- self.assertAlmostEquals(move_orig.date, datetime.now(), delta=timedelta(seconds=10), msg='date should be now')
- self.assertAlmostEquals(move_dest.date_expected, move_dest_initial_date + timedelta(days=6), delta=timedelta(seconds=10), msg='date should be propagated')
+ self.assertAlmostEqual(move_orig.date_expected, move_orig_initial_date, delta=timedelta(seconds=10), msg='schedule date should not be impacted by action_done')
+ self.assertAlmostEqual(move_orig.date, datetime.now(), delta=timedelta(seconds=10), msg='date should be now')
+ self.assertAlmostEqual(move_dest.date_expected, move_dest_initial_date + timedelta(days=6), delta=timedelta(seconds=10), msg='date should be propagated')
diff --git a/addons/stock/tests/test_stock_flow.py b/addons/stock/tests/test_stock_flow.py
index 27990338ad3..5f8de5e40d8 100644
--- a/addons/stock/tests/test_stock_flow.py
+++ b/addons/stock/tests/test_stock_flow.py
@@ -1542,7 +1542,7 @@ class TestStockFlow(TestStockCommon):
'location_dest_id': self.customer_location})
# validate this delivery order, it should be in the waiting state
picking_out.action_assign()
- self.assertEquals(picking_out.state, "confirmed")
+ self.assertEqual(picking_out.state, "confirmed")
# receive one product in stock
inventory = self.env['stock.inventory'].create({
@@ -1558,9 +1558,9 @@ class TestStockFlow(TestStockCommon):
inventory.action_validate()
# recheck availability of the delivery order, it should be assigned
picking_out.action_assign()
- self.assertEquals(len(picking_out.move_lines), 1.0)
- self.assertEquals(picking_out.move_lines.product_qty, 2.0)
- self.assertEquals(picking_out.state, "assigned")
+ self.assertEqual(len(picking_out.move_lines), 1.0)
+ self.assertEqual(picking_out.move_lines.product_qty, 2.0)
+ self.assertEqual(picking_out.state, "assigned")
def test_71_picking_state_all_at_once_force_assign(self):
""" This test will check that the state of the picking is correctly computed according
@@ -1590,7 +1590,7 @@ class TestStockFlow(TestStockCommon):
# validate this delivery order, it should be in the waiting state
picking_out.action_assign()
- self.assertEquals(picking_out.state, "confirmed")
+ self.assertEqual(picking_out.state, "confirmed")
def test_72_picking_state_partial_reserve(self):
""" This test will check that the state of the picking is correctly computed according
@@ -1633,7 +1633,7 @@ class TestStockFlow(TestStockCommon):
# validate this delivery order, it should be in partially available
picking_out.action_assign()
- self.assertEquals(picking_out.state, "assigned")
+ self.assertEqual(picking_out.state, "assigned")
# receive one product in stock
inventory = self.env['stock.inventory'].create({
@@ -1650,7 +1650,7 @@ class TestStockFlow(TestStockCommon):
# recheck availability of the delivery order, it should be assigned
picking_out.action_assign()
- self.assertEquals(picking_out.state, "assigned")
+ self.assertEqual(picking_out.state, "assigned")
def test_73_picking_state_partial_force_assign(self):
""" This test will check that the state of the picking is correctly computed according
@@ -1679,7 +1679,7 @@ class TestStockFlow(TestStockCommon):
# validate this delivery order, it should be in the waiting state
picking_out.action_assign()
- self.assertEquals(picking_out.state, "confirmed")
+ self.assertEqual(picking_out.state, "confirmed")
def test_74_move_state_waiting_mto(self):
""" This test will check that when a move is unreserved, its state changes to 'waiting' if
@@ -1734,9 +1734,9 @@ class TestStockFlow(TestStockCommon):
move_with_ancestors._do_unreserve()
other_move._do_unreserve()
- self.assertEquals(move_mto_alone.state, "draft")
- self.assertEquals(move_with_ancestors.state, "waiting")
- self.assertEquals(other_move.state, "confirmed")
+ self.assertEqual(move_mto_alone.state, "draft")
+ self.assertEqual(move_with_ancestors.state, "waiting")
+ self.assertEqual(other_move.state, "confirmed")
def test_80_partial_picking_without_backorder(self):
""" This test will create a picking with an initial demand for a product
@@ -1769,19 +1769,19 @@ class TestStockFlow(TestStockCommon):
self.assertFalse(picking.backorder_id)
# Checking that the original move is still in the same picking
- self.assertEquals(move_a.picking_id.id, picking.id)
+ self.assertEqual(move_a.picking_id.id, picking.id)
move_lines = picking.move_lines
move_done = move_lines.browse(move_a.id)
move_canceled = move_lines - move_done
# Checking that the original move was set to done
- self.assertEquals(move_done.product_uom_qty, 4)
- self.assertEquals(move_done.state, 'done')
+ self.assertEqual(move_done.product_uom_qty, 4)
+ self.assertEqual(move_done.state, 'done')
# Checking that the new move created was canceled
- self.assertEquals(move_canceled.product_uom_qty, 6)
- self.assertEquals(move_canceled.state, 'cancel')
+ self.assertEqual(move_canceled.product_uom_qty, 6)
+ self.assertEqual(move_canceled.state, 'cancel')
# Checking that the canceled move is in the original picking
self.assertIn(move_canceled.id, picking.move_lines.mapped('id'))
@@ -1968,11 +1968,11 @@ class TestStockFlow(TestStockCommon):
f.scheduled_date = fields.Datetime.now()
picking = f.save()
- self.assertEquals(f.state, 'draft')
+ self.assertEqual(f.state, 'draft')
picking.action_confirm()
f = Form(picking, view='stock.view_picking_form')
f.scheduled_date = fields.Datetime.now()
picking = f.save()
- self.assertEquals(f.state, 'confirmed')
+ self.assertEqual(f.state, 'confirmed')
diff --git a/addons/stock/tests/test_stock_location_search.py b/addons/stock/tests/test_stock_location_search.py
index ac83eebdfdc..a7e27f54f60 100644
--- a/addons/stock/tests/test_stock_location_search.py
+++ b/addons/stock/tests/test_stock_location_search.py
@@ -20,9 +20,9 @@ class TestStockLocationSearch(common.TransactionCase):
def test_10_location_search_by_barcode(self):
"""Search stock location by barcode"""
location_names = self.location.name_search(name=self.barcode)
- self.assertEquals(len(location_names), 1)
+ self.assertEqual(len(location_names), 1)
location_id_found = location_names[0][0]
- self.assertEquals(self.location_barcode_id, location_id_found)
+ self.assertEqual(self.location_barcode_id, location_id_found)
def test_20_location_search_by_name(self):
"""Search stock location by name"""
diff --git a/addons/stock_dropshipping/tests/test_dropship.py b/addons/stock_dropshipping/tests/test_dropship.py
index 1c24dbea88d..93c56163af2 100644
--- a/addons/stock_dropshipping/tests/test_dropship.py
+++ b/addons/stock_dropshipping/tests/test_dropship.py
@@ -109,8 +109,8 @@ class TestDropship(common.TransactionCase):
purchase = self.env['purchase.order'].search([('partner_id', '=', supplier_dropship.id)])
self.assertTrue(purchase, "an RFQ should have been created by the scheduler")
purchase.button_confirm()
- self.assertEquals(purchase.state, 'purchase', 'Purchase order should be in the approved state')
- self.assertEquals(len(purchase.ids), 1, 'There should be one picking')
+ self.assertEqual(purchase.state, 'purchase', 'Purchase order should be in the approved state')
+ self.assertEqual(len(purchase.ids), 1, 'There should be one picking')
# Send the 200 pieces
purchase.picking_ids.move_lines.quantity_done = purchase.picking_ids.move_lines.product_qty
@@ -120,4 +120,4 @@ class TestDropship(common.TransactionCase):
move_line = self.env['stock.move.line'].search([
('location_dest_id', '=', self.env.ref('stock.stock_location_customers').id),
('product_id', '=', drop_shop_product.id)])
- self.assertEquals(len(move_line.ids), 1, 'There should be exactly one move line')
+ self.assertEqual(len(move_line.ids), 1, 'There should be exactly one move line')
diff --git a/addons/stock_picking_batch/models/stock_picking_batch.py b/addons/stock_picking_batch/models/stock_picking_batch.py
index fe12ddd52ec..31158707979 100644
--- a/addons/stock_picking_batch/models/stock_picking_batch.py
+++ b/addons/stock_picking_batch/models/stock_picking_batch.py
@@ -119,4 +119,3 @@ class StockPicking(models.Model):
check_company=True,
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
help='Batch associated to this transfer', copy=False)
-
diff --git a/addons/survey/tests/test_certification_badge.py b/addons/survey/tests/test_certification_badge.py
index 60ca29d62e6..6cb3bbbafc8 100644
--- a/addons/survey/tests/test_certification_badge.py
+++ b/addons/survey/tests/test_certification_badge.py
@@ -101,7 +101,7 @@ class TestCertificationBadge(common.SurveyCase, HttpCase):
def test_badge_configuration(self):
# add a certification badge on a new survey
challenge = self.env['gamification.challenge'].search([('reward_id', '=', self.certification_badge.id)])
- self.assertEquals(len(challenge), 0, """A challenge should not exist or be linked to the certification badge
+ self.assertEqual(len(challenge), 0, """A challenge should not exist or be linked to the certification badge
if the certification badge have not been activated on a certification survey""")
self.certification_survey.write({
@@ -110,13 +110,13 @@ class TestCertificationBadge(common.SurveyCase, HttpCase):
})
challenge = self.env['gamification.challenge'].search([('reward_id', '=', self.certification_badge.id)])
- self.assertEquals(len(challenge), 1,
+ self.assertEqual(len(challenge), 1,
"A challenge should be created if the certification badge is activated on a certification survey")
challenge_line = self.env['gamification.challenge.line'].search([('challenge_id', '=', challenge.id)])
- self.assertEquals(len(challenge_line), 1,
+ self.assertEqual(len(challenge_line), 1,
"A challenge_line should be created if the certification badge is activated on a certification survey")
goal = challenge_line.definition_id
- self.assertEquals(len(goal), 1,
+ self.assertEqual(len(goal), 1,
"A goal should be created if the certification badge is activated on a certification survey")
# don't give badge anymore
@@ -127,13 +127,13 @@ class TestCertificationBadge(common.SurveyCase, HttpCase):
'The certification badge should be inactive if give_badge is false.')
challenge = self.env['gamification.challenge'].search([('id', '=', challenge.id)])
- self.assertEquals(len(challenge), 0,
+ self.assertEqual(len(challenge), 0,
"The challenge should be deleted if the certification badge is unset from the certification survey")
challenge_line = self.env['gamification.challenge.line'].search([('id', '=', challenge_line.id)])
- self.assertEquals(len(challenge_line), 0,
+ self.assertEqual(len(challenge_line), 0,
"The challenge_line should be deleted if the certification badge is unset from the certification survey")
goal = self.env['gamification.goal'].search([('id', '=', goal.id)])
- self.assertEquals(len(goal), 0,
+ self.assertEqual(len(goal), 0,
"The goal should be deleted if the certification badge is unset from the certification survey")
# re active the badge in the survey
@@ -142,13 +142,13 @@ class TestCertificationBadge(common.SurveyCase, HttpCase):
'The certification badge should be active if give_badge is true.')
challenge = self.env['gamification.challenge'].search([('reward_id', '=', self.certification_badge.id)])
- self.assertEquals(len(challenge), 1,
+ self.assertEqual(len(challenge), 1,
"A challenge should be created if the certification badge is activated on a certification survey")
challenge_line = self.env['gamification.challenge.line'].search([('challenge_id', '=', challenge.id)])
- self.assertEquals(len(challenge_line), 1,
+ self.assertEqual(len(challenge_line), 1,
"A challenge_line should be created if the certification badge is activated on a certification survey")
goal = challenge_line.definition_id
- self.assertEquals(len(goal), 1,
+ self.assertEqual(len(goal), 1,
"A goal should be created if the certification badge is activated on a certification survey")
def test_certification_badge_access(self):
@@ -183,11 +183,11 @@ class TestCertificationBadge(common.SurveyCase, HttpCase):
self.assertEqual(len(certification_surveys), 3, 'There should be 3 certification survey created')
challenges = self.env['gamification.challenge'].search([('reward_id', 'in', certification_surveys.mapped('certification_badge_id').ids)])
- self.assertEquals(len(challenges), 3, "3 challenges should be created")
+ self.assertEqual(len(challenges), 3, "3 challenges should be created")
challenge_lines = self.env['gamification.challenge.line'].search([('challenge_id', 'in', challenges.ids)])
- self.assertEquals(len(challenge_lines), 3, "3 challenge_lines should be created")
+ self.assertEqual(len(challenge_lines), 3, "3 challenge_lines should be created")
goals = challenge_lines.mapped('definition_id')
- self.assertEquals(len(goals), 3, "3 goals should be created")
+ self.assertEqual(len(goals), 3, "3 goals should be created")
# Test write multi
certification_surveys.write({'certification_give_badge': False})
@@ -196,11 +196,11 @@ class TestCertificationBadge(common.SurveyCase, HttpCase):
'Every badge should be inactive if the 3 survey does not give badge anymore')
challenges = self.env['gamification.challenge'].search([('id', 'in', challenges.ids)])
- self.assertEquals(len(challenges), 0, "The 3 challenges should be deleted")
+ self.assertEqual(len(challenges), 0, "The 3 challenges should be deleted")
challenge_lines = self.env['gamification.challenge.line'].search([('id', 'in', challenge_lines.ids)])
- self.assertEquals(len(challenge_lines), 0, "The 3 challenge_lines should be deleted")
+ self.assertEqual(len(challenge_lines), 0, "The 3 challenge_lines should be deleted")
goals = self.env['gamification.goal'].search([('id', 'in', goals.ids)])
- self.assertEquals(len(goals), 0, "The 3 goals should be deleted")
+ self.assertEqual(len(goals), 0, "The 3 goals should be deleted")
certification_surveys.write({'certification_give_badge': True})
for survey in certification_surveys:
@@ -208,8 +208,8 @@ class TestCertificationBadge(common.SurveyCase, HttpCase):
'Every badge should be reactivated if the 3 survey give badges again')
challenges = self.env['gamification.challenge'].search([('reward_id', 'in', certification_surveys.mapped('certification_badge_id').ids)])
- self.assertEquals(len(challenges), 3, "3 challenges should be created")
+ self.assertEqual(len(challenges), 3, "3 challenges should be created")
challenge_lines = self.env['gamification.challenge.line'].search([('challenge_id', 'in', challenges.ids)])
- self.assertEquals(len(challenge_lines), 3, "3 challenge_lines should be created")
+ self.assertEqual(len(challenge_lines), 3, "3 challenge_lines should be created")
goals = challenge_lines.mapped('definition_id')
- self.assertEquals(len(goals), 3, "3 goals should be created")
+ self.assertEqual(len(goals), 3, "3 goals should be created")
diff --git a/addons/survey/tests/test_survey_invite.py b/addons/survey/tests/test_survey_invite.py
index bf99cc93089..04a76d099dd 100644
--- a/addons/survey/tests/test_survey_invite.py
+++ b/addons/survey/tests/test_survey_invite.py
@@ -143,7 +143,7 @@ class TestSurveyInvite(common.SurveyCase):
self.assertEqual(len(answers), 3)
self.assertEqual(
set(answers.mapped('email')),
- set(['test1@example.com', 'Raoulette Vignolette <test2@example.com>', self.customer.email]))
+ set(['test1@example.com', '"Raoulette Vignolette" <test2@example.com>', self.customer.email]))
self.assertEqual(answers.mapped('partner_id'), self.customer)
@users('survey_manager')
@@ -164,7 +164,7 @@ class TestSurveyInvite(common.SurveyCase):
self.assertEqual(len(answers), 3)
self.assertEqual(
set(answers.mapped('email')),
- set(['test1@example.com', 'Raoulette Vignolette <test2@example.com>', self.customer.email]))
+ set(['test1@example.com', '"Raoulette Vignolette" <test2@example.com>', self.customer.email]))
self.assertEqual(answers.mapped('partner_id'), self.customer)
@users('survey_manager')
diff --git a/addons/survey/views/survey_menus.xml b/addons/survey/views/survey_menus.xml
index 50b99a0f48a..2f87d8186f0 100644
--- a/addons/survey/views/survey_menus.xml
+++ b/addons/survey/views/survey_menus.xml
@@ -1,22 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
-<data>
+ <data>
<!-- Main menu -->
<menuitem name="Surveys"
- id="menu_surveys"
- sequence="70"
- groups="group_survey_user"
- web_icon="survey,static/description/icon.png"/>
+ id="menu_surveys"
+ sequence="70"
+ groups="group_survey_user"
+ web_icon="survey,static/description/icon.png"/>
- <!-- Parent menus -->
+ <!-- Parent menus -->
<menuitem name="Questions"
- id="survey_menu_questions"
- parent="menu_surveys"
- groups="base.group_no_one"
- sequence="80"/>
+ id="survey_menu_questions"
+ parent="menu_surveys"
+ groups="base.group_no_one"
+ sequence="80"/>
<menuitem name="Participations"
- id="survey_menu_user_inputs"
- parent="menu_surveys"
- sequence="90"/>
-</data>
-</odoo> \ No newline at end of file
+ id="survey_menu_user_inputs"
+ parent="menu_surveys"
+ sequence="90"/>
+ </data>
+</odoo>
diff --git a/addons/survey/wizard/survey_invite.py b/addons/survey/wizard/survey_invite.py
index 06b60407119..48a6ee57c31 100644
--- a/addons/survey/wizard/survey_invite.py
+++ b/addons/survey/wizard/survey_invite.py
@@ -4,8 +4,6 @@
import logging
import re
-from email.utils import formataddr
-
from odoo import api, fields, models, tools, _
from odoo.exceptions import UserError
@@ -21,7 +19,7 @@ class SurveyInvite(models.TransientModel):
@api.model
def _get_default_from(self):
if self.env.user.email:
- return formataddr((self.env.user.name, self.env.user.email))
+ return tools.formataddr((self.env.user.name, self.env.user.email))
raise UserError(_("Unable to post message, please configure the sender's email address."))
@api.model
diff --git a/addons/test_mail/data/test_mail_data.py b/addons/test_mail/data/test_mail_data.py
index cfce6317440..fa64fd56e26 100644
--- a/addons/test_mail/data/test_mail_data.py
+++ b/addons/test_mail/data/test_mail_data.py
@@ -184,6 +184,40 @@ Mime-Version: 1.0 (Mac OS X Mail 7.3 \(1878.6\))
SSBhbSB0aGUgQmF0TWFuCg=="""
+MAIL_MULTIPART_WEIRD_FILENAME = """X-Original-To: john@doe.com
+Delivered-To: johndoe@example.com
+Received: by mail.example.com (Postfix, from userid 10002)
+ id E8166BFACB; Fri, 23 Aug 2013 13:18:02 +0200 (CEST)
+From: "Bruce Wayne" <bruce@wayneenterprises.com>
+Subject: test
+Message-ID: <c0c20fdd-a38e-b296-865b-d9232bf30ce5@odoo.com>
+Date: Mon, 26 Aug 2019 16:55:09 +0200
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="------------FACA7766210AAA981EAE01F3"
+Content-Language: en-US
+
+This is a multi-part message in MIME format.
+--------------FACA7766210AAA981EAE01F3
+Content-Type: text/plain; charset=utf-8; format=flowed
+Content-Transfer-Encoding: 7bit
+
+plop
+
+
+--------------FACA7766210AAA981EAE01F3
+Content-Type: text/plain; charset=UTF-8;
+ name="=?UTF-8?B?NjJfQDssXVspPS4ow4fDgMOJLnR4dA==?="
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename*0*=utf-8'en-us'%36%32%5F%40%3B%2C%5D%5B%29%3D%2E%28%C3%87%C3%80%C3%89;
+ filename*1*=%2E%74%78%74
+
+SSBhbSBhIGZpbGUgd2l0aCBhIHZhbGlkIHdpbmRvd3MgZmlsZW5hbWUK
+--------------FACA7766210AAA981EAE01F3--
+"""
+
+
MAIL_MULTIPART_IMAGE = """X-Original-To: raoul@example.com
Delivered-To: micheline@example.com
Received: by mail1.example.com (Postfix, from userid 99999)
diff --git a/addons/test_mail/tests/common.py b/addons/test_mail/tests/common.py
index 17d8594a7d9..d73a091b7b2 100644
--- a/addons/test_mail/tests/common.py
+++ b/addons/test_mail/tests/common.py
@@ -4,12 +4,12 @@ import json
import time
from contextlib import contextmanager
-from email.utils import formataddr
from functools import partial
from odoo import api
from odoo.addons.bus.models.bus import json_dump
from odoo.tests import common, tagged, new_test_user
+from odoo.tools import formataddr
mail_new_test_user = partial(new_test_user, context={'mail_create_nolog': True, 'mail_create_nosubscribe': True, 'mail_notrack': True, 'no_reset_password': True})
@@ -327,7 +327,7 @@ class MockEmails(common.SingleTransactionCase):
self._mails[:] = []
def format(self, template, to='groups@example.com, other@gmail.com', subject='Frogs',
- extra='', email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>',
+ extra='', email_from='"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>',
cc='', msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>'):
return template.format(to=to, subject=subject, cc=cc, extra=extra, email_from=email_from, msg_id=msg_id)
diff --git a/addons/test_mail/tests/test_discuss.py b/addons/test_mail/tests/test_discuss.py
index be255274ea3..f4fb89a209e 100644
--- a/addons/test_mail/tests/test_discuss.py
+++ b/addons/test_mail/tests/test_discuss.py
@@ -126,7 +126,7 @@ class TestDiscuss(BaseFunctionalTest, TestRecipients, MockEmails):
record = self.env['mail.test.cc'].create({'email_cc': 'cc1@example.com, cc2@example.com, cc3 <cc3@example.com>'})
suggestions = record._message_get_suggested_recipients()[record.id]
self.assertEqual(sorted(suggestions), [
+ (False, '"cc3" <cc3@example.com>', 'CC Email'),
(False, 'cc1@example.com', 'CC Email'),
(False, 'cc2@example.com', 'CC Email'),
- (False, 'cc3 <cc3@example.com>', 'CC Email')
], 'cc should be in suggestions')
diff --git a/addons/test_mail/tests/test_mail_channel.py b/addons/test_mail/tests/test_mail_channel.py
index f1b482028c9..74ad29a6d3e 100644
--- a/addons/test_mail/tests/test_mail_channel.py
+++ b/addons/test_mail/tests/test_mail_channel.py
@@ -1,11 +1,9 @@
# -*- coding: utf-8 -*-
-from email.utils import formataddr
-
from odoo.tests import tagged
from odoo.addons.test_mail.tests import common
from odoo.addons.test_mail.tests.common import mail_new_test_user
from odoo.exceptions import AccessError, except_orm, ValidationError, UserError
-from odoo.tools import mute_logger
+from odoo.tools import mute_logger, formataddr
class TestChannelAccessRights(common.BaseFunctionalTest, common.MockEmails):
diff --git a/addons/test_mail/tests/test_mail_gateway.py b/addons/test_mail/tests/test_mail_gateway.py
index 52ed239bc0e..440085af206 100644
--- a/addons/test_mail/tests/test_mail_gateway.py
+++ b/addons/test_mail/tests/test_mail_gateway.py
@@ -2,9 +2,9 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import email
+import email.policy
import socket
-from email.utils import formataddr
from unittest.mock import DEFAULT
from unittest.mock import patch
@@ -15,7 +15,10 @@ from odoo.addons.test_mail.models.test_mail_models import MailTestGateway
from odoo.addons.test_mail.tests.common import BaseFunctionalTest, MockEmails
from odoo.addons.test_mail.tests.common import mail_new_test_user
from odoo.tests import tagged
-from odoo.tools import email_split_and_format, mute_logger, pycompat
+from odoo.tools import email_split_and_format, mute_logger, pycompat, formataddr
+
+def from_string(text):
+ return email.message_from_string(pycompat.to_text(text), policy=email.policy.SMTP)
@tagged('mail_gateway')
@@ -23,17 +26,17 @@ class TestEmailParsing(BaseFunctionalTest, MockEmails):
def test_message_parse_body(self):
# test pure plaintext
- plaintext = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>')
- res = self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(plaintext)))
+ plaintext = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>')
+ res = self.env['mail.thread'].message_parse(from_string(plaintext))
self.assertIn('Please call me as soon as possible this afternoon!', res['body'])
# test multipart / text and html -> html has priority
- multipart = self.format(MAIL_TEMPLATE, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>')
- res = self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(multipart)))
+ multipart = self.format(MAIL_TEMPLATE, email_from='"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>')
+ res = self.env['mail.thread'].message_parse(from_string(multipart))
self.assertIn('<p>Please call me as soon as possible this afternoon!</p>', res['body'])
# test multipart / mixed
- res = self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(test_mail_data.MAIL_MULTIPART_MIXED)))
+ res = self.env['mail.thread'].message_parse(from_string(test_mail_data.MAIL_MULTIPART_MIXED))
self.assertNotIn(
'Should create a multipart/mixed: from gmail, *bold*, with attachment', res['body'],
'message_parse: text version should not be in body after parsing multipart/mixed')
@@ -41,7 +44,7 @@ class TestEmailParsing(BaseFunctionalTest, MockEmails):
'<div dir="ltr">Should create a multipart/mixed: from gmail, <b>bold</b>, with attachment.<br clear="all"><div><br></div>', res['body'],
'message_parse: html version should be in body after parsing multipart/mixed')
- res = self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(test_mail_data.MAIL_MULTIPART_MIXED_TWO)))
+ res = self.env['mail.thread'].message_parse(from_string(test_mail_data.MAIL_MULTIPART_MIXED_TWO))
self.assertNotIn('First and second part', res['body'],
'message_parse: text version should not be in body after parsing multipart/mixed')
self.assertIn('First part', res['body'],
@@ -49,24 +52,27 @@ class TestEmailParsing(BaseFunctionalTest, MockEmails):
self.assertIn('Second part', res['body'],
'message_parse: second part of the html version should be in body after parsing multipart/mixed')
- res = self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(test_mail_data.MAIL_SINGLE_BINARY)))
+ res = self.env['mail.thread'].message_parse(from_string(test_mail_data.MAIL_SINGLE_BINARY))
self.assertEqual(res['body'], '')
self.assertEqual(res['attachments'][0][0], 'thetruth.pdf')
+ res = self.env['mail.thread'].message_parse(from_string(test_mail_data.MAIL_MULTIPART_WEIRD_FILENAME))
+ self.assertEqual(res['attachments'][0][0], '62_@;,][)=.(ÇÀÉ.txt')
+
def test_message_parse_eml(self):
# Test that the parsing of mail with embedded emails as eml(msg) which generates empty attachments, can be processed.
- mail = self.format(test_mail_data.MAIL_EML_ATTACHMENT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')
- self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(mail)))
+ mail = self.format(test_mail_data.MAIL_EML_ATTACHMENT, email_from='"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')
+ self.env['mail.thread'].message_parse(from_string(mail))
def test_message_parse_plaintext(self):
""" Incoming email in plaintext should be stored as html """
- mail = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')
- res = self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(mail)))
+ mail = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')
+ res = self.env['mail.thread'].message_parse(from_string(mail))
self.assertIn('<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>', res['body'])
def test_message_parse_xhtml(self):
# Test that the parsing of XHTML mails does not fail
- self.env['mail.thread'].message_parse(email.message_from_string(pycompat.to_text(test_mail_data.MAIL_XHTML)))
+ self.env['mail.thread'].message_parse(from_string(test_mail_data.MAIL_XHTML))
@tagged('mail_gateway')
@@ -84,7 +90,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
def setUpClass(cls):
super(TestMailgateway, cls).setUpClass()
cls.test_model = cls.env['ir.model']._get('mail.test.gateway')
- cls.email_from = 'Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>'
+ cls.email_from = '"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>'
cls.test_record = cls.env['mail.test.gateway'].with_context(cls._test_context).create({
'name': 'Test',
@@ -186,7 +192,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
self.assertEqual(len(self._mails), 0, 'No notification / bounce should be sent')
# Email recognized if partner has a formatted email
- self.partner_1.write({'email': 'Valid Lelitre <%s>' % self.partner_1.email})
+ self.partner_1.write({'email': '"Valid Lelitre" <%s>' % self.partner_1.email})
record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email, 'groups@test.com', subject='Test2')
self.assertEqual(record.message_ids[0].author_id, self.partner_1,
@@ -265,7 +271,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
# Test bounce email
self.assertEqual(self._mails[0].get('subject'), 'Re: Should Bounce')
self.assertEqual(self._mails[0].get('email_to')[0], 'whatever-2a840@postmaster.twitter.com')
- self.assertEqual(self._mails[0].get('email_from'), formataddr(('MAILER-DAEMON', 'bounce.test@test.com')))
+ self.assertEqual(self._mails[0].get('email_from'), 'MAILER-DAEMON <bounce.test@test.com>')
@mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
def test_message_process_alias_followers_bounce(self):
@@ -283,7 +289,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
'message_process: incoming email on Followers alias should send a bounce email')
self.assertEqual(self._mails[0].get('subject'), 'Re: Should Bounce')
self.assertEqual(self._mails[0].get('email_to')[0], 'whatever-2a840@postmaster.twitter.com')
- self.assertEqual(self._mails[0].get('email_from'), formataddr(('MAILER-DAEMON', 'bounce.test@test.com')))
+ self.assertEqual(self._mails[0].get('email_from'), 'MAILER-DAEMON <bounce.test@test.com>')
# Test: partner on followers alias -> bounce
self._init_mock_build_email()
@@ -293,7 +299,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
'message_process: incoming email on Followers alias should send a bounce email')
self.assertEqual(self._mails[0].get('subject'), 'Re: Should Bounce')
self.assertEqual(self._mails[0].get('email_to')[0], 'whatever-2a840@postmaster.twitter.com')
- self.assertEqual(self._mails[0].get('email_from'), formataddr(('MAILER-DAEMON', 'bounce.test@test.com')))
+ self.assertEqual(self._mails[0].get('email_from'), 'MAILER-DAEMON <bounce.test@test.com>')
@mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models')
def test_message_process_alias_partner(self):
@@ -417,7 +423,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
# Test bounce email
self.assertEqual(self._mails[0].get('subject'), 'Re: Should Bounce')
self.assertEqual(self._mails[0].get('email_to')[0], 'whatever-2a840@postmaster.twitter.com')
- self.assertEqual(self._mails[0].get('email_from'), formataddr(('MAILER-DAEMON', 'bounce.test@test.com')))
+ self.assertEqual(self._mails[0].get('email_from'), 'MAILER-DAEMON <bounce.test@test.com>')
@mute_logger('odoo.addons.mail.models.mail_thread')
def test_message_process_bounce_alias(self):
@@ -778,7 +784,7 @@ class TestMailThreadCC(BaseFunctionalTest, MockEmails):
self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com',
cc='cc2 <cc2@example.com>, cc3@example.com', target_model='mail.test.cc')
cc = email_split_and_format(record.email_cc)
- self.assertEqual(sorted(cc), ['cc1 <cc1@example.com>', 'cc2@example.com', 'cc3@example.com'], 'new cc should have been added on record (unique)')
+ self.assertEqual(sorted(cc), ['"cc1" <cc1@example.com>', 'cc2@example.com', 'cc3@example.com'], 'new cc should have been added on record (unique)')
@mute_logger('odoo.addons.mail.models.mail_thread')
def test_message_cc_update_no_old(self):
@@ -788,4 +794,4 @@ class TestMailThreadCC(BaseFunctionalTest, MockEmails):
self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com',
cc='cc2 <cc2@example.com>, cc3@example.com', target_model='mail.test.cc')
cc = email_split_and_format(record.email_cc)
- self.assertEqual(sorted(cc), ['cc2 <cc2@example.com>', 'cc3@example.com'], 'new cc should have been added on record (unique)')
+ self.assertEqual(sorted(cc), ['"cc2" <cc2@example.com>', 'cc3@example.com'], 'new cc should have been added on record (unique)')
diff --git a/addons/test_mail/tests/test_mail_message.py b/addons/test_mail/tests/test_mail_message.py
index 6d3290a8a95..13a41ab84b1 100644
--- a/addons/test_mail/tests/test_mail_message.py
+++ b/addons/test_mail/tests/test_mail_message.py
@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
-from email.utils import formataddr
from odoo.addons.test_mail.tests import common
from odoo.addons.test_mail.tests.common import mail_new_test_user
from odoo.exceptions import AccessError, except_orm
-from odoo.tools import mute_logger
+from odoo.tools import mute_logger, formataddr
from odoo.tests import tagged
@@ -70,7 +69,7 @@ class TestMessageValues(common.BaseFunctionalTest, common.MockEmails):
reply_to_name = '%s %s' % (self.env.user.company_id.name, self.alias_record.name)
reply_to_email = '%s@%s' % (self.alias_record.alias_name, self.alias_domain)
self.assertEqual(msg.reply_to, formataddr((reply_to_name, reply_to_email)))
- self.assertEqual(msg.email_from, '%s <%s>' % (self.user_employee.name, self.user_employee.email))
+ self.assertEqual(msg.email_from, formataddr((self.user_employee.name, self.user_employee.email)))
# no alias domain -> author
self.env['ir.config_parameter'].search([('key', '=', 'mail.catchall.domain')]).unlink()
diff --git a/addons/test_mail/tests/test_message_management.py b/addons/test_mail/tests/test_message_management.py
index 557eebd4ca7..2a45477b9bf 100644
--- a/addons/test_mail/tests/test_message_management.py
+++ b/addons/test_mail/tests/test_message_management.py
@@ -1,157 +1,157 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from unittest.mock import patch
-
-from odoo import api
-from odoo.addons.base.models.ir_mail_server import IrMailServer
-from odoo.addons.test_mail.tests import common
-from odoo.addons.test_mail.tests.common import mail_new_test_user
-from odoo.tests import tagged
-from odoo.tools import mute_logger
-
-
-@tagged('mail_wizards')
-class TestMailResend(common.BaseFunctionalTest, common.MockEmails):
-
- @classmethod
- def setUpClass(cls):
- super(TestMailResend, cls).setUpClass()
- cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
-
- #Two users
- cls.user1 = mail_new_test_user(cls.env, login='e1', groups='base.group_public', name='Employee 1', email='e1') # invalid email
- cls.user2 = mail_new_test_user(cls.env, login='e2', groups='base.group_portal', name='Employee 2', email='e2@example.com')
- #Two partner
- cls.partner1 = cls.env['res.partner'].with_context(cls._test_context).create({
- 'name': 'Partner 1',
- 'email': 'p1' # invalid email
- })
- cls.partner2 = cls.env['res.partner'].with_context(cls._test_context).create({
- 'name': 'Partner 2',
- 'email': 'p2@example.com'
- })
-
- @api.model
- def send_email(self, message, *args, **kwargs):
- assert '@' in message['To'], self.NO_VALID_RECIPIENT
- return message['Message-Id']
- cls.bus_update_failure = []
- def sendone(self, channel, message):
- if 'type' in message and message['type'] == 'mail_failure':
- cls.bus_update_failure.append((channel, message))
- cls.env['ir.mail_server']._patch_method('send_email', send_email)
- cls.env['bus.bus']._patch_method('sendone', sendone)
- cls.partners = cls.env['res.partner'].concat(cls.user1.partner_id, cls.user2.partner_id, cls.partner1, cls.partner2)
- cls.invalid_email_partners = cls.env['res.partner'].concat(cls.user1.partner_id, cls.partner1)
-
- def setUp(self):
- super(TestMailResend, self).setUp()
- TestMailResend.bus_update_failure = []
-
- def assertPartnerNotif(self, partner, state, message):
- # TDE CLEANME: quick fix, should be cleaned and moved to mail testing tools
- notif = self.env['mail.notification'].search([
- ('res_partner_id', '=', partner.id),
- ('mail_message_id', '=', message.id)]
- )
- self.assertEquals(notif.notification_status, state)
- return notif
-
- def assertBusMessage(self, partners):
- partner_ids = [elem[0][2] for elem in self.bus_update_failure]
- self.assertEquals(partner_ids, [partner.id for partner in partners])
- self.bus_update_failure.clear()
-
- @classmethod
- def tearDownClass(cls):
- # Remove mocks
- cls.env['ir.mail_server']._revert_method('send_email')
- cls.env['bus.bus']._revert_method('sendone')
- super(TestMailResend, cls).tearDownClass()
-
- def assertEmails(self, *args, **kwargs):
- res = super(TestMailResend, self).assertEmails(*args, **kwargs)
- self._mails.clear()
-
- @mute_logger('odoo.addons.mail.models.mail_mail')
- def test_mail_resend_workflow(self):
- cls = TestMailResend
- #missconfigured server
- @api.model
- def connect_failure(**kwargs):
- raise Exception
- with patch.object(IrMailServer, 'connect', side_effect=connect_failure):
- message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype='mail.mt_comment', message_type='notification')
- self.assertBusMessage([self.partner_admin])
- self.assertEmails(self.partner_admin, [])
- for partner in [self.user1.partner_id, self.user2.partner_id, self.partner1, self.partner2]:
- self.assertPartnerNotif(partner, 'exception', message)
-
- wizard = self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({})
- self.assertEqual(wizard.notification_ids.mapped('res_partner_id'), self.partners, "wizard should manage notifications for each failed partner")
- wizard.resend_mail_action()
- self.assertBusMessage([self.partner_admin] * 3) # three more failure sent on bus, one for each mail in failure and one for resend
- self.assertEmails(self.partner_admin, self.partners)
- for partner in [self.user1.partner_id, self.partner1]:
- self.assertPartnerNotif(partner, 'exception', message)
- for partner in [self.user2.partner_id, self.partner2]:
- self.assertPartnerNotif(partner, 'sent', message)
-
- self.user1.write({"email": 'u1@example.com'})
- self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({}).resend_mail_action()
- self.assertBusMessage([self.partner_admin] * 2) # two more failure update sent on bus, one for failed mail and one for resend
- self.assertEmails(self.partner_admin, self.invalid_email_partners)
- for partner in [self.partner1]:
- self.assertPartnerNotif(partner, 'exception', message)
- for partner in [self.user1.partner_id, self.user2.partner_id, self.partner2]:
- self.assertPartnerNotif(partner, 'sent', message)
-
- self.partner1.write({"email": 'p1@example.com'})
- self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({}).resend_mail_action()
- self.assertBusMessage([self.partner_admin]) # A success update should be sent on bus once the email has no more failure
- self.assertEmails(self.partner_admin, self.partner1)
- for partner in [self.user1.partner_id, self.user2.partner_id, self.partner1, self.partner2]:
- self.assertPartnerNotif(partner, 'sent', message)
-
- @mute_logger('odoo.addons.mail.models.mail_mail')
- def test_mail_send_no_failure(self):
- self.user1.write({"email": 'u1@example.com'})
- self.partner1.write({"email": 'p1@example.com'})
- message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype='mail.mt_comment', message_type='notification')
- self.assertBusMessage([]) # one update for cancell
-
- @mute_logger('odoo.addons.mail.models.mail_mail')
- def test_remove_mail_become_canceled(self):
- message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype='mail.mt_comment', message_type='notification')
- self.assertEmails(self.partner_admin, self.partners)
- self.assertBusMessage([self.partner_admin] * 2) # two failure sent on bus, one for each mail
- wizard = self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({})
- partners = wizard.partner_ids.mapped("partner_id")
- self.assertEqual(self.invalid_email_partners, partners)
- wizard.partner_ids.filtered(lambda p: p.partner_id == self.partner1).write({"resend": False})
- wizard.resend_mail_action()
- self.assertEmails(self.partner_admin, self.user1)
- self.assertBusMessage([self.partner_admin] * 2) # two more failure sent on bus, one for failure, one for resend
- for partner in [self.user1.partner_id]:
- self.assertPartnerNotif(partner, 'exception', message)
- for partner in [self.partner1]:
- self.assertPartnerNotif(partner, 'canceled', message)
- for partner in [self.user2.partner_id, self.partner2]:
- self.assertPartnerNotif(partner, 'sent', message)
-
- @mute_logger('odoo.addons.mail.models.mail_mail')
- def test_cancel_all(self):
- message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype='mail.mt_comment', message_type='notification')
- for partner in [self.user1.partner_id, self.partner1]:
- self.assertPartnerNotif(partner, 'exception', message)
- for partner in [self.user2.partner_id, self.partner2]:
- self.assertPartnerNotif(partner, 'sent', message)
- self.assertBusMessage([self.partner_admin] * 2)
- wizard = self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({})
- wizard.cancel_mail_action()
- for partner in [self.user1.partner_id, self.partner1]:
- self.assertPartnerNotif(partner, 'canceled', message)
- for partner in [self.user2.partner_id, self.partner2]:
- self.assertPartnerNotif(partner, 'sent', message)
- self.assertBusMessage([self.partner_admin]) # one update for cancell
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from unittest.mock import patch
+
+from odoo import api
+from odoo.addons.base.models.ir_mail_server import IrMailServer
+from odoo.addons.test_mail.tests import common
+from odoo.addons.test_mail.tests.common import mail_new_test_user
+from odoo.tests import tagged
+from odoo.tools import mute_logger
+
+
+@tagged('mail_wizards')
+class TestMailResend(common.BaseFunctionalTest, common.MockEmails):
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestMailResend, cls).setUpClass()
+ cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
+
+ #Two users
+ cls.user1 = mail_new_test_user(cls.env, login='e1', groups='base.group_public', name='Employee 1', email='e1') # invalid email
+ cls.user2 = mail_new_test_user(cls.env, login='e2', groups='base.group_portal', name='Employee 2', email='e2@example.com')
+ #Two partner
+ cls.partner1 = cls.env['res.partner'].with_context(cls._test_context).create({
+ 'name': 'Partner 1',
+ 'email': 'p1' # invalid email
+ })
+ cls.partner2 = cls.env['res.partner'].with_context(cls._test_context).create({
+ 'name': 'Partner 2',
+ 'email': 'p2@example.com'
+ })
+
+ @api.model
+ def send_email(self, message, *args, **kwargs):
+ assert '@' in message['To'], self.NO_VALID_RECIPIENT
+ return message['Message-Id']
+ cls.bus_update_failure = []
+ def sendone(self, channel, message):
+ if 'type' in message and message['type'] == 'mail_failure':
+ cls.bus_update_failure.append((channel, message))
+ cls.env['ir.mail_server']._patch_method('send_email', send_email)
+ cls.env['bus.bus']._patch_method('sendone', sendone)
+ cls.partners = cls.env['res.partner'].concat(cls.user1.partner_id, cls.user2.partner_id, cls.partner1, cls.partner2)
+ cls.invalid_email_partners = cls.env['res.partner'].concat(cls.user1.partner_id, cls.partner1)
+
+ def setUp(self):
+ super(TestMailResend, self).setUp()
+ TestMailResend.bus_update_failure = []
+
+ def assertPartnerNotif(self, partner, state, message):
+ # TDE CLEANME: quick fix, should be cleaned and moved to mail testing tools
+ notif = self.env['mail.notification'].search([
+ ('res_partner_id', '=', partner.id),
+ ('mail_message_id', '=', message.id)]
+ )
+ self.assertEqual(notif.notification_status, state)
+ return notif
+
+ def assertBusMessage(self, partners):
+ partner_ids = [elem[0][2] for elem in self.bus_update_failure]
+ self.assertEqual(partner_ids, [partner.id for partner in partners])
+ self.bus_update_failure.clear()
+
+ @classmethod
+ def tearDownClass(cls):
+ # Remove mocks
+ cls.env['ir.mail_server']._revert_method('send_email')
+ cls.env['bus.bus']._revert_method('sendone')
+ super(TestMailResend, cls).tearDownClass()
+
+ def assertEmails(self, *args, **kwargs):
+ res = super(TestMailResend, self).assertEmails(*args, **kwargs)
+ self._mails.clear()
+
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_mail_resend_workflow(self):
+ cls = TestMailResend
+ #missconfigured server
+ @api.model
+ def connect_failure(**kwargs):
+ raise Exception
+ with patch.object(IrMailServer, 'connect', side_effect=connect_failure):
+ message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype='mail.mt_comment', message_type='notification')
+ self.assertBusMessage([self.partner_admin])
+ self.assertEmails(self.partner_admin, [])
+ for partner in [self.user1.partner_id, self.user2.partner_id, self.partner1, self.partner2]:
+ self.assertPartnerNotif(partner, 'exception', message)
+
+ wizard = self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({})
+ self.assertEqual(wizard.notification_ids.mapped('res_partner_id'), self.partners, "wizard should manage notifications for each failed partner")
+ wizard.resend_mail_action()
+ self.assertBusMessage([self.partner_admin] * 3) # three more failure sent on bus, one for each mail in failure and one for resend
+ self.assertEmails(self.partner_admin, self.partners)
+ for partner in [self.user1.partner_id, self.partner1]:
+ self.assertPartnerNotif(partner, 'exception', message)
+ for partner in [self.user2.partner_id, self.partner2]:
+ self.assertPartnerNotif(partner, 'sent', message)
+
+ self.user1.write({"email": 'u1@example.com'})
+ self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({}).resend_mail_action()
+ self.assertBusMessage([self.partner_admin] * 2) # two more failure update sent on bus, one for failed mail and one for resend
+ self.assertEmails(self.partner_admin, self.invalid_email_partners)
+ for partner in [self.partner1]:
+ self.assertPartnerNotif(partner, 'exception', message)
+ for partner in [self.user1.partner_id, self.user2.partner_id, self.partner2]:
+ self.assertPartnerNotif(partner, 'sent', message)
+
+ self.partner1.write({"email": 'p1@example.com'})
+ self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({}).resend_mail_action()
+ self.assertBusMessage([self.partner_admin]) # A success update should be sent on bus once the email has no more failure
+ self.assertEmails(self.partner_admin, self.partner1)
+ for partner in [self.user1.partner_id, self.user2.partner_id, self.partner1, self.partner2]:
+ self.assertPartnerNotif(partner, 'sent', message)
+
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_mail_send_no_failure(self):
+ self.user1.write({"email": 'u1@example.com'})
+ self.partner1.write({"email": 'p1@example.com'})
+ message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype='mail.mt_comment', message_type='notification')
+ self.assertBusMessage([]) # one update for cancell
+
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_remove_mail_become_canceled(self):
+ message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype='mail.mt_comment', message_type='notification')
+ self.assertEmails(self.partner_admin, self.partners)
+ self.assertBusMessage([self.partner_admin] * 2) # two failure sent on bus, one for each mail
+ wizard = self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({})
+ partners = wizard.partner_ids.mapped("partner_id")
+ self.assertEqual(self.invalid_email_partners, partners)
+ wizard.partner_ids.filtered(lambda p: p.partner_id == self.partner1).write({"resend": False})
+ wizard.resend_mail_action()
+ self.assertEmails(self.partner_admin, self.user1)
+ self.assertBusMessage([self.partner_admin] * 2) # two more failure sent on bus, one for failure, one for resend
+ for partner in [self.user1.partner_id]:
+ self.assertPartnerNotif(partner, 'exception', message)
+ for partner in [self.partner1]:
+ self.assertPartnerNotif(partner, 'canceled', message)
+ for partner in [self.user2.partner_id, self.partner2]:
+ self.assertPartnerNotif(partner, 'sent', message)
+
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_cancel_all(self):
+ message = self.test_record.with_user(self.user_admin).message_post(partner_ids=self.partners.ids, subtype='mail.mt_comment', message_type='notification')
+ for partner in [self.user1.partner_id, self.partner1]:
+ self.assertPartnerNotif(partner, 'exception', message)
+ for partner in [self.user2.partner_id, self.partner2]:
+ self.assertPartnerNotif(partner, 'sent', message)
+ self.assertBusMessage([self.partner_admin] * 2)
+ wizard = self.env['mail.resend.message'].with_context({'mail_message_to_resend': message.id}).create({})
+ wizard.cancel_mail_action()
+ for partner in [self.user1.partner_id, self.partner1]:
+ self.assertPartnerNotif(partner, 'canceled', message)
+ for partner in [self.user2.partner_id, self.partner2]:
+ self.assertPartnerNotif(partner, 'sent', message)
+ self.assertBusMessage([self.partner_admin]) # one update for cancell
diff --git a/addons/test_mail/tests/test_message_post.py b/addons/test_mail/tests/test_message_post.py
index 2eecfb98b9e..4b4fa16493e 100644
--- a/addons/test_mail/tests/test_message_post.py
+++ b/addons/test_mail/tests/test_message_post.py
@@ -3,14 +3,13 @@
import base64
-from email.utils import formataddr
from unittest.mock import patch
from odoo.addons.test_mail.tests.common import BaseFunctionalTest, MockEmails, TestRecipients
from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE_PLAINTEXT
from odoo.addons.test_mail.models.test_mail_models import MailTestSimple
from odoo.exceptions import AccessError
-from odoo.tools import mute_logger
+from odoo.tools import mute_logger, formataddr
class TestMessagePost(BaseFunctionalTest, TestRecipients, MockEmails):
diff --git a/addons/test_mail/tests/test_message_track.py b/addons/test_mail/tests/test_message_track.py
index 2771e7b8d45..c4482be9e9d 100644
--- a/addons/test_mail/tests/test_message_track.py
+++ b/addons/test_mail/tests/test_message_track.py
@@ -1,9 +1,7 @@
# -*- coding: utf-8 -*-
-
-from email.utils import formataddr
from unittest.mock import patch
-
from odoo.addons.test_mail.tests import common
+from odoo.tools import formataddr
class TestTracking(common.BaseFunctionalTest, common.MockEmails):
diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py
index 919d6ac9dd7..2d70c1b727c 100644
--- a/addons/test_mail/tests/test_performance.py
+++ b/addons/test_mail/tests/test_performance.py
@@ -2,11 +2,9 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
-from email.utils import formataddr
-
from odoo.tests.common import TransactionCase, users, warmup
from odoo.tests import tagged
-from odoo.tools import mute_logger
+from odoo.tools import mute_logger, formataddr
@tagged('mail_performance')
diff --git a/addons/test_mail_full/tests/test_sms_composer.py b/addons/test_mail_full/tests/test_sms_composer.py
index 04ff799408f..060863e4f02 100644
--- a/addons/test_mail_full/tests/test_sms_composer.py
+++ b/addons/test_mail_full/tests/test_sms_composer.py
@@ -463,8 +463,8 @@ class TestSMSComposerMass(test_mail_full_common.BaseFunctionalTest, sms_common.M
})
# Call manually the onchange
composer._onchange_template_id()
- self.assertEquals(composer.composition_mode, "comment")
- self.assertEquals(composer.body, "Hello %s ceci est en français." % test_record_2.display_name)
+ self.assertEqual(composer.composition_mode, "comment")
+ self.assertEqual(composer.body, "Hello %s ceci est en français." % test_record_2.display_name)
with self.mockSMSGateway():
messages = composer._action_send_sms()
@@ -487,9 +487,9 @@ class TestSMSComposerMass(test_mail_full_common.BaseFunctionalTest, sms_common.M
})
# Call manually the onchange
composer._onchange_template_id()
- self.assertEquals(composer.composition_mode, "mass")
+ self.assertEqual(composer.composition_mode, "mass")
# In english because by default but when sinding depending of record
- self.assertEquals(composer.body, "Dear ${object.display_name} this is an SMS.")
+ self.assertEqual(composer.body, "Dear ${object.display_name} this is an SMS.")
with self.mockSMSGateway():
composer.action_send_sms()
diff --git a/addons/test_mass_mailing/tests/test_mail_channel.py b/addons/test_mass_mailing/tests/test_mail_channel.py
index 9d285f42d47..c966447ca76 100644
--- a/addons/test_mass_mailing/tests/test_mail_channel.py
+++ b/addons/test_mass_mailing/tests/test_mail_channel.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from email.utils import formataddr
from odoo.addons.test_mail.tests import common
+from odoo.tools import formataddr
class TestChannelPartnersNotification(common.MockEmails):
diff --git a/addons/test_website/tests/test_controller_args.py b/addons/test_website/tests/test_controller_args.py
index 4204cb1e5b4..fc21c22a2d6 100644
--- a/addons/test_website/tests/test_controller_args.py
+++ b/addons/test_website/tests/test_controller_args.py
@@ -7,25 +7,25 @@ class TestWebsiteControllerArgs(odoo.tests.HttpCase):
def test_crawl_args(self):
req = self.url_open('/ignore_args/converter/valueA/?b=valueB&c=valueC')
- self.assertEquals(req.status_code, 200)
- self.assertEquals(req.json(), {'a': 'valueA', 'b': 'valueB', 'kw': {'c': 'valueC'}})
+ self.assertEqual(req.status_code, 200)
+ self.assertEqual(req.json(), {'a': 'valueA', 'b': 'valueB', 'kw': {'c': 'valueC'}})
req = self.url_open('/ignore_args/converter/valueA/nokw?b=valueB&c=valueC')
- self.assertEquals(req.status_code, 200)
- self.assertEquals(req.json(), {'a': 'valueA', 'b': 'valueB'})
+ self.assertEqual(req.status_code, 200)
+ self.assertEqual(req.json(), {'a': 'valueA', 'b': 'valueB'})
req = self.url_open('/ignore_args/converteronly/valueA/?b=valueB&c=valueC')
- self.assertEquals(req.status_code, 200)
- self.assertEquals(req.json(), {'a': 'valueA', 'kw': None})
+ self.assertEqual(req.status_code, 200)
+ self.assertEqual(req.json(), {'a': 'valueA', 'kw': None})
req = self.url_open('/ignore_args/none?a=valueA&b=valueB')
- self.assertEquals(req.status_code, 200)
- self.assertEquals(req.json(), {'a': None, 'kw': None})
+ self.assertEqual(req.status_code, 200)
+ self.assertEqual(req.json(), {'a': None, 'kw': None})
req = self.url_open('/ignore_args/a?a=valueA&b=valueB')
- self.assertEquals(req.status_code, 200)
- self.assertEquals(req.json(), {'a': 'valueA', 'kw': None})
+ self.assertEqual(req.status_code, 200)
+ self.assertEqual(req.json(), {'a': 'valueA', 'kw': None})
req = self.url_open('/ignore_args/kw?a=valueA&b=valueB')
- self.assertEquals(req.status_code, 200)
- self.assertEquals(req.json(), {'a': 'valueA', 'kw': {'b': 'valueB'}})
+ self.assertEqual(req.status_code, 200)
+ self.assertEqual(req.json(), {'a': 'valueA', 'kw': {'b': 'valueB'}})
diff --git a/addons/test_website/tests/test_views_during_module_operation.py b/addons/test_website/tests/test_views_during_module_operation.py
index 58cf943daf4..530284e2645 100644
--- a/addons/test_website/tests/test_views_during_module_operation.py
+++ b/addons/test_website/tests/test_views_during_module_operation.py
@@ -58,7 +58,7 @@ class TestViewsDuringModuleOperation(TransactionCase):
update_module_child_view.with_context(website_id=1).write({'name': 'Child View (W1)'})
# Ensure views are correctly setup
- self.assertEquals(View.search_count([('type', '=', 'qweb'), ('key', '=', update_module_child_view.key)]), 2)
+ self.assertEqual(View.search_count([('type', '=', 'qweb'), ('key', '=', update_module_child_view.key)]), 2)
self.assertTrue(self.env.ref(update_module_view_to_be_t_called.key))
self.assertTrue(self.env.ref(update_module_base_view.key))
@@ -69,4 +69,4 @@ class TestViewsDuringModuleOperation(TransactionCase):
# Ensure generic views got removed
self.assertFalse(self.env.ref('test_website.update_module_view_to_be_t_called', raise_if_not_found=False))
# Ensure specific COW views got removed
- self.assertEquals(View.search_count([('type', '=', 'qweb'), ('key', '=', 'test_website.update_module_child_view')]), 0)
+ self.assertEqual(View.search_count([('type', '=', 'qweb'), ('key', '=', 'test_website.update_module_child_view')]), 0)
diff --git a/addons/uom/tests/test_uom.py b/addons/uom/tests/test_uom.py
index 26cbb5ef66a..1b7dd2b1e1a 100644
--- a/addons/uom/tests/test_uom.py
+++ b/addons/uom/tests/test_uom.py
@@ -18,10 +18,10 @@ class TestUom(TransactionCase):
def test_10_conversion(self):
qty = self.uom_gram._compute_quantity(1020000, self.uom_ton)
- self.assertEquals(qty, 1.02, "Converted quantity does not correspond.")
+ self.assertEqual(qty, 1.02, "Converted quantity does not correspond.")
price = self.uom_gram._compute_price(2, self.uom_ton)
- self.assertEquals(price, 2000000.0, "Converted price does not correspond.")
+ self.assertEqual(price, 2000000.0, "Converted price does not correspond.")
# If the conversion factor for Dozens (1/12) is not stored with sufficient precision,
# the conversion of 1 Dozen into Units will give e.g. 12.00000000000047 Units
@@ -29,13 +29,13 @@ class TestUom(TransactionCase):
# This is a partial regression test for rev. 311c77bb, which is further improved
# by rev. fa2f7b86.
qty = self.uom_dozen._compute_quantity(1, self.uom_unit)
- self.assertEquals(qty, 12.0, "Converted quantity does not correspond.")
+ self.assertEqual(qty, 12.0, "Converted quantity does not correspond.")
# Regression test for side-effect of commit 311c77bb - converting 1234 Grams
# into Kilograms should work even if grams are rounded to 1.
self.uom_gram.write({'rounding': 1})
qty = self.uom_gram._compute_quantity(1234, self.uom_kgm)
- self.assertEquals(qty, 1.234, "Converted quantity does not correspond.")
+ self.assertEqual(qty, 1.234, "Converted quantity does not correspond.")
def test_20_rounding(self):
product_uom = self.env['uom.uom'].create({
@@ -47,7 +47,7 @@ class TestUom(TransactionCase):
})
qty = self.uom_unit._compute_quantity(2, product_uom)
- self.assertEquals(qty, 1, "Converted quantity should be rounded up.")
+ self.assertEqual(qty, 1, "Converted quantity should be rounded up.")
def test_30_reference_uniqueness(self):
""" Check the uniqueness of the reference UoM in a category """
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index 37647b6e46e..cb8d4d82ecc 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -567,7 +567,7 @@ class HomeStaticTemplateHelpers(object):
:returns: (concatenation_result, checksum)
:rtype: (bytes, str)
"""
- checksum = hashlib.new('sha1')
+ checksum = hashlib.new('sha512') # sha512/256
if not file_dict:
return b'', checksum.hexdigest()
@@ -586,7 +586,7 @@ class HomeStaticTemplateHelpers(object):
for template in addon.values():
root.append(template)
- return etree.tostring(root, encoding='utf-8') if root is not None else b'', checksum.hexdigest()
+ return etree.tostring(root, encoding='utf-8') if root is not None else b'', checksum.hexdigest()[:64]
def _get_qweb_templates(self):
"""One and only entry point that gets and evaluates static qweb templates
@@ -1293,7 +1293,7 @@ class Binary(http.Controller):
try:
attachment = Model.create({
'name': filename,
- 'datas': base64.encodestring(ufile.read()),
+ 'datas': base64.encodebytes(ufile.read()),
'res_model': model,
'res_id': int(id)
})
diff --git a/addons/web/models/ir_http.py b/addons/web/models/ir_http.py
index f5a9f922abf..65ae8073326 100644
--- a/addons/web/models/ir_http.py
+++ b/addons/web/models/ir_http.py
@@ -62,9 +62,9 @@ class Http(models.AbstractModel):
"show_effect": True,
"display_switch_company_menu": user.has_group('base.group_multi_company') and len(user.company_ids) > 1,
"cache_hashes": {
- "load_menus": hashlib.sha1(menu_json_utf8).hexdigest(),
+ "load_menus": hashlib.sha512(menu_json_utf8).hexdigest()[:64], # sha512/256
"qweb": qweb_checksum,
- "translations": hashlib.sha1(translations_json_utf8).hexdigest(),
+ "translations": hashlib.sha512(translations_json_utf8).hexdigest()[:64], # sha512/256
},
}
diff --git a/addons/web/models/ir_qweb.py b/addons/web/models/ir_qweb.py
index 1083916ebf5..6776dbbff14 100644
--- a/addons/web/models/ir_qweb.py
+++ b/addons/web/models/ir_qweb.py
@@ -43,7 +43,7 @@ class Image(models.AbstractModel):
if max_width or max_height:
max_size = '%sx%s' % (max_width, max_height)
- sha = hashlib.sha1(str(getattr(record, '__last_update')).encode('utf-8')).hexdigest()[0:7]
+ sha = hashlib.sha512(str(getattr(record, '__last_update')).encode('utf-8')).hexdigest()[:7]
max_size = '' if max_size is None else '/%s' % max_size
if options.get('filename-field') and getattr(record, options['filename-field'], None):
diff --git a/addons/web_diagram/__init__.py b/addons/web_diagram/__init__.py
deleted file mode 100644
index dd15b5c32e5..00000000000
--- a/addons/web_diagram/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from . import controllers
diff --git a/addons/web_diagram/__manifest__.py b/addons/web_diagram/__manifest__.py
deleted file mode 100644
index 537e8785f85..00000000000
--- a/addons/web_diagram/__manifest__.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-{
- 'name': 'Odoo Web Diagram',
- 'category': 'Hidden',
- 'description': """
-Openerp Web Diagram view.
-=========================
-
-""",
- 'version': '2.0',
- 'depends': ['web'],
- 'data': [
- 'views/web_diagram_templates.xml',
- ],
- 'qweb': [
- 'static/src/xml/*.xml',
- ],
- 'auto_install': True,
-}
diff --git a/addons/web_diagram/controllers/__init__.py b/addons/web_diagram/controllers/__init__.py
deleted file mode 100644
index 5d4b25db9c0..00000000000
--- a/addons/web_diagram/controllers/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from . import main
diff --git a/addons/web_diagram/controllers/main.py b/addons/web_diagram/controllers/main.py
deleted file mode 100644
index d29b60df40e..00000000000
--- a/addons/web_diagram/controllers/main.py
+++ /dev/null
@@ -1,116 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-import odoo.http as http
-
-from odoo.tools.safe_eval import safe_eval
-
-
-class DiagramView(http.Controller):
-
- @http.route('/web_diagram/diagram/get_diagram_info', type='json', auth='user')
- def get_diagram_info(self, id, model, node, connector,
- src_node, des_node, label, **kw):
-
- visible_node_fields = kw.get('visible_node_fields', [])
- invisible_node_fields = kw.get('invisible_node_fields', [])
- node_fields_string = kw.get('node_fields_string', [])
- connector_fields = kw.get('connector_fields', [])
- connector_fields_string = kw.get('connector_fields_string', [])
-
- bgcolors = {}
- shapes = {}
- bgcolor = kw.get('bgcolor', '')
- shape = kw.get('shape', '')
-
- if bgcolor:
- for color_spec in bgcolor.split(';'):
- if color_spec:
- colour, color_state = color_spec.split(':')
- bgcolors[colour] = color_state
-
- if shape:
- for shape_spec in shape.split(';'):
- if shape_spec:
- shape_colour, shape_color_state = shape_spec.split(':')
- shapes[shape_colour] = shape_color_state
-
- ir_view = http.request.env['ir.ui.view']
- graphs = ir_view.graph_get(int(id), model, node, connector, src_node,
- des_node, label, (140, 180))
- nodes = graphs['nodes']
- transitions = graphs['transitions']
- isolate_nodes = {}
- for blnk_node in graphs['blank_nodes']:
- isolate_nodes[blnk_node['id']] = blnk_node
- y = [
- t['y']
- for t in nodes.values()
- if t['x'] == 20
- if t['y']
- ]
- y_max = (y and max(y)) or 120
-
- connectors = {}
- list_tr = []
-
- for tr in transitions:
- list_tr.append(tr)
- connectors.setdefault(tr, {
- 'id': int(tr),
- 's_id': transitions[tr][0],
- 'd_id': transitions[tr][1]
- })
-
- connector_model = http.request.env[connector]
- data_connectors = connector_model.search([('id', 'in', list_tr)]).read(connector_fields)
-
- for tr in data_connectors:
- transition_id = str(tr['id'])
- _sourceid, label = graphs['label'][transition_id]
- t = connectors[transition_id]
- t.update(
- source=tr[src_node][1],
- destination=tr[des_node][1],
- options={},
- signal=label
- )
-
- for i, fld in enumerate(connector_fields):
- t['options'][connector_fields_string[i]] = tr[fld]
-
- fields = http.request.env['ir.model.fields']
- field = fields.search([('model', '=', model), ('relation', '=', node)])
- node_act = http.request.env[node]
- search_acts = node_act.search([(field.relation_field, '=', id)])
- data_acts = search_acts.read(invisible_node_fields + visible_node_fields)
-
- for act in data_acts:
- n = nodes.get(str(act['id']))
- if not n:
- n = isolate_nodes.get(act['id'], {})
- y_max += 140
- n.update(x=20, y=y_max)
- nodes[act['id']] = n
-
- n.update(
- id=act['id'],
- color='white',
- options={}
- )
- for color, expr in bgcolors.items():
- if safe_eval(expr, act):
- n['color'] = color
-
- for shape, expr in shapes.items():
- if safe_eval(expr, act):
- n['shape'] = shape
-
- for i, fld in enumerate(visible_node_fields):
- n['options'][node_fields_string[i]] = act[fld]
-
- _id, name = http.request.env[model].browse([id]).name_get()[0]
- return dict(nodes=nodes,
- conn=connectors,
- display_name=name,
- parent_field=graphs['node_parent_field'])
diff --git a/addons/web_diagram/i18n/af.po b/addons/web_diagram/i18n/af.po
deleted file mode 100644
index 6fc1afb00fb..00000000000
--- a/addons/web_diagram/i18n/af.po
+++ /dev/null
@@ -1,93 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-08-25 10:26+0000\n"
-"Last-Translator: <>\n"
-"Language-Team: Afrikaans (http://www.transifex.com/odoo/odoo-9/language/"
-"af/)\n"
-"Language: af\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Aktiwiteit"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nuwe"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/am.po b/addons/web_diagram/i18n/am.po
deleted file mode 100644
index 887435258eb..00000000000
--- a/addons/web_diagram/i18n/am.po
+++ /dev/null
@@ -1,92 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:26+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Amharic (http://www.transifex.com/odoo/odoo-9/language/am/)\n"
-"Language: am\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "አዲስ"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/ar.po b/addons/web_diagram/i18n/ar.po
deleted file mode 100644
index 9ef9f691284..00000000000
--- a/addons/web_diagram/i18n/ar.po
+++ /dev/null
@@ -1,84 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Akram Alfusayal <akram_ma@hotmail.com>, 2019
-# Martin Trigaux, 2019
-# Shaima Safar <shaima.safar@open-inside.com>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Shaima Safar <shaima.safar@open-inside.com>, 2019\n"
-"Language-Team: Arabic (https://www.transifex.com/odoo/teams/41243/ar/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: ar\n"
-"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "النشاط"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "إنشاء:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "الرسم التخطيطي"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "طرف جديد"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "انتقال"
diff --git a/addons/web_diagram/i18n/az.po b/addons/web_diagram/i18n/az.po
deleted file mode 100644
index e6f0a8d55ed..00000000000
--- a/addons/web_diagram/i18n/az.po
+++ /dev/null
@@ -1,78 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-08-24 09:33+0000\n"
-"Language-Team: Azerbaijani (https://www.transifex.com/odoo/teams/41243/az/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: az\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/bg.po b/addons/web_diagram/i18n/bg.po
deleted file mode 100644
index d58b49ed85d..00000000000
--- a/addons/web_diagram/i18n/bg.po
+++ /dev/null
@@ -1,100 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:26+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Bulgarian (http://www.transifex.com/odoo/odoo-9/language/"
-"bg/)\n"
-"Language: bg\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Дейност"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Създай:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Изтриването на този възел не може да бъде отменено.\n"
-"То също така ще изтрие и всички свързани преходи.\n"
-"\n"
-"Сигурни ли сте?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Изтриването на този възел не може да бъде отменено.\n"
-"\n"
-"Сигурни ли сте?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Диаграма"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nieuw"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Нов възел"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Отвори: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Преход"
diff --git a/addons/web_diagram/i18n/bn.po b/addons/web_diagram/i18n/bn.po
deleted file mode 100644
index 9f4bf195191..00000000000
--- a/addons/web_diagram/i18n/bn.po
+++ /dev/null
@@ -1,99 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:26+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Bengali (http://www.transifex.com/odoo/odoo-9/language/bn/)\n"
-"Language: bn\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "কর্মকাণ্ড"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "তৈরি করুন:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"এই সংযোস্থল মুছে ফেলার পূর্বাবস্থায় ফেরানো যাবে না।\n"
-"এটি সমস্ত সংযুক্ত পরিবর্তনসমূহ মুছে ফেলবে।\n"
-"\n"
-"আপনি কি নিশ্চিত?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"এই পরিবর্তন মুছে ফেলার পূর্বাবস্থায় ফেরানো যাবে না.\n"
-"\n"
-"আপনি কি নিশ্চিত?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "নকশা"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "নতুন সংযোগস্থল"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "খুলুন: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "পরিবর্তন"
diff --git a/addons/web_diagram/i18n/bs.po b/addons/web_diagram/i18n/bs.po
deleted file mode 100644
index 241a70aad4d..00000000000
--- a/addons/web_diagram/i18n/bs.po
+++ /dev/null
@@ -1,81 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-09-18 09:49+0000\n"
-"Last-Translator: Martin Trigaux, 2018\n"
-"Language-Team: Bosnian (https://www.transifex.com/odoo/teams/41243/bs/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: bs\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Aktivnost"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Kreiraj:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Dijagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Novi čvor"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Prelaz"
diff --git a/addons/web_diagram/i18n/ca.po b/addons/web_diagram/i18n/ca.po
deleted file mode 100644
index 29abfe2e9a8..00000000000
--- a/addons/web_diagram/i18n/ca.po
+++ /dev/null
@@ -1,84 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-# Marc Tormo i Bochaca <mtbochaca@gmail.com>, 2018
-# Quim - eccit <quim@eccit.com>, 2018
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-08-24 09:33+0000\n"
-"Last-Translator: Quim - eccit <quim@eccit.com>, 2018\n"
-"Language-Team: Catalan (https://www.transifex.com/odoo/teams/41243/ca/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: ca\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Activitat"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Crea"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Node nou"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Transició"
diff --git a/addons/web_diagram/i18n/cs.po b/addons/web_diagram/i18n/cs.po
deleted file mode 100644
index 4cb5d5b0a3e..00000000000
--- a/addons/web_diagram/i18n/cs.po
+++ /dev/null
@@ -1,85 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Michal Veselý <michal@veselyberanek.net>, 2019
-# trendspotter <j.podhorecky@volny.cz>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: trendspotter <j.podhorecky@volny.cz>, 2019\n"
-"Language-Team: Czech (https://www.transifex.com/odoo/teams/41243/cs/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: cs\n"
-"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Činnost"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-"Opravdu chcete tento uzel odstranit? Tímto odstraníte také propojené "
-"přechody."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "Opravdu chcete tento přechod odstranit?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Vytvořit:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nový uzel"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Otevřít:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Přechod"
diff --git a/addons/web_diagram/i18n/da.po b/addons/web_diagram/i18n/da.po
deleted file mode 100644
index f361f565acb..00000000000
--- a/addons/web_diagram/i18n/da.po
+++ /dev/null
@@ -1,82 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-# lhmflexerp <lhm@flexerp.dk>, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-09-18 09:49+0000\n"
-"Last-Translator: lhmflexerp <lhm@flexerp.dk>, 2018\n"
-"Language-Team: Danish (https://www.transifex.com/odoo/teams/41243/da/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: da\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Aktivitet"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Opret:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Ny knude"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Åben:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Overgang"
diff --git a/addons/web_diagram/i18n/de.po b/addons/web_diagram/i18n/de.po
deleted file mode 100644
index 940e2f3fa89..00000000000
--- a/addons/web_diagram/i18n/de.po
+++ /dev/null
@@ -1,87 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# e2f <projects@e2f.com>, 2019
-# Martin Trigaux, 2019
-# darenkster <inactive+darenkster@transifex.com>, 2019
-# Katharina Moritz <kmo@e2f.com>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Katharina Moritz <kmo@e2f.com>, 2019\n"
-"Language-Team: German (https://www.transifex.com/odoo/teams/41243/de/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: de\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Aktivität"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-"Möchten Sie diesen Knoten wirklich entfernen? Dadurch werden alle "
-"zugehörigen Übergänge ebenfalls entfernt."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "Sind Sie sicher, dass Sie diesen Übergang entfernen möchten?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Anlegen:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagramm"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Neuer Knoten"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Öffnen:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Übergang"
diff --git a/addons/web_diagram/i18n/el.po b/addons/web_diagram/i18n/el.po
deleted file mode 100644
index 986833b84d0..00000000000
--- a/addons/web_diagram/i18n/el.po
+++ /dev/null
@@ -1,84 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-# Kostas Goutoudis <goutoudis@gmail.com>, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-09-18 09:49+0000\n"
-"Last-Translator: Kostas Goutoudis <goutoudis@gmail.com>, 2018\n"
-"Language-Team: Greek (https://www.transifex.com/odoo/teams/41243/el/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: el\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Δραστηριότητα"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-"Είστε βέβαιοι ότι θέλετε να καταργήσετε αυτόν τον κόμβο; Αυτό θα αφαιρέσει "
-"επίσης τις συνδεδεμένες μεταβάσεις του."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "Είστε βέβαιοι ότι θέλετε να καταργήσετε αυτήν τη μετάβαση;"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Δημιουργία:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Διάγραμμα"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Νέος Κόμβος"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Άνοιγμα:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Ροή"
diff --git a/addons/web_diagram/i18n/en_AU.po b/addons/web_diagram/i18n/en_AU.po
deleted file mode 100644
index 8835c64f883..00000000000
--- a/addons/web_diagram/i18n/en_AU.po
+++ /dev/null
@@ -1,100 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:26+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: English (Australia) (http://www.transifex.com/odoo/odoo-9/"
-"language/en_AU/)\n"
-"Language: en_AU\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Activity"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Create:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "New"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "New Node"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Open: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Transition"
diff --git a/addons/web_diagram/i18n/en_GB.po b/addons/web_diagram/i18n/en_GB.po
deleted file mode 100644
index c89894ccb8c..00000000000
--- a/addons/web_diagram/i18n/en_GB.po
+++ /dev/null
@@ -1,100 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:26+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: English (United Kingdom) (http://www.transifex.com/odoo/"
-"odoo-9/language/en_GB/)\n"
-"Language: en_GB\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Activity"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Create:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "New"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "New Node"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Open: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Transition"
diff --git a/addons/web_diagram/i18n/es.po b/addons/web_diagram/i18n/es.po
deleted file mode 100644
index 05f299aea83..00000000000
--- a/addons/web_diagram/i18n/es.po
+++ /dev/null
@@ -1,86 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# e2f <projects@e2f.com>, 2019
-# Martin Trigaux, 2019
-# Luis M. Ontalba <luis.martinez@tecnativa.com>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Luis M. Ontalba <luis.martinez@tecnativa.com>, 2019\n"
-"Language-Team: Spanish (https://www.transifex.com/odoo/teams/41243/es/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: es\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Actividad"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-"¿Seguro que quiere eliminar este nodo? Si lo hace, también se eliminarán sus"
-" transiciones conectadas."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "¿Seguro que desea eliminar esta transición?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Crear:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nuevo Nodo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Abrir:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Transición"
diff --git a/addons/web_diagram/i18n/es_AR.po b/addons/web_diagram/i18n/es_AR.po
deleted file mode 100644
index a9390997cb7..00000000000
--- a/addons/web_diagram/i18n/es_AR.po
+++ /dev/null
@@ -1,100 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-04 14:20+0000\n"
-"Last-Translator: Leonardo Germán Chianea <noamixcontenidos@gmail.com>\n"
-"Language-Team: Spanish (Argentina) (http://www.transifex.com/odoo/odoo-9/"
-"language/es_AR/)\n"
-"Language: es_AR\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Actividad"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Crear:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"La eliminación de este nodo no puede ser deshecha.\n"
-"También eliminará todas las transiciones conectadas.\n"
-"\n"
-"¿Está seguro?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"La eliminación de esta transición no se puede deshacer.\n"
-"\n"
-"¿Está seguro?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nuevo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nuevo Nodo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Abrir: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Transición"
diff --git a/addons/web_diagram/i18n/es_BO.po b/addons/web_diagram/i18n/es_BO.po
deleted file mode 100644
index 6a8b6f5ec1a..00000000000
--- a/addons/web_diagram/i18n/es_BO.po
+++ /dev/null
@@ -1,93 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-08-25 10:26+0000\n"
-"Last-Translator: <>\n"
-"Language-Team: Spanish (Bolivia) (http://www.transifex.com/odoo/odoo-9/"
-"language/es_BO/)\n"
-"Language: es_BO\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nuevo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/es_CL.po b/addons/web_diagram/i18n/es_CL.po
deleted file mode 100644
index a8f012e71ea..00000000000
--- a/addons/web_diagram/i18n/es_CL.po
+++ /dev/null
@@ -1,97 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:28+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Spanish (Chile) (http://www.transifex.com/odoo/odoo-9/"
-"language/es_CL/)\n"
-"Language: es_CL\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Actividad"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Crear:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"La eliminación de este nodo no puede ser desecha.\n"
-"¿Está Seguro?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"La eliminación de esta transacción no puede ser desecha.\n"
-"¿Está Seguro?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nuevo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nuevo nodo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Abrir: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Transición"
diff --git a/addons/web_diagram/i18n/es_CO.po b/addons/web_diagram/i18n/es_CO.po
deleted file mode 100644
index 4033586fb3c..00000000000
--- a/addons/web_diagram/i18n/es_CO.po
+++ /dev/null
@@ -1,101 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Mateo Tibaquirá <nestormateo@gmail.com>, 2015
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-11-26 23:18+0000\n"
-"Last-Translator: Mateo Tibaquirá <nestormateo@gmail.com>\n"
-"Language-Team: Spanish (Colombia) (http://www.transifex.com/odoo/odoo-9/"
-"language/es_CO/)\n"
-"Language: es_CO\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Actividad"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Crear:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"No se puede deshacer la eliminación de este nodo.\n"
-"También se eliminarán todas las transiciones conectadas.\n"
-"\n"
-"Está seguro/a?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"No se puede deshacer la eliminación de esta transición.\n"
-"\n"
-"Está seguro/a?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nuevo(a)"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nuevo Nodo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Abrir:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Transición"
diff --git a/addons/web_diagram/i18n/es_CR.po b/addons/web_diagram/i18n/es_CR.po
deleted file mode 100644
index 0518bb3aa33..00000000000
--- a/addons/web_diagram/i18n/es_CR.po
+++ /dev/null
@@ -1,93 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:28+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Spanish (Costa Rica) (http://www.transifex.com/odoo/odoo-9/"
-"language/es_CR/)\n"
-"Language: es_CR\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Actividad"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Crear:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nuevo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nuevo Nodo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Abrir: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Transición"
diff --git a/addons/web_diagram/i18n/es_DO.po b/addons/web_diagram/i18n/es_DO.po
deleted file mode 100644
index cf9d6655145..00000000000
--- a/addons/web_diagram/i18n/es_DO.po
+++ /dev/null
@@ -1,100 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:28+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Spanish (Dominican Republic) (http://www.transifex.com/odoo/"
-"odoo-9/language/es_DO/)\n"
-"Language: es_DO\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Actividad"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Crear:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Eliminar este nodo no se puede deshacer.\n"
-"Asimismo, se eliminarán todas las transacciones conectadas.\n"
-"\n"
-"¿Estás seguro?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Eliminar esta transición no se puede deshacer.\n"
-"\n"
-"¿Estás seguro?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nuevo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nuevo Nodo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Abrir: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Transición"
diff --git a/addons/web_diagram/i18n/es_EC.po b/addons/web_diagram/i18n/es_EC.po
deleted file mode 100644
index a66b93e1beb..00000000000
--- a/addons/web_diagram/i18n/es_EC.po
+++ /dev/null
@@ -1,100 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:28+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Spanish (Ecuador) (http://www.transifex.com/odoo/odoo-9/"
-"language/es_EC/)\n"
-"Language: es_EC\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Actividad"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Crear:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Borrando esta transición no podrá recuparala.\n"
-"\n"
-"Está seguro ?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nuevo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nuevo Nodo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Abrir: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Transición"
diff --git a/addons/web_diagram/i18n/es_PE.po b/addons/web_diagram/i18n/es_PE.po
deleted file mode 100644
index e2d577faf59..00000000000
--- a/addons/web_diagram/i18n/es_PE.po
+++ /dev/null
@@ -1,101 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Carlos Eduardo Rodriguez Rossi <crodriguez@samemotion.com>, 2016
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2016-06-16 19:10+0000\n"
-"Last-Translator: Carlos Eduardo Rodriguez Rossi <crodriguez@samemotion.com>\n"
-"Language-Team: Spanish (Peru) (http://www.transifex.com/odoo/odoo-9/language/"
-"es_PE/)\n"
-"Language: es_PE\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Actividad"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Crear:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"No se puede deshacer la eliminación de este nodo.\n"
-"También se eliminarán todas las transiciones conectadas.\n"
-"\n"
-"¿Está seguro?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"No se puede deshacer la eliminación de esta transición.\n"
-"\n"
-"¿Está seguro?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nuevo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nuevo Nodo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Abierto:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Transición"
diff --git a/addons/web_diagram/i18n/es_PY.po b/addons/web_diagram/i18n/es_PY.po
deleted file mode 100644
index 7a1f4dc315d..00000000000
--- a/addons/web_diagram/i18n/es_PY.po
+++ /dev/null
@@ -1,93 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:28+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Spanish (Paraguay) (http://www.transifex.com/odoo/odoo-9/"
-"language/es_PY/)\n"
-"Language: es_PY\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nuevo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/es_VE.po b/addons/web_diagram/i18n/es_VE.po
deleted file mode 100644
index 7bfd52ad30f..00000000000
--- a/addons/web_diagram/i18n/es_VE.po
+++ /dev/null
@@ -1,93 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:28+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Spanish (Venezuela) (http://www.transifex.com/odoo/odoo-9/"
-"language/es_VE/)\n"
-"Language: es_VE\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Actividad"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nuevo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Transición"
diff --git a/addons/web_diagram/i18n/et.po b/addons/web_diagram/i18n/et.po
deleted file mode 100644
index 043708a9132..00000000000
--- a/addons/web_diagram/i18n/et.po
+++ /dev/null
@@ -1,83 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Rivo Zängov <eraser@eraser.ee>, 2018
-# Martin Trigaux, 2018
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-08-24 09:33+0000\n"
-"Last-Translator: Martin Trigaux, 2018\n"
-"Language-Team: Estonian (https://www.transifex.com/odoo/teams/41243/et/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: et\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Tegevus"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Loo:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagramm"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Uus sõlm"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Ava:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Siire"
diff --git a/addons/web_diagram/i18n/eu.po b/addons/web_diagram/i18n/eu.po
deleted file mode 100644
index e7feba5b6ba..00000000000
--- a/addons/web_diagram/i18n/eu.po
+++ /dev/null
@@ -1,92 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-08-25 10:26+0000\n"
-"Last-Translator: <>\n"
-"Language-Team: Basque (http://www.transifex.com/odoo/odoo-9/language/eu/)\n"
-"Language: eu\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Berria"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/fa.po b/addons/web_diagram/i18n/fa.po
deleted file mode 100644
index 36640355169..00000000000
--- a/addons/web_diagram/i18n/fa.po
+++ /dev/null
@@ -1,81 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-09-18 09:49+0000\n"
-"Last-Translator: Martin Trigaux, 2018\n"
-"Language-Team: Persian (https://www.transifex.com/odoo/teams/41243/fa/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: fa\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "فعالیت"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "ایجاد"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "دیاگرام"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "گره جدید"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "گذار"
diff --git a/addons/web_diagram/i18n/fi.po b/addons/web_diagram/i18n/fi.po
deleted file mode 100644
index 97217cb4d7c..00000000000
--- a/addons/web_diagram/i18n/fi.po
+++ /dev/null
@@ -1,82 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-# Tuomo Aura <tuomo.aura@web-veistamo.fi>, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.2\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-03-22 14:11+0000\n"
-"PO-Revision-Date: 2018-03-22 14:11+0000\n"
-"Last-Translator: Tuomo Aura <tuomo.aura@web-veistamo.fi>, 2018\n"
-"Language-Team: Finnish (https://www.transifex.com/odoo/teams/41243/fi/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: fi\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Toimenpide"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Luo:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Kaavio"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Uusi Noodi"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Siirtymä"
diff --git a/addons/web_diagram/i18n/fr.po b/addons/web_diagram/i18n/fr.po
deleted file mode 100644
index 814b227a100..00000000000
--- a/addons/web_diagram/i18n/fr.po
+++ /dev/null
@@ -1,86 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# e2f <projects@e2f.com>, 2019
-# Martin Trigaux, 2019
-# Lucas Deliege <lud@odoo.com>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Lucas Deliege <lud@odoo.com>, 2019\n"
-"Language-Team: French (https://www.transifex.com/odoo/teams/41243/fr/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: fr\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Activité"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-"Voulez-vous vraiment supprimer ce nœud ? Ses transitions associées seront "
-"également supprimées."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "Voulez-vous vraiment supprimer cette transition ?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Créer:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagramme"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nouvelle activité"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Ouvrir :"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Transition"
diff --git a/addons/web_diagram/i18n/fr_BE.po b/addons/web_diagram/i18n/fr_BE.po
deleted file mode 100644
index 6b4d664668b..00000000000
--- a/addons/web_diagram/i18n/fr_BE.po
+++ /dev/null
@@ -1,93 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:27+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: French (Belgium) (http://www.transifex.com/odoo/odoo-9/"
-"language/fr_BE/)\n"
-"Language: fr_BE\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nouveau"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/fr_CA.po b/addons/web_diagram/i18n/fr_CA.po
deleted file mode 100644
index dabdd9cbd60..00000000000
--- a/addons/web_diagram/i18n/fr_CA.po
+++ /dev/null
@@ -1,93 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-08-25 10:26+0000\n"
-"Last-Translator: <>\n"
-"Language-Team: French (Canada) (http://www.transifex.com/odoo/odoo-9/"
-"language/fr_CA/)\n"
-"Language: fr_CA\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nouveau"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/gl.po b/addons/web_diagram/i18n/gl.po
deleted file mode 100644
index 8692b056334..00000000000
--- a/addons/web_diagram/i18n/gl.po
+++ /dev/null
@@ -1,99 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:27+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Galician (http://www.transifex.com/odoo/odoo-9/language/gl/)\n"
-"Language: gl\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Actividade"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Crear:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"O borrado deste nodo non se poderá desfacer.\n"
-"Tamén borrará tódalas transicións conectadas.\n"
-"\n"
-"¿Está seguro?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"O borrado desta transición non se poderá desfacer.\n"
-"\n"
-"Está seguro?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Novo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Novo nodo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Abrir: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Transición"
diff --git a/addons/web_diagram/i18n/gu.po b/addons/web_diagram/i18n/gu.po
deleted file mode 100644
index 68f4420b6d7..00000000000
--- a/addons/web_diagram/i18n/gu.po
+++ /dev/null
@@ -1,81 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.2\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-03-22 14:11+0000\n"
-"PO-Revision-Date: 2018-03-22 14:11+0000\n"
-"Last-Translator: Martin Trigaux, 2018\n"
-"Language-Team: Gujarati (https://www.transifex.com/odoo/teams/41243/gu/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: gu\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "પ્રવૃત્તિ"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/he.po b/addons/web_diagram/i18n/he.po
deleted file mode 100644
index 4bc469225dc..00000000000
--- a/addons/web_diagram/i18n/he.po
+++ /dev/null
@@ -1,82 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-# Yihya Hugirat <hugirat@gmail.com>, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-09-18 09:49+0000\n"
-"Last-Translator: Yihya Hugirat <hugirat@gmail.com>, 2018\n"
-"Language-Team: Hebrew (https://www.transifex.com/odoo/teams/41243/he/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: he\n"
-"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "פעילות"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "תרשים"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "שינוי"
diff --git a/addons/web_diagram/i18n/hi.po b/addons/web_diagram/i18n/hi.po
deleted file mode 100644
index a3068bf5d05..00000000000
--- a/addons/web_diagram/i18n/hi.po
+++ /dev/null
@@ -1,92 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:27+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Hindi (http://www.transifex.com/odoo/odoo-9/language/hi/)\n"
-"Language: hi\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "नया"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "खोलने के लिए "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/hr.po b/addons/web_diagram/i18n/hr.po
deleted file mode 100644
index 187f788d1a7..00000000000
--- a/addons/web_diagram/i18n/hr.po
+++ /dev/null
@@ -1,85 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2019
-# Bole <bole@dajmi5.com>, 2019
-# Đurđica Žarković <durdica.zarkovic@storm.hr>, 2019
-# Karolina Tonković <karolina.tonkovic@storm.hr>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Karolina Tonković <karolina.tonkovic@storm.hr>, 2019\n"
-"Language-Team: Croatian (https://www.transifex.com/odoo/teams/41243/hr/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: hr\n"
-"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Aktivnost"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Kreiraj:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Dijagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Novi čvor"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Tranzicija"
diff --git a/addons/web_diagram/i18n/hu.po b/addons/web_diagram/i18n/hu.po
deleted file mode 100644
index 3a47f322fa3..00000000000
--- a/addons/web_diagram/i18n/hu.po
+++ /dev/null
@@ -1,83 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2019
-# krnkris, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: krnkris, 2019\n"
-"Language-Team: Hungarian (https://www.transifex.com/odoo/teams/41243/hu/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: hu\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Tevékenység"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Létrehoz:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Új csomópont"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Átmenet"
diff --git a/addons/web_diagram/i18n/hy.po b/addons/web_diagram/i18n/hy.po
deleted file mode 100644
index ac659cbc89c..00000000000
--- a/addons/web_diagram/i18n/hy.po
+++ /dev/null
@@ -1,92 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:26+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Armenian (http://www.transifex.com/odoo/odoo-9/language/hy/)\n"
-"Language: hy\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Գործունեություն"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/id.po b/addons/web_diagram/i18n/id.po
deleted file mode 100644
index ede21f1c960..00000000000
--- a/addons/web_diagram/i18n/id.po
+++ /dev/null
@@ -1,82 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux <mat@odoo.com>, 2017
-# Muhammad Herdiansyah <herdiansyah@openmailbox.org>, 2017
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server 10.saas~18\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-09-20 09:54+0000\n"
-"PO-Revision-Date: 2017-09-20 09:54+0000\n"
-"Last-Translator: Muhammad Herdiansyah <herdiansyah@openmailbox.org>, 2017\n"
-"Language-Team: Indonesian (https://www.transifex.com/odoo/teams/41243/id/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: id\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Aktivitas"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Buat :"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Node baru"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Transisi"
diff --git a/addons/web_diagram/i18n/is.po b/addons/web_diagram/i18n/is.po
deleted file mode 100644
index b5122e64365..00000000000
--- a/addons/web_diagram/i18n/is.po
+++ /dev/null
@@ -1,82 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Bjorn Ingvarsson <boi@exigo.is>, 2018
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-08-24 09:33+0000\n"
-"Last-Translator: Bjorn Ingvarsson <boi@exigo.is>, 2018\n"
-"Language-Team: Icelandic (https://www.transifex.com/odoo/teams/41243/is/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: is\n"
-"Plural-Forms: nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Virkni"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Transition"
diff --git a/addons/web_diagram/i18n/it.po b/addons/web_diagram/i18n/it.po
deleted file mode 100644
index b39c47bdd81..00000000000
--- a/addons/web_diagram/i18n/it.po
+++ /dev/null
@@ -1,84 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Francesco Garganese <francesco.garganese@aeromnia.aero>, 2019
-# Martin Trigaux, 2019
-# Paolo Valier, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Paolo Valier, 2019\n"
-"Language-Team: Italian (https://www.transifex.com/odoo/teams/41243/it/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: it\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Attività"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Crea:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagramma"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nuovo Nodo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Transizione"
diff --git a/addons/web_diagram/i18n/ja.po b/addons/web_diagram/i18n/ja.po
deleted file mode 100644
index bc174f96abb..00000000000
--- a/addons/web_diagram/i18n/ja.po
+++ /dev/null
@@ -1,82 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-# Yoshi Tashiro <tashiro@roomsfor.hk>, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-09-18 09:49+0000\n"
-"Last-Translator: Yoshi Tashiro <tashiro@roomsfor.hk>, 2018\n"
-"Language-Team: Japanese (https://www.transifex.com/odoo/teams/41243/ja/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: ja\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "活動"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "作成:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "ダイアグラム"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "新規ノード"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "オープン:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "遷移"
diff --git a/addons/web_diagram/i18n/ka.po b/addons/web_diagram/i18n/ka.po
deleted file mode 100644
index 6a7b1e3fde6..00000000000
--- a/addons/web_diagram/i18n/ka.po
+++ /dev/null
@@ -1,99 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:27+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Georgian (http://www.transifex.com/odoo/odoo-9/language/ka/)\n"
-"Language: ka\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "აქტივობა"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "შექმნა:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"აღნიშნული კვანძის წაშლა ვერ დაბრუნდება უკან.\n"
-"იგი ასევე წაშლის დაკავშირებულ გარდაქმნებს.\n"
-"\n"
-"დარწმუნებული ხართ?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"აღნიშნული გარდაქმნის წაშლის ოპერაცია უკან ვერ შებრუნდება.\n"
-"\n"
-"დარწმუნებული ხართ?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "დიაგრამა"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "ახალი"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "ახალი კვანძი"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "ღია: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "გარდაქმნა"
diff --git a/addons/web_diagram/i18n/kab.po b/addons/web_diagram/i18n/kab.po
deleted file mode 100644
index d29d0169089..00000000000
--- a/addons/web_diagram/i18n/kab.po
+++ /dev/null
@@ -1,92 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:27+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Kabyle (http://www.transifex.com/odoo/odoo-9/language/kab/)\n"
-"Language: kab\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Armud"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Rnu:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Amaynut"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Lli: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Asaka"
diff --git a/addons/web_diagram/i18n/kk.po b/addons/web_diagram/i18n/kk.po
deleted file mode 100644
index 2769e4e4b3e..00000000000
--- a/addons/web_diagram/i18n/kk.po
+++ /dev/null
@@ -1,92 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:27+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Kazakh (http://www.transifex.com/odoo/odoo-9/language/kk/)\n"
-"Language: kk\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Ісі"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/km.po b/addons/web_diagram/i18n/km.po
deleted file mode 100644
index 80242a2225e..00000000000
--- a/addons/web_diagram/i18n/km.po
+++ /dev/null
@@ -1,81 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Sengtha Chay <sengtha@gmail.com>, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-09-18 09:49+0000\n"
-"Last-Translator: Sengtha Chay <sengtha@gmail.com>, 2018\n"
-"Language-Team: Khmer (https://www.transifex.com/odoo/teams/41243/km/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: km\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "សកម្មភាព"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/ko.po b/addons/web_diagram/i18n/ko.po
deleted file mode 100644
index 63865bc7973..00000000000
--- a/addons/web_diagram/i18n/ko.po
+++ /dev/null
@@ -1,83 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Link Up링크업 <linkup.way@gmail.com>, 2018
-# Seongseok Shin <shinss61@hotmail.com>, 2018
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-08-24 09:33+0000\n"
-"Last-Translator: Seongseok Shin <shinss61@hotmail.com>, 2018\n"
-"Language-Team: Korean (https://www.transifex.com/odoo/teams/41243/ko/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: ko\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "활동"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr "이 노드를 제거하시겠습니까? 제거하시면, 연결된 화면 전환도 함께 제거됩니다."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "이 화면전환을 제거하시겠습니까?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "생성:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "도표"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "신규 노드"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "열기 : "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "화면 전환"
diff --git a/addons/web_diagram/i18n/lt.po b/addons/web_diagram/i18n/lt.po
deleted file mode 100644
index 19c60826b6f..00000000000
--- a/addons/web_diagram/i18n/lt.po
+++ /dev/null
@@ -1,85 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2019
-# Linas Versada <linaskrisiukenas@gmail.com>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Linas Versada <linaskrisiukenas@gmail.com>, 2019\n"
-"Language-Team: Lithuanian (https://www.transifex.com/odoo/teams/41243/lt/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: lt\n"
-"Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Veikla"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-"Ar tikrai norite pašalinti šį mazgą? Tai taip pat pašalins juo sujungtus "
-"perėjimus."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "Ar tikrai norite pašalinti šį perėjimą?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Sukurti:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Naujas mazgas"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Atidaryti: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Perėjimas"
diff --git a/addons/web_diagram/i18n/lv.po b/addons/web_diagram/i18n/lv.po
deleted file mode 100644
index 1584d7551f2..00000000000
--- a/addons/web_diagram/i18n/lv.po
+++ /dev/null
@@ -1,78 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server 10.saas~18\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-09-20 09:54+0000\n"
-"PO-Revision-Date: 2017-09-20 09:54+0000\n"
-"Language-Team: Latvian (https://www.transifex.com/odoo/teams/41243/lv/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: lv\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/mk.po b/addons/web_diagram/i18n/mk.po
deleted file mode 100644
index 391c08ca59d..00000000000
--- a/addons/web_diagram/i18n/mk.po
+++ /dev/null
@@ -1,100 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:27+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Macedonian (http://www.transifex.com/odoo/odoo-9/language/"
-"mk/)\n"
-"Language: mk\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Активност"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Креирај:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Избришаниот јазол не може да се врати.\n"
-"Ова ќе ги избрише сите поврзани премини.\n"
-"\n"
-"Дали си сигурен ?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Избришаната транзиција не може да се врати.\n"
-"\n"
-"Дали си сигурен ?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Дијаграм"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Ново"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Нов јазол"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Отвори: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Преод"
diff --git a/addons/web_diagram/i18n/mn.po b/addons/web_diagram/i18n/mn.po
deleted file mode 100644
index ed0572fb2b4..00000000000
--- a/addons/web_diagram/i18n/mn.po
+++ /dev/null
@@ -1,82 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Martin Trigaux, 2019\n"
-"Language-Team: Mongolian (https://www.transifex.com/odoo/teams/41243/mn/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: mn\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Ажилбар"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Үүсгэх"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Диаграм"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Шинэ Зангилаа"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Шилжилт"
diff --git a/addons/web_diagram/i18n/nb.po b/addons/web_diagram/i18n/nb.po
deleted file mode 100644
index 82dae7d61f9..00000000000
--- a/addons/web_diagram/i18n/nb.po
+++ /dev/null
@@ -1,85 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2019
-# Jorunn D. Newth, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Jorunn D. Newth, 2019\n"
-"Language-Team: Norwegian Bokmål (https://www.transifex.com/odoo/teams/41243/nb/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: nb\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Aktivitet"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-"Er du sikker på at du vil fjerne denne noden? Det vil fjerne de tilknyttede "
-"overgangene også."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "Er du sikker på at du vil fjerne denne overgangen?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Opprett:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Ny node"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Åpne:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Overgang"
diff --git a/addons/web_diagram/i18n/nl.po b/addons/web_diagram/i18n/nl.po
deleted file mode 100644
index e80451e03c1..00000000000
--- a/addons/web_diagram/i18n/nl.po
+++ /dev/null
@@ -1,86 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2019
-# Yenthe Van Ginneken <yenthespam@gmail.com>, 2019
-# Cas Vissers <casvissers@brahoo.nl>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Cas Vissers <casvissers@brahoo.nl>, 2019\n"
-"Language-Team: Dutch (https://www.transifex.com/odoo/teams/41243/nl/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: nl\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Activiteit"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-"Bent u zeker dat u dit knooppunt wil verwijderen? Dit verwijdert ook alle "
-"verbonden overgangen."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "Weet u zeker dat u deze transactie wil verwijderen?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Aanmaken:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nieuw knooppunt"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Open: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Overgang"
diff --git a/addons/web_diagram/i18n/nl_BE.po b/addons/web_diagram/i18n/nl_BE.po
deleted file mode 100644
index 822c3e68258..00000000000
--- a/addons/web_diagram/i18n/nl_BE.po
+++ /dev/null
@@ -1,100 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:26+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Dutch (Belgium) (http://www.transifex.com/odoo/odoo-9/"
-"language/nl_BE/)\n"
-"Language: nl_BE\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "Activiteit"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr "Maken:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Het verwijderen van dit knooppunt kan niet ongedaan worden gemaakt.\n"
-"Ook alle gekoppelde overgangen zullen hiermee worden verwijderd.\n"
-"\n"
-"Bent u zeker?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-"Het verwijderen van deze overgang kan niet ongedaan worden gemaakt.\n"
-"\n"
-"Bent u zeker?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "Nieuw"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nieuw knooppunt"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr "Openen: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr "Overgang"
diff --git a/addons/web_diagram/i18n/pl.po b/addons/web_diagram/i18n/pl.po
deleted file mode 100644
index 7f649eb5922..00000000000
--- a/addons/web_diagram/i18n/pl.po
+++ /dev/null
@@ -1,88 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2019
-# Grzegorz Grzelak <grzegorz.grzelak@openglobe.pl>, 2019
-# Tomasz Leppich <t.leppich@gmail.com>, 2019
-# Piotr Szlązak <szlazakpiotr@gmail.com>, 2019
-# Paweł Wodyński <pw@myodoo.pl>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Paweł Wodyński <pw@myodoo.pl>, 2019\n"
-"Language-Team: Polish (https://www.transifex.com/odoo/teams/41243/pl/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: pl\n"
-"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Czynność"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-"Czy na pewno chcesz usunąć ten węzeł? Spowoduje to również usunięcie "
-"połączonych przejść."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "Czy na pewno chcesz usunąć to przejście?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Utwórz:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nowy węzeł"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Otwórz:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Przejście"
diff --git a/addons/web_diagram/i18n/pt.po b/addons/web_diagram/i18n/pt.po
deleted file mode 100644
index 681428b6ef0..00000000000
--- a/addons/web_diagram/i18n/pt.po
+++ /dev/null
@@ -1,83 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# MS, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-09-18 09:49+0000\n"
-"Last-Translator: MS, 2018\n"
-"Language-Team: Portuguese (https://www.transifex.com/odoo/teams/41243/pt/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: pt\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Atividade"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-"Tem a certeza que pretende remover este nodo? Isto também irá remover as "
-"transições ligadas."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "Tem a certeza que pretende remover esta transição?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Criar:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Novo Nodo"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Abrir: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Transição"
diff --git a/addons/web_diagram/i18n/pt_BR.po b/addons/web_diagram/i18n/pt_BR.po
deleted file mode 100644
index 5ca0f9913a5..00000000000
--- a/addons/web_diagram/i18n/pt_BR.po
+++ /dev/null
@@ -1,83 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2019
-# Mateus Lopes <mateus1@gmail.com>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Mateus Lopes <mateus1@gmail.com>, 2019\n"
-"Language-Team: Portuguese (Brazil) (https://www.transifex.com/odoo/teams/41243/pt_BR/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: pt_BR\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Atividade"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Criar:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagrama"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Novo Nó"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Transição"
diff --git a/addons/web_diagram/i18n/ro.po b/addons/web_diagram/i18n/ro.po
deleted file mode 100644
index caf11f5717f..00000000000
--- a/addons/web_diagram/i18n/ro.po
+++ /dev/null
@@ -1,81 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.2\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-03-22 14:11+0000\n"
-"PO-Revision-Date: 2018-03-22 14:11+0000\n"
-"Last-Translator: Martin Trigaux, 2018\n"
-"Language-Team: Romanian (https://www.transifex.com/odoo/teams/41243/ro/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: ro\n"
-"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Activitate"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Creeaza:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagramă"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nod Nou"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Tranzitie"
diff --git a/addons/web_diagram/i18n/ru.po b/addons/web_diagram/i18n/ru.po
deleted file mode 100644
index ca1e7a9f5af..00000000000
--- a/addons/web_diagram/i18n/ru.po
+++ /dev/null
@@ -1,82 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Martin Trigaux, 2019\n"
-"Language-Team: Russian (https://www.transifex.com/odoo/teams/41243/ru/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: ru\n"
-"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Мероприятие"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Создать:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Диаграмма"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Новый узел"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Переход"
diff --git a/addons/web_diagram/i18n/sk.po b/addons/web_diagram/i18n/sk.po
deleted file mode 100644
index 881d8070fdf..00000000000
--- a/addons/web_diagram/i18n/sk.po
+++ /dev/null
@@ -1,82 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Matus Krnac <matus.krnac@gmail.com>, 2018
-# Jaroslav Bosansky <jaro.bosansky@ekoenergo.sk>, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-09-18 09:49+0000\n"
-"Last-Translator: Jaroslav Bosansky <jaro.bosansky@ekoenergo.sk>, 2018\n"
-"Language-Team: Slovak (https://www.transifex.com/odoo/teams/41243/sk/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: sk\n"
-"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Aktivita"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Vytvoriť:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Nový uzol"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Prechod"
diff --git a/addons/web_diagram/i18n/sl.po b/addons/web_diagram/i18n/sl.po
deleted file mode 100644
index 0cfdb4fa180..00000000000
--- a/addons/web_diagram/i18n/sl.po
+++ /dev/null
@@ -1,78 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server 10.saas~18\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-09-20 09:54+0000\n"
-"PO-Revision-Date: 2017-09-20 09:54+0000\n"
-"Language-Team: Slovenian (https://www.transifex.com/odoo/teams/41243/sl/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: sl\n"
-"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/sr.po b/addons/web_diagram/i18n/sr.po
deleted file mode 100644
index 8a22e163047..00000000000
--- a/addons/web_diagram/i18n/sr.po
+++ /dev/null
@@ -1,82 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-# Slobodan Simić <slsimic@gmail.com>, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-09-18 09:49+0000\n"
-"Last-Translator: Slobodan Simić <slsimic@gmail.com>, 2018\n"
-"Language-Team: Serbian (https://www.transifex.com/odoo/teams/41243/sr/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: sr\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Активност"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Dijagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Prelaz"
diff --git a/addons/web_diagram/i18n/sr@latin.po b/addons/web_diagram/i18n/sr@latin.po
deleted file mode 100644
index 1a8183a57bd..00000000000
--- a/addons/web_diagram/i18n/sr@latin.po
+++ /dev/null
@@ -1,81 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux <mat@odoo.com>, 2017
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server 10.saas~18\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-09-20 09:54+0000\n"
-"PO-Revision-Date: 2017-09-20 09:54+0000\n"
-"Last-Translator: Martin Trigaux <mat@odoo.com>, 2017\n"
-"Language-Team: Serbian (Latin) (https://www.transifex.com/odoo/teams/41243/sr%40latin/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: sr@latin\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Aktivnost"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Kreiraj:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Dijagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Novi Čvor"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Tranzicija"
diff --git a/addons/web_diagram/i18n/sv.po b/addons/web_diagram/i18n/sv.po
deleted file mode 100644
index 2713e11ca2b..00000000000
--- a/addons/web_diagram/i18n/sv.po
+++ /dev/null
@@ -1,81 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.2\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-03-22 14:11+0000\n"
-"PO-Revision-Date: 2018-03-22 14:11+0000\n"
-"Last-Translator: Martin Trigaux, 2018\n"
-"Language-Team: Swedish (https://www.transifex.com/odoo/teams/41243/sv/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: sv\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Aktivitet"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Skapa:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Ny nod"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Övergång"
diff --git a/addons/web_diagram/i18n/th.po b/addons/web_diagram/i18n/th.po
deleted file mode 100644
index 423ef043994..00000000000
--- a/addons/web_diagram/i18n/th.po
+++ /dev/null
@@ -1,83 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2018
-# gsong <gsong2014@foxmail.com>, 2018
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-08-24 09:33+0000\n"
-"Last-Translator: gsong <gsong2014@foxmail.com>, 2018\n"
-"Language-Team: Thai (https://www.transifex.com/odoo/teams/41243/th/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: th\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "กิจกรรม"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "สร้าง"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "ไดอะแกรม"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "สร้างโหนดใหม่"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "เปิด:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "การเปลี่ยนแปลง"
diff --git a/addons/web_diagram/i18n/tr.po b/addons/web_diagram/i18n/tr.po
deleted file mode 100644
index 68b5af00591..00000000000
--- a/addons/web_diagram/i18n/tr.po
+++ /dev/null
@@ -1,86 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2019
-# Murat Kaplan <muratk@projetgrup.com>, 2019
-# Umur Akın <umura@projetgrup.com>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Umur Akın <umura@projetgrup.com>, 2019\n"
-"Language-Team: Turkish (https://www.transifex.com/odoo/teams/41243/tr/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: tr\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Aktivite"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-"Bu düğümü kaldırmak istediğinizden emin misiniz? Bu bağlı geçişleri de "
-"kaldıracaktır."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "Bu geçişi kaldırmak istediğinize emin misiniz?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Oluştur:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Diyagram"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Yeni Düğüm"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Aç:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Geçiş"
diff --git a/addons/web_diagram/i18n/uk.po b/addons/web_diagram/i18n/uk.po
deleted file mode 100644
index e5d20a984bc..00000000000
--- a/addons/web_diagram/i18n/uk.po
+++ /dev/null
@@ -1,86 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2019
-# ТАрас <tratatuta@i.ua>, 2019
-# Alina Lisnenko <alinasemeniuk1@gmail.com>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Alina Lisnenko <alinasemeniuk1@gmail.com>, 2019\n"
-"Language-Team: Ukrainian (https://www.transifex.com/odoo/teams/41243/uk/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: uk\n"
-"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Дія"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-"Ви впевнені, що хочете видалити цей вузол? Це також призведе до видалення "
-"його пов'язаних переходів."
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "Ви впевнені, що хочете видалити цей перехід?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Створити:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Діаграма"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Новий вузол"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Відкрити:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Переміщення"
diff --git a/addons/web_diagram/i18n/vi.po b/addons/web_diagram/i18n/vi.po
deleted file mode 100644
index 5d1f7a3929b..00000000000
--- a/addons/web_diagram/i18n/vi.po
+++ /dev/null
@@ -1,85 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# fanha99 <fanha99@hotmail.com>, 2019
-# Duy BQ <duybq86@gmail.com>, 2019
-# Minh Nguyen <ndminh210994@gmail.com>, 2019
-# Dung Nguyen Thi <dungnt@trobz.com>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: Dung Nguyen Thi <dungnt@trobz.com>, 2019\n"
-"Language-Team: Vietnamese (https://www.transifex.com/odoo/teams/41243/vi/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: vi\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "Hoạt động"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "Tạo:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "Biểu đồ"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "Node Mới"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "Mở: "
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "Transition"
diff --git a/addons/web_diagram/i18n/web_diagram.pot b/addons/web_diagram/i18n/web_diagram.pot
deleted file mode 100644
index b4694095063..00000000000
--- a/addons/web_diagram/i18n/web_diagram.pot
+++ /dev/null
@@ -1,78 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-12 11:32+0000\n"
-"Last-Translator: \n"
-"Language-Team: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: \n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/zh_CN.po b/addons/web_diagram/i18n/zh_CN.po
deleted file mode 100644
index 657fdba558c..00000000000
--- a/addons/web_diagram/i18n/zh_CN.po
+++ /dev/null
@@ -1,86 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Jeanphy <hzh0292@qq.com>, 2019
-# Martin Trigaux, 2019
-# Jeffery CHEN <jeffery9@gmail.com>, 2019
-# bf2549c5415a9287249cba2b8a5823c7, 2019
-# inspur qiuguodong <qiuguodong@inspur.com>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: inspur qiuguodong <qiuguodong@inspur.com>, 2019\n"
-"Language-Team: Chinese (China) (https://www.transifex.com/odoo/teams/41243/zh_CN/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: zh_CN\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "活动"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr "你确定要移除这个节点? 这也将移除其连接的转换。"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "你确定要移除这个转换吗?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "创建:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "图表"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "新建:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "打开:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "转换"
diff --git a/addons/web_diagram/i18n/zh_HK.po b/addons/web_diagram/i18n/zh_HK.po
deleted file mode 100644
index 19f29f94554..00000000000
--- a/addons/web_diagram/i18n/zh_HK.po
+++ /dev/null
@@ -1,93 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo 9.0\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-08-18 14:08+0000\n"
-"PO-Revision-Date: 2015-09-08 09:26+0000\n"
-"Last-Translator: Martin Trigaux\n"
-"Language-Team: Chinese (Hong Kong) (http://www.transifex.com/odoo/odoo-9/"
-"language/zh_HK/)\n"
-"Language: zh_HK\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:249
-#: code:addons/web_diagram/static/src/js/diagram.js:277
-#, python-format
-msgid "Activity"
-msgstr "動態"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:282
-#: code:addons/web_diagram/static/src/js/diagram.js:326
-#, python-format
-msgid "Create:"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:220
-#, python-format
-msgid ""
-"Deleting this node cannot be undone.\n"
-"It will also delete all connected transitions.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:238
-#, python-format
-msgid ""
-"Deleting this transition cannot be undone.\n"
-"\n"
-"Are you sure ?"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:19
-#, python-format
-msgid "Diagram"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:124
-#, python-format
-msgid "New"
-msgstr "新建"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:254
-#: code:addons/web_diagram/static/src/js/diagram.js:310
-#, python-format
-msgid "Open: "
-msgstr ""
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram.js:305
-#: code:addons/web_diagram/static/src/js/diagram.js:321
-#, python-format
-msgid "Transition"
-msgstr ""
diff --git a/addons/web_diagram/i18n/zh_TW.po b/addons/web_diagram/i18n/zh_TW.po
deleted file mode 100644
index 63dc3de062f..00000000000
--- a/addons/web_diagram/i18n/zh_TW.po
+++ /dev/null
@@ -1,83 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * web_diagram
-#
-# Translators:
-# Martin Trigaux, 2019
-# 敬雲 林 <chingyun@yuanchih-consult.com>, 2019
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~12.4\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-08-12 11:32+0000\n"
-"PO-Revision-Date: 2019-08-26 09:15+0000\n"
-"Last-Translator: 敬雲 林 <chingyun@yuanchih-consult.com>, 2019\n"
-"Language-Team: Chinese (Taiwan) (https://www.transifex.com/odoo/teams/41243/zh_TW/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: zh_TW\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Activity"
-msgstr "活動"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:192
-#, python-format
-msgid ""
-"Are you sure you want to remove this node ? This will remove its connected "
-"transitions as well."
-msgstr "您確定要刪除這個節點? 這也將刪除其連接的轉換。"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:172
-#, python-format
-msgid "Are you sure you want to remove this transition?"
-msgstr "您確定要刪除這個轉換嗎?"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:73
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#, python-format
-msgid "Create:"
-msgstr "創建:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_view.js:16
-#, python-format
-msgid "Diagram"
-msgstr "圖表"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/xml/base_diagram.xml:5
-#, python-format
-msgid "New Node"
-msgstr "新建節點"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:160
-#, python-format
-msgid "Open:"
-msgstr "打開:"
-
-#. module: web_diagram
-#. openerp-web
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:110
-#: code:addons/web_diagram/static/src/js/diagram_controller.js:143
-#, python-format
-msgid "Transition"
-msgstr "轉換"
diff --git a/addons/web_diagram/static/lib/js/jquery.mousewheel.js b/addons/web_diagram/static/lib/js/jquery.mousewheel.js
deleted file mode 100644
index 38b60951b20..00000000000
--- a/addons/web_diagram/static/lib/js/jquery.mousewheel.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*! Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net)
- * Licensed under the MIT License (LICENSE.txt).
- *
- * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
- * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
- * Thanks to: Seamus Leahy for adding deltaX and deltaY
- *
- * Version: 3.0.6
- *
- * Requires: 1.2.2+
- */
-
-(function($) {
-
-var types = ['DOMMouseScroll', 'mousewheel'];
-
-if ($.event.fixHooks) {
- for ( var i=types.length; i; ) {
- $.event.fixHooks[ types[--i] ] = $.event.mouseHooks;
- }
-}
-
-$.event.special.mousewheel = {
- setup: function() {
- if ( this.addEventListener ) {
- for ( var i=types.length; i; ) {
- this.addEventListener( types[--i], handler, false );
- }
- } else {
- this.onmousewheel = handler;
- }
- },
-
- teardown: function() {
- if ( this.removeEventListener ) {
- for ( var i=types.length; i; ) {
- this.removeEventListener( types[--i], handler, false );
- }
- } else {
- this.onmousewheel = null;
- }
- }
-};
-
-$.fn.extend({
- mousewheel: function(fn) {
- return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
- },
-
- unmousewheel: function(fn) {
- return this.unbind("mousewheel", fn);
- }
-});
-
-
-function handler(event) {
- var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0;
- event = $.event.fix(orgEvent);
- event.type = "mousewheel";
-
- // Old school scrollwheel delta
- if ( orgEvent.wheelDelta ) { delta = orgEvent.wheelDelta/120; }
- if ( orgEvent.detail ) { delta = -orgEvent.detail/3; }
-
- // New school multidimensional scroll (touchpads) deltas
- deltaY = delta;
-
- // Gecko
- if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
- deltaY = 0;
- deltaX = -1*delta;
- }
-
- // Webkit
- if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; }
- if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; }
-
- // Add event and delta to the front of the arguments
- args.unshift(event, delta, deltaX, deltaY);
-
- return ($.event.dispatch || $.event.handle).apply(this, args);
-}
-
-})(jQuery);
diff --git a/addons/web_diagram/static/lib/js/raphael.js b/addons/web_diagram/static/lib/js/raphael.js
deleted file mode 100644
index 0081b8a6ea2..00000000000
--- a/addons/web_diagram/static/lib/js/raphael.js
+++ /dev/null
@@ -1,5501 +0,0 @@
-// ┌────────────────────────────────────────────────────────────────────┐ \\
-// │ Raphaël 2.0.2 - JavaScript Vector Library │ \\
-// ├────────────────────────────────────────────────────────────────────┤ \\
-// │ Copyright © 2008-2012 Dmitry Baranovskiy (http://raphaeljs.com) │ \\
-// │ Copyright © 2008-2012 Sencha Labs (http://sencha.com) │ \\
-// ├────────────────────────────────────────────────────────────────────┤ \\
-// │ Licensed under the MIT (http://raphaeljs.com/license.html) license.│ \\
-// └────────────────────────────────────────────────────────────────────┘ \\
-// ┌──────────────────────────────────────────────────────────────────────────────────────┐ \\
-// │ Eve 0.3.4 - JavaScript Events Library │ \\
-// ├──────────────────────────────────────────────────────────────────────────────────────┤ \\
-// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) │ \\
-// │ Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. │ \\
-// └──────────────────────────────────────────────────────────────────────────────────────┘ \\
-
-(function (glob) {
- var version = "0.3.4",
- has = "hasOwnProperty",
- separator = /[\.\/]/,
- wildcard = "*",
- fun = function () {},
- numsort = function (a, b) {
- return a - b;
- },
- current_event,
- stop,
- events = {n: {}},
-
- eve = function (name, scope) {
- var e = events,
- oldstop = stop,
- args = Array.prototype.slice.call(arguments, 2),
- listeners = eve.listeners(name),
- z = 0,
- f = false,
- l,
- indexed = [],
- queue = {},
- out = [],
- ce = current_event,
- errors = [];
- current_event = name;
- stop = 0;
- for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) {
- indexed.push(listeners[i].zIndex);
- if (listeners[i].zIndex < 0) {
- queue[listeners[i].zIndex] = listeners[i];
- }
- }
- indexed.sort(numsort);
- while (indexed[z] < 0) {
- l = queue[indexed[z++]];
- out.push(l.apply(scope, args));
- if (stop) {
- stop = oldstop;
- return out;
- }
- }
- for (i = 0; i < ii; i++) {
- l = listeners[i];
- if ("zIndex" in l) {
- if (l.zIndex == indexed[z]) {
- out.push(l.apply(scope, args));
- if (stop) {
- break;
- }
- do {
- z++;
- l = queue[indexed[z]];
- l && out.push(l.apply(scope, args));
- if (stop) {
- break;
- }
- } while (l)
- } else {
- queue[l.zIndex] = l;
- }
- } else {
- out.push(l.apply(scope, args));
- if (stop) {
- break;
- }
- }
- }
- stop = oldstop;
- current_event = ce;
- return out.length ? out : null;
- };
-
- eve.listeners = function (name) {
- var names = name.split(separator),
- e = events,
- item,
- items,
- k,
- i,
- ii,
- j,
- jj,
- nes,
- es = [e],
- out = [];
- for (i = 0, ii = names.length; i < ii; i++) {
- nes = [];
- for (j = 0, jj = es.length; j < jj; j++) {
- e = es[j].n;
- items = [e[names[i]], e[wildcard]];
- k = 2;
- while (k--) {
- item = items[k];
- if (item) {
- nes.push(item);
- out = out.concat(item.f || []);
- }
- }
- }
- es = nes;
- }
- return out;
- };
-
-
- eve.on = function (name, f) {
- var names = name.split(separator),
- e = events;
- for (var i = 0, ii = names.length; i < ii; i++) {
- e = e.n;
- !e[names[i]] && (e[names[i]] = {n: {}});
- e = e[names[i]];
- }
- e.f = e.f || [];
- for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
- return fun;
- }
- e.f.push(f);
- return function (zIndex) {
- if (+zIndex == +zIndex) {
- f.zIndex = +zIndex;
- }
- };
- };
-
- eve.stop = function () {
- stop = 1;
- };
-
- eve.nt = function (subname) {
- if (subname) {
- return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event);
- }
- return current_event;
- };
-
-
- eve.off = eve.unbind = function (name, f) {
- var names = name.split(separator),
- e,
- key,
- splice,
- i, ii, j, jj,
- cur = [events];
- for (i = 0, ii = names.length; i < ii; i++) {
- for (j = 0; j < cur.length; j += splice.length - 2) {
- splice = [j, 1];
- e = cur[j].n;
- if (names[i] != wildcard) {
- if (e[names[i]]) {
- splice.push(e[names[i]]);
- }
- } else {
- for (key in e) if (e[has](key)) {
- splice.push(e[key]);
- }
- }
- cur.splice.apply(cur, splice);
- }
- }
- for (i = 0, ii = cur.length; i < ii; i++) {
- e = cur[i];
- while (e.n) {
- if (f) {
- if (e.f) {
- for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) {
- e.f.splice(j, 1);
- break;
- }
- !e.f.length && delete e.f;
- }
- for (key in e.n) if (e.n[has](key) && e.n[key].f) {
- var funcs = e.n[key].f;
- for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) {
- funcs.splice(j, 1);
- break;
- }
- !funcs.length && delete e.n[key].f;
- }
- } else {
- delete e.f;
- for (key in e.n) if (e.n[has](key) && e.n[key].f) {
- delete e.n[key].f;
- }
- }
- e = e.n;
- }
- }
- };
-
- eve.once = function (name, f) {
- var f2 = function () {
- var res = f.apply(this, arguments);
- eve.unbind(name, f2);
- return res;
- };
- return eve.on(name, f2);
- };
-
- eve.version = version;
- eve.toString = function () {
- return "You are running Eve " + version;
- };
- (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (typeof define != "undefined" ? (define("eve", [], function() { return eve; })) : (glob.eve = eve));
-})(this);
-
-
-// ┌─────────────────────────────────────────────────────────────────────┐ \\
-// │ "Raphaël 2.0.2" - JavaScript Vector Library │ \\
-// ├─────────────────────────────────────────────────────────────────────┤ \\
-// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com) │ \\
-// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com) │ \\
-// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
-// └─────────────────────────────────────────────────────────────────────┘ \\
-(function () {
-
- function R(first) {
- if (R.is(first, "function")) {
- return loaded ? first() : eve.on("DOMload", first);
- } else if (R.is(first, array)) {
- return R._engine.create[apply](R, first.splice(0, 3 + R.is(first[0], nu))).add(first);
- } else {
- var args = Array.prototype.slice.call(arguments, 0);
- if (R.is(args[args.length - 1], "function")) {
- var f = args.pop();
- return loaded ? f.call(R._engine.create[apply](R, args)) : eve.on("DOMload", function () {
- f.call(R._engine.create[apply](R, args));
- });
- } else {
- return R._engine.create[apply](R, arguments);
- }
- }
- }
- R.version = "2.0.2";
- R.eve = eve;
- var loaded,
- separator = /[, ]+/,
- elements = {circle: 1, rect: 1, path: 1, ellipse: 1, text: 1, image: 1},
- formatrg = /\{(\d+)\}/g,
- proto = "prototype",
- has = "hasOwnProperty",
- g = {
- doc: document,
- win: window
- },
- oldRaphael = {
- was: Object.prototype[has].call(g.win, "Raphael"),
- is: g.win.Raphael
- },
- Paper = function () {
-
-
- this.ca = this.customAttributes = {};
- },
- paperproto,
- appendChild = "appendChild",
- apply = "apply",
- concat = "concat",
- supportsTouch = "createTouch" in g.doc,
- E = "",
- S = " ",
- Str = String,
- split = "split",
- events = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[split](S),
- touchMap = {
- mousedown: "touchstart",
- mousemove: "touchmove",
- mouseup: "touchend"
- },
- lowerCase = Str.prototype.toLowerCase,
- math = Math,
- mmax = math.max,
- mmin = math.min,
- abs = math.abs,
- pow = math.pow,
- PI = math.PI,
- nu = "number",
- string = "string",
- array = "array",
- toString = "toString",
- fillString = "fill",
- objectToString = Object.prototype.toString,
- paper = {},
- push = "push",
- ISURL = R._ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i,
- colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,
- isnan = {"NaN": 1, "Infinity": 1, "-Infinity": 1},
- bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
- round = math.round,
- setAttribute = "setAttribute",
- toFloat = parseFloat,
- toInt = parseInt,
- upperCase = Str.prototype.toUpperCase,
- availableAttrs = R._availableAttrs = {
- "arrow-end": "none",
- "arrow-start": "none",
- blur: 0,
- "clip-rect": "0 0 1e9 1e9",
- cursor: "default",
- cx: 0,
- cy: 0,
- fill: "#fff",
- "fill-opacity": 1,
- font: '10px "Arial"',
- "font-family": '"Arial"',
- "font-size": "10",
- "font-style": "normal",
- "font-weight": 400,
- gradient: 0,
- height: 0,
- href: "http://raphaeljs.com/",
- "letter-spacing": 0,
- opacity: 1,
- path: "M0,0",
- r: 0,
- rx: 0,
- ry: 0,
- src: "",
- stroke: "#000",
- "stroke-dasharray": "",
- "stroke-linecap": "butt",
- "stroke-linejoin": "butt",
- "stroke-miterlimit": 0,
- "stroke-opacity": 1,
- "stroke-width": 1,
- target: "_blank",
- "text-anchor": "middle",
- title: "Raphael",
- transform: "",
- width: 0,
- x: 0,
- y: 0
- },
- availableAnimAttrs = R._availableAnimAttrs = {
- blur: nu,
- "clip-rect": "csv",
- cx: nu,
- cy: nu,
- fill: "colour",
- "fill-opacity": nu,
- "font-size": nu,
- height: nu,
- opacity: nu,
- path: "path",
- r: nu,
- rx: nu,
- ry: nu,
- stroke: "colour",
- "stroke-opacity": nu,
- "stroke-width": nu,
- transform: "transform",
- width: nu,
- x: nu,
- y: nu
- },
- whitespace = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]/g,
- commaSpaces = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/,
- hsrg = {hs: 1, rg: 1},
- p2s = /,?([achlmqrstvxz]),?/gi,
- pathCommand = /([achlmrqstvz])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig,
- tCommand = /([rstm])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig,
- pathValues = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/ig,
- radial_gradient = R._radial_gradient = /^r(?:\(([^,]+?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*([^\)]+?)\))?/,
- eldata = {},
- sortByKey = function (a, b) {
- return a.key - b.key;
- },
- sortByNumber = function (a, b) {
- return toFloat(a) - toFloat(b);
- },
- fun = function () {},
- pipe = function (x) {
- return x;
- },
- rectPath = R._rectPath = function (x, y, w, h, r) {
- if (r) {
- return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];
- }
- return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
- },
- ellipsePath = function (x, y, rx, ry) {
- if (ry == null) {
- ry = rx;
- }
- return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];
- },
- getPath = R._getPath = {
- path: function (el) {
- return el.attr("path");
- },
- circle: function (el) {
- var a = el.attrs;
- return ellipsePath(a.cx, a.cy, a.r);
- },
- ellipse: function (el) {
- var a = el.attrs;
- return ellipsePath(a.cx, a.cy, a.rx, a.ry);
- },
- rect: function (el) {
- var a = el.attrs;
- return rectPath(a.x, a.y, a.width, a.height, a.r);
- },
- image: function (el) {
- var a = el.attrs;
- return rectPath(a.x, a.y, a.width, a.height);
- },
- text: function (el) {
- var bbox = el._getBBox();
- return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
- }
- },
- mapPath = R.mapPath = function (path, matrix) {
- if (!matrix) {
- return path;
- }
- var x, y, i, j, ii, jj, pathi;
- path = path2curve(path);
- for (i = 0, ii = path.length; i < ii; i++) {
- pathi = path[i];
- for (j = 1, jj = pathi.length; j < jj; j += 2) {
- x = matrix.x(pathi[j], pathi[j + 1]);
- y = matrix.y(pathi[j], pathi[j + 1]);
- pathi[j] = x;
- pathi[j + 1] = y;
- }
- }
- return path;
- };
-
- R._g = g;
-
- R.type = (g.win.SVGAngle || g.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");
- if (R.type == "VML") {
- var d = g.doc.createElement("div"),
- b;
- d.innerHTML = '<v:shape adj="1"/>';
- b = d.firstChild;
- b.style.behavior = "url(#default#VML)";
- if (!(b && typeof b.adj == "object")) {
- return (R.type = E);
- }
- d = null;
- }
-
-
- R.svg = !(R.vml = R.type == "VML");
- R._Paper = Paper;
-
- R.fn = paperproto = Paper.prototype = R.prototype;
- R._id = 0;
- R._oid = 0;
-
- R.is = function (o, type) {
- type = lowerCase.call(type);
- if (type == "finite") {
- return !isnan[has](+o);
- }
- if (type == "array") {
- return o instanceof Array;
- }
- return (type == "null" && o === null) ||
- (type == typeof o && o !== null) ||
- (type == "object" && o === Object(o)) ||
- (type == "array" && Array.isArray && Array.isArray(o)) ||
- objectToString.call(o).slice(8, -1).toLowerCase() == type;
- };
-
- R.angle = function (x1, y1, x2, y2, x3, y3) {
- if (x3 == null) {
- var x = x1 - x2,
- y = y1 - y2;
- if (!x && !y) {
- return 0;
- }
- return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360;
- } else {
- return R.angle(x1, y1, x3, y3) - R.angle(x2, y2, x3, y3);
- }
- };
-
- R.rad = function (deg) {
- return deg % 360 * PI / 180;
- };
-
- R.deg = function (rad) {
- return rad * 180 / PI % 360;
- };
-
- R.snapTo = function (values, value, tolerance) {
- tolerance = R.is(tolerance, "finite") ? tolerance : 10;
- if (R.is(values, array)) {
- var i = values.length;
- while (i--) if (abs(values[i] - value) <= tolerance) {
- return values[i];
- }
- } else {
- values = +values;
- var rem = value % values;
- if (rem < tolerance) {
- return value - rem;
- }
- if (rem > values - tolerance) {
- return value - rem + values;
- }
- }
- return value;
- };
-
-
- var createUUID = R.createUUID = (function (uuidRegEx, uuidReplacer) {
- return function () {
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(uuidRegEx, uuidReplacer).toUpperCase();
- };
- })(/[xy]/g, function (c) {
- var r = math.random() * 16 | 0,
- v = c == "x" ? r : (r & 3 | 8);
- return v.toString(16);
- });
-
-
- R.setWindow = function (newwin) {
- eve("setWindow", R, g.win, newwin);
- g.win = newwin;
- g.doc = g.win.document;
- if (R._engine.initWin) {
- R._engine.initWin(g.win);
- }
- };
- var toHex = function (color) {
- if (R.vml) {
- // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/
- var trim = /^\s+|\s+$/g;
- var bod;
- try {
- var docum = new ActiveXObject("htmlfile");
- docum.write("<body>");
- docum.close();
- bod = docum.body;
- } catch(e) {
- bod = createPopup().document.body;
- }
- var range = bod.createTextRange();
- toHex = cacher(function (color) {
- try {
- bod.style.color = Str(color).replace(trim, E);
- var value = range.queryCommandValue("ForeColor");
- value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16);
- return "#" + ("000000" + value.toString(16)).slice(-6);
- } catch(e) {
- return "none";
- }
- });
- } else {
- var i = g.doc.createElement("i");
- i.title = "Rapha\xebl Colour Picker";
- i.style.display = "none";
- g.doc.body.appendChild(i);
- toHex = cacher(function (color) {
- i.style.color = color;
- return g.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color");
- });
- }
- return toHex(color);
- },
- hsbtoString = function () {
- return "hsb(" + [this.h, this.s, this.b] + ")";
- },
- hsltoString = function () {
- return "hsl(" + [this.h, this.s, this.l] + ")";
- },
- rgbtoString = function () {
- return this.hex;
- },
- prepareRGB = function (r, g, b) {
- if (g == null && R.is(r, "object") && "r" in r && "g" in r && "b" in r) {
- b = r.b;
- g = r.g;
- r = r.r;
- }
- if (g == null && R.is(r, string)) {
- var clr = R.getRGB(r);
- r = clr.r;
- g = clr.g;
- b = clr.b;
- }
- if (r > 1 || g > 1 || b > 1) {
- r /= 255;
- g /= 255;
- b /= 255;
- }
-
- return [r, g, b];
- },
- packageRGB = function (r, g, b, o) {
- r *= 255;
- g *= 255;
- b *= 255;
- var rgb = {
- r: r,
- g: g,
- b: b,
- hex: R.rgb(r, g, b),
- toString: rgbtoString
- };
- R.is(o, "finite") && (rgb.opacity = o);
- return rgb;
- };
-
-
- R.color = function (clr) {
- var rgb;
- if (R.is(clr, "object") && "h" in clr && "s" in clr && "b" in clr) {
- rgb = R.hsb2rgb(clr);
- clr.r = rgb.r;
- clr.g = rgb.g;
- clr.b = rgb.b;
- clr.hex = rgb.hex;
- } else if (R.is(clr, "object") && "h" in clr && "s" in clr && "l" in clr) {
- rgb = R.hsl2rgb(clr);
- clr.r = rgb.r;
- clr.g = rgb.g;
- clr.b = rgb.b;
- clr.hex = rgb.hex;
- } else {
- if (R.is(clr, "string")) {
- clr = R.getRGB(clr);
- }
- if (R.is(clr, "object") && "r" in clr && "g" in clr && "b" in clr) {
- rgb = R.rgb2hsl(clr);
- clr.h = rgb.h;
- clr.s = rgb.s;
- clr.l = rgb.l;
- rgb = R.rgb2hsb(clr);
- clr.v = rgb.b;
- } else {
- clr = {hex: "none"};
- clr.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1;
- }
- }
- clr.toString = rgbtoString;
- return clr;
- };
-
- R.hsb2rgb = function (h, s, v, o) {
- if (this.is(h, "object") && "h" in h && "s" in h && "b" in h) {
- v = h.b;
- s = h.s;
- h = h.h;
- o = h.o;
- }
- h *= 360;
- var R, G, B, X, C;
- h = (h % 360) / 60;
- C = v * s;
- X = C * (1 - abs(h % 2 - 1));
- R = G = B = v - C;
-
- h = ~~h;
- R += [C, X, 0, 0, X, C][h];
- G += [X, C, C, X, 0, 0][h];
- B += [0, 0, X, C, C, X][h];
- return packageRGB(R, G, B, o);
- };
-
- R.hsl2rgb = function (h, s, l, o) {
- if (this.is(h, "object") && "h" in h && "s" in h && "l" in h) {
- l = h.l;
- s = h.s;
- h = h.h;
- }
- if (h > 1 || s > 1 || l > 1) {
- h /= 360;
- s /= 100;
- l /= 100;
- }
- h *= 360;
- var R, G, B, X, C;
- h = (h % 360) / 60;
- C = 2 * s * (l < .5 ? l : 1 - l);
- X = C * (1 - abs(h % 2 - 1));
- R = G = B = l - C / 2;
-
- h = ~~h;
- R += [C, X, 0, 0, X, C][h];
- G += [X, C, C, X, 0, 0][h];
- B += [0, 0, X, C, C, X][h];
- return packageRGB(R, G, B, o);
- };
-
- R.rgb2hsb = function (r, g, b) {
- b = prepareRGB(r, g, b);
- r = b[0];
- g = b[1];
- b = b[2];
-
- var H, S, V, C;
- V = mmax(r, g, b);
- C = V - mmin(r, g, b);
- H = (C == 0 ? null :
- V == r ? (g - b) / C :
- V == g ? (b - r) / C + 2 :
- (r - g) / C + 4
- );
- H = ((H + 360) % 6) * 60 / 360;
- S = C == 0 ? 0 : C / V;
- return {h: H, s: S, b: V, toString: hsbtoString};
- };
-
- R.rgb2hsl = function (r, g, b) {
- b = prepareRGB(r, g, b);
- r = b[0];
- g = b[1];
- b = b[2];
-
- var H, S, L, M, m, C;
- M = mmax(r, g, b);
- m = mmin(r, g, b);
- C = M - m;
- H = (C == 0 ? null :
- M == r ? (g - b) / C :
- M == g ? (b - r) / C + 2 :
- (r - g) / C + 4);
- H = ((H + 360) % 6) * 60 / 360;
- L = (M + m) / 2;
- S = (C == 0 ? 0 :
- L < .5 ? C / (2 * L) :
- C / (2 - 2 * L));
- return {h: H, s: S, l: L, toString: hsltoString};
- };
- R._path2string = function () {
- return this.join(",").replace(p2s, "$1");
- };
- function repush(array, item) {
- for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
- return array.push(array.splice(i, 1)[0]);
- }
- }
- function cacher(f, scope, postprocessor) {
- function newf() {
- var arg = Array.prototype.slice.call(arguments, 0),
- args = arg.join("\u2400"),
- cache = newf.cache = newf.cache || {},
- count = newf.count = newf.count || [];
- if (cache[has](args)) {
- repush(count, args);
- return postprocessor ? postprocessor(cache[args]) : cache[args];
- }
- count.length >= 1e3 && delete cache[count.shift()];
- count.push(args);
- cache[args] = f[apply](scope, arg);
- return postprocessor ? postprocessor(cache[args]) : cache[args];
- }
- return newf;
- }
-
- var preload = R._preload = function (src, f) {
- var img = g.doc.createElement("img");
- img.style.cssText = "position:absolute;left:-9999em;top:-9999em";
- img.onload = function () {
- f.call(this);
- this.onload = null;
- g.doc.body.removeChild(this);
- };
- img.onerror = function () {
- g.doc.body.removeChild(this);
- };
- g.doc.body.appendChild(img);
- img.src = src;
- };
-
- function clrToString() {
- return this.hex;
- }
-
-
- R.getRGB = cacher(function (colour) {
- if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) {
- return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString};
- }
- if (colour == "none") {
- return {r: -1, g: -1, b: -1, hex: "none", toString: clrToString};
- }
- !(hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour));
- var res,
- red,
- green,
- blue,
- opacity,
- t,
- values,
- rgb = colour.match(colourRegExp);
- if (rgb) {
- if (rgb[2]) {
- blue = toInt(rgb[2].substring(5), 16);
- green = toInt(rgb[2].substring(3, 5), 16);
- red = toInt(rgb[2].substring(1, 3), 16);
- }
- if (rgb[3]) {
- blue = toInt((t = rgb[3].charAt(3)) + t, 16);
- green = toInt((t = rgb[3].charAt(2)) + t, 16);
- red = toInt((t = rgb[3].charAt(1)) + t, 16);
- }
- if (rgb[4]) {
- values = rgb[4][split](commaSpaces);
- red = toFloat(values[0]);
- values[0].slice(-1) == "%" && (red *= 2.55);
- green = toFloat(values[1]);
- values[1].slice(-1) == "%" && (green *= 2.55);
- blue = toFloat(values[2]);
- values[2].slice(-1) == "%" && (blue *= 2.55);
- rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3]));
- values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
- }
- if (rgb[5]) {
- values = rgb[5][split](commaSpaces);
- red = toFloat(values[0]);
- values[0].slice(-1) == "%" && (red *= 2.55);
- green = toFloat(values[1]);
- values[1].slice(-1) == "%" && (green *= 2.55);
- blue = toFloat(values[2]);
- values[2].slice(-1) == "%" && (blue *= 2.55);
- (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
- rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3]));
- values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
- return R.hsb2rgb(red, green, blue, opacity);
- }
- if (rgb[6]) {
- values = rgb[6][split](commaSpaces);
- red = toFloat(values[0]);
- values[0].slice(-1) == "%" && (red *= 2.55);
- green = toFloat(values[1]);
- values[1].slice(-1) == "%" && (green *= 2.55);
- blue = toFloat(values[2]);
- values[2].slice(-1) == "%" && (blue *= 2.55);
- (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
- rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3]));
- values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
- return R.hsl2rgb(red, green, blue, opacity);
- }
- rgb = {r: red, g: green, b: blue, toString: clrToString};
- rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1);
- R.is(opacity, "finite") && (rgb.opacity = opacity);
- return rgb;
- }
- return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString};
- }, R);
-
- R.hsb = cacher(function (h, s, b) {
- return R.hsb2rgb(h, s, b).hex;
- });
-
- R.hsl = cacher(function (h, s, l) {
- return R.hsl2rgb(h, s, l).hex;
- });
-
- R.rgb = cacher(function (r, g, b) {
- return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1);
- });
-
- R.getColor = function (value) {
- var start = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: value || .75},
- rgb = this.hsb2rgb(start.h, start.s, start.b);
- start.h += .075;
- if (start.h > 1) {
- start.h = 0;
- start.s -= .2;
- start.s <= 0 && (this.getColor.start = {h: 0, s: 1, b: start.b});
- }
- return rgb.hex;
- };
-
- R.getColor.reset = function () {
- delete this.start;
- };
-
- // http://schepers.cc/getting-to-the-point
- function catmullRom2bezier(crp, z) {
- var d = [];
- for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
- var p = [
- {x: +crp[i - 2], y: +crp[i - 1]},
- {x: +crp[i], y: +crp[i + 1]},
- {x: +crp[i + 2], y: +crp[i + 3]},
- {x: +crp[i + 4], y: +crp[i + 5]}
- ];
- if (z) {
- if (!i) {
- p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]};
- } else if (iLen - 4 == i) {
- p[3] = {x: +crp[0], y: +crp[1]};
- } else if (iLen - 2 == i) {
- p[2] = {x: +crp[0], y: +crp[1]};
- p[3] = {x: +crp[2], y: +crp[3]};
- }
- } else {
- if (iLen - 4 == i) {
- p[3] = p[2];
- } else if (!i) {
- p[0] = {x: +crp[i], y: +crp[i + 1]};
- }
- }
- d.push(["C",
- (-p[0].x + 6 * p[1].x + p[2].x) / 6,
- (-p[0].y + 6 * p[1].y + p[2].y) / 6,
- (p[1].x + 6 * p[2].x - p[3].x) / 6,
- (p[1].y + 6*p[2].y - p[3].y) / 6,
- p[2].x,
- p[2].y
- ]);
- }
-
- return d;
- }
-
- R.parsePathString = cacher(function (pathString) {
- if (!pathString) {
- return null;
- }
- var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0},
- data = [];
- if (R.is(pathString, array) && R.is(pathString[0], array)) { // rough assumption
- data = pathClone(pathString);
- }
- if (!data.length) {
- Str(pathString).replace(pathCommand, function (a, b, c) {
- var params = [],
- name = b.toLowerCase();
- c.replace(pathValues, function (a, b) {
- b && params.push(+b);
- });
- if (name == "m" && params.length > 2) {
- data.push([b][concat](params.splice(0, 2)));
- name = "l";
- b = b == "m" ? "l" : "L";
- }
- if (name == "r") {
- data.push([b][concat](params));
- } else while (params.length >= paramCounts[name]) {
- data.push([b][concat](params.splice(0, paramCounts[name])));
- if (!paramCounts[name]) {
- break;
- }
- }
- });
- }
- data.toString = R._path2string;
- return data;
- });
-
- R.parseTransformString = cacher(function (TString) {
- if (!TString) {
- return null;
- }
- var paramCounts = {r: 3, s: 4, t: 2, m: 6},
- data = [];
- if (R.is(TString, array) && R.is(TString[0], array)) { // rough assumption
- data = pathClone(TString);
- }
- if (!data.length) {
- Str(TString).replace(tCommand, function (a, b, c) {
- var params = [],
- name = lowerCase.call(b);
- c.replace(pathValues, function (a, b) {
- b && params.push(+b);
- });
- data.push([b][concat](params));
- });
- }
- data.toString = R._path2string;
- return data;
- });
-
- R.findDotsAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
- var t1 = 1 - t,
- t13 = pow(t1, 3),
- t12 = pow(t1, 2),
- t2 = t * t,
- t3 = t2 * t,
- x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,
- y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y,
- mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
- my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
- nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
- ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
- ax = t1 * p1x + t * c1x,
- ay = t1 * p1y + t * c1y,
- cx = t1 * c2x + t * p2x,
- cy = t1 * c2y + t * p2y,
- alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI);
- (mx > nx || my < ny) && (alpha += 180);
- return {
- x: x,
- y: y,
- m: {x: mx, y: my},
- n: {x: nx, y: ny},
- start: {x: ax, y: ay},
- end: {x: cx, y: cy},
- alpha: alpha
- };
- };
- R._removedFactory = function (methodname) {
- return function () {
- throw new Error("Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object");
- };
- };
- var pathDimensions = cacher(function (path) {
- if (!path) {
- return {x: 0, y: 0, width: 0, height: 0};
- }
- path = path2curve(path);
- var x = 0,
- y = 0,
- X = [],
- Y = [],
- p;
- for (var i = 0, ii = path.length; i < ii; i++) {
- p = path[i];
- if (p[0] == "M") {
- x = p[1];
- y = p[2];
- X.push(x);
- Y.push(y);
- } else {
- var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
- X = X[concat](dim.min.x, dim.max.x);
- Y = Y[concat](dim.min.y, dim.max.y);
- x = p[5];
- y = p[6];
- }
- }
- var xmin = mmin[apply](0, X),
- ymin = mmin[apply](0, Y);
- return {
- x: xmin,
- y: ymin,
- width: mmax[apply](0, X) - xmin,
- height: mmax[apply](0, Y) - ymin
- };
- }, null, function (o) {
- return {
- x: o.x,
- y: o.y,
- width: o.width,
- height: o.height
- };
- }),
- pathClone = function (pathArray) {
- var res = [];
- if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
- pathArray = R.parsePathString(pathArray);
- }
- for (var i = 0, ii = pathArray.length; i < ii; i++) {
- res[i] = [];
- for (var j = 0, jj = pathArray[i].length; j < jj; j++) {
- res[i][j] = pathArray[i][j];
- }
- }
- res.toString = R._path2string;
- return res;
- },
- pathToRelative = R._pathToRelative = cacher(function (pathArray) {
- if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
- pathArray = R.parsePathString(pathArray);
- }
- var res = [],
- x = 0,
- y = 0,
- mx = 0,
- my = 0,
- start = 0;
- if (pathArray[0][0] == "M") {
- x = pathArray[0][1];
- y = pathArray[0][2];
- mx = x;
- my = y;
- start++;
- res.push(["M", x, y]);
- }
- for (var i = start, ii = pathArray.length; i < ii; i++) {
- var r = res[i] = [],
- pa = pathArray[i];
- if (pa[0] != lowerCase.call(pa[0])) {
- r[0] = lowerCase.call(pa[0]);
- switch (r[0]) {
- case "a":
- r[1] = pa[1];
- r[2] = pa[2];
- r[3] = pa[3];
- r[4] = pa[4];
- r[5] = pa[5];
- r[6] = +(pa[6] - x).toFixed(3);
- r[7] = +(pa[7] - y).toFixed(3);
- break;
- case "v":
- r[1] = +(pa[1] - y).toFixed(3);
- break;
- case "m":
- mx = pa[1];
- my = pa[2];
- default:
- for (var j = 1, jj = pa.length; j < jj; j++) {
- r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
- }
- }
- } else {
- r = res[i] = [];
- if (pa[0] == "m") {
- mx = pa[1] + x;
- my = pa[2] + y;
- }
- for (var k = 0, kk = pa.length; k < kk; k++) {
- res[i][k] = pa[k];
- }
- }
- var len = res[i].length;
- switch (res[i][0]) {
- case "z":
- x = mx;
- y = my;
- break;
- case "h":
- x += +res[i][len - 1];
- break;
- case "v":
- y += +res[i][len - 1];
- break;
- default:
- x += +res[i][len - 2];
- y += +res[i][len - 1];
- }
- }
- res.toString = R._path2string;
- return res;
- }, 0, pathClone),
- pathToAbsolute = R._pathToAbsolute = cacher(function (pathArray) {
- if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
- pathArray = R.parsePathString(pathArray);
- }
- if (!pathArray || !pathArray.length) {
- return [["M", 0, 0]];
- }
- var res = [],
- x = 0,
- y = 0,
- mx = 0,
- my = 0,
- start = 0;
- if (pathArray[0][0] == "M") {
- x = +pathArray[0][1];
- y = +pathArray[0][2];
- mx = x;
- my = y;
- start++;
- res[0] = ["M", x, y];
- }
- var crz = pathArray.length == 3 && pathArray[0][0] == "M" && pathArray[1][0].toUpperCase() == "R" && pathArray[2][0].toUpperCase() == "Z";
- for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
- res.push(r = []);
- pa = pathArray[i];
- if (pa[0] != upperCase.call(pa[0])) {
- r[0] = upperCase.call(pa[0]);
- switch (r[0]) {
- case "A":
- r[1] = pa[1];
- r[2] = pa[2];
- r[3] = pa[3];
- r[4] = pa[4];
- r[5] = pa[5];
- r[6] = +(pa[6] + x);
- r[7] = +(pa[7] + y);
- break;
- case "V":
- r[1] = +pa[1] + y;
- break;
- case "H":
- r[1] = +pa[1] + x;
- break;
- case "R":
- var dots = [x, y][concat](pa.slice(1));
- for (var j = 2, jj = dots.length; j < jj; j++) {
- dots[j] = +dots[j] + x;
- dots[++j] = +dots[j] + y;
- }
- res.pop();
- res = res[concat](catmullRom2bezier(dots, crz));
- break;
- case "M":
- mx = +pa[1] + x;
- my = +pa[2] + y;
- default:
- for (j = 1, jj = pa.length; j < jj; j++) {
- r[j] = +pa[j] + ((j % 2) ? x : y);
- }
- }
- } else if (pa[0] == "R") {
- dots = [x, y][concat](pa.slice(1));
- res.pop();
- res = res[concat](catmullRom2bezier(dots, crz));
- r = ["R"][concat](pa.slice(-2));
- } else {
- for (var k = 0, kk = pa.length; k < kk; k++) {
- r[k] = pa[k];
- }
- }
- switch (r[0]) {
- case "Z":
- x = mx;
- y = my;
- break;
- case "H":
- x = r[1];
- break;
- case "V":
- y = r[1];
- break;
- case "M":
- mx = r[r.length - 2];
- my = r[r.length - 1];
- default:
- x = r[r.length - 2];
- y = r[r.length - 1];
- }
- }
- res.toString = R._path2string;
- return res;
- }, null, pathClone),
- l2c = function (x1, y1, x2, y2) {
- return [x1, y1, x2, y2, x2, y2];
- },
- q2c = function (x1, y1, ax, ay, x2, y2) {
- var _13 = 1 / 3,
- _23 = 2 / 3;
- return [
- _13 * x1 + _23 * ax,
- _13 * y1 + _23 * ay,
- _13 * x2 + _23 * ax,
- _13 * y2 + _23 * ay,
- x2,
- y2
- ];
- },
- a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
- // for more information of where this math came from visit:
- // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
- var _120 = PI * 120 / 180,
- rad = PI / 180 * (+angle || 0),
- res = [],
- xy,
- rotate = cacher(function (x, y, rad) {
- var X = x * math.cos(rad) - y * math.sin(rad),
- Y = x * math.sin(rad) + y * math.cos(rad);
- return {x: X, y: Y};
- });
- if (!recursive) {
- xy = rotate(x1, y1, -rad);
- x1 = xy.x;
- y1 = xy.y;
- xy = rotate(x2, y2, -rad);
- x2 = xy.x;
- y2 = xy.y;
- var cos = math.cos(PI / 180 * angle),
- sin = math.sin(PI / 180 * angle),
- x = (x1 - x2) / 2,
- y = (y1 - y2) / 2;
- var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
- if (h > 1) {
- h = math.sqrt(h);
- rx = h * rx;
- ry = h * ry;
- }
- var rx2 = rx * rx,
- ry2 = ry * ry,
- k = (large_arc_flag == sweep_flag ? -1 : 1) *
- math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
- cx = k * rx * y / ry + (x1 + x2) / 2,
- cy = k * -ry * x / rx + (y1 + y2) / 2,
- f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
- f2 = math.asin(((y2 - cy) / ry).toFixed(9));
-
- f1 = x1 < cx ? PI - f1 : f1;
- f2 = x2 < cx ? PI - f2 : f2;
- f1 < 0 && (f1 = PI * 2 + f1);
- f2 < 0 && (f2 = PI * 2 + f2);
- if (sweep_flag && f1 > f2) {
- f1 = f1 - PI * 2;
- }
- if (!sweep_flag && f2 > f1) {
- f2 = f2 - PI * 2;
- }
- } else {
- f1 = recursive[0];
- f2 = recursive[1];
- cx = recursive[2];
- cy = recursive[3];
- }
- var df = f2 - f1;
- if (abs(df) > _120) {
- var f2old = f2,
- x2old = x2,
- y2old = y2;
- f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
- x2 = cx + rx * math.cos(f2);
- y2 = cy + ry * math.sin(f2);
- res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
- }
- df = f2 - f1;
- var c1 = math.cos(f1),
- s1 = math.sin(f1),
- c2 = math.cos(f2),
- s2 = math.sin(f2),
- t = math.tan(df / 4),
- hx = 4 / 3 * rx * t,
- hy = 4 / 3 * ry * t,
- m1 = [x1, y1],
- m2 = [x1 + hx * s1, y1 - hy * c1],
- m3 = [x2 + hx * s2, y2 - hy * c2],
- m4 = [x2, y2];
- m2[0] = 2 * m1[0] - m2[0];
- m2[1] = 2 * m1[1] - m2[1];
- if (recursive) {
- return [m2, m3, m4][concat](res);
- } else {
- res = [m2, m3, m4][concat](res).join()[split](",");
- var newres = [];
- for (var i = 0, ii = res.length; i < ii; i++) {
- newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
- }
- return newres;
- }
- },
- findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
- var t1 = 1 - t;
- return {
- x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
- y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y
- };
- },
- curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
- var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
- b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
- c = p1x - c1x,
- t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a,
- t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a,
- y = [p1y, p2y],
- x = [p1x, p2x],
- dot;
- abs(t1) > "1e12" && (t1 = .5);
- abs(t2) > "1e12" && (t2 = .5);
- if (t1 > 0 && t1 < 1) {
- dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
- x.push(dot.x);
- y.push(dot.y);
- }
- if (t2 > 0 && t2 < 1) {
- dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
- x.push(dot.x);
- y.push(dot.y);
- }
- a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
- b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
- c = p1y - c1y;
- t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a;
- t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a;
- abs(t1) > "1e12" && (t1 = .5);
- abs(t2) > "1e12" && (t2 = .5);
- if (t1 > 0 && t1 < 1) {
- dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
- x.push(dot.x);
- y.push(dot.y);
- }
- if (t2 > 0 && t2 < 1) {
- dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
- x.push(dot.x);
- y.push(dot.y);
- }
- return {
- min: {x: mmin[apply](0, x), y: mmin[apply](0, y)},
- max: {x: mmax[apply](0, x), y: mmax[apply](0, y)}
- };
- }),
- path2curve = R._path2curve = cacher(function (path, path2) {
- var p = pathToAbsolute(path),
- p2 = path2 && pathToAbsolute(path2),
- attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
- attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
- processPath = function (path, d) {
- var nx, ny;
- if (!path) {
- return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
- }
- !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null);
- switch (path[0]) {
- case "M":
- d.X = path[1];
- d.Y = path[2];
- break;
- case "A":
- path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1))));
- break;
- case "S":
- nx = d.x + (d.x - (d.bx || d.x));
- ny = d.y + (d.y - (d.by || d.y));
- path = ["C", nx, ny][concat](path.slice(1));
- break;
- case "T":
- d.qx = d.x + (d.x - (d.qx || d.x));
- d.qy = d.y + (d.y - (d.qy || d.y));
- path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
- break;
- case "Q":
- d.qx = path[1];
- d.qy = path[2];
- path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
- break;
- case "L":
- path = ["C"][concat](l2c(d.x, d.y, path[1], path[2]));
- break;
- case "H":
- path = ["C"][concat](l2c(d.x, d.y, path[1], d.y));
- break;
- case "V":
- path = ["C"][concat](l2c(d.x, d.y, d.x, path[1]));
- break;
- case "Z":
- path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y));
- break;
- }
- return path;
- },
- fixArc = function (pp, i) {
- if (pp[i].length > 7) {
- pp[i].shift();
- var pi = pp[i];
- while (pi.length) {
- pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6)));
- }
- pp.splice(i, 1);
- ii = mmax(p.length, p2 && p2.length || 0);
- }
- },
- fixM = function (path1, path2, a1, a2, i) {
- if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
- path2.splice(i, 0, ["M", a2.x, a2.y]);
- a1.bx = 0;
- a1.by = 0;
- a1.x = path1[i][1];
- a1.y = path1[i][2];
- ii = mmax(p.length, p2 && p2.length || 0);
- }
- };
- for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) {
- p[i] = processPath(p[i], attrs);
- fixArc(p, i);
- p2 && (p2[i] = processPath(p2[i], attrs2));
- p2 && fixArc(p2, i);
- fixM(p, p2, attrs, attrs2, i);
- fixM(p2, p, attrs2, attrs, i);
- var seg = p[i],
- seg2 = p2 && p2[i],
- seglen = seg.length,
- seg2len = p2 && seg2.length;
- attrs.x = seg[seglen - 2];
- attrs.y = seg[seglen - 1];
- attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
- attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
- attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
- attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
- attrs2.x = p2 && seg2[seg2len - 2];
- attrs2.y = p2 && seg2[seg2len - 1];
- }
- return p2 ? [p, p2] : p;
- }, null, pathClone),
- parseDots = R._parseDots = cacher(function (gradient) {
- var dots = [];
- for (var i = 0, ii = gradient.length; i < ii; i++) {
- var dot = {},
- par = gradient[i].match(/^([^:]*):?([\d\.]*)/);
- dot.color = R.getRGB(par[1]);
- if (dot.color.error) {
- return null;
- }
- dot.color = dot.color.hex;
- par[2] && (dot.offset = par[2] + "%");
- dots.push(dot);
- }
- for (i = 1, ii = dots.length - 1; i < ii; i++) {
- if (!dots[i].offset) {
- var start = toFloat(dots[i - 1].offset || 0),
- end = 0;
- for (var j = i + 1; j < ii; j++) {
- if (dots[j].offset) {
- end = dots[j].offset;
- break;
- }
- }
- if (!end) {
- end = 100;
- j = ii;
- }
- end = toFloat(end);
- var d = (end - start) / (j - i + 1);
- for (; i < j; i++) {
- start += d;
- dots[i].offset = start + "%";
- }
- }
- }
- return dots;
- }),
- tear = R._tear = function (el, paper) {
- el == paper.top && (paper.top = el.prev);
- el == paper.bottom && (paper.bottom = el.next);
- el.next && (el.next.prev = el.prev);
- el.prev && (el.prev.next = el.next);
- },
- tofront = R._tofront = function (el, paper) {
- if (paper.top === el) {
- return;
- }
- tear(el, paper);
- el.next = null;
- el.prev = paper.top;
- paper.top.next = el;
- paper.top = el;
- },
- toback = R._toback = function (el, paper) {
- if (paper.bottom === el) {
- return;
- }
- tear(el, paper);
- el.next = paper.bottom;
- el.prev = null;
- paper.bottom.prev = el;
- paper.bottom = el;
- },
- insertafter = R._insertafter = function (el, el2, paper) {
- tear(el, paper);
- el2 == paper.top && (paper.top = el);
- el2.next && (el2.next.prev = el);
- el.next = el2.next;
- el.prev = el2;
- el2.next = el;
- },
- insertbefore = R._insertbefore = function (el, el2, paper) {
- tear(el, paper);
- el2 == paper.bottom && (paper.bottom = el);
- el2.prev && (el2.prev.next = el);
- el.prev = el2.prev;
- el2.prev = el;
- el.next = el2;
- },
- extractTransform = R._extractTransform = function (el, tstr) {
- if (tstr == null) {
- return el._.transform;
- }
- tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || E);
- var tdata = R.parseTransformString(tstr),
- deg = 0,
- dx = 0,
- dy = 0,
- sx = 1,
- sy = 1,
- _ = el._,
- m = new Matrix;
- _.transform = tdata || [];
- if (tdata) {
- for (var i = 0, ii = tdata.length; i < ii; i++) {
- var t = tdata[i],
- tlen = t.length,
- command = Str(t[0]).toLowerCase(),
- absolute = t[0] != command,
- inver = absolute ? m.invert() : 0,
- x1,
- y1,
- x2,
- y2,
- bb;
- if (command == "t" && tlen == 3) {
- if (absolute) {
- x1 = inver.x(0, 0);
- y1 = inver.y(0, 0);
- x2 = inver.x(t[1], t[2]);
- y2 = inver.y(t[1], t[2]);
- m.translate(x2 - x1, y2 - y1);
- } else {
- m.translate(t[1], t[2]);
- }
- } else if (command == "r") {
- if (tlen == 2) {
- bb = bb || el.getBBox(1);
- m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2);
- deg += t[1];
- } else if (tlen == 4) {
- if (absolute) {
- x2 = inver.x(t[2], t[3]);
- y2 = inver.y(t[2], t[3]);
- m.rotate(t[1], x2, y2);
- } else {
- m.rotate(t[1], t[2], t[3]);
- }
- deg += t[1];
- }
- } else if (command == "s") {
- if (tlen == 2 || tlen == 3) {
- bb = bb || el.getBBox(1);
- m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2);
- sx *= t[1];
- sy *= t[tlen - 1];
- } else if (tlen == 5) {
- if (absolute) {
- x2 = inver.x(t[3], t[4]);
- y2 = inver.y(t[3], t[4]);
- m.scale(t[1], t[2], x2, y2);
- } else {
- m.scale(t[1], t[2], t[3], t[4]);
- }
- sx *= t[1];
- sy *= t[2];
- }
- } else if (command == "m" && tlen == 7) {
- m.add(t[1], t[2], t[3], t[4], t[5], t[6]);
- }
- _.dirtyT = 1;
- el.matrix = m;
- }
- }
-
- el.matrix = m;
-
- _.sx = sx;
- _.sy = sy;
- _.deg = deg;
- _.dx = dx = m.e;
- _.dy = dy = m.f;
-
- if (sx == 1 && sy == 1 && !deg && _.bbox) {
- _.bbox.x += +dx;
- _.bbox.y += +dy;
- } else {
- _.dirtyT = 1;
- }
- },
- getEmpty = function (item) {
- var l = item[0];
- switch (l.toLowerCase()) {
- case "t": return [l, 0, 0];
- case "m": return [l, 1, 0, 0, 1, 0, 0];
- case "r": if (item.length == 4) {
- return [l, 0, item[2], item[3]];
- } else {
- return [l, 0];
- }
- case "s": if (item.length == 5) {
- return [l, 1, 1, item[3], item[4]];
- } else if (item.length == 3) {
- return [l, 1, 1];
- } else {
- return [l, 1];
- }
- }
- },
- equaliseTransform = R._equaliseTransform = function (t1, t2) {
- t2 = Str(t2).replace(/\.{3}|\u2026/g, t1);
- t1 = R.parseTransformString(t1) || [];
- t2 = R.parseTransformString(t2) || [];
- var maxlength = mmax(t1.length, t2.length),
- from = [],
- to = [],
- i = 0, j, jj,
- tt1, tt2;
- for (; i < maxlength; i++) {
- tt1 = t1[i] || getEmpty(t2[i]);
- tt2 = t2[i] || getEmpty(tt1);
- if ((tt1[0] != tt2[0]) ||
- (tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) ||
- (tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4]))
- ) {
- return;
- }
- from[i] = [];
- to[i] = [];
- for (j = 0, jj = mmax(tt1.length, tt2.length); j < jj; j++) {
- j in tt1 && (from[i][j] = tt1[j]);
- j in tt2 && (to[i][j] = tt2[j]);
- }
- }
- return {
- from: from,
- to: to
- };
- };
- R._getContainer = function (x, y, w, h) {
- var container;
- container = h == null && !R.is(x, "object") ? g.doc.getElementById(x) : x;
- if (container == null) {
- return;
- }
- if (container.tagName) {
- if (y == null) {
- return {
- container: container,
- width: container.style.pixelWidth || container.offsetWidth,
- height: container.style.pixelHeight || container.offsetHeight
- };
- } else {
- return {
- container: container,
- width: y,
- height: w
- };
- }
- }
- return {
- container: 1,
- x: x,
- y: y,
- width: w,
- height: h
- };
- };
-
- R.pathToRelative = pathToRelative;
- R._engine = {};
-
- R.path2curve = path2curve;
-
- R.matrix = function (a, b, c, d, e, f) {
- return new Matrix(a, b, c, d, e, f);
- };
- function Matrix(a, b, c, d, e, f) {
- if (a != null) {
- this.a = +a;
- this.b = +b;
- this.c = +c;
- this.d = +d;
- this.e = +e;
- this.f = +f;
- } else {
- this.a = 1;
- this.b = 0;
- this.c = 0;
- this.d = 1;
- this.e = 0;
- this.f = 0;
- }
- }
- (function (matrixproto) {
-
- matrixproto.add = function (a, b, c, d, e, f) {
- var out = [[], [], []],
- m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]],
- matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
- x, y, z, res;
-
- if (a && a instanceof Matrix) {
- matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]];
- }
-
- for (x = 0; x < 3; x++) {
- for (y = 0; y < 3; y++) {
- res = 0;
- for (z = 0; z < 3; z++) {
- res += m[x][z] * matrix[z][y];
- }
- out[x][y] = res;
- }
- }
- this.a = out[0][0];
- this.b = out[1][0];
- this.c = out[0][1];
- this.d = out[1][1];
- this.e = out[0][2];
- this.f = out[1][2];
- };
-
- matrixproto.invert = function () {
- var me = this,
- x = me.a * me.d - me.b * me.c;
- return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x);
- };
-
- matrixproto.clone = function () {
- return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
- };
-
- matrixproto.translate = function (x, y) {
- this.add(1, 0, 0, 1, x, y);
- };
-
- matrixproto.scale = function (x, y, cx, cy) {
- y == null && (y = x);
- (cx || cy) && this.add(1, 0, 0, 1, cx, cy);
- this.add(x, 0, 0, y, 0, 0);
- (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy);
- };
-
- matrixproto.rotate = function (a, x, y) {
- a = R.rad(a);
- x = x || 0;
- y = y || 0;
- var cos = +math.cos(a).toFixed(9),
- sin = +math.sin(a).toFixed(9);
- this.add(cos, sin, -sin, cos, x, y);
- this.add(1, 0, 0, 1, -x, -y);
- };
-
- matrixproto.x = function (x, y) {
- return x * this.a + y * this.c + this.e;
- };
-
- matrixproto.y = function (x, y) {
- return x * this.b + y * this.d + this.f;
- };
- matrixproto.get = function (i) {
- return +this[Str.fromCharCode(97 + i)].toFixed(4);
- };
- matrixproto.toString = function () {
- return R.svg ?
- "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" :
- [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join();
- };
- matrixproto.toFilter = function () {
- return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) +
- ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) +
- ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')";
- };
- matrixproto.offset = function () {
- return [this.e.toFixed(4), this.f.toFixed(4)];
- };
- function norm(a) {
- return a[0] * a[0] + a[1] * a[1];
- }
- function normalize(a) {
- var mag = math.sqrt(norm(a));
- a[0] && (a[0] /= mag);
- a[1] && (a[1] /= mag);
- }
-
- matrixproto.split = function () {
- var out = {};
- // translation
- out.dx = this.e;
- out.dy = this.f;
-
- // scale and shear
- var row = [[this.a, this.c], [this.b, this.d]];
- out.scalex = math.sqrt(norm(row[0]));
- normalize(row[0]);
-
- out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
- row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];
-
- out.scaley = math.sqrt(norm(row[1]));
- normalize(row[1]);
- out.shear /= out.scaley;
-
- // rotation
- var sin = -row[0][1],
- cos = row[1][1];
- if (cos < 0) {
- out.rotate = R.deg(math.acos(cos));
- if (sin < 0) {
- out.rotate = 360 - out.rotate;
- }
- } else {
- out.rotate = R.deg(math.asin(sin));
- }
-
- out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
- out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate;
- out.noRotation = !+out.shear.toFixed(9) && !out.rotate;
- return out;
- };
-
- matrixproto.toTransformString = function (shorter) {
- var s = shorter || this[split]();
- if (s.isSimple) {
- s.scalex = +s.scalex.toFixed(4);
- s.scaley = +s.scaley.toFixed(4);
- s.rotate = +s.rotate.toFixed(4);
- return (s.dx || s.dy ? "t" + [s.dx, s.dy] : E) +
- (s.scalex != 1 || s.scaley != 1 ? "s" + [s.scalex, s.scaley, 0, 0] : E) +
- (s.rotate ? "r" + [s.rotate, 0, 0] : E);
- } else {
- return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
- }
- };
- })(Matrix.prototype);
-
- // WebKit rendering bug workaround method
- var version = navigator.userAgent.match(/Version\/(.*?)\s/) || navigator.userAgent.match(/Chrome\/(\d+)/);
- if ((navigator.vendor == "Apple Computer, Inc.") && (version && version[1] < 4 || navigator.platform.slice(0, 2) == "iP") ||
- (navigator.vendor == "Google Inc." && version && version[1] < 8)) {
-
- paperproto.safari = function () {
- var rect = this.rect(-99, -99, this.width + 99, this.height + 99).attr({stroke: "none"});
- setTimeout(function () {rect.remove();});
- };
- } else {
- paperproto.safari = fun;
- }
-
- var preventDefault = function () {
- this.returnValue = false;
- },
- preventTouch = function () {
- return this.originalEvent.preventDefault();
- },
- stopPropagation = function () {
- this.cancelBubble = true;
- },
- stopTouch = function () {
- return this.originalEvent.stopPropagation();
- },
- addEvent = (function () {
- if (g.doc.addEventListener) {
- return function (obj, type, fn, element) {
- var realName = supportsTouch && touchMap[type] ? touchMap[type] : type,
- f = function (e) {
- var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
- scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
- x = e.clientX + scrollX,
- y = e.clientY + scrollY;
- if (supportsTouch && touchMap[has](type)) {
- for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
- if (e.targetTouches[i].target == obj) {
- var olde = e;
- e = e.targetTouches[i];
- e.originalEvent = olde;
- e.preventDefault = preventTouch;
- e.stopPropagation = stopTouch;
- break;
- }
- }
- }
- return fn.call(element, e, x, y);
- };
- obj.addEventListener(realName, f, false);
- return function () {
- obj.removeEventListener(realName, f, false);
- return true;
- };
- };
- } else if (g.doc.attachEvent) {
- return function (obj, type, fn, element) {
- var f = function (e) {
- e = e || g.win.event;
- var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
- scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
- x = e.clientX + scrollX,
- y = e.clientY + scrollY;
- e.preventDefault = e.preventDefault || preventDefault;
- e.stopPropagation = e.stopPropagation || stopPropagation;
- return fn.call(element, e, x, y);
- };
- obj.attachEvent("on" + type, f);
- var detacher = function () {
- obj.detachEvent("on" + type, f);
- return true;
- };
- return detacher;
- };
- }
- })(),
- drag = [],
- dragMove = function (e) {
- var x = e.clientX,
- y = e.clientY,
- scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
- scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
- dragi,
- j = drag.length;
- while (j--) {
- dragi = drag[j];
- if (supportsTouch) {
- var i = e.touches.length,
- touch;
- while (i--) {
- touch = e.touches[i];
- if (touch.identifier == dragi.el._drag.id) {
- x = touch.clientX;
- y = touch.clientY;
- (e.originalEvent ? e.originalEvent : e).preventDefault();
- break;
- }
- }
- } else {
- e.preventDefault();
- }
- var node = dragi.el.node,
- o,
- next = node.nextSibling,
- parent = node.parentNode,
- display = node.style.display;
- g.win.opera && parent.removeChild(node);
- node.style.display = "none";
- o = dragi.el.paper.getElementByPoint(x, y);
- node.style.display = display;
- g.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node));
- o && eve("drag.over." + dragi.el.id, dragi.el, o);
- x += scrollX;
- y += scrollY;
- eve("drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e);
- }
- },
- dragUp = function (e) {
- R.unmousemove(dragMove).unmouseup(dragUp);
- var i = drag.length,
- dragi;
- while (i--) {
- dragi = drag[i];
- dragi.el._drag = {};
- eve("drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e);
- }
- drag = [];
- },
-
- elproto = R.el = {};
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- for (var i = events.length; i--;) {
- (function (eventName) {
- R[eventName] = elproto[eventName] = function (fn, scope) {
- if (R.is(fn, "function")) {
- this.events = this.events || [];
- this.events.push({name: eventName, f: fn, unbind: addEvent(this.shape || this.node || g.doc, eventName, fn, scope || this)});
- }
- return this;
- };
- R["un" + eventName] = elproto["un" + eventName] = function (fn) {
- var events = this.events || [],
- l = events.length;
- while (l--) if (events[l].name == eventName && events[l].f == fn) {
- events[l].unbind();
- events.splice(l, 1);
- !events.length && delete this.events;
- return this;
- }
- return this;
- };
- })(events[i]);
- }
-
-
- elproto.data = function (key, value) {
- var data = eldata[this.id] = eldata[this.id] || {};
- if (arguments.length == 1) {
- if (R.is(key, "object")) {
- for (var i in key) if (key[has](i)) {
- this.data(i, key[i]);
- }
- return this;
- }
- eve("data.get." + this.id, this, data[key], key);
- return data[key];
- }
- data[key] = value;
- eve("data.set." + this.id, this, value, key);
- return this;
- };
-
- elproto.removeData = function (key) {
- if (key == null) {
- eldata[this.id] = {};
- } else {
- eldata[this.id] && delete eldata[this.id][key];
- }
- return this;
- };
-
- elproto.hover = function (f_in, f_out, scope_in, scope_out) {
- return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in);
- };
-
- elproto.unhover = function (f_in, f_out) {
- return this.unmouseover(f_in).unmouseout(f_out);
- };
- var draggable = [];
-
- elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) {
- function start(e) {
- (e.originalEvent || e).preventDefault();
- var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
- scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft;
- this._drag.x = e.clientX + scrollX;
- this._drag.y = e.clientY + scrollY;
- this._drag.id = e.identifier;
- !drag.length && R.mousemove(dragMove).mouseup(dragUp);
- drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope});
- onstart && eve.on("drag.start." + this.id, onstart);
- onmove && eve.on("drag.move." + this.id, onmove);
- onend && eve.on("drag.end." + this.id, onend);
- eve("drag.start." + this.id, start_scope || move_scope || this, e.clientX + scrollX, e.clientY + scrollY, e);
- }
- this._drag = {};
- draggable.push({el: this, start: start});
- this.mousedown(start);
- return this;
- };
-
- elproto.onDragOver = function (f) {
- f ? eve.on("drag.over." + this.id, f) : eve.unbind("drag.over." + this.id);
- };
-
- elproto.undrag = function () {
- var i = draggable.length;
- while (i--) if (draggable[i].el == this) {
- this.unmousedown(draggable[i].start);
- draggable.splice(i, 1);
- eve.unbind("drag.*." + this.id);
- }
- !draggable.length && R.unmousemove(dragMove).unmouseup(dragUp);
- };
-
- paperproto.circle = function (x, y, r) {
- var out = R._engine.circle(this, x || 0, y || 0, r || 0);
- this.__set__ && this.__set__.push(out);
- return out;
- };
-
- paperproto.rect = function (x, y, w, h, r) {
- var out = R._engine.rect(this, x || 0, y || 0, w || 0, h || 0, r || 0);
- this.__set__ && this.__set__.push(out);
- return out;
- };
-
- paperproto.ellipse = function (x, y, rx, ry) {
- var out = R._engine.ellipse(this, x || 0, y || 0, rx || 0, ry || 0);
- this.__set__ && this.__set__.push(out);
- return out;
- };
-
- paperproto.path = function (pathString) {
- pathString && !R.is(pathString, string) && !R.is(pathString[0], array) && (pathString += E);
- var out = R._engine.path(R.format[apply](R, arguments), this);
- this.__set__ && this.__set__.push(out);
- return out;
- };
-
- paperproto.image = function (src, x, y, w, h) {
- var out = R._engine.image(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0);
- this.__set__ && this.__set__.push(out);
- return out;
- };
-
- paperproto.text = function (x, y, text) {
- var out = R._engine.text(this, x || 0, y || 0, Str(text));
- this.__set__ && this.__set__.push(out);
- return out;
- };
-
- paperproto.set = function (itemsArray) {
- !R.is(itemsArray, "array") && (itemsArray = Array.prototype.splice.call(arguments, 0, arguments.length));
- var out = new Set(itemsArray);
- this.__set__ && this.__set__.push(out);
- return out;
- };
-
- paperproto.setStart = function (set) {
- this.__set__ = set || this.set();
- };
-
- paperproto.setFinish = function (set) {
- var out = this.__set__;
- delete this.__set__;
- return out;
- };
-
- paperproto.setSize = function (width, height) {
- return R._engine.setSize.call(this, width, height);
- };
-
- paperproto.setViewBox = function (x, y, w, h, fit) {
- return R._engine.setViewBox.call(this, x, y, w, h, fit);
- };
-
-
- paperproto.top = paperproto.bottom = null;
-
- paperproto.raphael = R;
- var getOffset = function (elem) {
- var box = elem.getBoundingClientRect(),
- doc = elem.ownerDocument,
- body = doc.body,
- docElem = doc.documentElement,
- clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
- top = box.top + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop,
- left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
- return {
- y: top,
- x: left
- };
- };
-
- paperproto.getElementByPoint = function (x, y) {
- var paper = this,
- svg = paper.canvas,
- target = g.doc.elementFromPoint(x, y);
- if (g.win.opera && target.tagName == "svg") {
- var so = getOffset(svg),
- sr = svg.createSVGRect();
- sr.x = x - so.x;
- sr.y = y - so.y;
- sr.width = sr.height = 1;
- var hits = svg.getIntersectionList(sr, null);
- if (hits.length) {
- target = hits[hits.length - 1];
- }
- }
- if (!target) {
- return null;
- }
- while (target.parentNode && target != svg.parentNode && !target.raphael) {
- target = target.parentNode;
- }
- target == paper.canvas.parentNode && (target = svg);
- target = target && target.raphael ? paper.getById(target.raphaelid) : null;
- return target;
- };
-
- paperproto.getById = function (id) {
- var bot = this.bottom;
- while (bot) {
- if (bot.id == id) {
- return bot;
- }
- bot = bot.next;
- }
- return null;
- };
-
- paperproto.forEach = function (callback, thisArg) {
- var bot = this.bottom;
- while (bot) {
- if (callback.call(thisArg, bot) === false) {
- return this;
- }
- bot = bot.next;
- }
- return this;
- };
- function x_y() {
- return this.x + S + this.y;
- }
- function x_y_w_h() {
- return this.x + S + this.y + S + this.width + " \xd7 " + this.height;
- }
-
- elproto.getBBox = function (isWithoutTransform) {
- if (this.removed) {
- return {};
- }
- var _ = this._;
- if (isWithoutTransform) {
- if (_.dirty || !_.bboxwt) {
- this.realPath = getPath[this.type](this);
- _.bboxwt = pathDimensions(this.realPath);
- _.bboxwt.toString = x_y_w_h;
- _.dirty = 0;
- }
- return _.bboxwt;
- }
- if (_.dirty || _.dirtyT || !_.bbox) {
- if (_.dirty || !this.realPath) {
- _.bboxwt = 0;
- this.realPath = getPath[this.type](this);
- }
- _.bbox = pathDimensions(mapPath(this.realPath, this.matrix));
- _.bbox.toString = x_y_w_h;
- _.dirty = _.dirtyT = 0;
- }
- return _.bbox;
- };
-
- elproto.clone = function () {
- if (this.removed) {
- return null;
- }
- var out = this.paper[this.type]().attr(this.attr());
- this.__set__ && this.__set__.push(out);
- return out;
- };
-
- elproto.glow = function (glow) {
- if (this.type == "text") {
- return null;
- }
- glow = glow || {};
- var s = {
- width: (glow.width || 10) + (+this.attr("stroke-width") || 1),
- fill: glow.fill || false,
- opacity: glow.opacity || .5,
- offsetx: glow.offsetx || 0,
- offsety: glow.offsety || 0,
- color: glow.color || "#000"
- },
- c = s.width / 2,
- r = this.paper,
- out = r.set(),
- path = this.realPath || getPath[this.type](this);
- path = this.matrix ? mapPath(path, this.matrix) : path;
- for (var i = 1; i < c + 1; i++) {
- out.push(r.path(path).attr({
- stroke: s.color,
- fill: s.fill ? s.color : "none",
- "stroke-linejoin": "round",
- "stroke-linecap": "round",
- "stroke-width": +(s.width / c * i).toFixed(3),
- opacity: +(s.opacity / c).toFixed(3)
- }));
- }
- return out.insertBefore(this).translate(s.offsetx, s.offsety);
- };
- var curveslengths = {},
- getPointAtSegmentLength = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) {
- var len = 0,
- precision = 100,
- name = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y].join(),
- cache = curveslengths[name],
- old, dot;
- !cache && (curveslengths[name] = cache = {data: []});
- cache.timer && clearTimeout(cache.timer);
- cache.timer = setTimeout(function () {delete curveslengths[name];}, 2e3);
- if (length != null && !cache.precision) {
- var total = getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
- cache.precision = ~~total * 10;
- cache.data = [];
- }
- precision = cache.precision || precision;
- for (var i = 0; i < precision + 1; i++) {
- if (cache.data[i * precision]) {
- dot = cache.data[i * precision];
- } else {
- dot = R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i / precision);
- cache.data[i * precision] = dot;
- }
- i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5));
- if (length != null && len >= length) {
- return dot;
- }
- old = dot;
- }
- if (length == null) {
- return len;
- }
- },
- getLengthFactory = function (istotal, subpath) {
- return function (path, length, onlystart) {
- path = path2curve(path);
- var x, y, p, l, sp = "", subpaths = {}, point,
- len = 0;
- for (var i = 0, ii = path.length; i < ii; i++) {
- p = path[i];
- if (p[0] == "M") {
- x = +p[1];
- y = +p[2];
- } else {
- l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
- if (len + l > length) {
- if (subpath && !subpaths.start) {
- point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
- sp += ["C" + point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y];
- if (onlystart) {return sp;}
- subpaths.start = sp;
- sp = ["M" + point.x, point.y + "C" + point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]].join();
- len += l;
- x = +p[5];
- y = +p[6];
- continue;
- }
- if (!istotal && !subpath) {
- point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
- return {x: point.x, y: point.y, alpha: point.alpha};
- }
- }
- len += l;
- x = +p[5];
- y = +p[6];
- }
- sp += p.shift() + p;
- }
- subpaths.end = sp;
- point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
- point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha});
- return point;
- };
- };
- var getTotalLength = getLengthFactory(1),
- getPointAtLength = getLengthFactory(),
- getSubpathsAtLength = getLengthFactory(0, 1);
-
- R.getTotalLength = getTotalLength;
-
- R.getPointAtLength = getPointAtLength;
-
- R.getSubpath = function (path, from, to) {
- if (this.getTotalLength(path) - to < 1e-6) {
- return getSubpathsAtLength(path, from).end;
- }
- var a = getSubpathsAtLength(path, to, 1);
- return from ? getSubpathsAtLength(a, from).end : a;
- };
-
- elproto.getTotalLength = function () {
- if (this.type != "path") {return;}
- if (this.node.getTotalLength) {
- return this.node.getTotalLength();
- }
- return getTotalLength(this.attrs.path);
- };
-
- elproto.getPointAtLength = function (length) {
- if (this.type != "path") {return;}
- return getPointAtLength(this.attrs.path, length);
- };
-
- elproto.getSubpath = function (from, to) {
- if (this.type != "path") {return;}
- return R.getSubpath(this.attrs.path, from, to);
- };
-
- var ef = R.easing_formulas = {
- linear: function (n) {
- return n;
- },
- "<": function (n) {
- return pow(n, 1.7);
- },
- ">": function (n) {
- return pow(n, .48);
- },
- "<>": function (n) {
- var q = .48 - n / 1.04,
- Q = math.sqrt(.1734 + q * q),
- x = Q - q,
- X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
- y = -Q - q,
- Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
- t = X + Y + .5;
- return (1 - t) * 3 * t * t + t * t * t;
- },
- backIn: function (n) {
- var s = 1.70158;
- return n * n * ((s + 1) * n - s);
- },
- backOut: function (n) {
- n = n - 1;
- var s = 1.70158;
- return n * n * ((s + 1) * n + s) + 1;
- },
- elastic: function (n) {
- if (n == !!n) {
- return n;
- }
- return pow(2, -10 * n) * math.sin((n - .075) * (2 * PI) / .3) + 1;
- },
- bounce: function (n) {
- var s = 7.5625,
- p = 2.75,
- l;
- if (n < (1 / p)) {
- l = s * n * n;
- } else {
- if (n < (2 / p)) {
- n -= (1.5 / p);
- l = s * n * n + .75;
- } else {
- if (n < (2.5 / p)) {
- n -= (2.25 / p);
- l = s * n * n + .9375;
- } else {
- n -= (2.625 / p);
- l = s * n * n + .984375;
- }
- }
- }
- return l;
- }
- };
- ef.easeIn = ef["ease-in"] = ef["<"];
- ef.easeOut = ef["ease-out"] = ef[">"];
- ef.easeInOut = ef["ease-in-out"] = ef["<>"];
- ef["back-in"] = ef.backIn;
- ef["back-out"] = ef.backOut;
-
- var animationElements = [],
- requestAnimFrame = window.requestAnimationFrame ||
- window.webkitRequestAnimationFrame ||
- window.mozRequestAnimationFrame ||
- window.oRequestAnimationFrame ||
- window.msRequestAnimationFrame ||
- function (callback) {
- setTimeout(callback, 16);
- },
- animation = function () {
- var Now = +new Date,
- l = 0;
- for (; l < animationElements.length; l++) {
- var e = animationElements[l];
- if (e.el.removed || e.paused) {
- continue;
- }
- var time = Now - e.start,
- ms = e.ms,
- easing = e.easing,
- from = e.from,
- diff = e.diff,
- to = e.to,
- t = e.t,
- that = e.el,
- set = {},
- now,
- init = {},
- key;
- if (e.initstatus) {
- time = (e.initstatus * e.anim.top - e.prev) / (e.percent - e.prev) * ms;
- e.status = e.initstatus;
- delete e.initstatus;
- e.stop && animationElements.splice(l--, 1);
- } else {
- e.status = (e.prev + (e.percent - e.prev) * (time / ms)) / e.anim.top;
- }
- if (time < 0) {
- continue;
- }
- if (time < ms) {
- var pos = easing(time / ms);
- for (var attr in from) if (from[has](attr)) {
- switch (availableAnimAttrs[attr]) {
- case nu:
- now = +from[attr] + pos * ms * diff[attr];
- break;
- case "colour":
- now = "rgb(" + [
- upto255(round(from[attr].r + pos * ms * diff[attr].r)),
- upto255(round(from[attr].g + pos * ms * diff[attr].g)),
- upto255(round(from[attr].b + pos * ms * diff[attr].b))
- ].join(",") + ")";
- break;
- case "path":
- now = [];
- for (var i = 0, ii = from[attr].length; i < ii; i++) {
- now[i] = [from[attr][i][0]];
- for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
- now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j];
- }
- now[i] = now[i].join(S);
- }
- now = now.join(S);
- break;
- case "transform":
- if (diff[attr].real) {
- now = [];
- for (i = 0, ii = from[attr].length; i < ii; i++) {
- now[i] = [from[attr][i][0]];
- for (j = 1, jj = from[attr][i].length; j < jj; j++) {
- now[i][j] = from[attr][i][j] + pos * ms * diff[attr][i][j];
- }
- }
- } else {
- var get = function (i) {
- return +from[attr][i] + pos * ms * diff[attr][i];
- };
- // now = [["r", get(2), 0, 0], ["t", get(3), get(4)], ["s", get(0), get(1), 0, 0]];
- now = [["m", get(0), get(1), get(2), get(3), get(4), get(5)]];
- }
- break;
- case "csv":
- if (attr == "clip-rect") {
- now = [];
- i = 4;
- while (i--) {
- now[i] = +from[attr][i] + pos * ms * diff[attr][i];
- }
- }
- break;
- default:
- var from2 = [][concat](from[attr]);
- now = [];
- i = that.paper.customAttributes[attr].length;
- while (i--) {
- now[i] = +from2[i] + pos * ms * diff[attr][i];
- }
- break;
- }
- set[attr] = now;
- }
- that.attr(set);
- (function (id, that, anim) {
- setTimeout(function () {
- eve("anim.frame." + id, that, anim);
- });
- })(that.id, that, e.anim);
- } else {
- (function(f, el, a) {
- setTimeout(function() {
- eve("anim.frame." + el.id, el, a);
- eve("anim.finish." + el.id, el, a);
- R.is(f, "function") && f.call(el);
- });
- })(e.callback, that, e.anim);
- that.attr(to);
- animationElements.splice(l--, 1);
- if (e.repeat > 1 && !e.next) {
- for (key in to) if (to[has](key)) {
- init[key] = e.totalOrigin[key];
- }
- e.el.attr(init);
- runAnimation(e.anim, e.el, e.anim.percents[0], null, e.totalOrigin, e.repeat - 1);
- }
- if (e.next && !e.stop) {
- runAnimation(e.anim, e.el, e.next, null, e.totalOrigin, e.repeat);
- }
- }
- }
- R.svg && that && that.paper && that.paper.safari();
- animationElements.length && requestAnimFrame(animation);
- },
- upto255 = function (color) {
- return color > 255 ? 255 : color < 0 ? 0 : color;
- };
-
- elproto.animateWith = function (el, anim, params, ms, easing, callback) {
- var element = this;
- if (element.removed) {
- callback && callback.call(element);
- return element;
- }
- var a = params instanceof Animation ? params : R.animation(params, ms, easing, callback),
- x, y;
- runAnimation(a, element, a.percents[0], null, element.attr());
- for (var i = 0, ii = animationElements.length; i < ii; i++) {
- if (animationElements[i].anim == anim && animationElements[i].el == el) {
- animationElements[ii - 1].start = animationElements[i].start;
- break;
- }
- }
- return element;
- //
- //
- // var a = params ? R.animation(params, ms, easing, callback) : anim,
- // status = element.status(anim);
- // return this.animate(a).status(a, status * anim.ms / a.ms);
- };
- function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) {
- var cx = 3 * p1x,
- bx = 3 * (p2x - p1x) - cx,
- ax = 1 - cx - bx,
- cy = 3 * p1y,
- by = 3 * (p2y - p1y) - cy,
- ay = 1 - cy - by;
- function sampleCurveX(t) {
- return ((ax * t + bx) * t + cx) * t;
- }
- function solve(x, epsilon) {
- var t = solveCurveX(x, epsilon);
- return ((ay * t + by) * t + cy) * t;
- }
- function solveCurveX(x, epsilon) {
- var t0, t1, t2, x2, d2, i;
- for(t2 = x, i = 0; i < 8; i++) {
- x2 = sampleCurveX(t2) - x;
- if (abs(x2) < epsilon) {
- return t2;
- }
- d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
- if (abs(d2) < 1e-6) {
- break;
- }
- t2 = t2 - x2 / d2;
- }
- t0 = 0;
- t1 = 1;
- t2 = x;
- if (t2 < t0) {
- return t0;
- }
- if (t2 > t1) {
- return t1;
- }
- while (t0 < t1) {
- x2 = sampleCurveX(t2);
- if (abs(x2 - x) < epsilon) {
- return t2;
- }
- if (x > x2) {
- t0 = t2;
- } else {
- t1 = t2;
- }
- t2 = (t1 - t0) / 2 + t0;
- }
- return t2;
- }
- return solve(t, 1 / (200 * duration));
- }
- elproto.onAnimation = function (f) {
- f ? eve.on("anim.frame." + this.id, f) : eve.unbind("anim.frame." + this.id);
- return this;
- };
- function Animation(anim, ms) {
- var percents = [],
- newAnim = {};
- this.ms = ms;
- this.times = 1;
- if (anim) {
- for (var attr in anim) if (anim[has](attr)) {
- newAnim[toFloat(attr)] = anim[attr];
- percents.push(toFloat(attr));
- }
- percents.sort(sortByNumber);
- }
- this.anim = newAnim;
- this.top = percents[percents.length - 1];
- this.percents = percents;
- }
-
- Animation.prototype.delay = function (delay) {
- var a = new Animation(this.anim, this.ms);
- a.times = this.times;
- a.del = +delay || 0;
- return a;
- };
-
- Animation.prototype.repeat = function (times) {
- var a = new Animation(this.anim, this.ms);
- a.del = this.del;
- a.times = math.floor(mmax(times, 0)) || 1;
- return a;
- };
- function runAnimation(anim, element, percent, status, totalOrigin, times) {
- percent = toFloat(percent);
- var params,
- isInAnim,
- isInAnimSet,
- percents = [],
- next,
- prev,
- timestamp,
- ms = anim.ms,
- from = {},
- to = {},
- diff = {};
- if (status) {
- for (i = 0, ii = animationElements.length; i < ii; i++) {
- var e = animationElements[i];
- if (e.el.id == element.id && e.anim == anim) {
- if (e.percent != percent) {
- animationElements.splice(i, 1);
- isInAnimSet = 1;
- } else {
- isInAnim = e;
- }
- element.attr(e.totalOrigin);
- break;
- }
- }
- } else {
- status = +to; // NaN
- }
- for (var i = 0, ii = anim.percents.length; i < ii; i++) {
- if (anim.percents[i] == percent || anim.percents[i] > status * anim.top) {
- percent = anim.percents[i];
- prev = anim.percents[i - 1] || 0;
- ms = ms / anim.top * (percent - prev);
- next = anim.percents[i + 1];
- params = anim.anim[percent];
- break;
- } else if (status) {
- element.attr(anim.anim[anim.percents[i]]);
- }
- }
- if (!params) {
- return;
- }
- if (!isInAnim) {
- for (var attr in params) if (params[has](attr)) {
- if (availableAnimAttrs[has](attr) || element.paper.customAttributes[has](attr)) {
- from[attr] = element.attr(attr);
- (from[attr] == null) && (from[attr] = availableAttrs[attr]);
- to[attr] = params[attr];
- switch (availableAnimAttrs[attr]) {
- case nu:
- diff[attr] = (to[attr] - from[attr]) / ms;
- break;
- case "colour":
- from[attr] = R.getRGB(from[attr]);
- var toColour = R.getRGB(to[attr]);
- diff[attr] = {
- r: (toColour.r - from[attr].r) / ms,
- g: (toColour.g - from[attr].g) / ms,
- b: (toColour.b - from[attr].b) / ms
- };
- break;
- case "path":
- var pathes = path2curve(from[attr], to[attr]),
- toPath = pathes[1];
- from[attr] = pathes[0];
- diff[attr] = [];
- for (i = 0, ii = from[attr].length; i < ii; i++) {
- diff[attr][i] = [0];
- for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
- diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms;
- }
- }
- break;
- case "transform":
- var _ = element._,
- eq = equaliseTransform(_[attr], to[attr]);
- if (eq) {
- from[attr] = eq.from;
- to[attr] = eq.to;
- diff[attr] = [];
- diff[attr].real = true;
- for (i = 0, ii = from[attr].length; i < ii; i++) {
- diff[attr][i] = [from[attr][i][0]];
- for (j = 1, jj = from[attr][i].length; j < jj; j++) {
- diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
- }
- }
- } else {
- var m = (element.matrix || new Matrix),
- to2 = {
- _: {transform: _.transform},
- getBBox: function () {
- return element.getBBox(1);
- }
- };
- from[attr] = [
- m.a,
- m.b,
- m.c,
- m.d,
- m.e,
- m.f
- ];
- extractTransform(to2, to[attr]);
- to[attr] = to2._.transform;
- diff[attr] = [
- (to2.matrix.a - m.a) / ms,
- (to2.matrix.b - m.b) / ms,
- (to2.matrix.c - m.c) / ms,
- (to2.matrix.d - m.d) / ms,
- (to2.matrix.e - m.e) / ms,
- (to2.matrix.e - m.f) / ms
- ];
- // from[attr] = [_.sx, _.sy, _.deg, _.dx, _.dy];
- // var to2 = {_:{}, getBBox: function () { return element.getBBox(); }};
- // extractTransform(to2, to[attr]);
- // diff[attr] = [
- // (to2._.sx - _.sx) / ms,
- // (to2._.sy - _.sy) / ms,
- // (to2._.deg - _.deg) / ms,
- // (to2._.dx - _.dx) / ms,
- // (to2._.dy - _.dy) / ms
- // ];
- }
- break;
- case "csv":
- var values = Str(params[attr])[split](separator),
- from2 = Str(from[attr])[split](separator);
- if (attr == "clip-rect") {
- from[attr] = from2;
- diff[attr] = [];
- i = from2.length;
- while (i--) {
- diff[attr][i] = (values[i] - from[attr][i]) / ms;
- }
- }
- to[attr] = values;
- break;
- default:
- values = [][concat](params[attr]);
- from2 = [][concat](from[attr]);
- diff[attr] = [];
- i = element.paper.customAttributes[attr].length;
- while (i--) {
- diff[attr][i] = ((values[i] || 0) - (from2[i] || 0)) / ms;
- }
- break;
- }
- }
- }
- var easing = params.easing,
- easyeasy = R.easing_formulas[easing];
- if (!easyeasy) {
- easyeasy = Str(easing).match(bezierrg);
- if (easyeasy && easyeasy.length == 5) {
- var curve = easyeasy;
- easyeasy = function (t) {
- return CubicBezierAtTime(t, +curve[1], +curve[2], +curve[3], +curve[4], ms);
- };
- } else {
- easyeasy = pipe;
- }
- }
- timestamp = params.start || anim.start || +new Date;
- e = {
- anim: anim,
- percent: percent,
- timestamp: timestamp,
- start: timestamp + (anim.del || 0),
- status: 0,
- initstatus: status || 0,
- stop: false,
- ms: ms,
- easing: easyeasy,
- from: from,
- diff: diff,
- to: to,
- el: element,
- callback: params.callback,
- prev: prev,
- next: next,
- repeat: times || anim.times,
- origin: element.attr(),
- totalOrigin: totalOrigin
- };
- animationElements.push(e);
- if (status && !isInAnim && !isInAnimSet) {
- e.stop = true;
- e.start = new Date - ms * status;
- if (animationElements.length == 1) {
- return animation();
- }
- }
- if (isInAnimSet) {
- e.start = new Date - e.ms * status;
- }
- animationElements.length == 1 && requestAnimFrame(animation);
- } else {
- isInAnim.initstatus = status;
- isInAnim.start = new Date - isInAnim.ms * status;
- }
- eve("anim.start." + element.id, element, anim);
- }
-
- R.animation = function (params, ms, easing, callback) {
- if (params instanceof Animation) {
- return params;
- }
- if (R.is(easing, "function") || !easing) {
- callback = callback || easing || null;
- easing = null;
- }
- params = Object(params);
- ms = +ms || 0;
- var p = {},
- json,
- attr;
- for (attr in params) if (params[has](attr) && toFloat(attr) != attr && toFloat(attr) + "%" != attr) {
- json = true;
- p[attr] = params[attr];
- }
- if (!json) {
- return new Animation(params, ms);
- } else {
- easing && (p.easing = easing);
- callback && (p.callback = callback);
- return new Animation({100: p}, ms);
- }
- };
-
- elproto.animate = function (params, ms, easing, callback) {
- var element = this;
- if (element.removed) {
- callback && callback.call(element);
- return element;
- }
- var anim = params instanceof Animation ? params : R.animation(params, ms, easing, callback);
- runAnimation(anim, element, anim.percents[0], null, element.attr());
- return element;
- };
-
- elproto.setTime = function (anim, value) {
- if (anim && value != null) {
- this.status(anim, mmin(value, anim.ms) / anim.ms);
- }
- return this;
- };
-
- elproto.status = function (anim, value) {
- var out = [],
- i = 0,
- len,
- e;
- if (value != null) {
- runAnimation(anim, this, -1, mmin(value, 1));
- return this;
- } else {
- len = animationElements.length;
- for (; i < len; i++) {
- e = animationElements[i];
- if (e.el.id == this.id && (!anim || e.anim == anim)) {
- if (anim) {
- return e.status;
- }
- out.push({
- anim: e.anim,
- status: e.status
- });
- }
- }
- if (anim) {
- return 0;
- }
- return out;
- }
- };
-
- elproto.pause = function (anim) {
- for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
- if (eve("anim.pause." + this.id, this, animationElements[i].anim) !== false) {
- animationElements[i].paused = true;
- }
- }
- return this;
- };
-
- elproto.resume = function (anim) {
- for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
- var e = animationElements[i];
- if (eve("anim.resume." + this.id, this, e.anim) !== false) {
- delete e.paused;
- this.status(e.anim, e.status);
- }
- }
- return this;
- };
-
- elproto.stop = function (anim) {
- for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
- if (eve("anim.stop." + this.id, this, animationElements[i].anim) !== false) {
- animationElements.splice(i--, 1);
- }
- }
- return this;
- };
- elproto.toString = function () {
- return "Rapha\xebl\u2019s object";
- };
-
- // Set
- var Set = function (items) {
- this.items = [];
- this.length = 0;
- this.type = "set";
- if (items) {
- for (var i = 0, ii = items.length; i < ii; i++) {
- if (items[i] && (items[i].constructor == elproto.constructor || items[i].constructor == Set)) {
- this[this.items.length] = this.items[this.items.length] = items[i];
- this.length++;
- }
- }
- }
- },
- setproto = Set.prototype;
-
- setproto.push = function () {
- var item,
- len;
- for (var i = 0, ii = arguments.length; i < ii; i++) {
- item = arguments[i];
- if (item && (item.constructor == elproto.constructor || item.constructor == Set)) {
- len = this.items.length;
- this[len] = this.items[len] = item;
- this.length++;
- }
- }
- return this;
- };
-
- setproto.pop = function () {
- this.length && delete this[this.length--];
- return this.items.pop();
- };
-
- setproto.forEach = function (callback, thisArg) {
- for (var i = 0, ii = this.items.length; i < ii; i++) {
- if (callback.call(thisArg, this.items[i], i) === false) {
- return this;
- }
- }
- return this;
- };
- for (var method in elproto) if (elproto[has](method)) {
- setproto[method] = (function (methodname) {
- return function () {
- var arg = arguments;
- return this.forEach(function (el) {
- el[methodname][apply](el, arg);
- });
- };
- })(method);
- }
- setproto.attr = function (name, value) {
- if (name && R.is(name, array) && R.is(name[0], "object")) {
- for (var j = 0, jj = name.length; j < jj; j++) {
- this.items[j].attr(name[j]);
- }
- } else {
- for (var i = 0, ii = this.items.length; i < ii; i++) {
- this.items[i].attr(name, value);
- }
- }
- return this;
- };
-
- setproto.clear = function () {
- while (this.length) {
- this.pop();
- }
- };
-
- setproto.splice = function (index, count, insertion) {
- index = index < 0 ? mmax(this.length + index, 0) : index;
- count = mmax(0, mmin(this.length - index, count));
- var tail = [],
- todel = [],
- args = [],
- i;
- for (i = 2; i < arguments.length; i++) {
- args.push(arguments[i]);
- }
- for (i = 0; i < count; i++) {
- todel.push(this[index + i]);
- }
- for (; i < this.length - index; i++) {
- tail.push(this[index + i]);
- }
- var arglen = args.length;
- for (i = 0; i < arglen + tail.length; i++) {
- this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen];
- }
- i = this.items.length = this.length -= count - arglen;
- while (this[i]) {
- delete this[i++];
- }
- return new Set(todel);
- };
-
- setproto.exclude = function (el) {
- for (var i = 0, ii = this.length; i < ii; i++) if (this[i] == el) {
- this.splice(i, 1);
- return true;
- }
- };
- setproto.animate = function (params, ms, easing, callback) {
- (R.is(easing, "function") || !easing) && (callback = easing || null);
- var len = this.items.length,
- i = len,
- item,
- set = this,
- collector;
- if (!len) {
- return this;
- }
- callback && (collector = function () {
- !--len && callback.call(set);
- });
- easing = R.is(easing, string) ? easing : collector;
- var anim = R.animation(params, ms, easing, collector);
- item = this.items[--i].animate(anim);
- while (i--) {
- this.items[i] && !this.items[i].removed && this.items[i].animateWith(item, anim, anim);
- }
- return this;
- };
- setproto.insertAfter = function (el) {
- var i = this.items.length;
- while (i--) {
- this.items[i].insertAfter(el);
- }
- return this;
- };
- setproto.getBBox = function () {
- var x = [],
- y = [],
- w = [],
- h = [];
- for (var i = this.items.length; i--;) if (!this.items[i].removed) {
- var box = this.items[i].getBBox();
- x.push(box.x);
- y.push(box.y);
- w.push(box.x + box.width);
- h.push(box.y + box.height);
- }
- x = mmin[apply](0, x);
- y = mmin[apply](0, y);
- return {
- x: x,
- y: y,
- width: mmax[apply](0, w) - x,
- height: mmax[apply](0, h) - y
- };
- };
- setproto.clone = function (s) {
- s = new Set;
- for (var i = 0, ii = this.items.length; i < ii; i++) {
- s.push(this.items[i].clone());
- }
- return s;
- };
- setproto.toString = function () {
- return "Rapha\xebl\u2018s set";
- };
-
-
- R.registerFont = function (font) {
- if (!font.face) {
- return font;
- }
- this.fonts = this.fonts || {};
- var fontcopy = {
- w: font.w,
- face: {},
- glyphs: {}
- },
- family = font.face["font-family"];
- for (var prop in font.face) if (font.face[has](prop)) {
- fontcopy.face[prop] = font.face[prop];
- }
- if (this.fonts[family]) {
- this.fonts[family].push(fontcopy);
- } else {
- this.fonts[family] = [fontcopy];
- }
- if (!font.svg) {
- fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10);
- for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) {
- var path = font.glyphs[glyph];
- fontcopy.glyphs[glyph] = {
- w: path.w,
- k: {},
- d: path.d && "M" + path.d.replace(/[mlcxtrv]/g, function (command) {
- return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M";
- }) + "z"
- };
- if (path.k) {
- for (var k in path.k) if (path[has](k)) {
- fontcopy.glyphs[glyph].k[k] = path.k[k];
- }
- }
- }
- }
- return font;
- };
-
- paperproto.getFont = function (family, weight, style, stretch) {
- stretch = stretch || "normal";
- style = style || "normal";
- weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400;
- if (!R.fonts) {
- return;
- }
- var font = R.fonts[family];
- if (!font) {
- var name = new RegExp("(^|\\s)" + family.replace(/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i");
- for (var fontName in R.fonts) if (R.fonts[has](fontName)) {
- if (name.test(fontName)) {
- font = R.fonts[fontName];
- break;
- }
- }
- }
- var thefont;
- if (font) {
- for (var i = 0, ii = font.length; i < ii; i++) {
- thefont = font[i];
- if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) {
- break;
- }
- }
- }
- return thefont;
- };
-
- paperproto.print = function (x, y, string, font, size, origin, letter_spacing) {
- origin = origin || "middle"; // baseline|middle
- letter_spacing = mmax(mmin(letter_spacing || 0, 1), -1);
- var out = this.set(),
- letters = Str(string)[split](E),
- shift = 0,
- path = E,
- scale;
- R.is(font, string) && (font = this.getFont(font));
- if (font) {
- scale = (size || 16) / font.face["units-per-em"];
- var bb = font.face.bbox[split](separator),
- top = +bb[0],
- height = +bb[1] + (origin == "baseline" ? bb[3] - bb[1] + (+font.face.descent) : (bb[3] - bb[1]) / 2);
- for (var i = 0, ii = letters.length; i < ii; i++) {
- var prev = i && font.glyphs[letters[i - 1]] || {},
- curr = font.glyphs[letters[i]];
- shift += i ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) + (font.w * letter_spacing) : 0;
- curr && curr.d && out.push(this.path(curr.d).attr({
- fill: "#000",
- stroke: "none",
- transform: [["t", shift * scale, 0]]
- }));
- }
- out.transform(["...s", scale, scale, top, height, "t", (x - top) / scale, (y - height) / scale]);
- }
- return out;
- };
-
-
- paperproto.add = function (json) {
- if (R.is(json, "array")) {
- var res = this.set(),
- i = 0,
- ii = json.length,
- j;
- for (; i < ii; i++) {
- j = json[i] || {};
- elements[has](j.type) && res.push(this[j.type]().attr(j));
- }
- }
- return res;
- };
-
-
- R.format = function (token, params) {
- var args = R.is(params, array) ? [0][concat](params) : arguments;
- token && R.is(token, string) && args.length - 1 && (token = token.replace(formatrg, function (str, i) {
- return args[++i] == null ? E : args[i];
- }));
- return token || E;
- };
-
- R.fullfill = (function () {
- var tokenRegex = /\{([^\}]+)\}/g,
- objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties
- replacer = function (all, key, obj) {
- var res = obj;
- key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) {
- name = name || quotedName;
- if (res) {
- if (name in res) {
- res = res[name];
- }
- typeof res == "function" && isFunc && (res = res());
- }
- });
- res = (res == null || res == obj ? all : res) + "";
- return res;
- };
- return function (str, obj) {
- return String(str).replace(tokenRegex, function (all, key) {
- return replacer(all, key, obj);
- });
- };
- })();
-
- R.ninja = function () {
- oldRaphael.was ? (g.win.Raphael = oldRaphael.is) : delete Raphael;
- return R;
- };
-
- R.st = setproto;
- // Firefox <3.6 fix: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
- (function (doc, loaded, f) {
- if (doc.readyState == null && doc.addEventListener){
- doc.addEventListener(loaded, f = function () {
- doc.removeEventListener(loaded, f, false);
- doc.readyState = "complete";
- }, false);
- doc.readyState = "loading";
- }
- function isLoaded() {
- (/in/).test(doc.readyState) ? setTimeout(isLoaded, 9) : R.eve("DOMload");
- }
- isLoaded();
- })(document, "DOMContentLoaded");
-
- oldRaphael.was ? (g.win.Raphael = R) : (Raphael = R);
-
- eve.on("DOMload", function () {
- loaded = true;
- });
-})();
-
-
-// ┌─────────────────────────────────────────────────────────────────────┐ \\
-// │ Raphaël - JavaScript Vector Library │ \\
-// ├─────────────────────────────────────────────────────────────────────┤ \\
-// │ SVG Module │ \\
-// ├─────────────────────────────────────────────────────────────────────┤ \\
-// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com) │ \\
-// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com) │ \\
-// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
-// └─────────────────────────────────────────────────────────────────────┘ \\
-window.Raphael.svg && function (R) {
- var has = "hasOwnProperty",
- Str = String,
- toFloat = parseFloat,
- toInt = parseInt,
- math = Math,
- mmax = math.max,
- abs = math.abs,
- pow = math.pow,
- separator = /[, ]+/,
- eve = R.eve,
- E = "",
- S = " ";
- var xlink = "http://www.w3.org/1999/xlink",
- markers = {
- block: "M5,0 0,2.5 5,5z",
- classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z",
- diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z",
- open: "M6,1 1,3.5 6,6",
- oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"
- },
- markerCounter = {};
- R.toString = function () {
- return "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version;
- };
- var $ = function (el, attr) {
- if (attr) {
- if (typeof el == "string") {
- el = $(el);
- }
- for (var key in attr) if (attr[has](key)) {
- if (key.substring(0, 6) == "xlink:") {
- el.setAttributeNS(xlink, key.substring(6), Str(attr[key]));
- } else {
- el.setAttribute(key, Str(attr[key]));
- }
- }
- } else {
- el = R._g.doc.createElementNS("http://www.w3.org/2000/svg", el);
- el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)");
- }
- return el;
- },
- addGradientFill = function (element, gradient) {
- var type = "linear",
- id = element.id + gradient,
- fx = .5, fy = .5,
- o = element.node,
- SVG = element.paper,
- s = o.style,
- el = R._g.doc.getElementById(id);
- if (!el) {
- gradient = Str(gradient).replace(R._radial_gradient, function (all, _fx, _fy) {
- type = "radial";
- if (_fx && _fy) {
- fx = toFloat(_fx);
- fy = toFloat(_fy);
- var dir = ((fy > .5) * 2 - 1);
- pow(fx - .5, 2) + pow(fy - .5, 2) > .25 &&
- (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) &&
- fy != .5 &&
- (fy = fy.toFixed(5) - 1e-5 * dir);
- }
- return E;
- });
- gradient = gradient.split(/\s*\-\s*/);
- if (type == "linear") {
- var angle = gradient.shift();
- angle = -toFloat(angle);
- if (isNaN(angle)) {
- return null;
- }
- var vector = [0, 0, math.cos(R.rad(angle)), math.sin(R.rad(angle))],
- max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1);
- vector[2] *= max;
- vector[3] *= max;
- if (vector[2] < 0) {
- vector[0] = -vector[2];
- vector[2] = 0;
- }
- if (vector[3] < 0) {
- vector[1] = -vector[3];
- vector[3] = 0;
- }
- }
- var dots = R._parseDots(gradient);
- if (!dots) {
- return null;
- }
- id = id.replace(/[\(\)\s,\xb0#]/g, "_");
-
- if (element.gradient && id != element.gradient.id) {
- SVG.defs.removeChild(element.gradient);
- delete element.gradient;
- }
-
- if (!element.gradient) {
- el = $(type + "Gradient", {id: id});
- element.gradient = el;
- $(el, type == "radial" ? {
- fx: fx,
- fy: fy
- } : {
- x1: vector[0],
- y1: vector[1],
- x2: vector[2],
- y2: vector[3],
- gradientTransform: element.matrix.invert()
- });
- SVG.defs.appendChild(el);
- for (var i = 0, ii = dots.length; i < ii; i++) {
- el.appendChild($("stop", {
- offset: dots[i].offset ? dots[i].offset : i ? "100%" : "0%",
- "stop-color": dots[i].color || "#fff"
- }));
- }
- }
- }
- $(o, {
- fill: "url(#" + id + ")",
- opacity: 1,
- "fill-opacity": 1
- });
- s.fill = E;
- s.opacity = 1;
- s.fillOpacity = 1;
- return 1;
- },
- updatePosition = function (o) {
- var bbox = o.getBBox(1);
- $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"});
- },
- addArrow = function (o, value, isEnd) {
- if (o.type == "path") {
- var values = Str(value).toLowerCase().split("-"),
- p = o.paper,
- se = isEnd ? "end" : "start",
- node = o.node,
- attrs = o.attrs,
- stroke = attrs["stroke-width"],
- i = values.length,
- type = "classic",
- from,
- to,
- dx,
- refX,
- attr,
- w = 3,
- h = 3,
- t = 5;
- while (i--) {
- switch (values[i]) {
- case "block":
- case "classic":
- case "oval":
- case "diamond":
- case "open":
- case "none":
- type = values[i];
- break;
- case "wide": h = 5; break;
- case "narrow": h = 2; break;
- case "long": w = 5; break;
- case "short": w = 2; break;
- }
- }
- if (type == "open") {
- w += 2;
- h += 2;
- t += 2;
- dx = 1;
- refX = isEnd ? 4 : 1;
- attr = {
- fill: "none",
- stroke: attrs.stroke
- };
- } else {
- refX = dx = w / 2;
- attr = {
- fill: attrs.stroke,
- stroke: "none"
- };
- }
- if (o._.arrows) {
- if (isEnd) {
- o._.arrows.endPath && markerCounter[o._.arrows.endPath]--;
- o._.arrows.endMarker && markerCounter[o._.arrows.endMarker]--;
- } else {
- o._.arrows.startPath && markerCounter[o._.arrows.startPath]--;
- o._.arrows.startMarker && markerCounter[o._.arrows.startMarker]--;
- }
- } else {
- o._.arrows = {};
- }
- if (type != "none") {
- var pathId = "raphael-marker-" + type,
- markerId = "raphael-marker-" + se + type + w + h + o.id;
- if (!R._g.doc.getElementById(pathId)) {
- p.defs.appendChild($($("path"), {
- "stroke-linecap": "round",
- d: markers[type],
- id: pathId
- }));
- markerCounter[pathId] = 1;
- } else {
- markerCounter[pathId]++;
- }
- var marker = R._g.doc.getElementById(markerId),
- use;
- if (!marker) {
- marker = $($("marker"), {
- id: markerId,
- markerHeight: h,
- markerWidth: w,
- orient: "auto",
- refX: refX,
- refY: h / 2
- });
- use = $($("use"), {
- "xlink:href": "#" + pathId,
- transform: (isEnd ? "rotate(180 " + w / 2 + " " + h / 2 + ") " : E) + "scale(" + w / t + "," + h / t + ")",
- "stroke-width": (1 / ((w / t + h / t) / 2)).toFixed(4)
- });
- marker.appendChild(use);
- p.defs.appendChild(marker);
- markerCounter[markerId] = 1;
- } else {
- markerCounter[markerId]++;
- use = marker.getElementsByTagName("use")[0];
- }
- $(use, attr);
- var delta = dx * (type != "diamond" && type != "oval");
- if (isEnd) {
- from = o._.arrows.startdx * stroke || 0;
- to = R.getTotalLength(attrs.path) - delta * stroke;
- } else {
- from = delta * stroke;
- to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
- }
- attr = {};
- attr["marker-" + se] = "url(#" + markerId + ")";
- if (to || from) {
- attr.d = Raphael.getSubpath(attrs.path, from, to);
- }
- $(node, attr);
- o._.arrows[se + "Path"] = pathId;
- o._.arrows[se + "Marker"] = markerId;
- o._.arrows[se + "dx"] = delta;
- o._.arrows[se + "Type"] = type;
- o._.arrows[se + "String"] = value;
- } else {
- if (isEnd) {
- from = o._.arrows.startdx * stroke || 0;
- to = R.getTotalLength(attrs.path) - from;
- } else {
- from = 0;
- to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
- }
- o._.arrows[se + "Path"] && $(node, {d: Raphael.getSubpath(attrs.path, from, to)});
- delete o._.arrows[se + "Path"];
- delete o._.arrows[se + "Marker"];
- delete o._.arrows[se + "dx"];
- delete o._.arrows[se + "Type"];
- delete o._.arrows[se + "String"];
- }
- for (attr in markerCounter) if (markerCounter[has](attr) && !markerCounter[attr]) {
- var item = R._g.doc.getElementById(attr);
- item && item.parentNode.removeChild(item);
- }
- }
- },
- dasharray = {
- "": [0],
- "none": [0],
- "-": [3, 1],
- ".": [1, 1],
- "-.": [3, 1, 1, 1],
- "-..": [3, 1, 1, 1, 1, 1],
- ". ": [1, 3],
- "- ": [4, 3],
- "--": [8, 3],
- "- .": [4, 3, 1, 3],
- "--.": [8, 3, 1, 3],
- "--..": [8, 3, 1, 3, 1, 3]
- },
- addDashes = function (o, value, params) {
- value = dasharray[Str(value).toLowerCase()];
- if (value) {
- var width = o.attrs["stroke-width"] || "1",
- butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0,
- dashes = [],
- i = value.length;
- while (i--) {
- dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt;
- }
- $(o.node, {"stroke-dasharray": dashes.join(",")});
- }
- },
- setFillAndStroke = function (o, params) {
- var node = o.node,
- attrs = o.attrs,
- vis = node.style.visibility;
- node.style.visibility = "hidden";
- for (var att in params) {
- if (params[has](att)) {
- if (!R._availableAttrs[has](att)) {
- continue;
- }
- var value = params[att];
- attrs[att] = value;
- switch (att) {
- case "blur":
- o.blur(value);
- break;
- case "href":
- case "title":
- case "target":
- var pn = node.parentNode;
- if (pn.tagName.toLowerCase() != "a") {
- var hl = $("a");
- pn.insertBefore(hl, node);
- hl.appendChild(node);
- pn = hl;
- }
- if (att == "target") {
- pn.setAttributeNS(xlink, "show", value == "blank" ? "new" : value);
- } else {
- pn.setAttributeNS(xlink, att, value);
- }
- break;
- case "cursor":
- node.style.cursor = value;
- break;
- case "transform":
- o.transform(value);
- break;
- case "arrow-start":
- addArrow(o, value);
- break;
- case "arrow-end":
- addArrow(o, value, 1);
- break;
- case "clip-rect":
- var rect = Str(value).split(separator);
- if (rect.length == 4) {
- o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode);
- var el = $("clipPath"),
- rc = $("rect");
- el.id = R.createUUID();
- $(rc, {
- x: rect[0],
- y: rect[1],
- width: rect[2],
- height: rect[3]
- });
- el.appendChild(rc);
- o.paper.defs.appendChild(el);
- $(node, {"clip-path": "url(#" + el.id + ")"});
- o.clip = rc;
- }
- if (!value) {
- var path = node.getAttribute("clip-path");
- if (path) {
- var clip = R._g.doc.getElementById(path.replace(/(^url\(#|\)$)/g, E));
- clip && clip.parentNode.removeChild(clip);
- $(node, {"clip-path": E});
- delete o.clip;
- }
- }
- break;
- case "path":
- if (o.type == "path") {
- $(node, {d: value ? attrs.path = R._pathToAbsolute(value) : "M0,0"});
- o._.dirty = 1;
- if (o._.arrows) {
- "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
- "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
- }
- }
- break;
- case "width":
- node.setAttribute(att, value);
- o._.dirty = 1;
- if (attrs.fx) {
- att = "x";
- value = attrs.x;
- } else {
- break;
- }
- case "x":
- if (attrs.fx) {
- value = -attrs.x - (attrs.width || 0);
- }
- case "rx":
- if (att == "rx" && o.type == "rect") {
- break;
- }
- case "cx":
- node.setAttribute(att, value);
- o.pattern && updatePosition(o);
- o._.dirty = 1;
- break;
- case "height":
- node.setAttribute(att, value);
- o._.dirty = 1;
- if (attrs.fy) {
- att = "y";
- value = attrs.y;
- } else {
- break;
- }
- case "y":
- if (attrs.fy) {
- value = -attrs.y - (attrs.height || 0);
- }
- case "ry":
- if (att == "ry" && o.type == "rect") {
- break;
- }
- case "cy":
- node.setAttribute(att, value);
- o.pattern && updatePosition(o);
- o._.dirty = 1;
- break;
- case "r":
- if (o.type == "rect") {
- $(node, {rx: value, ry: value});
- } else {
- node.setAttribute(att, value);
- }
- o._.dirty = 1;
- break;
- case "src":
- if (o.type == "image") {
- node.setAttributeNS(xlink, "href", value);
- }
- break;
- case "stroke-width":
- if (o._.sx != 1 || o._.sy != 1) {
- value /= mmax(abs(o._.sx), abs(o._.sy)) || 1;
- }
- if (o.paper._vbSize) {
- value *= o.paper._vbSize;
- }
- node.setAttribute(att, value);
- if (attrs["stroke-dasharray"]) {
- addDashes(o, attrs["stroke-dasharray"], params);
- }
- if (o._.arrows) {
- "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
- "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
- }
- break;
- case "stroke-dasharray":
- addDashes(o, value, params);
- break;
- case "fill":
- var isURL = Str(value).match(R._ISURL);
- if (isURL) {
- el = $("pattern");
- var ig = $("image");
- el.id = R.createUUID();
- $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1});
- $(ig, {x: 0, y: 0, "xlink:href": isURL[1]});
- el.appendChild(ig);
-
- (function (el) {
- R._preload(isURL[1], function () {
- var w = this.offsetWidth,
- h = this.offsetHeight;
- $(el, {width: w, height: h});
- $(ig, {width: w, height: h});
- o.paper.safari();
- });
- })(el);
- o.paper.defs.appendChild(el);
- $(node, {fill: "url(#" + el.id + ")"});
- o.pattern = el;
- o.pattern && updatePosition(o);
- break;
- }
- var clr = R.getRGB(value);
- if (!clr.error) {
- delete params.gradient;
- delete attrs.gradient;
- !R.is(attrs.opacity, "undefined") &&
- R.is(params.opacity, "undefined") &&
- $(node, {opacity: attrs.opacity});
- !R.is(attrs["fill-opacity"], "undefined") &&
- R.is(params["fill-opacity"], "undefined") &&
- $(node, {"fill-opacity": attrs["fill-opacity"]});
- } else if ((o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value)) {
- if ("opacity" in attrs || "fill-opacity" in attrs) {
- var gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
- if (gradient) {
- var stops = gradient.getElementsByTagName("stop");
- $(stops[stops.length - 1], {"stop-opacity": ("opacity" in attrs ? attrs.opacity : 1) * ("fill-opacity" in attrs ? attrs["fill-opacity"] : 1)});
- }
- }
- attrs.gradient = value;
- attrs.fill = "none";
- break;
- }
- clr[has]("opacity") && $(node, {"fill-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
- case "stroke":
- clr = R.getRGB(value);
- node.setAttribute(att, clr.hex);
- att == "stroke" && clr[has]("opacity") && $(node, {"stroke-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
- if (att == "stroke" && o._.arrows) {
- "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
- "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
- }
- break;
- case "gradient":
- (o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value);
- break;
- case "opacity":
- if (attrs.gradient && !attrs[has]("stroke-opacity")) {
- $(node, {"stroke-opacity": value > 1 ? value / 100 : value});
- }
- // fall
- case "fill-opacity":
- if (attrs.gradient) {
- gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
- if (gradient) {
- stops = gradient.getElementsByTagName("stop");
- $(stops[stops.length - 1], {"stop-opacity": value});
- }
- break;
- }
- default:
- att == "font-size" && (value = toInt(value, 10) + "px");
- var cssrule = att.replace(/(\-.)/g, function (w) {
- return w.substring(1).toUpperCase();
- });
- node.style[cssrule] = value;
- o._.dirty = 1;
- node.setAttribute(att, value);
- break;
- }
- }
- }
-
- tuneText(o, params);
- node.style.visibility = vis;
- },
- leading = 1.2,
- tuneText = function (el, params) {
- if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) {
- return;
- }
- var a = el.attrs,
- node = el.node,
- fontSize = node.firstChild ? toInt(R._g.doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10;
-
- if (params[has]("text")) {
- a.text = params.text;
- while (node.firstChild) {
- node.removeChild(node.firstChild);
- }
- var texts = Str(params.text).split("\n"),
- tspans = [],
- tspan;
- for (var i = 0, ii = texts.length; i < ii; i++) {
- tspan = $("tspan");
- i && $(tspan, {dy: fontSize * leading, x: a.x});
- tspan.appendChild(R._g.doc.createTextNode(texts[i]));
- node.appendChild(tspan);
- tspans[i] = tspan;
- }
- } else {
- tspans = node.getElementsByTagName("tspan");
- for (i = 0, ii = tspans.length; i < ii; i++) if (i) {
- $(tspans[i], {dy: fontSize * leading, x: a.x});
- } else {
- $(tspans[0], {dy: 0});
- }
- }
- $(node, {x: a.x, y: a.y});
- el._.dirty = 1;
- var bb = el._getBBox(),
- dif = a.y - (bb.y + bb.height / 2);
- dif && R.is(dif, "finite") && $(tspans[0], {dy: dif});
- },
- Element = function (node, svg) {
- var X = 0,
- Y = 0;
-
- this[0] = this.node = node;
-
- node.raphael = true;
-
- this.id = R._oid++;
- node.raphaelid = this.id;
- this.matrix = R.matrix();
- this.realPath = null;
-
- this.paper = svg;
- this.attrs = this.attrs || {};
- this._ = {
- transform: [],
- sx: 1,
- sy: 1,
- deg: 0,
- dx: 0,
- dy: 0,
- dirty: 1
- };
- !svg.bottom && (svg.bottom = this);
-
- this.prev = svg.top;
- svg.top && (svg.top.next = this);
- svg.top = this;
-
- this.next = null;
- },
- elproto = R.el;
-
- Element.prototype = elproto;
- elproto.constructor = Element;
-
- R._engine.path = function (pathString, SVG) {
- var el = $("path");
- SVG.canvas && SVG.canvas.appendChild(el);
- var p = new Element(el, SVG);
- p.type = "path";
- setFillAndStroke(p, {
- fill: "none",
- stroke: "#000",
- path: pathString
- });
- return p;
- };
-
- elproto.rotate = function (deg, cx, cy) {
- if (this.removed) {
- return this;
- }
- deg = Str(deg).split(separator);
- if (deg.length - 1) {
- cx = toFloat(deg[1]);
- cy = toFloat(deg[2]);
- }
- deg = toFloat(deg[0]);
- (cy == null) && (cx = cy);
- if (cx == null || cy == null) {
- var bbox = this.getBBox(1);
- cx = bbox.x + bbox.width / 2;
- cy = bbox.y + bbox.height / 2;
- }
- this.transform(this._.transform.concat([["r", deg, cx, cy]]));
- return this;
- };
-
- elproto.scale = function (sx, sy, cx, cy) {
- if (this.removed) {
- return this;
- }
- sx = Str(sx).split(separator);
- if (sx.length - 1) {
- sy = toFloat(sx[1]);
- cx = toFloat(sx[2]);
- cy = toFloat(sx[3]);
- }
- sx = toFloat(sx[0]);
- (sy == null) && (sy = sx);
- (cy == null) && (cx = cy);
- if (cx == null || cy == null) {
- var bbox = this.getBBox(1);
- }
- cx = cx == null ? bbox.x + bbox.width / 2 : cx;
- cy = cy == null ? bbox.y + bbox.height / 2 : cy;
- this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
- return this;
- };
-
- elproto.translate = function (dx, dy) {
- if (this.removed) {
- return this;
- }
- dx = Str(dx).split(separator);
- if (dx.length - 1) {
- dy = toFloat(dx[1]);
- }
- dx = toFloat(dx[0]) || 0;
- dy = +dy || 0;
- this.transform(this._.transform.concat([["t", dx, dy]]));
- return this;
- };
-
- elproto.transform = function (tstr) {
- var _ = this._;
- if (tstr == null) {
- return _.transform;
- }
- R._extractTransform(this, tstr);
-
- this.clip && $(this.clip, {transform: this.matrix.invert()});
- this.pattern && updatePosition(this);
- this.node && $(this.node, {transform: this.matrix});
-
- if (_.sx != 1 || _.sy != 1) {
- var sw = this.attrs[has]("stroke-width") ? this.attrs["stroke-width"] : 1;
- this.attr({"stroke-width": sw});
- }
-
- return this;
- };
-
- elproto.hide = function () {
- !this.removed && this.paper.safari(this.node.style.display = "none");
- return this;
- };
-
- elproto.show = function () {
- !this.removed && this.paper.safari(this.node.style.display = "");
- return this;
- };
-
- elproto.remove = function () {
- if (this.removed) {
- return;
- }
- var paper = this.paper;
- paper.__set__ && paper.__set__.exclude(this);
- eve.unbind("*.*." + this.id);
- if (this.gradient) {
- paper.defs.removeChild(this.gradient);
- }
- R._tear(this, paper);
- if (this.node.parentNode.tagName.toLowerCase() == "a") {
- this.node.parentNode.parentNode.removeChild(this.node.parentNode);
- } else {
- this.node.parentNode.removeChild(this.node);
- }
- for (var i in this) {
- this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
- }
- this.removed = true;
- };
- elproto._getBBox = function () {
- if (this.node.style.display == "none") {
- this.show();
- var hide = true;
- }
- var bbox = {};
- try {
- bbox = this.node.getBBox();
- } catch(e) {
- // Firefox 3.0.x plays badly here
- } finally {
- bbox = bbox || {};
- }
- hide && this.hide();
- return bbox;
- };
-
- elproto.attr = function (name, value) {
- if (this.removed) {
- return this;
- }
- if (name == null) {
- var res = {};
- for (var a in this.attrs) if (this.attrs[has](a)) {
- res[a] = this.attrs[a];
- }
- res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
- res.transform = this._.transform;
- return res;
- }
- if (value == null && R.is(name, "string")) {
- if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) {
- return this.attrs.gradient;
- }
- if (name == "transform") {
- return this._.transform;
- }
- var names = name.split(separator),
- out = {};
- for (var i = 0, ii = names.length; i < ii; i++) {
- name = names[i];
- if (name in this.attrs) {
- out[name] = this.attrs[name];
- } else if (R.is(this.paper.customAttributes[name], "function")) {
- out[name] = this.paper.customAttributes[name].def;
- } else {
- out[name] = R._availableAttrs[name];
- }
- }
- return ii - 1 ? out : out[names[0]];
- }
- if (value == null && R.is(name, "array")) {
- out = {};
- for (i = 0, ii = name.length; i < ii; i++) {
- out[name[i]] = this.attr(name[i]);
- }
- return out;
- }
- if (value != null) {
- var params = {};
- params[name] = value;
- } else if (name != null && R.is(name, "object")) {
- params = name;
- }
- for (var key in params) {
- eve("attr." + key + "." + this.id, this, params[key]);
- }
- for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
- var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
- this.attrs[key] = params[key];
- for (var subkey in par) if (par[has](subkey)) {
- params[subkey] = par[subkey];
- }
- }
- setFillAndStroke(this, params);
- return this;
- };
-
- elproto.toFront = function () {
- if (this.removed) {
- return this;
- }
- if (this.node.parentNode.tagName.toLowerCase() == "a") {
- this.node.parentNode.parentNode.appendChild(this.node.parentNode);
- } else {
- this.node.parentNode.appendChild(this.node);
- }
- var svg = this.paper;
- svg.top != this && R._tofront(this, svg);
- return this;
- };
-
- elproto.toBack = function () {
- if (this.removed) {
- return this;
- }
- var parent = this.node.parentNode;
- if (parent.tagName.toLowerCase() == "a") {
- parent.parentNode.insertBefore(this.node.parentNode, this.node.parentNode.parentNode.firstChild);
- } else if (parent.firstChild != this.node) {
- parent.insertBefore(this.node, this.node.parentNode.firstChild);
- }
- R._toback(this, this.paper);
- var svg = this.paper;
- return this;
- };
-
- elproto.insertAfter = function (element) {
- if (this.removed) {
- return this;
- }
- var node = element.node || element[element.length - 1].node;
- if (node.nextSibling) {
- node.parentNode.insertBefore(this.node, node.nextSibling);
- } else {
- node.parentNode.appendChild(this.node);
- }
- R._insertafter(this, element, this.paper);
- return this;
- };
-
- elproto.insertBefore = function (element) {
- if (this.removed) {
- return this;
- }
- var node = element.node || element[0].node;
- node.parentNode.insertBefore(this.node, node);
- R._insertbefore(this, element, this.paper);
- return this;
- };
- elproto.blur = function (size) {
- // Experimental. No Safari support. Use it on your own risk.
- var t = this;
- if (+size !== 0) {
- var fltr = $("filter"),
- blur = $("feGaussianBlur");
- t.attrs.blur = size;
- fltr.id = R.createUUID();
- $(blur, {stdDeviation: +size || 1.5});
- fltr.appendChild(blur);
- t.paper.defs.appendChild(fltr);
- t._blur = fltr;
- $(t.node, {filter: "url(#" + fltr.id + ")"});
- } else {
- if (t._blur) {
- t._blur.parentNode.removeChild(t._blur);
- delete t._blur;
- delete t.attrs.blur;
- }
- t.node.removeAttribute("filter");
- }
- };
- R._engine.circle = function (svg, x, y, r) {
- var el = $("circle");
- svg.canvas && svg.canvas.appendChild(el);
- var res = new Element(el, svg);
- res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"};
- res.type = "circle";
- $(el, res.attrs);
- return res;
- };
- R._engine.rect = function (svg, x, y, w, h, r) {
- var el = $("rect");
- svg.canvas && svg.canvas.appendChild(el);
- var res = new Element(el, svg);
- res.attrs = {x: x, y: y, width: w, height: h, r: r || 0, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"};
- res.type = "rect";
- $(el, res.attrs);
- return res;
- };
- R._engine.ellipse = function (svg, x, y, rx, ry) {
- var el = $("ellipse");
- svg.canvas && svg.canvas.appendChild(el);
- var res = new Element(el, svg);
- res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"};
- res.type = "ellipse";
- $(el, res.attrs);
- return res;
- };
- R._engine.image = function (svg, src, x, y, w, h) {
- var el = $("image");
- $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"});
- el.setAttributeNS(xlink, "href", src);
- svg.canvas && svg.canvas.appendChild(el);
- var res = new Element(el, svg);
- res.attrs = {x: x, y: y, width: w, height: h, src: src};
- res.type = "image";
- return res;
- };
- R._engine.text = function (svg, x, y, text) {
- var el = $("text");
- svg.canvas && svg.canvas.appendChild(el);
- var res = new Element(el, svg);
- res.attrs = {
- x: x,
- y: y,
- "text-anchor": "middle",
- text: text,
- font: R._availableAttrs.font,
- stroke: "none",
- fill: "#000"
- };
- res.type = "text";
- setFillAndStroke(res, res.attrs);
- return res;
- };
- R._engine.setSize = function (width, height) {
- this.width = width || this.width;
- this.height = height || this.height;
- this.canvas.setAttribute("width", this.width);
- this.canvas.setAttribute("height", this.height);
- if (this._viewBox) {
- this.setViewBox.apply(this, this._viewBox);
- }
- return this;
- };
- R._engine.create = function () {
- var con = R._getContainer.apply(0, arguments),
- container = con && con.container,
- x = con.x,
- y = con.y,
- width = con.width,
- height = con.height;
- if (!container) {
- throw new Error("SVG container not found.");
- }
- var cnvs = $("svg"),
- css = "overflow:hidden;",
- isFloating;
- x = x || 0;
- y = y || 0;
- width = width || 512;
- height = height || 342;
- $(cnvs, {
- height: height,
- version: 1.1,
- width: width,
- xmlns: "http://www.w3.org/2000/svg"
- });
- if (container == 1) {
- cnvs.style.cssText = css + "position:absolute;left:" + x + "px;top:" + y + "px";
- R._g.doc.body.appendChild(cnvs);
- isFloating = 1;
- } else {
- cnvs.style.cssText = css + "position:relative";
- if (container.firstChild) {
- container.insertBefore(cnvs, container.firstChild);
- } else {
- container.appendChild(cnvs);
- }
- }
- container = new R._Paper;
- container.width = width;
- container.height = height;
- container.canvas = cnvs;
- container.clear();
- container._left = container._top = 0;
- isFloating && (container.renderfix = function () {});
- container.renderfix();
- return container;
- };
- R._engine.setViewBox = function (x, y, w, h, fit) {
- eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]);
- var size = mmax(w / this.width, h / this.height),
- top = this.top,
- aspectRatio = fit ? "meet" : "xMinYMin",
- vb,
- sw;
- if (x == null) {
- if (this._vbSize) {
- size = 1;
- }
- delete this._vbSize;
- vb = "0 0 " + this.width + S + this.height;
- } else {
- this._vbSize = size;
- vb = x + S + y + S + w + S + h;
- }
- $(this.canvas, {
- viewBox: vb,
- preserveAspectRatio: aspectRatio
- });
- while (size && top) {
- sw = "stroke-width" in top.attrs ? top.attrs["stroke-width"] : 1;
- top.attr({"stroke-width": sw});
- top._.dirty = 1;
- top._.dirtyT = 1;
- top = top.prev;
- }
- this._viewBox = [x, y, w, h, !!fit];
- return this;
- };
-
- R.prototype.renderfix = function () {
- var cnvs = this.canvas,
- s = cnvs.style,
- pos;
- try {
- pos = cnvs.getScreenCTM() || cnvs.createSVGMatrix();
- } catch (e) {
- pos = cnvs.createSVGMatrix();
- }
- var left = -pos.e % 1,
- top = -pos.f % 1;
- if (left || top) {
- if (left) {
- this._left = (this._left + left) % 1;
- s.left = this._left + "px";
- }
- if (top) {
- this._top = (this._top + top) % 1;
- s.top = this._top + "px";
- }
- }
- };
-
- R.prototype.clear = function () {
- R.eve("clear", this);
- var c = this.canvas;
- while (c.firstChild) {
- c.removeChild(c.firstChild);
- }
- this.bottom = this.top = null;
- (this.desc = $("desc")).appendChild(R._g.doc.createTextNode("Created with Rapha\xebl " + R.version));
- c.appendChild(this.desc);
- c.appendChild(this.defs = $("defs"));
- };
-
- R.prototype.remove = function () {
- eve("remove", this);
- this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
- for (var i in this) {
- this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
- }
- };
- var setproto = R.st;
- for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
- setproto[method] = (function (methodname) {
- return function () {
- var arg = arguments;
- return this.forEach(function (el) {
- el[methodname].apply(el, arg);
- });
- };
- })(method);
- }
-}(window.Raphael);
-
-// ┌─────────────────────────────────────────────────────────────────────┐ \\
-// │ Raphaël - JavaScript Vector Library │ \\
-// ├─────────────────────────────────────────────────────────────────────┤ \\
-// │ VML Module │ \\
-// ├─────────────────────────────────────────────────────────────────────┤ \\
-// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com) │ \\
-// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com) │ \\
-// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
-// └─────────────────────────────────────────────────────────────────────┘ \\
-window.Raphael.vml && function (R) {
- var has = "hasOwnProperty",
- Str = String,
- toFloat = parseFloat,
- math = Math,
- round = math.round,
- mmax = math.max,
- mmin = math.min,
- abs = math.abs,
- fillString = "fill",
- separator = /[, ]+/,
- eve = R.eve,
- ms = " progid:DXImageTransform.Microsoft",
- S = " ",
- E = "",
- map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},
- bites = /([clmz]),?([^clmz]*)/gi,
- blurregexp = / progid:\S+Blur\([^\)]+\)/g,
- val = /-?[^,\s-]+/g,
- cssDot = "position:absolute;left:0;top:0;width:1px;height:1px",
- zoom = 21600,
- pathTypes = {path: 1, rect: 1, image: 1},
- ovalTypes = {circle: 1, ellipse: 1},
- path2vml = function (path) {
- var total = /[ahqstv]/ig,
- command = R._pathToAbsolute;
- Str(path).match(total) && (command = R._path2curve);
- total = /[clmz]/g;
- if (command == R._pathToAbsolute && !Str(path).match(total)) {
- var res = Str(path).replace(bites, function (all, command, args) {
- var vals = [],
- isMove = command.toLowerCase() == "m",
- res = map[command];
- args.replace(val, function (value) {
- if (isMove && vals.length == 2) {
- res += vals + map[command == "m" ? "l" : "L"];
- vals = [];
- }
- vals.push(round(value * zoom));
- });
- return res + vals;
- });
- return res;
- }
- var pa = command(path), p, r;
- res = [];
- for (var i = 0, ii = pa.length; i < ii; i++) {
- p = pa[i];
- r = pa[i][0].toLowerCase();
- r == "z" && (r = "x");
- for (var j = 1, jj = p.length; j < jj; j++) {
- r += round(p[j] * zoom) + (j != jj - 1 ? "," : E);
- }
- res.push(r);
- }
- return res.join(S);
- },
- compensation = function (deg, dx, dy) {
- var m = R.matrix();
- m.rotate(-deg, .5, .5);
- return {
- dx: m.x(dx, dy),
- dy: m.y(dx, dy)
- };
- },
- setCoords = function (p, sx, sy, dx, dy, deg) {
- var _ = p._,
- m = p.matrix,
- fillpos = _.fillpos,
- o = p.node,
- s = o.style,
- y = 1,
- flip = "",
- dxdy,
- kx = zoom / sx,
- ky = zoom / sy;
- s.visibility = "hidden";
- if (!sx || !sy) {
- return;
- }
- o.coordsize = abs(kx) + S + abs(ky);
- s.rotation = deg * (sx * sy < 0 ? -1 : 1);
- if (deg) {
- var c = compensation(deg, dx, dy);
- dx = c.dx;
- dy = c.dy;
- }
- sx < 0 && (flip += "x");
- sy < 0 && (flip += " y") && (y = -1);
- s.flip = flip;
- o.coordorigin = (dx * -kx) + S + (dy * -ky);
- if (fillpos || _.fillsize) {
- var fill = o.getElementsByTagName(fillString);
- fill = fill && fill[0];
- o.removeChild(fill);
- if (fillpos) {
- c = compensation(deg, m.x(fillpos[0], fillpos[1]), m.y(fillpos[0], fillpos[1]));
- fill.position = c.dx * y + S + c.dy * y;
- }
- if (_.fillsize) {
- fill.size = _.fillsize[0] * abs(sx) + S + _.fillsize[1] * abs(sy);
- }
- o.appendChild(fill);
- }
- s.visibility = "visible";
- };
- R.toString = function () {
- return "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version;
- };
- var addArrow = function (o, value, isEnd) {
- var values = Str(value).toLowerCase().split("-"),
- se = isEnd ? "end" : "start",
- i = values.length,
- type = "classic",
- w = "medium",
- h = "medium";
- while (i--) {
- switch (values[i]) {
- case "block":
- case "classic":
- case "oval":
- case "diamond":
- case "open":
- case "none":
- type = values[i];
- break;
- case "wide":
- case "narrow": h = values[i]; break;
- case "long":
- case "short": w = values[i]; break;
- }
- }
- var stroke = o.node.getElementsByTagName("stroke")[0];
- stroke[se + "arrow"] = type;
- stroke[se + "arrowlength"] = w;
- stroke[se + "arrowwidth"] = h;
- },
- setFillAndStroke = function (o, params) {
- // o.paper.canvas.style.display = "none";
- o.attrs = o.attrs || {};
- var node = o.node,
- a = o.attrs,
- s = node.style,
- xy,
- newpath = pathTypes[o.type] && (params.x != a.x || params.y != a.y || params.width != a.width || params.height != a.height || params.cx != a.cx || params.cy != a.cy || params.rx != a.rx || params.ry != a.ry || params.r != a.r),
- isOval = ovalTypes[o.type] && (a.cx != params.cx || a.cy != params.cy || a.r != params.r || a.rx != params.rx || a.ry != params.ry),
- res = o;
-
-
- for (var par in params) if (params[has](par)) {
- a[par] = params[par];
- }
- if (newpath) {
- a.path = R._getPath[o.type](o);
- o._.dirty = 1;
- }
- params.href && (node.href = params.href);
- params.title && (node.title = params.title);
- params.target && (node.target = params.target);
- params.cursor && (s.cursor = params.cursor);
- "blur" in params && o.blur(params.blur);
- if (params.path && o.type == "path" || newpath) {
- node.path = path2vml(~Str(a.path).toLowerCase().indexOf("r") ? R._pathToAbsolute(a.path) : a.path);
- if (o.type == "image") {
- o._.fillpos = [a.x, a.y];
- o._.fillsize = [a.width, a.height];
- setCoords(o, 1, 1, 0, 0, 0);
- }
- }
- "transform" in params && o.transform(params.transform);
- if (isOval) {
- var cx = +a.cx,
- cy = +a.cy,
- rx = +a.rx || +a.r || 0,
- ry = +a.ry || +a.r || 0;
- node.path = R.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x", round((cx - rx) * zoom), round((cy - ry) * zoom), round((cx + rx) * zoom), round((cy + ry) * zoom), round(cx * zoom));
- }
- if ("clip-rect" in params) {
- var rect = Str(params["clip-rect"]).split(separator);
- if (rect.length == 4) {
- rect[2] = +rect[2] + (+rect[0]);
- rect[3] = +rect[3] + (+rect[1]);
- var div = node.clipRect || R._g.doc.createElement("div"),
- dstyle = div.style;
- dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect);
- if (!node.clipRect) {
- dstyle.position = "absolute";
- dstyle.top = 0;
- dstyle.left = 0;
- dstyle.width = o.paper.width + "px";
- dstyle.height = o.paper.height + "px";
- node.parentNode.insertBefore(div, node);
- div.appendChild(node);
- node.clipRect = div;
- }
- }
- if (!params["clip-rect"]) {
- node.clipRect && (node.clipRect.style.clip = "auto");
- }
- }
- if (o.textpath) {
- var textpathStyle = o.textpath.style;
- params.font && (textpathStyle.font = params.font);
- params["font-family"] && (textpathStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g, E) + '"');
- params["font-size"] && (textpathStyle.fontSize = params["font-size"]);
- params["font-weight"] && (textpathStyle.fontWeight = params["font-weight"]);
- params["font-style"] && (textpathStyle.fontStyle = params["font-style"]);
- }
- if ("arrow-start" in params) {
- addArrow(res, params["arrow-start"]);
- }
- if ("arrow-end" in params) {
- addArrow(res, params["arrow-end"], 1);
- }
- if (params.opacity != null ||
- params["stroke-width"] != null ||
- params.fill != null ||
- params.src != null ||
- params.stroke != null ||
- params["stroke-width"] != null ||
- params["stroke-opacity"] != null ||
- params["fill-opacity"] != null ||
- params["stroke-dasharray"] != null ||
- params["stroke-miterlimit"] != null ||
- params["stroke-linejoin"] != null ||
- params["stroke-linecap"] != null) {
- var fill = node.getElementsByTagName(fillString),
- newfill = false;
- fill = fill && fill[0];
- !fill && (newfill = fill = createNode(fillString));
- if (o.type == "image" && params.src) {
- fill.src = params.src;
- }
- params.fill && (fill.on = true);
- if (fill.on == null || params.fill == "none" || params.fill === null) {
- fill.on = false;
- }
- if (fill.on && params.fill) {
- var isURL = Str(params.fill).match(R._ISURL);
- if (isURL) {
- fill.parentNode == node && node.removeChild(fill);
- fill.rotate = true;
- fill.src = isURL[1];
- fill.type = "tile";
- var bbox = o.getBBox(1);
- fill.position = bbox.x + S + bbox.y;
- o._.fillpos = [bbox.x, bbox.y];
-
- R._preload(isURL[1], function () {
- o._.fillsize = [this.offsetWidth, this.offsetHeight];
- });
- } else {
- fill.color = R.getRGB(params.fill).hex;
- fill.src = E;
- fill.type = "solid";
- if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill, fill)) {
- a.fill = "none";
- a.gradient = params.fill;
- fill.rotate = false;
- }
- }
- }
- if ("fill-opacity" in params || "opacity" in params) {
- var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+R.getRGB(params.fill).o + 1 || 2) - 1);
- opacity = mmin(mmax(opacity, 0), 1);
- fill.opacity = opacity;
- if (fill.src) {
- fill.color = "none";
- }
- }
- node.appendChild(fill);
- var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]),
- newstroke = false;
- !stroke && (newstroke = stroke = createNode("stroke"));
- if ((params.stroke && params.stroke != "none") ||
- params["stroke-width"] ||
- params["stroke-opacity"] != null ||
- params["stroke-dasharray"] ||
- params["stroke-miterlimit"] ||
- params["stroke-linejoin"] ||
- params["stroke-linecap"]) {
- stroke.on = true;
- }
- (params.stroke == "none" || params.stroke === null || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false);
- var strokeColor = R.getRGB(params.stroke);
- stroke.on && params.stroke && (stroke.color = strokeColor.hex);
- opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.o + 1 || 2) - 1);
- var width = (toFloat(params["stroke-width"]) || 1) * .75;
- opacity = mmin(mmax(opacity, 0), 1);
- params["stroke-width"] == null && (width = a["stroke-width"]);
- params["stroke-width"] && (stroke.weight = width);
- width && width < 1 && (opacity *= width) && (stroke.weight = 1);
- stroke.opacity = opacity;
-
- params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
- stroke.miterlimit = params["stroke-miterlimit"] || 8;
- params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round");
- if (params["stroke-dasharray"]) {
- var dasharray = {
- "-": "shortdash",
- ".": "shortdot",
- "-.": "shortdashdot",
- "-..": "shortdashdotdot",
- ". ": "dot",
- "- ": "dash",
- "--": "longdash",
- "- .": "dashdot",
- "--.": "longdashdot",
- "--..": "longdashdotdot"
- };
- stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E;
- }
- newstroke && node.appendChild(stroke);
- }
- if (res.type == "text") {
- res.paper.canvas.style.display = E;
- var span = res.paper.span,
- m = 100,
- fontSize = a.font && a.font.match(/\d+(?:\.\d*)?(?=px)/);
- s = span.style;
- a.font && (s.font = a.font);
- a["font-family"] && (s.fontFamily = a["font-family"]);
- a["font-weight"] && (s.fontWeight = a["font-weight"]);
- a["font-style"] && (s.fontStyle = a["font-style"]);
- fontSize = toFloat(a["font-size"] || fontSize && fontSize[0]) || 10;
- s.fontSize = fontSize * m + "px";
- res.textpath.string && (span.innerHTML = Str(res.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br>"));
- var brect = span.getBoundingClientRect();
- res.W = a.w = (brect.right - brect.left) / m;
- res.H = a.h = (brect.bottom - brect.top) / m;
- // res.paper.canvas.style.display = "none";
- res.X = a.x;
- res.Y = a.y + res.H / 2;
-
- ("x" in params || "y" in params) && (res.path.v = R.format("m{0},{1}l{2},{1}", round(a.x * zoom), round(a.y * zoom), round(a.x * zoom) + 1));
- var dirtyattrs = ["x", "y", "text", "font", "font-family", "font-weight", "font-style", "font-size"];
- for (var d = 0, dd = dirtyattrs.length; d < dd; d++) if (dirtyattrs[d] in params) {
- res._.dirty = 1;
- break;
- }
-
- // text-anchor emulation
- switch (a["text-anchor"]) {
- case "start":
- res.textpath.style["v-text-align"] = "left";
- res.bbx = res.W / 2;
- break;
- case "end":
- res.textpath.style["v-text-align"] = "right";
- res.bbx = -res.W / 2;
- break;
- default:
- res.textpath.style["v-text-align"] = "center";
- res.bbx = 0;
- break;
- }
- res.textpath.style["v-text-kern"] = true;
- }
- // res.paper.canvas.style.display = E;
- },
- addGradientFill = function (o, gradient, fill) {
- o.attrs = o.attrs || {};
- var attrs = o.attrs,
- pow = Math.pow,
- opacity,
- oindex,
- type = "linear",
- fxfy = ".5 .5";
- o.attrs.gradient = gradient;
- gradient = Str(gradient).replace(R._radial_gradient, function (all, fx, fy) {
- type = "radial";
- if (fx && fy) {
- fx = toFloat(fx);
- fy = toFloat(fy);
- pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5);
- fxfy = fx + S + fy;
- }
- return E;
- });
- gradient = gradient.split(/\s*\-\s*/);
- if (type == "linear") {
- var angle = gradient.shift();
- angle = -toFloat(angle);
- if (isNaN(angle)) {
- return null;
- }
- }
- var dots = R._parseDots(gradient);
- if (!dots) {
- return null;
- }
- o = o.shape || o.node;
- if (dots.length) {
- o.removeChild(fill);
- fill.on = true;
- fill.method = "none";
- fill.color = dots[0].color;
- fill.color2 = dots[dots.length - 1].color;
- var clrs = [];
- for (var i = 0, ii = dots.length; i < ii; i++) {
- dots[i].offset && clrs.push(dots[i].offset + S + dots[i].color);
- }
- fill.colors = clrs.length ? clrs.join() : "0% " + fill.color;
- if (type == "radial") {
- fill.type = "gradientTitle";
- fill.focus = "100%";
- fill.focussize = "0 0";
- fill.focusposition = fxfy;
- fill.angle = 0;
- } else {
- // fill.rotate= true;
- fill.type = "gradient";
- fill.angle = (270 - angle) % 360;
- }
- o.appendChild(fill);
- }
- return 1;
- },
- Element = function (node, vml) {
- this[0] = this.node = node;
- node.raphael = true;
- this.id = R._oid++;
- node.raphaelid = this.id;
- this.X = 0;
- this.Y = 0;
- this.attrs = {};
- this.paper = vml;
- this.matrix = R.matrix();
- this._ = {
- transform: [],
- sx: 1,
- sy: 1,
- dx: 0,
- dy: 0,
- deg: 0,
- dirty: 1,
- dirtyT: 1
- };
- !vml.bottom && (vml.bottom = this);
- this.prev = vml.top;
- vml.top && (vml.top.next = this);
- vml.top = this;
- this.next = null;
- };
- var elproto = R.el;
-
- Element.prototype = elproto;
- elproto.constructor = Element;
- elproto.transform = function (tstr) {
- if (tstr == null) {
- return this._.transform;
- }
- var vbs = this.paper._viewBoxShift,
- vbt = vbs ? "s" + [vbs.scale, vbs.scale] + "-1-1t" + [vbs.dx, vbs.dy] : E,
- oldt;
- if (vbs) {
- oldt = tstr = Str(tstr).replace(/\.{3}|\u2026/g, this._.transform || E);
- }
- R._extractTransform(this, vbt + tstr);
- var matrix = this.matrix.clone(),
- skew = this.skew,
- o = this.node,
- split,
- isGrad = ~Str(this.attrs.fill).indexOf("-"),
- isPatt = !Str(this.attrs.fill).indexOf("url(");
- matrix.translate(-.5, -.5);
- if (isPatt || isGrad || this.type == "image") {
- skew.matrix = "1 0 0 1";
- skew.offset = "0 0";
- split = matrix.split();
- if ((isGrad && split.noRotation) || !split.isSimple) {
- o.style.filter = matrix.toFilter();
- var bb = this.getBBox(),
- bbt = this.getBBox(1),
- dx = bb.x - bbt.x,
- dy = bb.y - bbt.y;
- o.coordorigin = (dx * -zoom) + S + (dy * -zoom);
- setCoords(this, 1, 1, dx, dy, 0);
- } else {
- o.style.filter = E;
- setCoords(this, split.scalex, split.scaley, split.dx, split.dy, split.rotate);
- }
- } else {
- o.style.filter = E;
- skew.matrix = Str(matrix);
- skew.offset = matrix.offset();
- }
- oldt && (this._.transform = oldt);
- return this;
- };
- elproto.rotate = function (deg, cx, cy) {
- if (this.removed) {
- return this;
- }
- if (deg == null) {
- return;
- }
- deg = Str(deg).split(separator);
- if (deg.length - 1) {
- cx = toFloat(deg[1]);
- cy = toFloat(deg[2]);
- }
- deg = toFloat(deg[0]);
- (cy == null) && (cx = cy);
- if (cx == null || cy == null) {
- var bbox = this.getBBox(1);
- cx = bbox.x + bbox.width / 2;
- cy = bbox.y + bbox.height / 2;
- }
- this._.dirtyT = 1;
- this.transform(this._.transform.concat([["r", deg, cx, cy]]));
- return this;
- };
- elproto.translate = function (dx, dy) {
- if (this.removed) {
- return this;
- }
- dx = Str(dx).split(separator);
- if (dx.length - 1) {
- dy = toFloat(dx[1]);
- }
- dx = toFloat(dx[0]) || 0;
- dy = +dy || 0;
- if (this._.bbox) {
- this._.bbox.x += dx;
- this._.bbox.y += dy;
- }
- this.transform(this._.transform.concat([["t", dx, dy]]));
- return this;
- };
- elproto.scale = function (sx, sy, cx, cy) {
- if (this.removed) {
- return this;
- }
- sx = Str(sx).split(separator);
- if (sx.length - 1) {
- sy = toFloat(sx[1]);
- cx = toFloat(sx[2]);
- cy = toFloat(sx[3]);
- isNaN(cx) && (cx = null);
- isNaN(cy) && (cy = null);
- }
- sx = toFloat(sx[0]);
- (sy == null) && (sy = sx);
- (cy == null) && (cx = cy);
- if (cx == null || cy == null) {
- var bbox = this.getBBox(1);
- }
- cx = cx == null ? bbox.x + bbox.width / 2 : cx;
- cy = cy == null ? bbox.y + bbox.height / 2 : cy;
-
- this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
- this._.dirtyT = 1;
- return this;
- };
- elproto.hide = function () {
- !this.removed && (this.node.style.display = "none");
- return this;
- };
- elproto.show = function () {
- !this.removed && (this.node.style.display = E);
- return this;
- };
- elproto._getBBox = function () {
- if (this.removed) {
- return {};
- }
- return {
- x: this.X + (this.bbx || 0) - this.W / 2,
- y: this.Y - this.H,
- width: this.W,
- height: this.H
- };
- };
- elproto.remove = function () {
- if (this.removed) {
- return;
- }
- this.paper.__set__ && this.paper.__set__.exclude(this);
- R.eve.unbind("*.*." + this.id);
- R._tear(this, this.paper);
- this.node.parentNode.removeChild(this.node);
- this.shape && this.shape.parentNode.removeChild(this.shape);
- for (var i in this) {
- this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
- }
- this.removed = true;
- };
- elproto.attr = function (name, value) {
- if (this.removed) {
- return this;
- }
- if (name == null) {
- var res = {};
- for (var a in this.attrs) if (this.attrs[has](a)) {
- res[a] = this.attrs[a];
- }
- res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
- res.transform = this._.transform;
- return res;
- }
- if (value == null && R.is(name, "string")) {
- if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
- return this.attrs.gradient;
- }
- var names = name.split(separator),
- out = {};
- for (var i = 0, ii = names.length; i < ii; i++) {
- name = names[i];
- if (name in this.attrs) {
- out[name] = this.attrs[name];
- } else if (R.is(this.paper.customAttributes[name], "function")) {
- out[name] = this.paper.customAttributes[name].def;
- } else {
- out[name] = R._availableAttrs[name];
- }
- }
- return ii - 1 ? out : out[names[0]];
- }
- if (this.attrs && value == null && R.is(name, "array")) {
- out = {};
- for (i = 0, ii = name.length; i < ii; i++) {
- out[name[i]] = this.attr(name[i]);
- }
- return out;
- }
- var params;
- if (value != null) {
- params = {};
- params[name] = value;
- }
- value == null && R.is(name, "object") && (params = name);
- for (var key in params) {
- eve("attr." + key + "." + this.id, this, params[key]);
- }
- if (params) {
- for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
- var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
- this.attrs[key] = params[key];
- for (var subkey in par) if (par[has](subkey)) {
- params[subkey] = par[subkey];
- }
- }
- // this.paper.canvas.style.display = "none";
- if (params.text && this.type == "text") {
- this.textpath.string = params.text;
- }
- setFillAndStroke(this, params);
- // this.paper.canvas.style.display = E;
- }
- return this;
- };
- elproto.toFront = function () {
- !this.removed && this.node.parentNode.appendChild(this.node);
- this.paper && this.paper.top != this && R._tofront(this, this.paper);
- return this;
- };
- elproto.toBack = function () {
- if (this.removed) {
- return this;
- }
- if (this.node.parentNode.firstChild != this.node) {
- this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
- R._toback(this, this.paper);
- }
- return this;
- };
- elproto.insertAfter = function (element) {
- if (this.removed) {
- return this;
- }
- if (element.constructor == R.st.constructor) {
- element = element[element.length - 1];
- }
- if (element.node.nextSibling) {
- element.node.parentNode.insertBefore(this.node, element.node.nextSibling);
- } else {
- element.node.parentNode.appendChild(this.node);
- }
- R._insertafter(this, element, this.paper);
- return this;
- };
- elproto.insertBefore = function (element) {
- if (this.removed) {
- return this;
- }
- if (element.constructor == R.st.constructor) {
- element = element[0];
- }
- element.node.parentNode.insertBefore(this.node, element.node);
- R._insertbefore(this, element, this.paper);
- return this;
- };
- elproto.blur = function (size) {
- var s = this.node.runtimeStyle,
- f = s.filter;
- f = f.replace(blurregexp, E);
- if (+size !== 0) {
- this.attrs.blur = size;
- s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")";
- s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5));
- } else {
- s.filter = f;
- s.margin = 0;
- delete this.attrs.blur;
- }
- };
-
- R._engine.path = function (pathString, vml) {
- var el = createNode("shape");
- el.style.cssText = cssDot;
- el.coordsize = zoom + S + zoom;
- el.coordorigin = vml.coordorigin;
- var p = new Element(el, vml),
- attr = {fill: "none", stroke: "#000"};
- pathString && (attr.path = pathString);
- p.type = "path";
- p.path = [];
- p.Path = E;
- setFillAndStroke(p, attr);
- vml.canvas.appendChild(el);
- var skew = createNode("skew");
- skew.on = true;
- el.appendChild(skew);
- p.skew = skew;
- p.transform(E);
- return p;
- };
- R._engine.rect = function (vml, x, y, w, h, r) {
- var path = R._rectPath(x, y, w, h, r),
- res = vml.path(path),
- a = res.attrs;
- res.X = a.x = x;
- res.Y = a.y = y;
- res.W = a.width = w;
- res.H = a.height = h;
- a.r = r;
- a.path = path;
- res.type = "rect";
- return res;
- };
- R._engine.ellipse = function (vml, x, y, rx, ry) {
- var res = vml.path(),
- a = res.attrs;
- res.X = x - rx;
- res.Y = y - ry;
- res.W = rx * 2;
- res.H = ry * 2;
- res.type = "ellipse";
- setFillAndStroke(res, {
- cx: x,
- cy: y,
- rx: rx,
- ry: ry
- });
- return res;
- };
- R._engine.circle = function (vml, x, y, r) {
- var res = vml.path(),
- a = res.attrs;
- res.X = x - r;
- res.Y = y - r;
- res.W = res.H = r * 2;
- res.type = "circle";
- setFillAndStroke(res, {
- cx: x,
- cy: y,
- r: r
- });
- return res;
- };
- R._engine.image = function (vml, src, x, y, w, h) {
- var path = R._rectPath(x, y, w, h),
- res = vml.path(path).attr({stroke: "none"}),
- a = res.attrs,
- node = res.node,
- fill = node.getElementsByTagName(fillString)[0];
- a.src = src;
- res.X = a.x = x;
- res.Y = a.y = y;
- res.W = a.width = w;
- res.H = a.height = h;
- a.path = path;
- res.type = "image";
- fill.parentNode == node && node.removeChild(fill);
- fill.rotate = true;
- fill.src = src;
- fill.type = "tile";
- res._.fillpos = [x, y];
- res._.fillsize = [w, h];
- node.appendChild(fill);
- setCoords(res, 1, 1, 0, 0, 0);
- return res;
- };
- R._engine.text = function (vml, x, y, text) {
- var el = createNode("shape"),
- path = createNode("path"),
- o = createNode("textpath");
- x = x || 0;
- y = y || 0;
- text = text || "";
- path.v = R.format("m{0},{1}l{2},{1}", round(x * zoom), round(y * zoom), round(x * zoom) + 1);
- path.textpathok = true;
- o.string = Str(text);
- o.on = true;
- el.style.cssText = cssDot;
- el.coordsize = zoom + S + zoom;
- el.coordorigin = "0 0";
- var p = new Element(el, vml),
- attr = {
- fill: "#000",
- stroke: "none",
- font: R._availableAttrs.font,
- text: text
- };
- p.shape = el;
- p.path = path;
- p.textpath = o;
- p.type = "text";
- p.attrs.text = Str(text);
- p.attrs.x = x;
- p.attrs.y = y;
- p.attrs.w = 1;
- p.attrs.h = 1;
- setFillAndStroke(p, attr);
- el.appendChild(o);
- el.appendChild(path);
- vml.canvas.appendChild(el);
- var skew = createNode("skew");
- skew.on = true;
- el.appendChild(skew);
- p.skew = skew;
- p.transform(E);
- return p;
- };
- R._engine.setSize = function (width, height) {
- var cs = this.canvas.style;
- this.width = width;
- this.height = height;
- width == +width && (width += "px");
- height == +height && (height += "px");
- cs.width = width;
- cs.height = height;
- cs.clip = "rect(0 " + width + " " + height + " 0)";
- if (this._viewBox) {
- R._engine.setViewBox.apply(this, this._viewBox);
- }
- return this;
- };
- R._engine.setViewBox = function (x, y, w, h, fit) {
- R.eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]);
- var width = this.width,
- height = this.height,
- size = 1 / mmax(w / width, h / height),
- H, W;
- if (fit) {
- H = height / h;
- W = width / w;
- if (w * H < width) {
- x -= (width - w * H) / 2 / H;
- }
- if (h * W < height) {
- y -= (height - h * W) / 2 / W;
- }
- }
- this._viewBox = [x, y, w, h, !!fit];
- this._viewBoxShift = {
- dx: -x,
- dy: -y,
- scale: size
- };
- this.forEach(function (el) {
- el.transform("...");
- });
- return this;
- };
- var createNode;
- R._engine.initWin = function (win) {
- var doc = win.document;
- doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
- try {
- !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
- createNode = function (tagName) {
- return doc.createElement('<rvml:' + tagName + ' class="rvml">');
- };
- } catch (e) {
- createNode = function (tagName) {
- return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
- };
- }
- };
- R._engine.initWin(R._g.win);
- R._engine.create = function () {
- var con = R._getContainer.apply(0, arguments),
- container = con.container,
- height = con.height,
- s,
- width = con.width,
- x = con.x,
- y = con.y;
- if (!container) {
- throw new Error("VML container not found.");
- }
- var res = new R._Paper,
- c = res.canvas = R._g.doc.createElement("div"),
- cs = c.style;
- x = x || 0;
- y = y || 0;
- width = width || 512;
- height = height || 342;
- res.width = width;
- res.height = height;
- width == +width && (width += "px");
- height == +height && (height += "px");
- res.coordsize = zoom * 1e3 + S + zoom * 1e3;
- res.coordorigin = "0 0";
- res.span = R._g.doc.createElement("span");
- res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;";
- c.appendChild(res.span);
- cs.cssText = R.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", width, height);
- if (container == 1) {
- R._g.doc.body.appendChild(c);
- cs.left = x + "px";
- cs.top = y + "px";
- cs.position = "absolute";
- } else {
- if (container.firstChild) {
- container.insertBefore(c, container.firstChild);
- } else {
- container.appendChild(c);
- }
- }
- // plugins.call(res, res, R.fn);
- res.renderfix = function () {};
- return res;
- };
- R.prototype.clear = function () {
- R.eve("clear", this);
- this.canvas.innerHTML = E;
- this.span = R._g.doc.createElement("span");
- this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
- this.canvas.appendChild(this.span);
- this.bottom = this.top = null;
- };
- R.prototype.remove = function () {
- R.eve("remove", this);
- this.canvas.parentNode.removeChild(this.canvas);
- for (var i in this) {
- this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
- }
- return true;
- };
-
- var setproto = R.st;
- for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
- setproto[method] = (function (methodname) {
- return function () {
- var arg = arguments;
- return this.forEach(function (el) {
- el[methodname].apply(el, arg);
- });
- };
- })(method);
- }
-}(window.Raphael);
diff --git a/addons/web_diagram/static/src/js/diagram_controller.js b/addons/web_diagram/static/src/js/diagram_controller.js
deleted file mode 100644
index 35c4b1bbf4e..00000000000
--- a/addons/web_diagram/static/src/js/diagram_controller.js
+++ /dev/null
@@ -1,209 +0,0 @@
-odoo.define('web_diagram.DiagramController', function (require) {
-"use strict";
-
-var AbstractController = require('web.AbstractController');
-var core = require('web.core');
-var Dialog = require('web.Dialog');
-var view_dialogs = require('web.view_dialogs');
-
-var _t = core._t;
-var QWeb = core.qweb;
-var FormViewDialog = view_dialogs.FormViewDialog;
-
-/**
- * Diagram Controller
- */
-var DiagramController = AbstractController.extend({
- className: 'o_diagram_view',
- custom_events: {
- add_edge: '_onAddEdge',
- edit_edge: '_onEditEdge',
- edit_node: '_onEditNode',
- remove_edge: '_onRemoveEdge',
- remove_node: '_onRemoveNode',
- },
- /**
- * @override
- * @param {Widget} parent
- * @param {DiagramModel} model
- * @param {DiagramRenderer} renderer
- * @param {Object} params
- */
- init: function (parent, model, renderer, params) {
- this._super.apply(this, arguments);
- this.domain = params.domain || [];
- this.context = params.context;
- this.ids = params.ids;
- this.currentId = params.currentId;
- },
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * Render the buttons according to the DiagramView.buttons template and add
- * listeners on it. Set this.$buttons with the produced jQuery element
- *
- * @param {jQuery} [$node] a jQuery node where the rendered buttons should
- * be inserted $node may be undefined, in which case they are inserted
- * into this.options.$buttons
- */
- renderButtons: function ($node) {
- this.$buttons = $(QWeb.render("DiagramView.buttons", {widget: this}));
- this.$buttons.on('click', '.o_diagram_new_button', this._addNode.bind(this));
- this.$buttons.appendTo($node);
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * Creates a popup to add a node to the diagram
- *
- * @private
- */
- _addNode: function () {
- var state = this.model.get();
- var pop = new FormViewDialog(this, {
- res_model: state.node_model,
- domain: this.domain,
- context: this.context,
- title: _.str.sprintf("%s %s", _t("Create:"), _t('Activity')),
- disable_multiple_selection: true,
- on_saved: this.reload.bind(this, {}),
- }).open();
-
- // manually trigger a 'field_changed' on the dialog's form_view to set
- // the default value of the parent_id field
- pop.opened().then(function () {
- var changes = {};
- changes[state.parent_field] = {
- id: state.res_id
- };
- pop.form_view.trigger_up('field_changed', {
- dataPointID: pop.form_view.handle,
- changes: changes,
- });
- });
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * Custom event handler that opens a popup to add an edge from given source
- * and dest nodes.
- *
- * @private
- * @param {OdooEvent} event
- */
- _onAddEdge: function (event) {
- var self = this;
- var state = this.model.get();
- var pop = new FormViewDialog(self, {
- res_model: state.connector_model,
- domain: this.domain,
- context: this.context,
- title: _.str.sprintf("%s %s", _t("Create:"), _t('Transition')),
- disable_multiple_selection: true,
- }).open();
-
- // manually trigger a 'field_changed' on the dialog's form_view to set
- // the default source and destination values
- pop.opened().then(function () {
- var changes = {};
- changes[state.connectors.attrs.source] = {
- id: event.data.source_id
- };
- changes[state.connectors.attrs.destination] = {
- id: event.data.dest_id
- };
- pop.form_view.trigger_up('field_changed', {
- dataPointID: pop.form_view.handle,
- changes: changes,
- });
- });
- pop.on('closed', this, this.reload.bind(this, {}));
- },
- /**
- * Custom event handler that opens a popup to edit an edge given its id
- *
- * @private
- * @param {OdooEvent} event
- */
- _onEditEdge: function (event) {
- var state = this.model.get();
- new FormViewDialog(this, {
- res_model: state.connector_model,
- res_id: parseInt(event.data.id, 10),
- context: this.context,
- title: _.str.sprintf("%s %s", _t("Open:"), _t('Transition')),
- on_saved: this.reload.bind(this, {}),
- }).open();
- },
- /**
- * Custom event handler that opens a popup to edit the content of a node
- * given its id
- *
- * @private
- * @param {OdooEvent} event
- */
- _onEditNode: function (event) {
- var state = this.model.get();
- new FormViewDialog(this, {
- res_model: state.node_model,
- res_id: event.data.id,
- context: this.context,
- title: _.str.sprintf("%s %s", _t("Open:"), _t('Activity')),
- on_saved: this.reload.bind(this, {}),
- }).open();
- },
- /**
- * Custom event handler that removes an edge given its id
- *
- * @private
- * @param {OdooEvent} event
- */
- _onRemoveEdge: function (event) {
- var self = this;
- Dialog.confirm(this, (_t("Are you sure you want to remove this transition?")), {
- confirm_callback: function () {
- var state = self.model.get();
- self._rpc({
- model: state.connector_model,
- method: 'unlink',
- args: [event.data.id],
- })
- .then(self.reload.bind(self, {}));
- },
- });
- },
- /**
- * Custom event handler that removes a node given its id
- *
- * @private
- * @param {OdooEvent} event
- */
- _onRemoveNode: function (event) {
- var self = this;
- var msg = _t("Are you sure you want to remove this node ? This will remove its connected transitions as well.");
- Dialog.confirm(this, (msg), {
- confirm_callback: function () {
- var state = self.model.get();
- self._rpc({
- model: state.node_model,
- method: 'unlink',
- args: [event.data.id],
- })
- .then(self.reload.bind(self, {}));
- },
- });
- },
-});
-
-return DiagramController;
-
-});
diff --git a/addons/web_diagram/static/src/js/diagram_model.js b/addons/web_diagram/static/src/js/diagram_model.js
deleted file mode 100644
index 5d83de0d699..00000000000
--- a/addons/web_diagram/static/src/js/diagram_model.js
+++ /dev/null
@@ -1,92 +0,0 @@
-odoo.define('web_diagram.DiagramModel', function (require) {
-"use strict";
-
-var AbstractModel = require('web.AbstractModel');
-
-/**
- * DiagramModel
- */
-var DiagramModel = AbstractModel.extend({
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * @override
- * @returns {Object}
- */
- get: function () {
- return $.extend(true, {}, {
- labels: this.labels,
- nodes: this.datanodes,
- edges: this.edges,
- node_model: this.node_model,
- parent_field: this.parent_field,
- res_id: this.res_id,
- connector_model: this.connector_model,
- connectors: this.connectors,
- });
- },
- /**
- * @override
- * @param {Object} params
- * @returns {Promise}
- */
- load: function (params) {
- this.modelName = params.modelName;
- this.res_id = params.res_id;
- this.node_model = params.node_model;
- this.connector_model = params.connector_model;
- this.connectors = params.connectors;
- this.nodes = params.nodes;
- this.visible_nodes = params.visible_nodes;
- this.invisible_nodes = params.invisible_nodes;
- this.node_fields_string = params.node_fields_string;
- this.connector_fields_string = params.connector_fields_string;
- this.labels = params.labels;
-
- return this._fetchDiagramInfo();
- },
- reload: function () {
- return this._fetchDiagramInfo();
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @private
- * @param {any} record
- * @returns {Promise}
- */
- _fetchDiagramInfo: function () {
- var self = this;
- return this._rpc({
- route: '/web_diagram/diagram/get_diagram_info',
- params: {
- id: this.res_id,
- model: this.modelName,
- node: this.node_model,
- connector: this.connector_model,
- src_node: this.connectors.attrs.source,
- des_node: this.connectors.attrs.destination,
- label: this.connectors.attrs.label || false,
- bgcolor: this.nodes.attrs.bgcolor,
- shape: this.nodes.attrs.shape,
- visible_nodes: this.visible_nodes,
- invisible_nodes: this.invisible_nodes,
- node_fields_string: this.node_fields_string,
- connector_fields_string: this.connector_fields_string,
- },
- })
- .then(function (data) {
- self.datanodes = data.nodes;
- self.edges = data.conn;
- self.parent_field = data.parent_field;
- });
- },
-});
-
-return DiagramModel;
-});
diff --git a/addons/web_diagram/static/src/js/diagram_renderer.js b/addons/web_diagram/static/src/js/diagram_renderer.js
deleted file mode 100644
index 02539009790..00000000000
--- a/addons/web_diagram/static/src/js/diagram_renderer.js
+++ /dev/null
@@ -1,143 +0,0 @@
-odoo.define('web_diagram.DiagramRenderer', function (require) {
-"use strict";
-
-var AbstractRenderer = require('web.AbstractRenderer');
-
-/**
- * Diagram Renderer
- *
- * The diagram renderer responsability is to render a diagram view, that is, a
- * set of (labelled) nodes and edges. To do that, it uses the Raphael.js
- * library.
- */
-var DiagramRenderer = AbstractRenderer.extend({
- template: 'DiagramView',
- /**
- * @override
- * @returns {Promise}
- */
- start: function () {
- var $header = this.$el.filter('.o_diagram_header');
- _.each(this.state.labels, function (label) {
- $header.append($('<span>').html(label));
- });
- this.$diagram_container = this.$el.filter('.o_diagram');
-
- return this._super.apply(this, arguments);
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @override
- * @returns {Promise}
- */
- _render: function () {
- var self = this;
- var nodes = this.state.nodes;
- var edges = this.state.edges;
- var id_to_node = {};
- var style = {
- edge_color: "#A0A0A0",
- edge_label_color: "#555",
- edge_label_font_size: 10,
- edge_width: 2,
- edge_spacing: 100,
- edge_loop_radius: 100,
-
- node_label_color: "#333",
- node_label_font_size: 12,
- node_outline_color: "#333",
- node_outline_width: 1,
- node_selected_color: "#0097BE",
- node_selected_width: 2,
- node_size_x: 110,
- node_size_y: 80,
- connector_active_color: "#FFF",
- connector_radius: 4,
-
- close_button_radius: 8,
- close_button_color: "#333",
- close_button_x_color: "#FFF",
-
- gray: "#DCDCDC",
- white: "#FFF",
-
- viewport_margin: 50
- };
-
- // remove previous diagram
- this.$diagram_container.empty();
-
- // for the node and edge's label to be correctly positioned, the diagram
- // must be rendered directly in the DOM, so we render it in a fake
- // element appended in the body, and then move it to this widget's $el
- var $div = $('<div>')
- .css({position: 'absolute', top: -10000, right: -10000})
- .appendTo($('body'));
- var r = new Raphael($div[0], '100%','100%');
- var graph = new CuteGraph(r, style, this.$diagram_container[0]);
- _.each(nodes, function (node) {
- var n = new CuteNode(
- graph,
- node.x + 50, // FIXME the +50 should be in the layout algorithm
- node.y + 50,
- CuteGraph.wordwrap(node.name, 14),
- node.shape === 'rectangle' ? 'rect' : 'circle',
- node.color === 'white' ? style.white : style.gray);
-
- n.id = node.id;
- id_to_node[node.id] = n;
- });
- _.each(edges, function (edge) {
- var e = new CuteEdge(
- graph,
- CuteGraph.wordwrap(edge.signal, 32),
- id_to_node[edge.s_id],
- id_to_node[edge.d_id] || id_to_node[edge.s_id]); // WORKAROUND
- e.id = edge.id;
- });
-
- // move the renderered diagram to the widget's $el
- $div.contents().appendTo(this.$diagram_container);
- $div.remove();
-
- CuteNode.double_click_callback = function (cutenode) {
- self.trigger_up('edit_node', {id: cutenode.id});
- };
- CuteNode.destruction_callback = function (cutenode) {
- self.trigger_up('remove_node', {id: cutenode.id});
- // return a rejected promise to prevent the library from removing
- // the node directly,as the diagram will be redrawn once the node is
- // deleted
- return Promise.reject();
- };
- CuteEdge.double_click_callback = function (cuteedge) {
- self.trigger_up('edit_edge', {id: cuteedge.id});
- };
-
- CuteEdge.creation_callback = function (node_start, node_end) {
- return {label: ''};
- };
- CuteEdge.new_edge_callback = function (cuteedge) {
- self.trigger_up('add_edge', {
- source_id: cuteedge.get_start().id,
- dest_id: cuteedge.get_end().id,
- });
- };
- CuteEdge.destruction_callback = function (cuteedge) {
- self.trigger_up('remove_edge', {id: cuteedge.id});
- // return a rejected promise to prevent the library from removing
- // the edge directly, as the diagram will be redrawn once the edge
- // is deleted
- return Promise.reject();
- };
- return this._super.apply(this, arguments);
- },
-});
-
-return DiagramRenderer;
-
-});
diff --git a/addons/web_diagram/static/src/js/diagram_view.js b/addons/web_diagram/static/src/js/diagram_view.js
deleted file mode 100644
index f95a0b69564..00000000000
--- a/addons/web_diagram/static/src/js/diagram_view.js
+++ /dev/null
@@ -1,123 +0,0 @@
-odoo.define('web_diagram.DiagramView', function (require) {
-"use strict";
-
-var BasicView = require('web.BasicView');
-var core = require('web.core');
-var DiagramModel = require('web_diagram.DiagramModel');
-var DiagramRenderer = require('web_diagram.DiagramRenderer');
-var DiagramController = require('web_diagram.DiagramController');
-
-var _lt = core._lt;
-
-/**
- * Diagram View
- */
-var DiagramView = BasicView.extend({
- display_name: _lt('Diagram'),
- icon: 'fa-code-fork',
- multi_record: false,
- withSearchBar: false,
- searchMenuTypes: [],
- jsLibs: [[
- '/web_diagram/static/lib/js/jquery.mousewheel.js',
- '/web_diagram/static/lib/js/raphael.js',
- ]],
- config: _.extend({}, BasicView.prototype.config, {
- Model: DiagramModel,
- Renderer: DiagramRenderer,
- Controller: DiagramController,
- }),
- viewType: 'diagram',
-
- /**
- * @override
- * @param {Object} viewInfo
- * @param {Object} params
- */
- init: function (viewInfo, params) {
- this._super.apply(this, arguments);
- var self = this;
- var arch = this.arch;
- // Compute additional data for diagram model
- function toTitleCase(str) {
- return str.replace(/\w\S*/g, function (txt) {
- return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
- });
- }
-
- var nodes = arch.children[0];
- var connectors = arch.children[1];
- var node_model = nodes.attrs.object;
- var connector_model = connectors.attrs.object;
- var labels = _.map(_.where(arch.children, {tag: 'label'}), function (label) {
- return label.attrs.string;
- });
-
- var invisible_nodes = [];
- var visible_nodes = [];
- var node_fields_string = [];
- _.each(nodes.children, function (child) {
- if (child.attrs.invisible === '1')
- invisible_nodes.push(child.attrs.name);
- else {
- var fieldString = self.fields[child.attrs.name].string || toTitleCase(child.attrs.name);
- visible_nodes.push(child.attrs.name);
- node_fields_string.push(fieldString);
- }
- });
-
- var connector_fields_string = _.map(connectors.children, function (conn) {
- return self.fields[conn.attrs.name].string || toTitleCase(conn.attrs.name);
- });
-
- this.loadParams = _.extend({}, this.loadParams, {
- currentId: params.currentId,
- nodes: nodes,
- labels: labels,
- invisible_nodes: invisible_nodes,
- visible_nodes: visible_nodes,
- node_fields_string: node_fields_string,
- node_model: node_model,
- connectors: connectors,
- connector_model: connector_model,
- connector_fields_string: connector_fields_string,
- });
-
- this.controllerParams = _.extend({}, this.controllerParams, {
- domain: params.domain,
- context: params.context,
- ids: params.ids,
- currentId: params.currentId,
- });
- },
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * This override is quite tricky: the graph renderer uses Raphael.js to
- * render itself, so it needs it to be loaded in the window before rendering
- * However, the raphael.js library is built in such a way that if it detects
- * that a module system is present, it will try to use it. So, in that
- * case, it is not available on window.Raphael. This means that the diagram
- * view is then broken.
- *
- * As a workaround, we simply remove and restore the define function, if
- * present, while we are loading Raphael.
- *
- * @override
- */
- getController: function () {
- var oldDefine = window.define;
- delete window.define;
- return this._super.apply(this, arguments).then(function (view) {
- window.define = oldDefine;
- return view;
- });
- },
-});
-
-return DiagramView;
-
-});
diff --git a/addons/web_diagram/static/src/js/graph.js b/addons/web_diagram/static/src/js/graph.js
deleted file mode 100644
index d7e54389340..00000000000
--- a/addons/web_diagram/static/src/js/graph.js
+++ /dev/null
@@ -1,998 +0,0 @@
-
-(function(window){
-
- // this serves as the end of an edge when creating a link
- function EdgeEnd(pos_x,pos_y){
- this.x = pos_x;
- this.y = pos_y;
-
- this.get_pos = function(){
- return new Vec2(this.x,this.y);
- }
- }
-
- // A close button,
- // if entity_type == "node":
- // GraphNode.destruction_callback(entity) is called where entity is a node.
- // If it returns true the node and all connected edges are destroyed.
- // if entity_type == "edge":
- // GraphEdge.destruction_callback(entity) is called where entity is an edge
- // If it returns true the edge is destroyed
- // pos_x,pos_y is the relative position of the close button to the entity position (entity.get_pos())
-
- function CloseButton(graph, entity, entity_type, pos_x,pos_y){
- var self = this;
- var visible = false;
- var close_button_radius = graph.style.close_button_radius || 8;
- var close_circle = graph.r.circle( entity.get_pos().x + pos_x,
- entity.get_pos().y + pos_y,
- close_button_radius );
- //the outer gray circle
- close_circle.attr({ 'opacity': 0,
- 'fill': graph.style.close_button_color || "black",
- 'cursor': 'pointer',
- 'stroke': 'none' });
- close_circle.transform(graph.get_transform());
- graph.set_scrolling(close_circle);
-
- //the 'x' inside the circle
- var close_label = graph.r.text( entity.get_pos().x + pos_x, entity.get_pos().y + pos_y,"x");
- close_label.attr({ 'fill': graph.style.close_button_x_color || "white",
- 'font-size': close_button_radius,
- 'cursor': 'pointer' });
-
- close_label.transform(graph.get_transform());
- graph.set_scrolling(close_label);
-
- // the dummy_circle is used to catch events, and avoid hover in/out madness
- // between the 'x' and the button
- var dummy_circle = graph.r.circle( entity.get_pos().x + pos_x,
- entity.get_pos().y + pos_y,
- close_button_radius );
- dummy_circle.attr({'opacity':1, 'fill': 'transparent', 'stroke':'none', 'cursor':'pointer'});
- dummy_circle.transform(graph.get_transform());
- graph.set_scrolling(dummy_circle);
-
- this.get_pos = function(){
- return entity.get_pos().add_xy(pos_x,pos_y);
- };
-
- this.update_pos = function(){
- var pos = self.get_pos();
- close_circle.attr({'cx':pos.x, 'cy':pos.y});
- dummy_circle.attr({'cx':pos.x, 'cy':pos.y});
- close_label.attr({'x':pos.x, 'y':pos.y});
- };
-
- function hover_in(){
- if(!visible){ return; }
- close_circle.animate({'r': close_button_radius * 1.5}, 300, 'elastic');
- dummy_circle.animate({'r': close_button_radius * 1.5}, 300, 'elastic');
- }
- function hover_out(){
- if(!visible){ return; }
- close_circle.animate({'r': close_button_radius},400,'linear');
- dummy_circle.animate({'r': close_button_radius},400,'linear');
- }
- dummy_circle.hover(hover_in,hover_out);
- close_circle.hover(hover_in,hover_out);
- close_label.hover(hover_in,hover_out);
-
- function click_action(){
- if(!visible){ return; }
-
- close_circle.attr({'r': close_button_radius * 2 });
- dummy_circle.attr({'r': close_button_radius * 2 });
- close_circle.animate({'r': close_button_radius }, 400, 'linear');
- dummy_circle.animate({'r': close_button_radius }, 400, 'linear');
-
- if(entity_type == "node"){
- Promise.resolve(GraphNode.destruction_callback(entity)).then(function () {
- entity.remove();
- });
- }else if(entity_type == "edge"){
- Promise.resolve(GraphEdge.destruction_callback(entity)).then(function () {
- entity.remove();
- });
- }
- }
- dummy_circle.click(click_action);
- close_circle.click(click_action);
- close_label.click(click_action);
-
- this.show = function(){
- if(!visible){
- close_circle.animate({'opacity':1}, 100, 'linear');
- close_label.animate({'opacity':1}, 100, 'linear');
- visible = true;
- }
- }
- this.hide = function(){
- if(visible){
- close_circle.animate({'opacity':0}, 100, 'linear');
- close_label.animate({'opacity':0}, 100, 'linear');
- visible = false;
- }
- }
- //destroy this object and remove it from the graph
- this.remove = function(){
- if(visible){
- visible = false;
- close_circle.animate({'opacity':0}, 100, 'linear');
- close_label.animate({'opacity':0}, 100, 'linear',self.remove);
- }else{
- close_circle.remove();
- close_label.remove();
- dummy_circle.remove();
- }
- }
- }
-
- // connectors are start and end point of edge creation drags.
- function Connector(graph,node,pos_x,pos_y){
- var visible = false;
- var conn_circle = graph.r.circle(node.get_pos().x + pos_x, node.get_pos().y + pos_y,4);
- conn_circle.attr({ 'opacity': 0,
- 'fill': graph.style.node_outline_color,
- 'stroke': 'none' });
- conn_circle.transform(graph.get_transform());
- graph.set_scrolling(conn_circle);
-
- var self = this;
-
- this.update_pos = function(){
- conn_circle.attr({'cx':node.get_pos().x + pos_x, 'cy':node.get_pos().y + pos_y});
- };
- this.get_pos = function(){
- return new node.get_pos().add_xy(pos_x,pos_y);
- };
- this.remove = function(){
- conn_circle.remove();
- }
- function hover_in(){
- if(!visible){ return;}
- conn_circle.animate({'r':8},300,'elastic');
- if(graph.creating_edge){
- graph.target_node = node;
- conn_circle.animate({ 'fill': graph.style.connector_active_color,
- 'stroke': graph.style.node_outline_color,
- 'stroke-width': graph.style.node_selected_width,
- },100,'linear');
- }
- }
- function hover_out(){
- if(!visible){ return;}
- conn_circle.animate({ 'r':graph.style.connector_radius,
- 'fill':graph.style.node_outline_color,
- 'stroke':'none'},400,'linear');
- graph.target_node = null;
- }
- conn_circle.hover(hover_in,hover_out);
-
-
- var drag_down = function(){
- if(!visible){ return; }
- self.ox = conn_circle.attr("cx");
- self.oy = conn_circle.attr("cy");
- self.edge_start = new EdgeEnd(self.ox,self.oy);
- self.edge_end = new EdgeEnd(self.ox, self.oy);
- self.edge_tmp = new GraphEdge(graph,'',self.edge_start,self.edge_end,true);
- graph.creating_edge = true;
- };
- var drag_move = function(dx,dy){
- if(!visible){ return; }
- self.edge_end.x = self.ox + dx;
- self.edge_end.y = self.oy + dy;
- self.edge_tmp.update();
- };
- var drag_up = function(){
- if(!visible){ return; }
- graph.creating_edge = false;
- self.edge_tmp.remove();
- if(graph.target_node){
- var edge_prop = GraphEdge.creation_callback(node,graph.target_node);
- if(edge_prop){
- var new_edge = new GraphEdge(graph,edge_prop.label, node,graph.target_node);
- GraphEdge.new_edge_callback(new_edge);
- }
- }
- };
- conn_circle.drag(drag_move,drag_down,drag_up);
-
- function show(){
- if(!visible){
- conn_circle.animate({'opacity':1}, 100, 'linear');
- visible = true;
- }
- }
- function hide(){
- if(visible){
- conn_circle.animate({'opacity':0}, 100, 'linear');
- visible = false;
- }
- }
- this.show = show;
- this.hide = hide;
- }
-
- //Creates a new graph on raphael document r.
- //style is a dictionary containing the style definitions
- //viewport (optional) is the dom element representing the viewport of the graph. It is used
- //to prevent scrolling to scroll the graph outside the viewport.
-
- function Graph(r,style,viewport){
- var self = this;
- var nodes = []; // list of all nodes in the graph
- var edges = []; // list of all edges in the graph
- var graph = {}; // graph[n1.uid][n2.uid] -> list of all edges from n1 to n2
- var links = {}; // links[n.uid] -> list of all edges from or to n
- var uid = 1; // all nodes and edges have an uid used to order their display when they are curved
- var selected_entity = null; //the selected entity (node or edge)
-
- self.creating_edge = false; // true if we are dragging a new edge onto a node
- self.target_node = null; // this holds the target node when creating an edge and hovering a connector
- self.r = r; // the raphael instance
- self.style = style; // definition of the colors, spacing, fonts, ... used by the elements
- var tr_x = 0, tr_y = 0; // global translation coordinate
-
- var background = r.rect(0,0,'100%','100%').attr({'fill':'white', 'stroke':'none', 'opacity':0, 'cursor':'move'});
-
- // return the global transform of the scene
- this.get_transform = function(){
- return "T"+tr_x+","+tr_y
- };
-
-
- // translate every element of the graph except the background.
- // elements inserted in the graph after a translate_all() must manually apply transformation
- // via get_transform()
- var translate_all = function(dx,dy){
- tr_x += dx;
- tr_y += dy;
- var tstr = self.get_transform();
-
- r.forEach(function(el){
- if(el != background){
- el.transform(tstr);
- }
- });
- };
- //returns {minx, miny, maxx, maxy}, the translated bounds containing all nodes
- var get_bounds = function(){
- var minx = Number.MAX_VALUE;
- var miny = Number.MAX_VALUE;
- var maxx = Number.MIN_VALUE;
- var maxy = Number.MIN_VALUE;
-
- for(var i = 0; i < nodes.length; i++){
- var pos = nodes[i].get_pos();
- minx = Math.min(minx,pos.x);
- miny = Math.min(miny,pos.y);
- maxx = Math.max(maxx,pos.x);
- maxy = Math.max(maxy,pos.y);
- }
-
- minx = minx - style.node_size_x / 2 + tr_x;
- miny = miny - style.node_size_y / 2 + tr_y;
- maxx = maxx + style.node_size_x / 2 + tr_x;
- maxy = maxy + style.node_size_y / 2 + tr_y;
-
- return { minx:minx, miny:miny, maxx:maxx, maxy:maxy };
-
- };
- // returns false if the translation dx,dy of the viewport
- // hides the graph (with optional margin)
- var translation_respects_viewport = function(dx,dy,margin){
- if(!viewport){
- return true;
- }
- margin = margin || 0;
- var b = get_bounds();
- var width = viewport.offsetWidth;
- var height = viewport.offsetHeight;
-
- if( ( dy < 0 && b.maxy + dy < margin ) ||
- ( dy > 0 && b.miny + dy > height - margin ) ||
- ( dx < 0 && b.maxx + dx < margin ) ||
- ( dx > 0 && b.minx + dx > width - margin ) ){
- return false;
- }
-
- return true;
- }
- //Adds a mousewheel event callback to raph_element that scrolls the viewport
- this.set_scrolling = function(raph_element){
- $(raph_element.node).bind('mousewheel',function(event,delta){
- var dy = delta * 20;
- if( translation_respects_viewport(0,dy, style.viewport_margin) ){
- translate_all(0,dy);
- }
- });
- };
-
- var px, py;
- // Graph translation when background is dragged
- var bg_drag_down = function(){
- px = py = 0;
- };
- var bg_drag_move = function(x,y){
- var dx = x - px;
- var dy = y - py;
- px = x;
- py = y;
- if( translation_respects_viewport(dx,dy, style.viewport_margin) ){
- translate_all(dx,dy);
- }
- };
- var bg_drag_up = function(){};
- background.drag( bg_drag_move, bg_drag_down, bg_drag_up);
-
- this.set_scrolling(background);
-
- //adds a node to the graph and sets its uid.
- this.add_node = function (n){
- nodes.push(n);
- n.uid = uid++;
- };
-
- //return the list of all nodes in the graph
- this.get_node_list = function(){
- return nodes;
- };
-
- //adds an edge to the graph and sets its uid
- this.add_edge = function (n1,n2,e){
- edges.push(e);
- e.uid = uid++;
- if(!graph[n1.uid]) graph[n1.uid] = {};
- if(!graph[n1.uid][n2.uid]) graph[n1.uid][n2.uid] = [];
- if(!links[n1.uid]) links[n1.uid] = [];
- if(!links[n2.uid]) links[n2.uid] = [];
-
- graph[n1.uid][n2.uid].push(e);
- links[n1.uid].push(e);
- if(n1 != n2){
- links[n2.uid].push(e);
- }
- };
-
- //removes an edge from the graph
- this.remove_edge = function(edge){
- edges = _.without(edges,edge);
- var n1 = edge.get_start();
- var n2 = edge.get_end();
- links[n1.uid] = _.without(links[n1.uid],edge);
- links[n2.uid] = _.without(links[n2.uid],edge);
- graph[n1.uid][n2.uid] = _.without(graph[n1.uid][n2.uid],edge);
- if ( selected_entity == edge ){
- selected_entity = null;
- }
- };
- //removes a node and all connected edges from the graph
- this.remove_node = function(node){
- var linked_edges = self.get_linked_edge_list(node);
- for(var i = 0; i < linked_edges.length; i++){
- linked_edges[i].remove();
- }
- nodes = _.without(nodes,node);
-
- if ( selected_entity == node ){
- selected_entity = null;
- }
- }
-
-
- //return the list of edges from n1 to n2
- this.get_edge_list = function(n1,n2){
- var list = [];
- if(!graph[n1.uid]) return list;
- if(!graph[n1.uid][n2.uid]) return list;
- return graph[n1.uid][n2.uid];
- };
- //returns the list of all edge connected to n
- this.get_linked_edge_list = function(n){
- if(!links[n.uid]) return [];
- return links[n.uid];
- };
- //return a curvature index so that all edges connecting n1,n2 have different curvatures
- this.get_edge_curvature = function(n1,n2,e){
- var el_12 = this.get_edge_list(n1,n2);
- var c12 = el_12.length;
- var el_21 = this.get_edge_list(n2,n1);
- var c21 = el_21.length;
- if(c12 + c21 == 1){ // only one edge
- return 0;
- }else{
- var index = 0;
- for(var i = 0; i < c12; i++){
- if (el_12[i].uid < e.uid){
- index++;
- }
- }
- if(c21 == 0){ // all edges in the same direction
- return index - (c12-1)/2.0;
- }else{
- return index + 0.5;
- }
- }
- };
-
-
- // Returns the angle in degrees of the edge loop. We do not support more than 8 loops on one node
- this.get_loop_angle = function(n,e){
- var loop_list = this.get_edge_list(n,n);
-
- var slots = []; // the 8 angles where we can put the loops
- for(var angle = 0; angle < 360; angle += 45){
- slots.push(Vec2.new_polar_deg(1,angle));
- }
-
- //we assign to each slot a score. The higher the score, the closer it is to other edges.
- var links = this.get_linked_edge_list(n);
- for(var i = 0; i < links.length; i++){
- var edge = links[i];
- if(!edge.is_loop || edge.is_loop()){
- continue;
- }
- var end = edge.get_end();
- if (end == n){
- end = edge.get_start();
- }
- var dir = end.get_pos().sub(n.get_pos()).normalize();
- for(var s = 0; s < slots.length; s++){
- var score = slots[s].dot(dir);
- if(score < 0){
- score = -0.2*Math.pow(score,2);
- }else{
- score = Math.pow(score,2);
- }
- if(!slots[s].score){
- slots[s].score = score;
- }else{
- slots[s].score += score;
- }
- }
- }
- //we want the loops with lower uid to get the slots with the lower score
- slots.sort(function(a,b){ return a.score < b.score ? -1: 1; });
-
- var index = 0;
- for(var i = 0; i < links.length; i++){
- var edge = links[i];
- if(!edge.is_loop || !edge.is_loop()){
- continue;
- }
- if(edge.uid < e.uid){
- index++;
- }
- }
- index %= slots.length;
-
- return slots[index].angle_deg();
- }
-
- //selects a node or an edge and deselects everything else
- this.select = function(entity){
- if(selected_entity){
- if(selected_entity == entity){
- return;
- }else{
- if(selected_entity.set_not_selected){
- selected_entity.set_not_selected();
- }
- selected_entity = null;
- }
- }
- selected_entity = entity;
- if(entity && entity.set_selected){
- entity.set_selected();
- }
- };
- }
-
- // creates a new Graph Node on Raphael document r, centered on [pos_x,pos_y], with label 'label',
- // and of type 'circle' or 'rect', and of color 'color'
- function GraphNode(graph,pos_x, pos_y,label,type,color){
- var self = this;
- var r = graph.r;
- var sy = graph.style.node_size_y;
- var sx = graph.style.node_size_x;
- var node_fig = null;
- var selected = false;
- this.connectors = [];
- this.close_button = null;
- this.uid = 0;
-
- graph.add_node(this);
-
- if(type == 'circle'){
- node_fig = r.ellipse(pos_x,pos_y,sx/2,sy/2);
- }else{
- node_fig = r.rect(pos_x-sx/2,pos_y-sy/2,sx,sy);
- }
- node_fig.attr({ 'fill': color,
- 'stroke': graph.style.node_outline_color,
- 'stroke-width': graph.style.node_outline_width,
- 'cursor':'pointer' });
- node_fig.transform(graph.get_transform());
- graph.set_scrolling(node_fig);
-
- var node_label = r.text(pos_x,pos_y,label);
- node_label.attr({ 'fill': graph.style.node_label_color,
- 'font-size': graph.style.node_label_font_size,
- 'cursor': 'pointer' });
- node_label.transform(graph.get_transform());
- graph.set_scrolling(node_label);
-
- // redraws all edges linked to this node
- var update_linked_edges = function(){
- var edges = graph.get_linked_edge_list(self);
- for(var i = 0; i < edges.length; i++){
- edges[i].update();
- }
- };
-
- // sets the center position of the node
- var set_pos = function(pos){
- if(type == 'circle'){
- node_fig.attr({'cx':pos.x,'cy':pos.y});
- }else{
- node_fig.attr({'x':pos.x-sx/2,'y':pos.y-sy/2});
- }
- node_label.attr({'x':pos.x,'y':pos.y});
- for(var i = 0; i < self.connectors.length; i++){
- self.connectors[i].update_pos();
- }
- if(self.close_button){
- self.close_button.update_pos();
- }
- update_linked_edges();
- };
- // returns the figure used to draw the node
- var get_fig = function(){
- return node_fig;
- };
- // returns the center coordinates
- var get_pos = function(){
- if(type == 'circle'){
- return new Vec2(node_fig.attr('cx'), node_fig.attr('cy'));
- }else{
- return new Vec2(node_fig.attr('x') + sx/2, node_fig.attr('y') + sy/2);
- }
- };
- // return the label string
- var get_label = function(){
- return node_label.attr("text");
- };
- // sets the label string
- var set_label = function(text){
- node_label.attr({'text':text});
- };
- var get_bound = function(){
- if(type == 'circle'){
- return new BEllipse(get_pos().x,get_pos().y,sx/2,sy/2);
- }else{
- return BRect.new_centered(get_pos().x,get_pos().y,sx,sy);
- }
- };
- // selects this node and deselects all other nodes
- var set_selected = function(){
- if(!selected){
- selected = true;
- node_fig.attr({ 'stroke': graph.style.node_selected_color,
- 'stroke-width': graph.style.node_selected_width });
- if(!self.close_button){
- self.close_button = new CloseButton(graph,self, "node" ,sx/2 , - sy/2);
- self.close_button.show();
- }
- for(var i = 0; i < self.connectors.length; i++){
- self.connectors[i].show();
- }
- }
- };
- // deselect this node
- var set_not_selected = function(){
- if(selected){
- node_fig.animate({ 'stroke': graph.style.node_outline_color,
- 'stroke-width': graph.style.node_outline_width },
- 100,'linear');
- if(self.close_button){
- self.close_button.remove();
- self.close_button = null;
- }
- selected = false;
- }
- for(var i = 0; i < self.connectors.length; i++){
- self.connectors[i].hide();
- }
- };
- var remove = function(){
- if(self.close_button){
- self.close_button.remove();
- }
- for(var i = 0; i < self.connectors.length; i++){
- self.connectors[i].remove();
- }
- graph.remove_node(self);
- node_fig.remove();
- node_label.remove();
- }
-
-
- this.set_pos = set_pos;
- this.get_pos = get_pos;
- this.set_label = set_label;
- this.get_label = get_label;
- this.get_bound = get_bound;
- this.get_fig = get_fig;
- this.set_selected = set_selected;
- this.set_not_selected = set_not_selected;
- this.update_linked_edges = update_linked_edges;
- this.remove = remove;
-
-
- //select the node and play an animation when clicked
- var click_action = function(){
- if(type == 'circle'){
- node_fig.attr({'rx':sx/2 + 3, 'ry':sy/2+ 3});
- node_fig.animate({'rx':sx/2, 'ry':sy/2},500,'elastic');
- }else{
- var cx = get_pos().x;
- var cy = get_pos().y;
- node_fig.attr({'x':cx - (sx/2) - 3, 'y':cy - (sy/2) - 3, 'ẃidth':sx+6, 'height':sy+6});
- node_fig.animate({'x':cx - sx/2, 'y':cy - sy/2, 'ẃidth':sx, 'height':sy},500,'elastic');
- }
- graph.select(self);
- };
- node_fig.click(click_action);
- node_label.click(click_action);
-
- //move the node when dragged
- var drag_down = function(){
- this.opos = get_pos();
- };
- var drag_move = function(dx,dy){
- // we disable labels when moving for performance reasons,
- // updating the label position is quite expensive
- // we put this here because drag_down is also called on simple clicks ... and this causes unwanted flicker
- var edges = graph.get_linked_edge_list(self);
- for(var i = 0; i < edges.length; i++){
- edges[i].label_disable();
- }
- if(self.close_button){
- self.close_button.hide();
- }
- set_pos(this.opos.add_xy(dx,dy));
- };
- var drag_up = function(){
- //we re-enable the
- var edges = graph.get_linked_edge_list(self);
- for(var i = 0; i < edges.length; i++){
- edges[i].label_enable();
- }
- if(self.close_button){
- self.close_button.show();
- }
- };
- node_fig.drag(drag_move,drag_down,drag_up);
- node_label.drag(drag_move,drag_down,drag_up);
-
- //allow the user to create edges by dragging onto the node
- function hover_in(){
- if(graph.creating_edge){
- graph.target_node = self;
- }
- }
- function hover_out(){
- graph.target_node = null;
- }
- node_fig.hover(hover_in,hover_out);
- node_label.hover(hover_in,hover_out);
-
- function double_click(){
- GraphNode.double_click_callback(self);
- }
- node_fig.dblclick(double_click);
- node_label.dblclick(double_click);
-
- this.connectors.push(new Connector(graph,this,-sx/2,0));
- this.connectors.push(new Connector(graph,this,sx/2,0));
- this.connectors.push(new Connector(graph,this,0,-sy/2));
- this.connectors.push(new Connector(graph,this,0,sy/2));
-
- this.close_button = new CloseButton(graph,this,"node",sx/2 , - sy/2 );
- }
-
- GraphNode.double_click_callback = function(node){
- console.log("double click from node:",node);
- };
-
- // this is the default node destruction callback. It is called before the node is removed from the graph
- // and before the connected edges are destroyed
- GraphNode.destruction_callback = function(node){ return true; };
-
- // creates a new edge with label 'label' from start to end. start and end must implement get_pos_*,
- // if tmp is true, the edge is not added to the graph, used for drag edges.
- // replace tmp == false by graph == null
- function GraphEdge(graph,label,start,end,tmp){
- var self = this;
- var r = graph.r;
- var curvature = 0; // 0 = straight, != 0 curved
- var s,e; // positions of the start and end point of the line between start and end
- var mc; // position of the middle of the curve (bezier control point)
- var mc1,mc2; // control points of the cubic bezier for the loop edges
- var elfs = graph.style.edge_label_font_size || 10 ;
- var label_enabled = true;
- this.uid = 0; // unique id used to order the curved edges
- var edge_path = ""; // svg definition of the edge vector path
- var selected = false;
-
- if(!tmp){
- graph.add_edge(start,end,this);
- }
-
- //Return the position of the label
- function get_label_pos(path){
- var cpos = path.getTotalLength() * 0.5;
- var cindex = Math.abs(Math.floor(curvature));
- var mod = ((cindex % 3)) * (elfs * 3.1) - (elfs * 0.5);
- var verticality = Math.abs(end.get_pos().sub(start.get_pos()).normalize().dot_xy(0,1));
- verticality = Math.max(verticality-0.5,0)*2;
-
- var lpos = path.getPointAtLength(cpos + mod * verticality);
- return new Vec2(lpos.x,lpos.y - elfs *(1-verticality));
- }
-
- //used by close_button
- this.get_pos = function(){
- if(!edge){
- return start.get_pos().lerp(end.get_pos(),0.5);
- }
- return get_label_pos(edge);
- /*
- var bbox = edge_label.getBBox(); Does not work... :(
- return new Vec2(bbox.x + bbox.width, bbox.y);*/
- }
-
- //Straight line from s to e
- function make_line(){
- return "M" + s.x + "," + s.y + "L" + e.x + "," + e.y ;
- }
- //Curved line from s to e by mc
- function make_curve(){
- return "M" + s.x + "," + s.y + "Q" + mc.x + "," + mc.y + " " + e.x + "," + e.y;
- }
- //Curved line from s to e by mc1 mc2
- function make_loop(){
- return "M" + s.x + " " + s.y +
- "C" + mc1.x + " " + mc1.y + " " + mc2.x + " " + mc2.y + " " + e.x + " " + e.y;
- }
-
- //computes new start and end line coordinates
- function update_curve(){
- if(start != end){
- if(!tmp){
- curvature = graph.get_edge_curvature(start,end,self);
- }else{
- curvature = 0;
- }
- s = start.get_pos();
- e = end.get_pos();
-
- mc = s.lerp(e,0.5); //middle of the line s->e
- var se = e.sub(s);
- se = se.normalize();
- se = se.rotate_deg(-90);
- se = se.scale(curvature * graph.style.edge_spacing);
- mc = mc.add(se);
-
- if(start.get_bound){
- var col = start.get_bound().collide_segment(s,mc);
- if(col.length > 0){
- s = col[0];
- }
- }
- if(end.get_bound){
- var col = end.get_bound().collide_segment(mc,e);
- if(col.length > 0){
- e = col[0];
- }
- }
-
- if(curvature != 0){
- edge_path = make_curve();
- }else{
- edge_path = make_line();
- }
- }else{ // start == end
- var rad = graph.style.edge_loop_radius || 100;
- s = start.get_pos();
- e = end.get_pos();
-
- var r = Vec2.new_polar_deg(rad,graph.get_loop_angle(start,self));
- mc = s.add(r);
- var p = r.rotate_deg(90);
- mc1 = mc.add(p.set_len(rad*0.5));
- mc2 = mc.add(p.set_len(-rad*0.5));
-
- if(start.get_bound){
- var col = start.get_bound().collide_segment(s,mc1);
- if(col.length > 0){
- s = col[0];
- }
- var col = start.get_bound().collide_segment(e,mc2);
- if(col.length > 0){
- e = col[0];
- }
- }
- edge_path = make_loop();
- }
- }
-
- update_curve();
- var edge = r.path(edge_path).attr({ 'stroke': graph.style.edge_color,
- 'stroke-width': graph.style.edge_width,
- 'arrow-end': 'block-wide-long',
- 'cursor':'pointer' }).insertBefore(graph.get_node_list()[0].get_fig());
- var labelpos = get_label_pos(edge);
- var edge_label = r.text(labelpos.x, labelpos.y - elfs, label).attr({
- 'fill': graph.style.edge_label_color,
- 'cursor': 'pointer',
- 'font-size': elfs });
-
- edge.transform(graph.get_transform());
- graph.set_scrolling(edge);
-
- edge_label.transform(graph.get_transform());
- graph.set_scrolling(edge_label);
-
-
- //since we create an edge we need to recompute the edges that have the same start and end positions as this one
- if(!tmp){
- var edges_start = graph.get_linked_edge_list(start);
- var edges_end = graph.get_linked_edge_list(end);
- var edges = edges_start.length < edges_end.length ? edges_start : edges_end;
- for(var i = 0; i < edges.length; i ++){
- if(edges[i] != self){
- edges[i].update();
- }
- }
- }
- function label_enable(){
- if(!label_enabled){
- label_enabled = true;
- edge_label.animate({'opacity':1},100,'linear');
- if(self.close_button){
- self.close_button.show();
- }
- self.update();
- }
- }
- function label_disable(){
- if(label_enabled){
- label_enabled = false;
- edge_label.animate({'opacity':0},100,'linear');
- if(self.close_button){
- self.close_button.hide();
- }
- }
- }
- //update the positions
- function update(){
- update_curve();
- edge.attr({'path':edge_path});
- if(label_enabled){
- var labelpos = get_label_pos(edge);
- edge_label.attr({'x':labelpos.x, 'y':labelpos.y - 14});
- }
- }
- // removes the edge from the scene, disconnects it from linked
- // nodes, destroy its drawable elements.
- function remove(){
- edge.remove();
- edge_label.remove();
- if(!tmp){
- graph.remove_edge(self);
- }
- if(start.update_linked_edges){
- start.update_linked_edges();
- }
- if(start != end && end.update_linked_edges){
- end.update_linked_edges();
- }
- if(self.close_button){
- self.close_button.remove();
- }
- }
-
- this.set_selected = function(){
- if(!selected){
- selected = true;
- edge.attr({ 'stroke': graph.style.node_selected_color,
- 'stroke-width': graph.style.node_selected_width });
- edge_label.attr({ 'fill': graph.style.node_selected_color });
- if(!self.close_button){
- self.close_button = new CloseButton(graph,self,"edge",0,30);
- self.close_button.show();
- }
- }
- };
-
- this.set_not_selected = function(){
- if(selected){
- selected = false;
- edge.animate({ 'stroke': graph.style.edge_color,
- 'stroke-width': graph.style.edge_width }, 100,'linear');
- edge_label.animate({ 'fill': graph.style.edge_label_color}, 100, 'linear');
- if(self.close_button){
- self.close_button.remove();
- self.close_button = null;
- }
- }
- };
- function click_action(){
- graph.select(self);
- }
- edge.click(click_action);
- edge_label.click(click_action);
-
- function double_click_action(){
- GraphEdge.double_click_callback(self);
- }
-
- edge.dblclick(double_click_action);
- edge_label.dblclick(double_click_action);
-
-
- this.label_enable = label_enable;
- this.label_disable = label_disable;
- this.update = update;
- this.remove = remove;
- this.is_loop = function(){ return start == end; };
- this.get_start = function(){ return start; };
- this.get_end = function(){ return end; };
- }
-
- GraphEdge.double_click_callback = function(edge){
- console.log("double click from edge:",edge);
- };
-
- // this is the default edge creation callback. It is called before an edge is created
- // It returns an object containing the properties of the edge.
- // If it returns null, the edge is not created.
- GraphEdge.creation_callback = function(start,end){
- var edge_prop = {};
- edge_prop.label = 'new edge!';
- return edge_prop;
- };
- // This is is called after a new edge is created, with the new edge
- // as parameter
- GraphEdge.new_edge_callback = function(new_edge){};
-
- // this is the default edge destruction callback. It is called before
- // an edge is removed from the graph.
- GraphEdge.destruction_callback = function(edge){ return true; };
-
-
-
- // returns a new string with the same content as str, but with lines of maximum 'width' characters.
- // lines are broken on words, or into words if a word is longer than 'width'
- function wordwrap( str, width) {
- // http://james.padolsey.com/javascript/wordwrap-for-javascript/
- width = width || 32;
- var cut = true;
- var brk = '\n';
- if (!str) { return str; }
- var regex = '.{1,' +width+ '}(\\s|$)' + (cut ? '|.{' +width+ '}|.+$' : '|\\S+?(\\s|$)');
- return str.match(new RegExp(regex, 'g') ).join( brk );
- }
-
- window.CuteGraph = Graph;
- window.CuteNode = GraphNode;
- window.CuteEdge = GraphEdge;
-
- window.CuteGraph.wordwrap = wordwrap;
-
-
-})(window);
-
diff --git a/addons/web_diagram/static/src/js/vec2.js b/addons/web_diagram/static/src/js/vec2.js
deleted file mode 100644
index 28790008be3..00000000000
--- a/addons/web_diagram/static/src/js/vec2.js
+++ /dev/null
@@ -1,358 +0,0 @@
-
-(function(window){
-
- // A Javascript 2D vector library
- // conventions :
- // method that returns a float value do not modify the vector
- // method that implement operators return a new vector with the modifications without
- // modifying the calling vector or the parameters.
- //
- // v3 = v1.add(v2); // v3 is set to v1 + v2, v1, v2 are not modified
- //
- // methods that take a single vector as a parameter are usually also available with
- // q '_xy' suffix. Those method takes two floats representing the x,y coordinates of
- // the vector parameter and allow you to avoid to needlessly create a vector object :
- //
- // v2 = v1.add(new Vec2(3,4));
- // v2 = v1.add_xy(3,4); //equivalent to previous line
- //
- // angles are in radians by default but method that takes angle as parameters
- // or return angle values usually have a variant with a '_deg' suffix that works in degrees
- //
-
- // The 2D vector object
- function Vec2(x,y){
- this.x = x;
- this.y = y;
- }
-
- window.Vec2 = Vec2;
-
- // Multiply a number expressed in radiant by rad2deg to convert it in degrees
- var rad2deg = 57.29577951308232;
- // Multiply a number expressed in degrees by deg2rad to convert it to radiant
- var deg2rad = 0.017453292519943295;
- // The numerical precision used to compare vector equality
- var epsilon = 0.0000001;
-
- // This static method creates a new vector from polar coordinates with the angle expressed
- // in degrees
- Vec2.new_polar_deg = function(len,angle){
- var v = new Vec2(len,0);
- return v.rotate_deg(angle);
- };
- // This static method creates a new vector from polar coordinates with the angle expressed in
- // radians
- Vec2.new_polar = function(len,angle){
- var v = new Vec2(len,0);
- v.rotate(angle);
- return v;
- };
- // returns the length or modulus or magnitude of the vector
- Vec2.prototype.len = function(){
- return Math.sqrt(this.x*this.x + this.y*this.y);
- };
- // returns the squared length of the vector, this method is much faster than len()
- Vec2.prototype.len_sq = function(){
- return this.x*this.x + this.y*this.y;
- };
- // return the distance between this vector and the vector v
- Vec2.prototype.dist = function(v){
- var dx = this.x - v.x;
- var dy = this.y - v.y;
- return Math.sqrt(dx*dx + dy*dy);
- };
- // return the distance between this vector and the vector of coordinates (x,y)
- Vec2.prototype.dist_xy = function(x,y){
- var dx = this.x - x;
- var dy = this.y - y;
- return Math.sqrt(dx*dx + dy*dy);
- };
- // return the squared distance between this vector and the vector and the vector v
- Vec2.prototype.dist_sq = function(v){
- var dx = this.x - v.x;
- var dy = this.y - v.y;
- return dx*dx + dy*dy;
- };
- // return the squared distance between this vector and the vector of coordinates (x,y)
- Vec2.prototype.dist_sq_xy = function(x,y){
- var dx = this.x - x;
- var dy = this.y - y;
- return dx*dx + dy*dy;
- };
- // return the dot product between this vector and the vector v
- Vec2.prototype.dot = function(v){
- return this.x*v.x + this.y*v.y;
- };
- // return the dot product between this vector and the vector of coordinate (x,y)
- Vec2.prototype.dot_xy = function(x,y){
- return this.x*x + this.y*y;
- };
- // return a new vector with the same coordinates as this
- Vec2.prototype.clone = function(){
- return new Vec2(this.x,this.y);
- };
- // return the sum of this and vector v as a new vector
- Vec2.prototype.add = function(v){
- return new Vec2(this.x+v.x,this.y+v.y);
- };
- // return the sum of this and vector (x,y) as a new vector
- Vec2.prototype.add_xy = function(x,y){
- return new Vec2(this.x+x,this.y+y);
- };
- // returns (this - v) as a new vector where v is a vector and - is the vector subtraction
- Vec2.prototype.sub = function(v){
- return new Vec2(this.x-v.x,this.y-v.y);
- };
- // returns (this - (x,y)) as a new vector where - is vector subtraction
- Vec2.prototype.sub_xy = function(x,y){
- return new Vec2(this.x-x,this.y-y);
- };
- // return (this * v) as a new vector where v is a vector and * is the by component product
- Vec2.prototype.mult = function(v){
- return new Vec2(this.x*v.x,this.y*v.y);
- };
- // return (this * (x,y)) as a new vector where * is the by component product
- Vec2.prototype.mult_xy = function(x,y){
- return new Vec2(this.x*x,this.y*y);
- };
- // return this scaled by float f as a new fector
- Vec2.prototype.scale = function(f){
- return new Vec2(this.x*f, this.y*f);
- };
- // return the negation of this vector
- Vec2.prototype.neg = function(f){
- return new Vec2(-this.x,-this.y);
- };
- // return this vector normalized as a new vector
- Vec2.prototype.normalize = function(){
- var len = this.len();
- if(len == 0){
- return new Vec2(0,1);
- }else if(len != 1){
- return this.scale(1.0/len);
- }
- return new Vec2(this.x,this.y);
- };
- // return a new vector with the same direction as this vector of length float l. (negative values of l will invert direction)
- Vec2.prototype.set_len = function(l){
- return this.normalize().scale(l);
- };
- // return the projection of this onto the vector v as a new vector
- Vec2.prototype.project = function(v){
- return v.set_len(this.dot(v));
- };
- // return a string representation of this vector
- Vec2.prototype.toString = function(){
- var str = "";
- str += "[";
- str += this.x;
- str += ",";
- str += this.y;
- str += "]";
- return str;
- };
- //return this vector counterclockwise rotated by rad radians as a new vector
- Vec2.prototype.rotate = function(rad){
- var c = Math.cos(rad);
- var s = Math.sin(rad);
- var px = this.x * c - this.y *s;
- var py = this.x * s + this.y *c;
- return new Vec2(px,py);
- };
- //return this vector counterclockwise rotated by deg degrees as a new vector
- Vec2.prototype.rotate_deg = function(deg){
- return this.rotate(deg * deg2rad);
- };
- //linearly interpolate this vector towards the vector v by float factor alpha.
- // alpha == 0 : does nothing
- // alpha == 1 : sets this to v
- Vec2.prototype.lerp = function(v,alpha){
- var inv_alpha = 1 - alpha;
- return new Vec2( this.x * inv_alpha + v.x * alpha,
- this.y * inv_alpha + v.y * alpha );
- };
- // returns the angle between this vector and the vector (1,0) in radians
- Vec2.prototype.angle = function(){
- return Math.atan2(this.y,this.x);
- };
- // returns the angle between this vector and the vector (1,0) in degrees
- Vec2.prototype.angle_deg = function(){
- return Math.atan2(this.y,this.x) * rad2deg;
- };
- // returns true if this vector is equal to the vector v, with a tolerance defined by the epsilon module constant
- Vec2.prototype.equals = function(v){
- if(Math.abs(this.x-v.x) > epsilon){
- return false;
- }else if(Math.abs(this.y-v.y) > epsilon){
- return false;
- }
- return true;
- };
- // returns true if this vector is equal to the vector (x,y) with a tolerance defined by the epsilon module constant
- Vec2.prototype.equals_xy = function(x,y){
- if(Math.abs(this.x-x) > epsilon){
- return false;
- }else if(Math.abs(this.y-y) > epsilon){
- return false;
- }
- return true;
- };
-})(window);
-
-(function(window){
- // A Bounding Shapes Library
-
-
- // A Bounding Ellipse
- // cx,cy : center of the ellipse
- // rx,ry : radius of the ellipse
- function BEllipse(cx,cy,rx,ry){
- this.type = 'ellipse';
- this.x = cx-rx; // minimum x coordinate contained in the ellipse
- this.y = cy-ry; // minimum y coordinate contained in the ellipse
- this.sx = 2*rx; // width of the ellipse on the x axis
- this.sy = 2*ry; // width of the ellipse on the y axis
- this.hx = rx; // half of the ellipse width on the x axis
- this.hy = ry; // half of the ellipse width on the y axis
- this.cx = cx; // x coordinate of the ellipse center
- this.cy = cy; // y coordinate of the ellipse center
- this.mx = cx + rx; // maximum x coordinate contained in the ellipse
- this.my = cy + ry; // maximum x coordinate contained in the ellipse
- }
- window.BEllipse = BEllipse;
-
- // returns an unordered list of vector defining the positions of the intersections between the ellipse's
- // boundary and a line segment defined by the start and end vectors a,b
- BEllipse.prototype.collide_segment = function(a,b){
- // http://paulbourke.net/geometry/sphereline/
- var collisions = [];
-
- if(a.equals(b)){ //we do not compute the intersection in this case. TODO ?
- return collisions;
- }
-
- // make all computations in a space where the ellipse is a circle
- // centered on zero
- var c = new Vec2(this.cx,this.cy);
- a = a.sub(c).mult_xy(1/this.hx,1/this.hy);
- b = b.sub(c).mult_xy(1/this.hx,1/this.hy);
-
-
- if(a.len_sq() < 1 && b.len_sq() < 1){ //both points inside the ellipse
- return collisions;
- }
-
- // compute the roots of the intersection
- var ab = b.sub(a);
- var A = (ab.x*ab.x + ab.y*ab.y);
- var B = 2*( ab.x*a.x + ab.y*a.y);
- var C = a.x*a.x + a.y*a.y - 1;
- var u = B * B - 4*A*C;
-
- if(u < 0){
- return collisions;
- }
-
- u = Math.sqrt(u);
- var u1 = (-B + u) / (2*A);
- var u2 = (-B - u) / (2*A);
-
- if(u1 >= 0 && u1 <= 1){
- var pos = a.add(ab.scale(u1));
- collisions.push(pos);
- }
- if(u1 != u2 && u2 >= 0 && u2 <= 1){
- var pos = a.add(ab.scale(u2));
- collisions.push(pos);
- }
- for(var i = 0; i < collisions.length; i++){
- collisions[i] = collisions[i].mult_xy(this.hx,this.hy);
- collisions[i] = collisions[i].add_xy(this.cx,this.cy);
- }
- return collisions;
- };
-
- // A bounding rectangle
- // x,y the minimum coordinate contained in the rectangle
- // sx,sy the size of the rectangle along the x,y axis
- function BRect(x,y,sx,sy){
- this.type = 'rect';
- this.x = x; // minimum x coordinate contained in the rectangle
- this.y = y; // minimum y coordinate contained in the rectangle
- this.sx = sx; // width of the rectangle on the x axis
- this.sy = sy; // width of the rectangle on the y axis
- this.hx = sx/2; // half of the rectangle width on the x axis
- this.hy = sy/2; // half of the rectangle width on the y axis
- this.cx = x + this.hx; // x coordinate of the rectangle center
- this.cy = y + this.hy; // y coordinate of the rectangle center
- this.mx = x + sx; // maximum x coordinate contained in the rectangle
- this.my = y + sy; // maximum x coordinate contained in the rectangle
- }
-
- window.BRect = BRect;
- // Static method creating a new bounding rectangle of size (sx,sy) centered on (cx,cy)
- BRect.new_centered = function(cx,cy,sx,sy){
- return new BRect(cx-sx/2,cy-sy/2,sx,sy);
- };
- //intersect line a,b with line c,d, returns null if no intersection
- function line_intersect(a,b,c,d){
- // http://paulbourke.net/geometry/lineline2d/
- var f = ((d.y - c.y)*(b.x - a.x) - (d.x - c.x)*(b.y - a.y));
- if(f == 0){
- return null;
- }
- f = 1 / f;
- var fab = ((d.x - c.x)*(a.y - c.y) - (d.y - c.y)*(a.x - c.x)) * f ;
- if(fab < 0 || fab > 1){
- return null;
- }
- var fcd = ((b.x - a.x)*(a.y - c.y) - (b.y - a.y)*(a.x - c.x)) * f ;
- if(fcd < 0 || fcd > 1){
- return null;
- }
- return new Vec2(a.x + fab * (b.x-a.x), a.y + fab * (b.y - a.y) );
- }
-
- // returns an unordered list of vector defining the positions of the intersections between the ellipse's
- // boundary and a line segment defined by the start and end vectors a,b
-
- BRect.prototype.collide_segment = function(a,b){
- var collisions = [];
- var corners = [ new Vec2(this.x,this.y), new Vec2(this.x,this.my),
- new Vec2(this.mx,this.my), new Vec2(this.mx,this.y) ];
- var pos = line_intersect(a,b,corners[0],corners[1]);
- if(pos) collisions.push(pos);
- pos = line_intersect(a,b,corners[1],corners[2]);
- if(pos) collisions.push(pos);
- pos = line_intersect(a,b,corners[2],corners[3]);
- if(pos) collisions.push(pos);
- pos = line_intersect(a,b,corners[3],corners[0]);
- if(pos) collisions.push(pos);
- return collisions;
- };
-
- // returns true if the rectangle contains the position defined by the vector 'vec'
- BRect.prototype.contains_vec = function(vec){
- return ( vec.x >= this.x && vec.x <= this.mx &&
- vec.y >= this.y && vec.y <= this.my );
- };
- // returns true if the rectangle contains the position (x,y)
- BRect.prototype.contains_xy = function(x,y){
- return ( x >= this.x && x <= this.mx &&
- y >= this.y && y <= this.my );
- };
- // returns true if the ellipse contains the position defined by the vector 'vec'
- BEllipse.prototype.contains_vec = function(v){
- v = v.mult_xy(this.hx,this.hy);
- return v.len_sq() <= 1;
- };
- // returns true if the ellipse contains the position (x,y)
- BEllipse.prototype.contains_xy = function(x,y){
- return this.contains(new Vec2(x,y));
- };
-
-
-})(window);
-
-
diff --git a/addons/web_diagram/static/src/js/view_registry.js b/addons/web_diagram/static/src/js/view_registry.js
deleted file mode 100644
index edf670050ab..00000000000
--- a/addons/web_diagram/static/src/js/view_registry.js
+++ /dev/null
@@ -1,10 +0,0 @@
-odoo.define('web_diagram.view_registry', function (require) {
-"use strict";
-
-var view_registry = require('web.view_registry');
-
-var DiagramView = require('web_diagram.DiagramView');
-
-view_registry.add('diagram', DiagramView);
-
-});
diff --git a/addons/web_diagram/static/src/scss/diagram_view.scss b/addons/web_diagram/static/src/scss/diagram_view.scss
deleted file mode 100644
index c56f5ac0b67..00000000000
--- a/addons/web_diagram/static/src/scss/diagram_view.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-.o_diagram_view, .o_diagram {
- height: 100%;
- background-color: white;
-
- .o_diagram_header {
- @include o-webclient-padding($top: 16px, $bottom: 16px);
- > span {
- display: block;
- text-align: justify;
- }
- }
-}
diff --git a/addons/web_diagram/static/src/xml/base_diagram.xml b/addons/web_diagram/static/src/xml/base_diagram.xml
deleted file mode 100644
index 3745e0a3838..00000000000
--- a/addons/web_diagram/static/src/xml/base_diagram.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<template>
-
-<t t-name="DiagramView.buttons">
- <div t-if="widget.is_action_enabled('create')">
- <button type="button" class="btn btn-primary o_diagram_new_button">
- New Node
- </button>
- </div>
-</t>
-
-<t t-name="DiagramView">
- <div class="o_diagram_header"/>
- <div class="o_diagram"/>
-</t>
-
-</template>
diff --git a/addons/web_diagram/static/tests/diagram_tests.js b/addons/web_diagram/static/tests/diagram_tests.js
deleted file mode 100644
index 0e28b2c3101..00000000000
--- a/addons/web_diagram/static/tests/diagram_tests.js
+++ /dev/null
@@ -1,308 +0,0 @@
-odoo.define('web_diagram.diagram_tests', function (require) {
-"use strict";
-
-var DiagramView = require('web_diagram.DiagramView');
-
-var testUtils = require('web.test_utils');
-
-var createView = testUtils.createView;
-
-QUnit.module('Views', {
- beforeEach: function () {
- this.data = {
- workflow: {
- fields: {
- name: {string: 'Name', type: "char"},
- },
- records: [
- {id: 1, name: "a workflow"},
- ],
- },
- node_model: {
- fields: {
- name: {string: 'Name', type: 'char'},
- workflow_id: {string: 'Workflow ID', type: 'many2one', relation: 'workflow'},
- },
- records: [
- {id: 1, name: "A first node", workflow_id: 1},
- {id: 2, name: "A second node", workflow_id: 1},
- {id: 3, name: "A third node", workflow_id: 1},
- ],
- },
- transition_model: {
- fields: {
- name: {string: 'Name', type: 'char'},
- source_id: {string: 'Source ID', type: 'many2one', relation: 'node_model'},
- dest_id: {string: 'Destination ID', type: 'many2one', relation: 'node_model'},
- },
- records: [
- {id: 1, name: 'a transition from 1 to 2', source_id: 1, dest_id: 2},
- {id: 2, name: 'a transition from 2 to 3', source_id: 2, dest_id: 3},
- {id: 3, name: 'a transition from 2 to 1', source_id: 2, dest_id: 1},
- ],
- },
- };
- this.arch = '<diagram>' +
- '<node object="node_model"/>' +
- '<arrow ' +
- 'object="transition_model" ' +
- 'source="source_id" ' +
- 'destination="dest_id" ' +
- 'label="[\'name\']"' +
- '/>' +
- '<label string="A first label"/>' +
- '<label string="A second label"/>' +
- '</diagram>';
- this.archs = {
- 'node_model,false,form': '<form string="Create a new node">' +
- '<field name="name"/>' +
- '<field name="workflow_id"/>' +
- '</form>',
- 'transition_model,false,form': '<form string="Create an edge">' +
- '<field name="name"/>' +
- '<field name="source_id"/>' +
- '<field name="dest_id"/>' +
- '</form>'
- };
- this.mockRPC = function (route, args) {
- if (route !== '/web_diagram/diagram/get_diagram_info') {
- return this._super.apply(this, arguments);
- }
- var data = this.data;
- var node_records = _.filter(data.node_model.records, {workflow_id: args.id});
- var transition_records = _.filter(data.transition_model.records, function (record) {
- return _.findWhere(node_records, {id: record.source_id});
- });
- return Promise.resolve({
- parent_field: 'workflow_id',
- display_name: _.findWhere(data.workflow.records, {id: args.id}).name,
- nodes: _.map(node_records, function (record) {
- return {
- id: record.id,
- name: record.name,
- color: 'gray',
- shape: record.id === 1 ? 'rectangle' : undefined,
- };
- }),
- conn: _.map(transition_records, function (record) {
- var source_node = _.findWhere(data.node_model.records, {id: record.source_id});
- var dest_node = _.findWhere(data.node_model.records, {id: record.dest_id});
- return {
- source: source_node ? source_node.name: '',
- destination: dest_node ? dest_node.name: '',
- s_id: record.source_id,
- d_id: record.dest_id,
- signal: record.name,
- };
- }),
- });
- };
- },
-}, function () {
-
- QUnit.module('DiagramView');
-
- QUnit.test('simple diagram rendering', async function (assert) {
- assert.expect(6);
-
- var diagram = await createView({
- View: DiagramView,
- model: 'workflow',
- data: this.data,
- arch: this.arch,
- res_id: 1,
- mockRPC: this.mockRPC,
- });
- assert.containsOnce(diagram, '.o_diagram svg',
- "draw the diagram inside the .o_diagram div");
- assert.strictEqual(diagram.$('.o_diagram path:not(#raphael-marker-block)').length, 3,
- "diagram should contain 3 transitions");
- assert.containsN(diagram, '.o_diagram ellipse', 2,
- "diagram should contain 2 'ellipse' nodes (nodes 2 and 3)");
- assert.strictEqual(diagram.$('.o_diagram rect').length - 1, 1, // -1 because the lib always generates a rect tag that isn't a node
- "diagram should contain 1 'rectangle' node (node 1)");
- assert.containsN(diagram, '.o_diagram_header span', 2,
- "diagram should contain 2 header rows");
- assert.strictEqual(diagram.$('.o_diagram_header span:eq(0)').text(), 'A first label',
- "diagram label is correctly inserted");
- diagram.destroy();
- });
-
- QUnit.test('node creation', async function (assert) {
- assert.expect(4);
-
- var diagram = await createView({
- View: DiagramView,
- model: 'workflow',
- data: this.data,
- arch: this.arch,
- archs: this.archs,
- res_id: 1,
- mockRPC: this.mockRPC,
- });
-
- assert.containsN(diagram, '.o_diagram ellipse', 2,
- "diagram should contain 2 'ellipse' nodes (nodes 2 and 3)");
- assert.strictEqual(diagram.$('text:contains(a new node)').length, 0,
- "diagram should only have the default nodes at start");
-
- await testUtils.dom.click(diagram.$buttons.find('.o_diagram_new_button'));
- await testUtils.fields.editInput($('.modal-body input:first'), 'a new node');
- await testUtils.fields.editInput($('.modal-body input:last'), 1);
- await testUtils.dom.click($('.modal-footer button.btn-primary'));
-
- assert.containsN(diagram, '.o_diagram ellipse', 3,
- "diagram should contain 3 'ellipse' nodes now (nodes 2, 3 and the new one)");
- assert.strictEqual(diagram.$('text:contains(a new node)').length, 1,
- "diagram should only have the default nodes at start");
-
- diagram.destroy();
- });
-
- QUnit.test('node edition', async function (assert) {
- assert.expect(2);
-
- var diagram = await createView({
- View: DiagramView,
- model: 'workflow',
- data: this.data,
- arch: this.arch,
- archs: this.archs,
- res_id: 1,
- mockRPC: this.mockRPC,
- });
-
- assert.strictEqual(diagram.$('.o_diagram text').first().text(), 'A first node',
- "diagram first node should have default name at first");
-
- CuteNode.double_click_callback({id: 1});
- await testUtils.nextTick();
- await testUtils.fields.editInput($('.modal-body input:first'), 'An edited node');
- await testUtils.dom.click($('.modal-footer button.btn-primary'));
-
- assert.strictEqual(diagram.$('text').first().text(), 'An edited node',
- "diagram first node should now have new name");
-
- diagram.destroy();
- });
-
- QUnit.test('node deletion', async function (assert) {
- assert.expect(2);
-
- var diagram = await createView({
- View: DiagramView,
- model: 'workflow',
- data: this.data,
- arch: this.arch,
- res_id: 1,
- mockRPC: this.mockRPC,
- });
-
- assert.containsN(diagram, '.o_diagram ellipse', 2,
- "diagram should contain 2 'ellipse' nodes (nodes 2 and 3)");
-
- CuteNode.destruction_callback({id: 2});
- await testUtils.nextTick();
- await testUtils.dom.click($('.modal-footer button.btn-primary'));
-
- assert.containsOnce(diagram, '.o_diagram ellipse',
- "diagram should contain 1 'ellipse' nodes (node 2)");
-
- diagram.destroy();
- });
-
- QUnit.test('edge creation', async function (assert) {
- assert.expect(4);
-
- var diagram = await createView({
- View: DiagramView,
- model: 'workflow',
- data: this.data,
- arch: this.arch,
- archs: this.archs,
- res_id: 1,
- mockRPC: this.mockRPC,
- });
-
- assert.containsN(diagram, '.o_diagram path', 4,
- "diagram should contain 4 'path' nodes (#raphael-marker-block, and transitions 1, 2 and 3)");
- assert.strictEqual(diagram.$('text:contains(a transition from 1 to 3)').length, 0,
- "diagram should not have a transition from 1 to 3 at start");
-
- CuteEdge.new_edge_callback({
- get_start: function () {return {id: 1};},
- get_end: function () {return {id: 3};},
- });
- await testUtils.nextTick();
- await testUtils.fields.editInput($('.modal-body input:first'), 'a transition from 1 to 3');
- await testUtils.dom.click($('.modal-footer button.btn-primary'));
-
- assert.containsN(diagram, '.o_diagram path', 5,
- "diagram should contain 4 'path' nodes (#raphael-marker-block, transitions 1, 2, 3, and the new one)");
- assert.strictEqual(diagram.$('text:contains(a transition from 1 to 3)').length, 1,
- "diagram should now have a transition from 1 to 3");
-
- diagram.destroy();
- });
-
- QUnit.test('edge edition', async function (assert) {
- assert.expect(4);
-
- var diagram = await createView({
- View: DiagramView,
- model: 'workflow',
- data: this.data,
- arch: this.arch,
- archs: this.archs,
- res_id: 1,
- mockRPC: this.mockRPC,
- });
-
- assert.strictEqual(diagram.$('text:contains(a transition from 1 to 2)').length, 1,
- "diagram edge should have default name at start");
- assert.strictEqual(diagram.$('text:contains(An edited edge)').length, 0,
- "diagram should only have the default edges at start");
-
- CuteEdge.double_click_callback({id: 1});
- await testUtils.nextTick();
- await testUtils.fields.editInput($('.modal-body input:first'), 'An edited edge');
- await testUtils.dom.click($('.modal-footer button.btn-primary'));
-
- assert.strictEqual(diagram.$('text:contains(a transition from 1 to 2)').length, 0,
- "diagram edge should not have default name anymore");
- assert.strictEqual(diagram.$('text:contains(An edited edge)').length, 1,
- "diagram should now have the new edge");
-
- diagram.destroy();
- });
-
- QUnit.test('edge deletion', async function (assert) {
- assert.expect(4);
-
- var diagram = await createView({
- View: DiagramView,
- model: 'workflow',
- data: this.data,
- arch: this.arch,
- res_id: 1,
- mockRPC: this.mockRPC,
- });
-
- assert.containsN(diagram, '.o_diagram path', 4,
- "diagram should contain 4 'path' nodes (#raphael-marker-block, and transitions 1, 2 and 3)");
- assert.strictEqual(diagram.$('text:contains(a transition from 2 to 1)').length, 1,
- "diagram edge should have default name at start");
-
- CuteEdge.destruction_callback({id: 3});
- await testUtils.nextTick();
- await testUtils.dom.click($('.modal-footer button.btn-primary'));
-
- assert.containsN(diagram, '.o_diagram path', 3,
- "diagram should contain 3 'path' nodes (#raphael-marker-block, and transitions 1 and 2)");
- assert.strictEqual(diagram.$('text:contains(a transition from 2 to 1)').length, 0,
- "diagram edge label should have been deleted");
-
- diagram.destroy();
- });
-});
-});
diff --git a/addons/web_diagram/views/web_diagram_templates.xml b/addons/web_diagram/views/web_diagram_templates.xml
deleted file mode 100644
index 23eb3738b3a..00000000000
--- a/addons/web_diagram/views/web_diagram_templates.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<odoo>
- <data>
- <template id="assets_backend" name="web_diagram assets" inherit_id="web.assets_backend">
- <xpath expr="." position="inside">
- <link rel="stylesheet" type="text/scss" href="/web_diagram/static/src/scss/diagram_view.scss"/>
- <script type="text/javascript" src="/web_diagram/static/src/js/vec2.js"></script>
- <script type="text/javascript" src="/web_diagram/static/src/js/graph.js"></script>
- <script type="text/javascript" src="/web_diagram/static/src/js/diagram_model.js"></script>
- <script type="text/javascript" src="/web_diagram/static/src/js/diagram_controller.js"></script>
- <script type="text/javascript" src="/web_diagram/static/src/js/diagram_renderer.js"></script>
- <script type="text/javascript" src="/web_diagram/static/src/js/diagram_view.js"></script>
- <script type="text/javascript" src="/web_diagram/static/src/js/view_registry.js"></script>
- </xpath>
- </template>
-
- <template id="qunit_suite" name="web_diagram tests" inherit_id="web.qunit_suite">
- <xpath expr="//script[contains(@src, '/web/static/tests/views/kanban_tests.js')]" position="after">
- <script type="text/javascript" src="/web_diagram/static/tests/diagram_tests.js"></script>
- </xpath>
- </template>
- </data>
-</odoo>
diff --git a/addons/web_editor/models/ir_qweb.py b/addons/web_editor/models/ir_qweb.py
index 9077fab0389..50e3a1a43e7 100644
--- a/addons/web_editor/models/ir_qweb.py
+++ b/addons/web_editor/models/ir_qweb.py
@@ -250,7 +250,7 @@ class DateTime(models.AbstractModel):
dt = user_tz.localize(dt).astimezone(utc)
except Exception:
- logger.warn(
+ logger.warning(
"Failed to convert the value for a field of the model"
" %s back from the user's timezone (%s) to UTC",
model, tz_name,
diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py
index ccfc2c531e4..db2a87df593 100644
--- a/addons/website/models/ir_http.py
+++ b/addons/website/models/ir_http.py
@@ -301,9 +301,9 @@ class Http(models.AbstractModel):
# Needed to show reset template on translated pages (`_prepare_qcontext` will set it for main lang)
values['editable'] = request.uid and request.website.is_publisher()
elif code == 403:
- logger.warn("403 Forbidden:\n\n%s", values['traceback'])
+ logger.warning("403 Forbidden:\n\n%s", values['traceback'])
elif code == 400:
- logger.warn("400 Bad Request:\n\n%s", values['traceback'])
+ logger.warning("400 Bad Request:\n\n%s", values['traceback'])
try:
html = env['ir.ui.view'].render_template('website.%s' % view_id, values)
except Exception:
diff --git a/addons/website/models/website.py b/addons/website/models/website.py
index 0ddca6810e2..56bdf09e972 100644
--- a/addons/website/models/website.py
+++ b/addons/website/models/website.py
@@ -667,14 +667,15 @@ class Website(models.Model):
return False
# dont't list routes without argument having no default value or converter
- spec = inspect.getargspec(endpoint.method.original_func)
-
- # remove self and arguments having a default value
- defaults_count = len(spec.defaults or [])
- args = spec.args[1:(-defaults_count or None)]
+ sign = inspect.signature(endpoint.method.original_func)
+ params = list(sign.parameters.values())[1:] # skip self
+ supported_kinds = (inspect.Parameter.POSITIONAL_ONLY,
+ inspect.Parameter.POSITIONAL_OR_KEYWORD)
+ has_no_default = lambda p: p.default is inspect.Parameter.empty
# check that all args have a converter
- return all((arg in rule._converters) for arg in args)
+ return all(p.name in rule._converters for p in params
+ if p.kind in supported_kinds and has_no_default(p))
def enumerate_pages(self, query_string=None, force=False):
""" Available pages in the website/CMS. This is mostly used for links
@@ -786,7 +787,7 @@ class Website(models.Model):
def image_url(self, record, field, size=None):
""" Returns a local url that points to the image field of a given browse record. """
sudo_record = record.sudo()
- sha = hashlib.sha1(str(getattr(sudo_record, '__last_update')).encode('utf-8')).hexdigest()[0:7]
+ sha = hashlib.sha512(str(getattr(sudo_record, '__last_update')).encode('utf-8')).hexdigest()[:7]
size = '' if size is None else '/%s' % size
return '/web/image/%s/%s/%s%s?unique=%s' % (record._name, record.id, field, size, sha)
diff --git a/addons/website_event_questions/views/event_templates.xml b/addons/website_event_questions/views/event_templates.xml
index cacaa567170..4f8329fc294 100644
--- a/addons/website_event_questions/views/event_templates.xml
+++ b/addons/website_event_questions/views/event_templates.xml
@@ -8,7 +8,6 @@
</template>
<template id="registration_attendee_details_questions" inherit_id="website_event.registration_attendee_details" name="Registration Attendee Details with questions">
- <data>
<!-- Attendee specific questions -->
<xpath expr="//t[@name='attendee_loop']" position="inside">
<t t-if="event.specific_question_ids">
@@ -49,7 +48,6 @@
</div>
</t>
</xpath>
- </data>
</template>
</odoo>
diff --git a/addons/website_form/controllers/main.py b/addons/website_form/controllers/main.py
index 81d7cf363fa..b2688c76c7e 100644
--- a/addons/website_form/controllers/main.py
+++ b/addons/website_form/controllers/main.py
@@ -214,7 +214,7 @@ class WebsiteForm(http.Controller):
custom_field = file.field_name not in authorized_fields
attachment_value = {
'name': file.filename,
- 'datas': base64.encodestring(file.read()),
+ 'datas': base64.encodebytes(file.read()),
'res_model': model_name,
'res_id': record.id,
}
diff --git a/addons/website_mail_channel/tests/test_unsubscribe.py b/addons/website_mail_channel/tests/test_unsubscribe.py
index ec1a915f232..28934d5f49d 100644
--- a/addons/website_mail_channel/tests/test_unsubscribe.py
+++ b/addons/website_mail_channel/tests/test_unsubscribe.py
@@ -25,7 +25,7 @@ class TestConfirmUnsubscribe(common.HttpCase):
def test_not_subscribed_no_template(self):
""" Test warning works on db without template (code update w/o module update) """
self.env.ref('website_mail_channel.not_subscribed').unlink()
- self.assertEquals(
+ self.assertEqual(
self.env['ir.model.data'].search_count([
('module', '=', 'website_mail_channel'),
('name', '=', 'not_subscribed'),
diff --git a/addons/website_sale/tests/test_website_sale_pricelist.py b/addons/website_sale/tests/test_website_sale_pricelist.py
index 59af832fc74..d6a372aae5c 100644
--- a/addons/website_sale/tests/test_website_sale_pricelist.py
+++ b/addons/website_sale/tests/test_website_sale_pricelist.py
@@ -91,7 +91,7 @@ class TestWebsitePriceList(TransactionCase):
}
for country, result in country_list.items():
pls = self.get_pl(show, current_pl, country)
- self.assertEquals(len(set(pls.mapped('name')) & set(result)), len(pls), 'Test failed for %s (%s %s vs %s %s)'
+ self.assertEqual(len(set(pls.mapped('name')) & set(result)), len(pls), 'Test failed for %s (%s %s vs %s %s)'
% (country, len(pls), pls.mapped('name'), len(result), result))
def test_get_pricelist_available_not_show(self):
@@ -108,7 +108,7 @@ class TestWebsitePriceList(TransactionCase):
for country, result in country_list.items():
pls = self.get_pl(show, current_pl, country)
- self.assertEquals(len(set(pls.mapped('name')) & set(result)), len(pls), 'Test failed for %s (%s %s vs %s %s)'
+ self.assertEqual(len(set(pls.mapped('name')) & set(result)), len(pls), 'Test failed for %s (%s %s vs %s %s)'
% (country, len(pls), pls.mapped('name'), len(result), result))
def test_get_pricelist_available_promocode(self):
@@ -145,7 +145,7 @@ class TestWebsitePriceList(TransactionCase):
}
for country, result in country_list.items():
pls = self.get_pl(show, current_pl, country)
- self.assertEquals(len(set(pls.mapped('name')) & set(result)), len(pls), 'Test failed for %s (%s %s vs %s %s)'
+ self.assertEqual(len(set(pls.mapped('name')) & set(result)), len(pls), 'Test failed for %s (%s %s vs %s %s)'
% (country, len(pls), pls.mapped('name'), len(result), result))
diff --git a/addons/website_sale_comparison/tests/test_website_sale_comparison.py b/addons/website_sale_comparison/tests/test_website_sale_comparison.py
index 21e1db939d7..9f68b49c6bc 100644
--- a/addons/website_sale_comparison/tests/test_website_sale_comparison.py
+++ b/addons/website_sale_comparison/tests/test_website_sale_comparison.py
@@ -39,9 +39,9 @@ class TestWebsiteSaleComparison(odoo.tests.TransactionCase):
product.with_context(website_id=1).write({'name': 'Trigger COW'})
# Verify initial state: the specific views exist
- self.assertEquals(Website1.viewref('website_sale.product').website_id.id, 1)
- self.assertEquals(Website1.viewref('website_sale_comparison.product_add_to_compare').website_id.id, 1)
- self.assertEquals(Website1.viewref(test_view_key).website_id.id, 1)
+ self.assertEqual(Website1.viewref('website_sale.product').website_id.id, 1)
+ self.assertEqual(Website1.viewref('website_sale_comparison.product_add_to_compare').website_id.id, 1)
+ self.assertEqual(Website1.viewref(test_view_key).website_id.id, 1)
# Remove the module (use `module_uninstall` because it is enough to test
# what we want here, no need/can't use `button_immediate_uninstall`
diff --git a/addons/website_slides/__manifest__.py b/addons/website_slides/__manifest__.py
index 1da8adc4eee..5373f5bd6de 100644
--- a/addons/website_slides/__manifest__.py
+++ b/addons/website_slides/__manifest__.py
@@ -29,7 +29,6 @@ Featuring
'views/res_config_settings_views.xml',
'views/res_partner_views.xml',
'views/rating_rating_views.xml',
- 'views/res_partner_views.xml',
'views/slide_question_views.xml',
'views/slide_slide_views.xml',
'views/slide_channel_partner_views.xml',
diff --git a/addons/website_slides/static/src/scss/rating_rating_views.scss b/addons/website_slides/static/src/scss/rating_rating_views.scss
index e86a1811c10..fa920c1b7f1 100644
--- a/addons/website_slides/static/src/scss/rating_rating_views.scss
+++ b/addons/website_slides/static/src/scss/rating_rating_views.scss
@@ -2,14 +2,14 @@ $o-kanban-large-record-width: 350px;
.o_kanban_view.o_slide_rating_kanban {
.o_kanban_record {
- width: $o-kanban-large-record-width
+ width: $o-kanban-large-record-width
}
.o_slide_rating_kanban_left {
- min-width: 80px;
+ min-width: 80px;
- .o_slide_rating_value {
- font-size: 4rem;
- }
+ .o_slide_rating_value {
+ font-size: 4rem;
+ }
}
}
diff --git a/addons/website_slides/static/src/scss/slide_views.scss b/addons/website_slides/static/src/scss/slide_views.scss
index 5edba5e0b9e..035f4699309 100644
--- a/addons/website_slides/static/src/scss/slide_views.scss
+++ b/addons/website_slides/static/src/scss/slide_views.scss
@@ -5,7 +5,7 @@ $o-kanban-large-record-width: 400px;
width: $o-kanban-large-record-width + 2*$o-kanban-group-padding;
}
.o_kanban_record {
- width: $o-kanban-large-record-width;
+ width: $o-kanban-large-record-width;
}
}
diff --git a/addons/website_slides/wizard/slide_channel_invite.py b/addons/website_slides/wizard/slide_channel_invite.py
index be7db01a12e..43e384ade30 100644
--- a/addons/website_slides/wizard/slide_channel_invite.py
+++ b/addons/website_slides/wizard/slide_channel_invite.py
@@ -4,10 +4,9 @@
import logging
import re
-from email.utils import formataddr
-
from odoo import api, fields, models, _
from odoo.exceptions import UserError
+from odoo.tools import formataddr
_logger = logging.getLogger(__name__)
diff --git a/addons/website_theme_install/tests/test_views.py b/addons/website_theme_install/tests/test_views.py
index 42be90dfbd5..a81fd1da4e8 100644
--- a/addons/website_theme_install/tests/test_views.py
+++ b/addons/website_theme_install/tests/test_views.py
@@ -70,13 +70,13 @@ class Crawler(HttpCase):
# It should crash as it should not find a view on website 1 for '_theme_kea_sale.products', !!and certainly not a theme.ir.ui.view!!.
view = View.with_context(website_id=website_1.id)._view_obj('_theme_kea_sale.products')
view = View.with_context(website_id=website_2.id)._view_obj('_theme_kea_sale.products')
- self.assertEquals(len(view), 1, "It should find the ir.ui.view with key '_theme_kea_sale.products' on website 2..")
- self.assertEquals(view._name, 'ir.ui.view', "..and not a theme.ir.ui.view")
+ self.assertEqual(len(view), 1, "It should find the ir.ui.view with key '_theme_kea_sale.products' on website 2..")
+ self.assertEqual(view._name, 'ir.ui.view', "..and not a theme.ir.ui.view")
views = View.with_context(website_id=website_1.id).get_related_views('_website_sale.products')
- self.assertEquals(len(views), 2, "It should not mix apples and oranges, only ir.ui.view ['_website_sale.products', '_website_sale.child_view_w1'] should be returned")
+ self.assertEqual(len(views), 2, "It should not mix apples and oranges, only ir.ui.view ['_website_sale.products', '_website_sale.child_view_w1'] should be returned")
views = View.with_context(website_id=website_2.id).get_related_views('_website_sale.products')
- self.assertEquals(len(views), 2, "It should not mix apples and oranges, only ir.ui.view ['_website_sale.products', '_theme_kea_sale.products'] should be returned")
+ self.assertEqual(len(views), 2, "It should not mix apples and oranges, only ir.ui.view ['_website_sale.products', '_theme_kea_sale.products'] should be returned")
# Part 2 of the test, it test the same stuff but from a higher level (get_related_views ends up calling _view_obj)
called_theme_view = self.env['theme.ir.ui.view'].with_context(install_filename='/testviews').create({
@@ -115,9 +115,9 @@ class Crawler(HttpCase):
# Next line should not crash (was mixing apples and oranges - ir.ui.view and theme.ir.ui.view)
views = View.with_context(website_id=website_1.id).get_related_views('_website_sale.products')
- self.assertEquals(len(views), 2, "It should not mix apples and oranges, only ir.ui.view ['_website_sale.products', '_website_sale.child_view_w1'] should be returned (2)")
+ self.assertEqual(len(views), 2, "It should not mix apples and oranges, only ir.ui.view ['_website_sale.products', '_website_sale.child_view_w1'] should be returned (2)")
views = View.with_context(website_id=website_2.id).get_related_views('_website_sale.products')
- self.assertEquals(len(views), 3, "It should not mix apples and oranges, only ir.ui.view ['_website_sale.products', '_theme_kea_sale.products', '_theme_kea_sale.t_called_view'] should be returned")
+ self.assertEqual(len(views), 3, "It should not mix apples and oranges, only ir.ui.view ['_website_sale.products', '_theme_kea_sale.products', '_theme_kea_sale.t_called_view'] should be returned")
# ########################################################
# Test the controller (which is calling get_related_views)
diff --git a/debian/copyright b/debian/copyright
index 28d0c476bf4..54600d6ddc6 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -42,15 +42,6 @@ Files: addons/point_of_sale/static/lib/fastclick.js
Copyright: (C) 2014 The Financial Times Limited [All Rights Reserved]
License: MIT
-Files: addons/web_diagram/static/lib/js/jquery.mousewheel.js
-Copyright: (C) 2011 Brandon Aaron (http://brandonaaron.net)
-License: MIT
-
-Files: addons/web_diagram/static/lib/js/raphael.js
-Copyright: Copyright © 2008-2012 Dmitry Baranovskiy (http://raphaeljs.com)
- Copyright © 2008-2012 Sencha Labs (http://sencha.com)
-License: MIT
-
Files: addons/pos_restaurant/static/lib/js/jquery.event.drag-2.2.js
Copyright: Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
License: MIT
diff --git a/doc/_extensions/odoo_ext/pygments_override.py b/doc/_extensions/odoo_ext/pygments_override.py
index d03fb4f0ebc..aa03003c0f1 100644
--- a/doc/_extensions/odoo_ext/pygments_override.py
+++ b/doc/_extensions/odoo_ext/pygments_override.py
@@ -118,9 +118,9 @@ class OdooStyle(Style):
Generic.Traceback: "",
}
-import imp
+import types
import sys
modname = 'pygments.styles.odoo'
-m = imp.new_module(modname)
+m = types.ModuleType(modname)
m.OdooStyle = OdooStyle
sys.modules[modname] = m
diff --git a/doc/cla/individual/bikibi.md b/doc/cla/individual/bikibi.md
new file mode 100644
index 00000000000..35433a6c36b
--- /dev/null
+++ b/doc/cla/individual/bikibi.md
@@ -0,0 +1,9 @@
+France, 2019-09-12
+
+I hereby agree to the terms of the Odoo Individual Contributor License Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this declaration.
+
+Signed,
+
+Gwenaël Serre gwenael.serre@gmail.com https://github.com/bikibi
diff --git a/doc/cla/individual/niyasraphy.md b/doc/cla/individual/niyasraphy.md
new file mode 100644
index 00000000000..0a6314d0f42
--- /dev/null
+++ b/doc/cla/individual/niyasraphy.md
@@ -0,0 +1,12 @@
+India, 2019-09-10
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Niyas Raphy niyasraphyk@gmail.com https://github.com/niyasraphy
+
diff --git a/doc/reference/views.rst b/doc/reference/views.rst
index c5788657b67..b67630bab93 100644
--- a/doc/reference/views.rst
+++ b/doc/reference/views.rst
@@ -1389,50 +1389,6 @@ take the following attributes:
will display the users avatars next to their names when grouped by user_id
-.. _reference/views/diagram:
-
-Diagram
-=======
-
-The diagram view can be used to display directed graphs of records. The root
-element is ``<diagram>`` and takes no attributes.
-
-Possible children of the diagram view are:
-
-``node`` (required, 1)
- Defines the nodes of the graph. Its attributes are:
-
- ``object``
- the node's Odoo model
- ``shape``
- conditional shape mapping similar to colors and fonts in :ref:`the list
- view <reference/views/list>`. The only valid shape is ``rectangle`` (the
- default shape is an ellipsis)
- ``bgcolor``
- same as ``shape``, but conditionally maps a background color for
- nodes. The default background color is white, the only valid alternative
- is ``grey``.
-``arrow`` (required, 1)
- Defines the directed edges of the graph. Its attributes are:
-
- ``object`` (required)
- the edge's Odoo model
- ``source`` (required)
- :class:`~odoo.fields.Many2one` field of the edge's model pointing to
- the edge's source node record
- ``destination`` (required)
- :class:`~odoo.fields.Many2one` field of the edge's model pointing to
- the edge's destination node record
- ``label``
- Python list of attributes (as quoted strings). The corresponding
- attributes's values will be concatenated and displayed as the edge's
- label
-
-``label``
- Explanatory note for the diagram, the ``string`` attribute defines the
- note's content. Each ``label`` is output as a paragraph in the diagram
- header, easily visible but without any special emphasis.
-
.. _reference/views/dashboard:
Dashboard
diff --git a/odoo/addons/base/models/assetsbundle.py b/odoo/addons/base/models/assetsbundle.py
index d1554594aa3..e46a779dd28 100644
--- a/odoo/addons/base/models/assetsbundle.py
+++ b/odoo/addons/base/models/assetsbundle.py
@@ -186,10 +186,10 @@ class AssetsBundle(object):
def checksum(self):
"""
Not really a full checksum.
- We compute a SHA1 on the rendered bundle + max linked files last_modified date
+ We compute a SHA512/256 on the rendered bundle + max linked files last_modified date
"""
check = u"%s%s" % (json.dumps(self.files, sort_keys=True), self.last_modified)
- return hashlib.sha1(check.encode('utf-8')).hexdigest()
+ return hashlib.sha512(check.encode('utf-8')).hexdigest()[:64]
def _get_asset_template_url(self):
return "/web/content/{id}-{unique}/{extra}{name}{sep}{type}"
diff --git a/odoo/addons/base/models/ir_actions_report.py b/odoo/addons/base/models/ir_actions_report.py
index a3d354cfabc..b27c801ac04 100644
--- a/odoo/addons/base/models/ir_actions_report.py
+++ b/odoo/addons/base/models/ir_actions_report.py
@@ -179,7 +179,7 @@ class IrActionsReport(models.Model):
img = Image.open(stream)
img.convert("RGB").save(stream, format="pdf")
return stream
- return io.BytesIO(base64.decodestring(attachment.datas))
+ return io.BytesIO(base64.decodebytes(attachment.datas))
def retrieve_attachment(self, record):
'''Retrieve an attachment for a specific record.
@@ -211,7 +211,7 @@ class IrActionsReport(models.Model):
return None
attachment_vals = {
'name': attachment_name,
- 'datas': base64.encodestring(buffer.getvalue()),
+ 'datas': base64.encodebytes(buffer.getvalue()),
'res_model': self.model,
'res_id': record.id,
'type': 'binary',
diff --git a/odoo/addons/base/models/ir_attachment.py b/odoo/addons/base/models/ir_attachment.py
index 27b52033ab5..a58ba54c448 100644
--- a/odoo/addons/base/models/ir_attachment.py
+++ b/odoo/addons/base/models/ir_attachment.py
@@ -11,7 +11,7 @@ from collections import defaultdict
import uuid
from odoo import api, fields, models, tools, _
-from odoo.exceptions import AccessError, ValidationError, MissingError
+from odoo.exceptions import AccessError, ValidationError, MissingError, UserError
from odoo.tools import config, human_size, ustr, html_escape
from odoo.tools.mimetypes import guess_mimetype
@@ -90,6 +90,9 @@ class IrAttachment(models.Model):
dirname = os.path.dirname(full_path)
if not os.path.isdir(dirname):
os.makedirs(dirname)
+ # prevent sha-1 collision
+ if os.path.isfile(full_path) and not self._same_content(bin_data, full_path):
+ raise UserError("The attachment is colliding with an existing file.")
return fname, full_path
@api.model
@@ -226,6 +229,20 @@ class IrAttachment(models.Model):
# an empty file has a checksum too (for caching)
return hashlib.sha1(bin_data or b'').hexdigest()
+ @api.model
+ def _same_content(self, bin_data, filepath):
+ BLOCK_SIZE = 1024
+ with open(filepath, 'rb') as fd:
+ i = 0
+ while True:
+ data = fd.read(BLOCK_SIZE)
+ if data != bin_data[i * BLOCK_SIZE:(i + 1) * BLOCK_SIZE]:
+ return False
+ if not data:
+ break
+ i += 1
+ return True
+
def _compute_mimetype(self, values):
""" compute the mimetype of the given values
:param values : dict of values to create or write an ir_attachment
diff --git a/odoo/addons/base/models/ir_http.py b/odoo/addons/base/models/ir_http.py
index 64ab86aea9b..5e142222eda 100644
--- a/odoo/addons/base/models/ir_http.py
+++ b/odoo/addons/base/models/ir_http.py
@@ -152,7 +152,7 @@ class IrHttp(models.AbstractModel):
wdate = attach[0]['__last_update']
datas = attach[0]['datas'] or b''
name = attach[0]['name']
- checksum = attach[0]['checksum'] or hashlib.sha1(datas).hexdigest()
+ checksum = attach[0]['checksum'] or hashlib.sha512(datas).hexdigest()[:64] # sha512/256
if (not datas and name != request.httprequest.path and
name.startswith(('http://', 'https://', '/'))):
diff --git a/odoo/addons/base/models/ir_mail_server.py b/odoo/addons/base/models/ir_mail_server.py
index 195f899172d..8a467e16f9f 100644
--- a/odoo/addons/base/models/ir_mail_server.py
+++ b/odoo/addons/base/models/ir_mail_server.py
@@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from email import encoders
-from email.charset import Charset
-from email.header import Header
-from email.mime.base import MIMEBase
-from email.mime.multipart import MIMEMultipart
-from email.mime.text import MIMEText
-from email.utils import COMMASPACE, formataddr, formatdate, getaddresses, make_msgid
+from email.message import EmailMessage
+from email.utils import make_msgid
+import datetime
+import email
+import email.policy
import logging
import re
import smtplib
@@ -44,48 +42,6 @@ smtplib.stderr = WriteToLogger()
def is_ascii(s):
return all(ord(cp) < 128 for cp in s)
-def encode_header(header_text):
- """Returns an appropriate representation of the given header value,
- suitable for direct assignment as a header value in an
- email.message.Message. RFC2822 assumes that headers contain
- only 7-bit characters, so we ensure it is the case, using
- RFC2047 encoding when needed.
-
- :param header_text: unicode or utf-8 encoded string with header value
- :rtype: string | email.header.Header
- :return: if ``header_text`` represents a plain ASCII string,
- return the same 7-bit string, otherwise returns an email.header.Header
- that will perform the appropriate RFC2047 encoding of
- non-ASCII values.
- """
- if not header_text:
- return ""
- header_text = ustr(header_text) # FIXME: require unicode higher up?
- if is_ascii(header_text):
- return pycompat.to_text(header_text)
- return Header(header_text, 'utf-8')
-
-def encode_header_param(param_text):
- """Returns an appropriate RFC2047 encoded representation of the given
- header parameter value, suitable for direct assignation as the
- param value (e.g. via Message.set_param() or Message.add_header())
- RFC2822 assumes that headers contain only 7-bit characters,
- so we ensure it is the case, using RFC2047 encoding when needed.
-
- :param param_text: unicode or utf-8 encoded string with header value
- :rtype: string
- :return: if ``param_text`` represents a plain ASCII string,
- return the same 7-bit string, otherwise returns an
- ASCII string containing the RFC2047 encoded text.
- """
- # For details see the encode_header() method that uses the same logic
- if not param_text:
- return ""
- param_text = ustr(param_text) # FIXME: require unicode higher up?
- if is_ascii(param_text):
- return pycompat.to_text(param_text) # TODO: is that actually necessary?
- return Charset("utf-8").header_encode(param_text)
-
address_pattern = re.compile(r'([^ ,<@]+@[^> ,]+)')
def extract_rfc2822_addresses(text):
@@ -99,40 +55,6 @@ def extract_rfc2822_addresses(text):
return [c for c in candidates if is_ascii(c)]
-def encode_rfc2822_address_header(header_text):
- """If ``header_text`` contains non-ASCII characters,
- attempts to locate patterns of the form
- ``"Name" <address@domain>`` and replace the
- ``"Name"`` portion by the RFC2047-encoded
- version, preserving the address part untouched.
- """
- def encode_addr(addr):
- name, email = addr
- # If s is a <text string>, then charset is a hint specifying the
- # character set of the characters in the string. The Unicode string
- # will be encoded using the following charsets in order: us-ascii,
- # the charset hint, utf-8. The first character set to not provoke a
- # UnicodeError is used.
- # -> always pass a text string to Header
-
- # also Header.__str__ in Python 3 "Returns an approximation of the
- # Header as a string, using an unlimited line length.", the old one
- # was "A synonym for Header.encode()." so call encode() directly?
- name = Header(pycompat.to_text(name)).encode()
- # if the from does not follow the (name <addr>),* convention, we might
- # try to encode meaningless strings as address, as getaddresses is naive
- # note it would also fail on real addresses with non-ascii characters
- try:
- return formataddr((name, email))
- except UnicodeEncodeError:
- _logger.warning(_('Failed to encode the address %s\n'
- 'from mail header:\n%s') % (addr, header_text))
- return ""
-
- addresses = getaddresses([pycompat.to_text(ustr(header_text))])
- return COMMASPACE.join(a for a in (encode_addr(addr) for addr in addresses) if a)
-
-
class IrMailServer(models.Model):
"""Represents an SMTP server, able to send outgoing emails, with SSL and TLS capabilities."""
_name = "ir.mail_server"
@@ -296,7 +218,7 @@ class IrMailServer(models.Model):
"""Constructs an RFC2822 email.message.Message object based on the keyword arguments passed, and returns it.
:param string email_from: sender email address
- :param list email_to: list of recipient addresses (to be joined with commas)
+ :param list email_to: list of recipient addresses (to be joined with commas)
:param string subject: email subject (no pre-encoding/quoting necessary)
:param string body: email body, of the type ``subtype`` (by default, plaintext).
If html subtype is used, the message will be automatically converted
@@ -318,7 +240,7 @@ class IrMailServer(models.Model):
:param list email_bcc: optional list of string values for BCC header (to be joined with commas)
:param dict headers: optional map of headers to set on the outgoing mail (may override the
other headers, including Subject, Reply-To, Message-Id, etc.)
- :rtype: email.message.Message (usually MIMEMultipart)
+ :rtype: email.message.EmailMessage
:return: the new RFC2822 email message
"""
email_from = email_from or tools.config.get('email_from')
@@ -326,78 +248,47 @@ class IrMailServer(models.Model):
"a global sender address in the server configuration or with the "\
"--email-from startup parameter."
- # Note: we must force all strings to to 8-bit utf-8 when crafting message,
- # or use encode_header() for headers, which does it automatically.
-
headers = headers or {} # need valid dict later
email_cc = email_cc or []
email_bcc = email_bcc or []
body = body or u''
- email_body = ustr(body)
- email_text_part = MIMEText(email_body, _subtype=subtype, _charset='utf-8')
- msg = MIMEMultipart()
-
+ msg = EmailMessage(policy=email.policy.SMTP)
if not message_id:
if object_id:
message_id = tools.generate_tracking_message_id(object_id)
else:
message_id = make_msgid()
- msg['Message-Id'] = encode_header(message_id)
+ msg['Message-Id'] = message_id
if references:
- msg['references'] = encode_header(references)
- msg['Subject'] = encode_header(subject)
- msg['From'] = encode_rfc2822_address_header(email_from)
+ msg['references'] = references
+ msg['Subject'] = subject
+ msg['From'] = email_from
del msg['Reply-To']
- if reply_to:
- msg['Reply-To'] = encode_rfc2822_address_header(reply_to)
- else:
- msg['Reply-To'] = msg['From']
- msg['To'] = encode_rfc2822_address_header(COMMASPACE.join(email_to))
+ msg['Reply-To'] = reply_to or email_from
+ msg['To'] = email_to
if email_cc:
- msg['Cc'] = encode_rfc2822_address_header(COMMASPACE.join(email_cc))
+ msg['Cc'] = email_cc
if email_bcc:
- msg['Bcc'] = encode_rfc2822_address_header(COMMASPACE.join(email_bcc))
- msg['Date'] = formatdate()
- # Custom headers may override normal headers or provide additional ones
+ msg['Bcc'] = email_bcc
+ msg['Date'] = datetime.datetime.utcnow()
for key, value in headers.items():
- msg[pycompat.to_text(ustr(key))] = encode_header(value)
+ msg[pycompat.to_text(ustr(key))] = value
+ email_body = ustr(body)
if subtype == 'html' and not body_alternative:
- # Always provide alternative text body ourselves if possible.
- text = html2text.html2text(email_body)
- alternative_part = MIMEMultipart(_subtype="alternative")
- alternative_part.attach(MIMEText(text, _charset='utf-8', _subtype='plain'))
- alternative_part.attach(email_text_part)
- msg.attach(alternative_part)
+ msg.add_alternative(email_body, subtype=subtype, charset='utf-8')
+ msg.add_alternative(html2text.html2text(email_body), subtype='plain', charset='utf-8')
elif body_alternative:
- # Include both alternatives, as specified, within a multipart/alternative part
- alternative_part = MIMEMultipart(_subtype="alternative")
- body_alternative_ = ustr(body_alternative)
- alternative_body_part = MIMEText(body_alternative_, _subtype=subtype_alternative, _charset='utf-8')
- alternative_part.attach(alternative_body_part)
- alternative_part.attach(email_text_part)
- msg.attach(alternative_part)
+ msg.add_alternative(email_body, subtype=subtype, charset='utf-8')
+ msg.add_alternative(ustr(body_alternative), subtype=subtype_alternative, charset='utf-8')
else:
- msg.attach(email_text_part)
+ msg.set_content(email_body, subtype=subtype, charset='utf-8')
if attachments:
for (fname, fcontent, mime) in attachments:
- filename_rfc2047 = encode_header_param(fname)
- if mime and '/' in mime:
- maintype, subtype = mime.split('/', 1)
- part = MIMEBase(maintype, subtype)
- else:
- part = MIMEBase('application', "octet-stream")
-
- # The default RFC2231 encoding of Message.add_header() works in Thunderbird but not GMail
- # so we fix it by using RFC2047 encoding for the filename instead.
- part.set_param('name', filename_rfc2047)
- part.add_header('Content-Disposition', 'attachment', filename=filename_rfc2047)
-
- part.set_payload(fcontent)
- encoders.encode_base64(part)
- msg.attach(part)
+ maintype, subtype = mime.split('/') if mime and '/' in mime else ('application', 'octet-stream')
+ msg.add_attachment(fcontent, maintype, subtype, filename=fname)
return msg
@api.model
diff --git a/odoo/addons/base/models/ir_model.py b/odoo/addons/base/models/ir_model.py
index 1a2aa104c24..7fb4e56ecbb 100644
--- a/odoo/addons/base/models/ir_model.py
+++ b/odoo/addons/base/models/ir_model.py
@@ -648,7 +648,7 @@ class IrModelFields(models.Model):
]))
else:
# uninstall mode
- _logger.warn("The following fields were force-deleted to prevent a registry crash "
+ _logger.warning("The following fields were force-deleted to prevent a registry crash "
+ ", ".join(str(f) for f in fields)
+ " the following view might be broken %s" % view.name)
finally:
diff --git a/odoo/addons/base/models/ir_ui_menu.py b/odoo/addons/base/models/ir_ui_menu.py
index 9cde7d2fb81..b63d201ecf3 100644
--- a/odoo/addons/base/models/ir_ui_menu.py
+++ b/odoo/addons/base/models/ir_ui_menu.py
@@ -67,7 +67,7 @@ class IrUiMenu(models.Model):
icon_image = False
if icon_path:
with tools.file_open(icon_path, 'rb') as icon_file:
- icon_image = base64.encodestring(icon_file.read())
+ icon_image = base64.encodebytes(icon_file.read())
return icon_image
@api.constrains('parent_id')
diff --git a/odoo/addons/base/models/ir_ui_view.py b/odoo/addons/base/models/ir_ui_view.py
index 9ec9e1777d7..2856313132b 100644
--- a/odoo/addons/base/models/ir_ui_view.py
+++ b/odoo/addons/base/models/ir_ui_view.py
@@ -212,7 +212,6 @@ class View(models.Model):
('graph', 'Graph'),
('pivot', 'Pivot'),
('calendar', 'Calendar'),
- ('diagram', 'Diagram'),
('gantt', 'Gantt'),
('kanban', 'Kanban'),
('search', 'Search'),
@@ -352,7 +351,7 @@ actual arch.
message = "View inheritance may not use attribute %r as a selector." % match.group(1)
self.raise_view_error(message, self.id)
if WRONGCLASS.search(node.get('expr', '')):
- _logger.warn(
+ _logger.warning(
"Error-prone use of @class in view %s (%s): use the "
"hasclass(*classes) function to filter elements by "
"their classes", self.name, self.xml_id
@@ -771,18 +770,7 @@ actual arch.
self.raise_view_error(_('Model not found: %(model)s') % dict(model=model), view_id)
Model = self.env[model]
- if node.tag in ('field', 'node', 'arrow'):
- if node.get('object'):
- attrs = {}
- views = {}
- xml_form = E.form(*(f for f in node if f.tag == 'field'))
- xarch, xfields = self.with_context(base_model_name=model).postprocess_and_fields(node.get('object'), xml_form, view_id)
- views['form'] = {
- 'arch': xarch,
- 'fields': xfields,
- }
- attrs = {'views': views}
- fields = xfields
+ if node.tag == 'field':
if node.get('name'):
attrs = {}
field = Model._fields.get(node.get('name'))
@@ -923,17 +911,7 @@ actual arch.
if model not in self.env:
self.raise_view_error(_('Model not found: %(model)s') % dict(model=model), view_id)
Model = self.env[model]
-
- if node.tag == 'diagram':
- if node.getchildren()[0].tag == 'node':
- node_model = self.env[node.getchildren()[0].get('object')]
- node_fields = node_model.fields_get(None)
- fields.update(node_fields)
- if node.getchildren()[1].tag == 'arrow':
- arrow_fields = self.env[node.getchildren()[1].get('object')].fields_get(None)
- fields.update(arrow_fields)
- else:
- fields = Model.fields_get(None)
+ fields = Model.fields_get(None)
node = self.add_on_change(model, node)
@@ -978,14 +956,6 @@ actual arch.
Model = self.env[model]
is_base_model = self.env.context.get('base_model_name', model) == model
- if node.tag == 'diagram':
- if node.getchildren()[0].tag == 'node':
- node_model = self.env[node.getchildren()[0].get('object')]
- if (not node.get("create") and
- not node_model.check_access_rights('create', raise_exception=False) or
- not self._context.get("create", True) and is_base_model):
- node.set("create", 'false')
-
if node.tag in ('kanban', 'tree', 'form', 'activity'):
for action, operation in (('create', 'create'), ('delete', 'unlink'), ('edit', 'write')):
if (not node.get(action) and
diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py
index 05238f3d31b..61d80287892 100644
--- a/odoo/addons/base/models/res_partner.py
+++ b/odoo/addons/base/models/res_partner.py
@@ -9,8 +9,6 @@ import pytz
import threading
import re
-from email.utils import formataddr
-
import requests
from lxml import etree
from werkzeug import urls
@@ -355,7 +353,7 @@ class Partner(models.Model):
def _compute_email_formatted(self):
for partner in self:
if partner.email:
- partner.email_formatted = formataddr((partner.name or u"False", partner.email or u"False"))
+ partner.email_formatted = tools.formataddr((partner.name or u"False", partner.email or u"False"))
else:
partner.email_formatted = ''
diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py
index 66b0b2f3e49..26f8cd6b611 100644
--- a/odoo/addons/base/models/res_users.py
+++ b/odoo/addons/base/models/res_users.py
@@ -855,7 +855,7 @@ class Users(models.Model):
source = request.httprequest.remote_addr
(failures, previous) = failures_map[source]
if self._on_login_cooldown(failures, previous):
- _logger.warn(
+ _logger.warning(
"Login attempt ignored for %s on %s: "
"%d failures since last success, last failure at %s. "
"You can configure the number of login failures before a "
@@ -864,7 +864,7 @@ class Users(models.Model):
"\"base.login_cooldown_after\" to 0.",
source, self.env.cr.dbname, failures, previous)
if ipaddress.ip_address(source).is_private:
- _logger.warn(
+ _logger.warning(
"The rate-limited IP address %s is classified as private "
"and *might* be a proxy. If your Odoo is behind a proxy, "
"it may be mis-configured. Check that you are running "
@@ -911,7 +911,7 @@ class Users(models.Model):
def _register_hook(self):
if hasattr(self, 'check_credentials'):
- _logger.warn("The check_credentials method of res.users has been renamed _check_credentials. One of your installed modules defines one, but it will not be called anymore.")
+ _logger.warning("The check_credentials method of res.users has been renamed _check_credentials. One of your installed modules defines one, but it will not be called anymore.")
#
# Implied groups
#
diff --git a/odoo/addons/base/rng/diagram_view.rng b/odoo/addons/base/rng/diagram_view.rng
deleted file mode 100644
index 9a1b2898727..00000000000
--- a/odoo/addons/base/rng/diagram_view.rng
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0"
- xmlns:a="http://relaxng.org/ns/annotation/1.0"
- datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
- <!-- Handling of element overloading when inheriting from a base
- template
- -->
- <rng:include href="common.rng"/>
- <rng:define name="diagram">
- <rng:element name="diagram">
- <rng:optional><rng:attribute name="create" /></rng:optional>
- <rng:optional><rng:attribute name="string"/></rng:optional>
- <rng:ref name="node"/>
- <rng:ref name="arrow"/>
- <rng:zeroOrMore>
- <rng:ref name="label"/>
- </rng:zeroOrMore>
- </rng:element>
- </rng:define>
-
- <rng:define name="node">
- <rng:element name="node">
- <rng:attribute name="object"/>
- <rng:optional><rng:attribute name="shape"/></rng:optional>
- <rng:optional><rng:attribute name="bgcolor"/></rng:optional>
- <rng:optional><rng:attribute name="form_view_ref"/></rng:optional>
- <rng:zeroOrMore>
- <rng:ref name="field"/>
- </rng:zeroOrMore>
- </rng:element>
- </rng:define>
-
- <rng:define name="arrow">
- <rng:element name="arrow">
- <rng:attribute name="object" />
- <rng:attribute name="source" />
- <rng:attribute name="destination" />
- <rng:optional><rng:attribute name="form_view_ref"/></rng:optional>
- <rng:optional><rng:attribute name="label"/></rng:optional>
- <rng:zeroOrMore>
- <rng:ref name="field"/>
- </rng:zeroOrMore>
- </rng:element>
- </rng:define>
- <rng:start>
- <rng:choice>
- <rng:ref name="diagram" />
- </rng:choice>
- </rng:start>
-</rng:grammar>
diff --git a/odoo/addons/base/tests/test_acl.py b/odoo/addons/base/tests/test_acl.py
index 4c8954d7f62..efecd0c201f 100644
--- a/odoo/addons/base/tests/test_acl.py
+++ b/odoo/addons/base/tests/test_acl.py
@@ -35,7 +35,7 @@ class TestACL(TransactionCase):
has_group_system = self.demo_user.has_group(GROUP_SYSTEM)
self.assertFalse(has_group_system, "`demo` user should not belong to the restricted group before the test")
self.assertIn('decimal_places', original_fields, "'decimal_places' field must be properly visible before the test")
- self.assertNotEquals(view_arch.xpath("//field[@name='decimal_places']"), [],
+ self.assertNotEqual(view_arch.xpath("//field[@name='decimal_places']"), [],
"Field 'decimal_places' must be found in view definition before the test")
# restrict access to the field and check it's gone
@@ -45,7 +45,7 @@ class TestACL(TransactionCase):
form_view = currency.fields_view_get(False, 'form')
view_arch = etree.fromstring(form_view.get('arch'))
self.assertNotIn('decimal_places', fields, "'decimal_places' field should be gone")
- self.assertEquals(view_arch.xpath("//field[@name='decimal_places']"), [],
+ self.assertEqual(view_arch.xpath("//field[@name='decimal_places']"), [],
"Field 'decimal_places' must not be found in view definition")
# Make demo user a member of the restricted group and check that the field is back
@@ -56,7 +56,7 @@ class TestACL(TransactionCase):
view_arch = etree.fromstring(form_view.get('arch'))
self.assertTrue(has_group_system, "`demo` user should now belong to the restricted group")
self.assertIn('decimal_places', fields, "'decimal_places' field must be properly visible again")
- self.assertNotEquals(view_arch.xpath("//field[@name='decimal_places']"), [],
+ self.assertNotEqual(view_arch.xpath("//field[@name='decimal_places']"), [],
"Field 'decimal_places' must be found in view definition again")
@mute_logger('odoo.models')
@@ -67,8 +67,8 @@ class TestACL(TransactionCase):
# Verify the test environment first
has_group_system = self.demo_user.has_group(GROUP_SYSTEM)
self.assertFalse(has_group_system, "`demo` user should not belong to the restricted group")
- self.assert_(partner.read(['bank_ids']))
- self.assert_(partner.write({'bank_ids': []}))
+ self.assertTrue(partner.read(['bank_ids']))
+ self.assertTrue(partner.write({'bank_ids': []}))
# Now restrict access to the field and check it's forbidden
self._set_field_groups(partner, 'bank_ids', GROUP_SYSTEM)
@@ -82,8 +82,8 @@ class TestACL(TransactionCase):
self.erp_system_group.users += self.demo_user
has_group_system = self.demo_user.has_group(GROUP_SYSTEM)
self.assertTrue(has_group_system, "`demo` user should now belong to the restricted group")
- self.assert_(partner.read(['bank_ids']))
- self.assert_(partner.write({'bank_ids': []}))
+ self.assertTrue(partner.read(['bank_ids']))
+ self.assertTrue(partner.write({'bank_ids': []}))
@mute_logger('odoo.models')
def test_fields_browse_restriction(self):
diff --git a/odoo/addons/base/tests/test_base.py b/odoo/addons/base/tests/test_base.py
index e1a209b325e..1ca22defd42 100644
--- a/odoo/addons/base/tests/test_base.py
+++ b/odoo/addons/base/tests/test_base.py
@@ -180,7 +180,7 @@ class TestBase(TransactionCase):
'street': 'Strongarm Avenue, 12',
'parent_id': ironshield.id,
})
- self.assertEquals(p1.type, 'contact', 'Default type must be "contact", not the copied parent type')
+ self.assertEqual(p1.type, 'contact', 'Default type must be "contact", not the copied parent type')
self.assertEqual(ironshield.street, p1.street, 'Address fields should be copied to company')
def test_40_res_partner_address_get(self):
@@ -333,30 +333,30 @@ class TestBase(TransactionCase):
p3 = res_partner.search([('email', '=', 'ugr@sunhelm.com')], limit=1)
for p in (p0, p1, p11, p2, p3):
- self.assertEquals(p.commercial_partner_id, sunhelm, 'Incorrect commercial entity resolution')
- self.assertEquals(p.vat, sunhelm.vat, 'Commercial fields must be automatically synced')
+ self.assertEqual(p.commercial_partner_id, sunhelm, 'Incorrect commercial entity resolution')
+ self.assertEqual(p.vat, sunhelm.vat, 'Commercial fields must be automatically synced')
sunhelmvat = 'BE0123456789'
sunhelm.write({'vat': sunhelmvat})
for p in (p0, p1, p11, p2, p3):
- self.assertEquals(p.vat, sunhelmvat, 'Commercial fields must be automatically and recursively synced')
+ self.assertEqual(p.vat, sunhelmvat, 'Commercial fields must be automatically and recursively synced')
p1vat = 'BE0987654321'
p1.write({'vat': p1vat})
for p in (sunhelm, p0, p11, p2, p3):
- self.assertEquals(p.vat, sunhelmvat, 'Sync to children should only work downstream and on commercial entities')
+ self.assertEqual(p.vat, sunhelmvat, 'Sync to children should only work downstream and on commercial entities')
# promote p1 to commercial entity
p1.write({'parent_id': sunhelm.id,
'is_company': True,
'name': 'Sunhelm Subsidiary'})
- self.assertEquals(p1.vat, p1vat, 'Setting is_company should stop auto-sync of commercial fields')
- self.assertEquals(p1.commercial_partner_id, p1, 'Incorrect commercial entity resolution after setting is_company')
+ self.assertEqual(p1.vat, p1vat, 'Setting is_company should stop auto-sync of commercial fields')
+ self.assertEqual(p1.commercial_partner_id, p1, 'Incorrect commercial entity resolution after setting is_company')
# writing on parent should not touch child commercial entities
sunhelmvat2 = 'BE0112233445'
sunhelm.write({'vat': sunhelmvat2})
- self.assertEquals(p1.vat, p1vat, 'Setting is_company should stop auto-sync of commercial fields')
- self.assertEquals(p0.vat, sunhelmvat2, 'Commercial fields must be automatically synced')
+ self.assertEqual(p1.vat, p1vat, 'Setting is_company should stop auto-sync of commercial fields')
+ self.assertEqual(p0.vat, sunhelmvat2, 'Commercial fields must be automatically synced')
def test_60_read_group(self):
title_sir = self.env['res.partner.title'].create({'name': 'Sir...'})
diff --git a/odoo/addons/base/tests/test_basecase.py b/odoo/addons/base/tests/test_basecase.py
index 8bd78f860fa..a4aa02559b8 100644
--- a/odoo/addons/base/tests/test_basecase.py
+++ b/odoo/addons/base/tests/test_basecase.py
@@ -67,6 +67,6 @@ class TestTransactionCase(common.TransactionCase):
xid = 'base.test_partner_yellow'
partner = self.env.ref(xid)
pid = self.ref(xid)
- self.assertEquals(pid, partner.id, "ref() should resolve xid to database ID")
+ self.assertEqual(pid, partner.id, "ref() should resolve xid to database ID")
partner2 = self.browse_ref(xid)
self.assertEqual(partner, partner2, "browse_ref() should resolve xid to browse records")
diff --git a/odoo/addons/base/tests/test_mail.py b/odoo/addons/base/tests/test_mail.py
index 11fac3a885d..1f67c2365f9 100644
--- a/odoo/addons/base/tests/test_mail.py
+++ b/odoo/addons/base/tests/test_mail.py
@@ -81,7 +81,7 @@ class TestSanitizer(BaseCase):
self.assertTrue('ha.ckers.org' not in html or 'http://ha.ckers.org/xss.css' in html, 'html_sanitize did not remove a malicious code in %s (%s)' % (content, html))
content = "<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]-->" # down-level hidden block
- self.assertEquals(html_sanitize(content, silent=False), '')
+ self.assertEqual(html_sanitize(content, silent=False), '')
def test_html(self):
sanitized_html = html_sanitize(test_mail_examples.MISC_HTML_SOURCE)
diff --git a/odoo/addons/base/tests/test_mimetypes.py b/odoo/addons/base/tests/test_mimetypes.py
index b5fa3e1ebf8..1cca62c917b 100644
--- a/odoo/addons/base/tests/test_mimetypes.py
+++ b/odoo/addons/base/tests/test_mimetypes.py
@@ -45,7 +45,7 @@ class test_guess_mimetype(BaseCase):
content = base64.b64decode(BMP)
mimetype = guess_mimetype(content, default='test')
# mimetype should match image/bmp, image/x-ms-bmp, ...
- self.assertRegexpMatches(mimetype, r'image/.*\bbmp')
+ self.assertRegex(mimetype, r'image/.*\bbmp')
def test_mimetype_jpg(self):
content = base64.b64decode(JPG)
diff --git a/odoo/addons/base/tests/test_osv.py b/odoo/addons/base/tests/test_osv.py
index f79dde6751a..9b97cfdb0e4 100644
--- a/odoo/addons/base/tests/test_osv.py
+++ b/odoo/addons/base/tests/test_osv.py
@@ -13,9 +13,9 @@ class QueryTestCase(BaseCase):
query.where_clause.append("product_product.template_id = product_template.id")
query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False) # add normal join
query.add_join(("product_product", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True) # outer join
- self.assertEquals(query.get_sql()[0].strip(),
+ self.assertEqual(query.get_sql()[0].strip(),
""""product_product" LEFT JOIN "res_user" as "product_product__user_id" ON ("product_product"."user_id" = "product_product__user_id"."id"),"product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") """.strip())
- self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip())
+ self.assertEqual(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip())
def test_query_chained_explicit_joins(self):
query = Query()
@@ -23,9 +23,9 @@ class QueryTestCase(BaseCase):
query.where_clause.append("product_product.template_id = product_template.id")
query.add_join(("product_template", "product_category", "categ_id", "id", "categ_id"), implicit=False, outer=False) # add normal join
query.add_join(("product_template__categ_id", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True) # CHAINED outer join
- self.assertEquals(query.get_sql()[0].strip(),
+ self.assertEqual(query.get_sql()[0].strip(),
""""product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id")""".strip())
- self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip())
+ self.assertEqual(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip())
def test_mixed_query_chained_explicit_implicit_joins(self):
query = Query()
@@ -35,9 +35,9 @@ class QueryTestCase(BaseCase):
query.add_join(("product_template__categ_id", "res_user", "user_id", "id", "user_id"), implicit=False, outer=True) # CHAINED outer join
query.tables.append('"account.account"')
query.where_clause.append("product_category.expense_account_id = account_account.id") # additional implicit join
- self.assertEquals(query.get_sql()[0].strip(),
+ self.assertEqual(query.get_sql()[0].strip(),
""""product_product","product_template" JOIN "product_category" as "product_template__categ_id" ON ("product_template"."categ_id" = "product_template__categ_id"."id") LEFT JOIN "res_user" as "product_template__categ_id__user_id" ON ("product_template__categ_id"."user_id" = "product_template__categ_id__user_id"."id"),"account.account" """.strip())
- self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id AND product_category.expense_account_id = account_account.id""".strip())
+ self.assertEqual(query.get_sql()[1].strip(), """product_product.template_id = product_template.id AND product_category.expense_account_id = account_account.id""".strip())
def test_raise_missing_lhs(self):
query = Query()
diff --git a/odoo/addons/base/tests/test_qweb.py b/odoo/addons/base/tests/test_qweb.py
index 444c2960fe0..9fa00468f06 100644
--- a/odoo/addons/base/tests/test_qweb.py
+++ b/odoo/addons/base/tests/test_qweb.py
@@ -55,13 +55,13 @@ class TestQWebTField(TransactionCase):
def test_reject_crummy_tags(self):
field = etree.Element('td', {'t-field': u'company.name'})
- with self.assertRaisesRegexp(QWebException, r'^RTE widgets do not work correctly'):
+ with self.assertRaisesRegex(QWebException, r'^RTE widgets do not work correctly'):
self.engine.render(field, {'company': None})
def test_reject_t_tag(self):
field = etree.Element('t', {'t-field': u'company.name'})
- with self.assertRaisesRegexp(QWebException, r'^t-field can not be used on a t element'):
+ with self.assertRaisesRegex(QWebException, r'^t-field can not be used on a t element'):
self.engine.render(field, {'company': None})
def test_render_t_options(self):
diff --git a/odoo/addons/base/tests/test_res_partner_bank.py b/odoo/addons/base/tests/test_res_partner_bank.py
index cbf533802a1..9d19b4ef395 100644
--- a/odoo/addons/base/tests/test_res_partner_bank.py
+++ b/odoo/addons/base/tests/test_res_partner_bank.py
@@ -14,18 +14,18 @@ class TestResPartnerBank(TransactionCase):
partner_bank_model = self.env['res.partner.bank']
acc_number = " BE-001 2518823 03 "
vals = partner_bank_model.search([('acc_number', '=', acc_number)])
- self.assertEquals(0, len(vals))
+ self.assertEqual(0, len(vals))
partner_bank = partner_bank_model.create({
'acc_number': acc_number,
'partner_id': self.ref('base.res_partner_2'),
'acc_type': 'bank',
})
vals = partner_bank_model.search([('acc_number', '=', acc_number)])
- self.assertEquals(1, len(vals))
- self.assertEquals(partner_bank, vals[0])
+ self.assertEqual(1, len(vals))
+ self.assertEqual(partner_bank, vals[0])
vals = partner_bank_model.search([('acc_number', 'in', [acc_number])])
- self.assertEquals(1, len(vals))
- self.assertEquals(partner_bank, vals[0])
+ self.assertEqual(1, len(vals))
+ self.assertEqual(partner_bank, vals[0])
self.assertEqual(partner_bank.acc_number, acc_number)
@@ -33,19 +33,19 @@ class TestResPartnerBank(TransactionCase):
sanitized_acc_number = 'BE001251882303'
vals = partner_bank_model.search(
[('acc_number', '=', sanitized_acc_number)])
- self.assertEquals(1, len(vals))
- self.assertEquals(partner_bank, vals[0])
+ self.assertEqual(1, len(vals))
+ self.assertEqual(partner_bank, vals[0])
vals = partner_bank_model.search(
[('acc_number', 'in', [sanitized_acc_number])])
- self.assertEquals(1, len(vals))
- self.assertEquals(partner_bank, vals[0])
+ self.assertEqual(1, len(vals))
+ self.assertEqual(partner_bank, vals[0])
self.assertEqual(partner_bank.sanitized_acc_number,
sanitized_acc_number)
# search is case insensitive
vals = partner_bank_model.search(
[('acc_number', '=', sanitized_acc_number.lower())])
- self.assertEquals(1, len(vals))
+ self.assertEqual(1, len(vals))
vals = partner_bank_model.search(
[('acc_number', '=', acc_number.lower())])
- self.assertEquals(1, len(vals))
+ self.assertEqual(1, len(vals))
diff --git a/odoo/addons/base/tests/test_translate.py b/odoo/addons/base/tests/test_translate.py
index 715d1b778a5..24fa1597515 100644
--- a/odoo/addons/base/tests/test_translate.py
+++ b/odoo/addons/base/tests/test_translate.py
@@ -18,7 +18,7 @@ class TranslationToolsTestCase(BaseCase):
#print "2:", repr(quoted)
unquoted = unquote("".join(quoted.split('"\n"')))
#print "3:", repr(unquoted)
- self.assertEquals(str, unquoted)
+ self.assertEqual(str, unquoted)
test_string("""test \nall kinds\n \n o\r
\\\\ nope\n\n"
@@ -41,7 +41,7 @@ class TranslationToolsTestCase(BaseCase):
<field name="foo"/>
</form>"""
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms,
['Form stuff', 'Blah blah blah', 'Put some more text here'])
@@ -50,7 +50,7 @@ class TranslationToolsTestCase(BaseCase):
terms = []
source = "Blah blah blah"
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms, [source])
def test_translate_xml_unicode(self):
@@ -58,7 +58,7 @@ class TranslationToolsTestCase(BaseCase):
terms = []
source = u"Un heureux évènement"
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms, [source])
def test_translate_xml_text_entity(self):
@@ -66,7 +66,7 @@ class TranslationToolsTestCase(BaseCase):
terms = []
source = "Blah&amp;nbsp;blah&amp;nbsp;blah"
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms, [source])
def test_translate_xml_inline1(self):
@@ -78,7 +78,7 @@ class TranslationToolsTestCase(BaseCase):
<field name="foo"/>
</form>"""
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms,
['Form stuff', 'Blah <i>blah</i> blah', 'Put some <b>more text</b> here'])
@@ -91,7 +91,7 @@ class TranslationToolsTestCase(BaseCase):
<field name="foo"/>
</form>"""
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms,
['Form stuff', 'Blah <i>blah</i> blah', 'Put <em>some <b>more text</b></em> here'])
@@ -108,7 +108,7 @@ class TranslationToolsTestCase(BaseCase):
</div>
</form>"""
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms,
['Form stuff', 'Blah blah blah'])
@@ -122,7 +122,7 @@ class TranslationToolsTestCase(BaseCase):
</div>
</form>"""
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms,
['Form stuff', '<span class="fa fa-globe" title="Title stuff"/>'])
@@ -136,7 +136,7 @@ class TranslationToolsTestCase(BaseCase):
</div>
</form>"""
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms, ['Form stuff'])
def test_translate_xml_t(self):
@@ -148,7 +148,7 @@ class TranslationToolsTestCase(BaseCase):
stuff after
</t>"""
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms,
['stuff before', 'stuff after'])
@@ -161,7 +161,7 @@ class TranslationToolsTestCase(BaseCase):
stuff after
</div>"""
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms,
['stuff before', 'stuff after'])
@@ -173,7 +173,7 @@ class TranslationToolsTestCase(BaseCase):
<attribute name="option">Do not translate this</attribute>
</field>"""
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms,
['Translate this'])
@@ -190,7 +190,7 @@ class TranslationToolsTestCase(BaseCase):
</ul>
</t>"""
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms,
['<span class="oe_menu_text">Blah</span>'])
@@ -205,10 +205,10 @@ class TranslationToolsTestCase(BaseCase):
</t>
</Invoice>"""
result = xml_translate(terms.append, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
self.assertItemsEqual(terms, ['Oasis'])
result = xml_translate(lambda term: term, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
def test_translate_xml_invalid_translations(self):
""" Test xml_translate() with invalid translations. """
@@ -226,21 +226,21 @@ class TranslationToolsTestCase(BaseCase):
<field name="foo"/>
</form>"""
result = xml_translate(translations.get, source)
- self.assertEquals(result, expect)
+ self.assertEqual(result, expect)
def test_translate_html(self):
""" Test html_translate(). """
source = """<blockquote>A <h2>B</h2> C</blockquote>"""
result = html_translate(lambda term: term, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
def test_translate_html_i(self):
""" Test xml_translate() and html_translate() with <i> elements. """
source = """<p>A <i class="fa-check"></i> B</p>"""
result = xml_translate(lambda term: term, source)
- self.assertEquals(result, """<p>A <i class="fa-check"/> B</p>""")
+ self.assertEqual(result, """<p>A <i class="fa-check"/> B</p>""")
result = html_translate(lambda term: term, source)
- self.assertEquals(result, source)
+ self.assertEqual(result, source)
class TestTranslation(TransactionCase):
diff --git a/odoo/addons/base/wizard/base_export_language.py b/odoo/addons/base/wizard/base_export_language.py
index 93284f54e8c..b0d09608df6 100644
--- a/odoo/addons/base/wizard/base_export_language.py
+++ b/odoo/addons/base/wizard/base_export_language.py
@@ -36,7 +36,7 @@ class BaseLanguageExport(models.TransientModel):
with contextlib.closing(io.BytesIO()) as buf:
tools.trans_export(lang, mods, buf, this.format, self._cr)
- out = base64.encodestring(buf.getvalue())
+ out = base64.encodebytes(buf.getvalue())
filename = 'new'
if lang:
diff --git a/odoo/addons/base/wizard/base_import_language.py b/odoo/addons/base/wizard/base_import_language.py
index c0d78c46299..461c95958c3 100644
--- a/odoo/addons/base/wizard/base_import_language.py
+++ b/odoo/addons/base/wizard/base_import_language.py
@@ -31,7 +31,7 @@ class BaseLanguageImport(models.TransientModel):
this = this.with_context(overwrite=this.overwrite)
with TemporaryFile('wb+') as buf:
try:
- buf.write(base64.decodestring(this.data))
+ buf.write(base64.decodebytes(this.data))
# now we determine the file format
buf.seek(0)
diff --git a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py
index 6e74fb0fc99..557b1396a04 100644
--- a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py
+++ b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py
@@ -64,15 +64,15 @@ class TestJavascriptAssetsBundle(FileTouchable):
self.bundle = self._get_asset(self.jsbundle_xmlid, env=self.env)
# there shouldn't be any attachment associated to this bundle
- self.assertEquals(len(self._any_ira_for_bundle('js')), 0)
- self.assertEquals(len(self.bundle.get_attachments('js')), 0)
+ self.assertEqual(len(self._any_ira_for_bundle('js')), 0)
+ self.assertEqual(len(self.bundle.get_attachments('js')), 0)
# trigger the first generation and, thus, the first save in database
self.bundle.js()
# there should be one attachment associated to this bundle
- self.assertEquals(len(self._any_ira_for_bundle('js')), 1)
- self.assertEquals(len(self.bundle.get_attachments('js')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('js')), 1)
+ self.assertEqual(len(self.bundle.get_attachments('js')), 1)
def test_02_access(self):
""" Checks that the bundle's cache is working, i.e. that the bundle creates only one
@@ -81,7 +81,7 @@ class TestJavascriptAssetsBundle(FileTouchable):
bundle0 = self._get_asset(self.jsbundle_xmlid)
bundle0.js()
- self.assertEquals(len(self._any_ira_for_bundle('js')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('js')), 1)
version0 = bundle0.version
ira0 = self._any_ira_for_bundle('js')
@@ -90,14 +90,14 @@ class TestJavascriptAssetsBundle(FileTouchable):
bundle1 = self._get_asset(self.jsbundle_xmlid)
bundle1.js()
- self.assertEquals(len(self._any_ira_for_bundle('js')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('js')), 1)
version1 = bundle1.version
ira1 = self._any_ira_for_bundle('js')
date1 = ira1.create_date
- self.assertEquals(version0, version1)
- self.assertEquals(date0, date1)
+ self.assertEqual(version0, version1)
+ self.assertEqual(date0, date1)
def test_03_date_invalidation(self):
""" Checks that a bundle is invalidated when one of its assets' modification date is changed.
@@ -114,11 +114,11 @@ class TestJavascriptAssetsBundle(FileTouchable):
bundle1.js()
last_modified1 = bundle1.last_modified
version1 = bundle1.version
- self.assertNotEquals(last_modified0, last_modified1)
- self.assertNotEquals(version0, version1)
+ self.assertNotEqual(last_modified0, last_modified1)
+ self.assertNotEqual(version0, version1)
# check if the previous attachment is correctly cleaned
- self.assertEquals(len(self._any_ira_for_bundle('js')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('js')), 1)
def test_04_content_invalidation(self):
""" Checks that a bundle is invalidated when its content is modified by adding a file to
@@ -129,7 +129,7 @@ class TestJavascriptAssetsBundle(FileTouchable):
files0 = bundle0.files
version0 = bundle0.version
- self.assertEquals(len(self._any_ira_for_bundle('js')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('js')), 1)
view_arch = """
<data>
@@ -151,11 +151,11 @@ class TestJavascriptAssetsBundle(FileTouchable):
files1 = bundle1.files
version1 = bundle1.version
- self.assertNotEquals(files0, files1)
- self.assertNotEquals(version0, version1)
+ self.assertNotEqual(files0, files1)
+ self.assertNotEqual(version0, version1)
# check if the previous attachment are correctly cleaned
- self.assertEquals(len(self._any_ira_for_bundle('js')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('js')), 1)
def test_05_debug(self):
""" Checks that a bundle rendered in debug mode outputs non-minified assets.
@@ -167,14 +167,14 @@ class TestJavascriptAssetsBundle(FileTouchable):
self.assertIn('/test_assetsbundle/static/src/js/test_jsfile1.js', content)
# there shouldn't be any assets created in debug mode
- self.assertEquals(len(self._any_ira_for_bundle('js')), 0)
+ self.assertEqual(len(self._any_ira_for_bundle('js')), 0)
def test_08_css_generation3(self):
# self.cssbundle_xlmid contains 3 rules
self.bundle = self._get_asset(self.cssbundle_xmlid)
self.bundle.css()
- self.assertEquals(len(self._any_ira_for_bundle('css')), 1)
- self.assertEquals(len(self.bundle.get_attachments('css')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('css')), 1)
+ self.assertEqual(len(self.bundle.get_attachments('css')), 1)
def test_09_css_access(self):
""" Checks that the bundle's cache is working, i.e. that a bundle creates only enough
@@ -183,7 +183,7 @@ class TestJavascriptAssetsBundle(FileTouchable):
bundle0 = self._get_asset(self.cssbundle_xmlid)
bundle0.css()
- self.assertEquals(len(self._any_ira_for_bundle('css')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('css')), 1)
version0 = bundle0.version
ira0 = self._any_ira_for_bundle('css')
@@ -192,14 +192,14 @@ class TestJavascriptAssetsBundle(FileTouchable):
bundle1 = self._get_asset(self.cssbundle_xmlid)
bundle1.css()
- self.assertEquals(len(self._any_ira_for_bundle('css')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('css')), 1)
version1 = bundle1.version
ira1 = self._any_ira_for_bundle('css')
date1 = ira1.create_date
- self.assertEquals(version0, version1)
- self.assertEquals(date0, date1)
+ self.assertEqual(version0, version1)
+ self.assertEqual(date0, date1)
def test_11_css_content_invalidation(self):
""" Checks that a bundle is invalidated when its content is modified by adding a file to
@@ -210,7 +210,7 @@ class TestJavascriptAssetsBundle(FileTouchable):
files0 = bundle0.files
version0 = bundle0.version
- self.assertEquals(len(self._any_ira_for_bundle('css')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('css')), 1)
view_arch = """
<data>
@@ -232,11 +232,11 @@ class TestJavascriptAssetsBundle(FileTouchable):
files1 = bundle1.files
version1 = bundle1.version
- self.assertNotEquals(files0, files1)
- self.assertNotEquals(version0, version1)
+ self.assertNotEqual(files0, files1)
+ self.assertNotEqual(version0, version1)
# check if the previous attachment are correctly cleaned
- self.assertEquals(len(self._any_ira_for_bundle('css')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('css')), 1)
def test_12_css_debug(self):
""" Check that a bundle in debug mode outputs non-minified assets.
@@ -248,7 +248,7 @@ class TestJavascriptAssetsBundle(FileTouchable):
self.assertIn('/test_assetsbundle/static/src/css/test_cssfile1.css', content)
# there shouldn't be any assets created in debug mode
- self.assertEquals(len(self._any_ira_for_bundle('css')), 0)
+ self.assertEqual(len(self._any_ira_for_bundle('css')), 0)
def test_14_duplicated_css_assets(self):
""" Checks that if the bundle's ir.attachment record is duplicated, the bundle is only sourced once. This could
@@ -256,13 +256,13 @@ class TestJavascriptAssetsBundle(FileTouchable):
"""
bundle0 = self._get_asset(self.cssbundle_xmlid)
bundle0.css()
- self.assertEquals(len(self._any_ira_for_bundle('css')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('css')), 1)
# duplicate the asset bundle
ira0 = self._any_ira_for_bundle('css')
ira1 = ira0.copy()
- self.assertEquals(len(self._any_ira_for_bundle('css')), 2)
- self.assertEquals(ira0.store_fname, ira1.store_fname)
+ self.assertEqual(len(self._any_ira_for_bundle('css')), 2)
+ self.assertEqual(ira0.store_fname, ira1.store_fname)
# the ir.attachment records should be deduplicated in the bundle's content
nodes = bundle0.to_node()
@@ -278,15 +278,15 @@ class TestJavascriptAssetsBundle(FileTouchable):
self.bundle = self._get_asset(self.cssbundle_xmlid, env=self.env(context={'lang': 'ar_SY'}))
# there shouldn't be any attachment associated to this bundle
- self.assertEquals(len(self._any_ira_for_bundle('css', lang='ar_SY')), 0)
- self.assertEquals(len(self.bundle.get_attachments('css')), 0)
+ self.assertEqual(len(self._any_ira_for_bundle('css', lang='ar_SY')), 0)
+ self.assertEqual(len(self.bundle.get_attachments('css')), 0)
# trigger the first generation and, thus, the first save in database
self.bundle.css()
# there should be one attachment associated to this bundle
- self.assertEquals(len(self._any_ira_for_bundle('css', lang='ar_SY')), 1)
- self.assertEquals(len(self.bundle.get_attachments('css')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('css', lang='ar_SY')), 1)
+ self.assertEqual(len(self.bundle.get_attachments('css')), 1)
def test_16_ltr_and_rtl_css_access(self):
""" Checks that the bundle's cache is working, i.e. that the bundle creates only one
@@ -297,7 +297,7 @@ class TestJavascriptAssetsBundle(FileTouchable):
ltr_bundle0 = self._get_asset(self.cssbundle_xmlid)
ltr_bundle0.css()
- self.assertEquals(len(self._any_ira_for_bundle('css')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('css')), 1)
ltr_version0 = ltr_bundle0.version
ltr_ira0 = self._any_ira_for_bundle('css')
@@ -306,20 +306,20 @@ class TestJavascriptAssetsBundle(FileTouchable):
ltr_bundle1 = self._get_asset(self.cssbundle_xmlid)
ltr_bundle1.css()
- self.assertEquals(len(self._any_ira_for_bundle('css')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('css')), 1)
ltr_version1 = ltr_bundle1.version
ltr_ira1 = self._any_ira_for_bundle('css')
ltr_date1 = ltr_ira1.create_date
- self.assertEquals(ltr_version0, ltr_version1)
- self.assertEquals(ltr_date0, ltr_date1)
+ self.assertEqual(ltr_version0, ltr_version1)
+ self.assertEqual(ltr_date0, ltr_date1)
# Assets access for ar_SY language
rtl_bundle0 = self._get_asset(self.cssbundle_xmlid, env=self.env(context={'lang': 'ar_SY'}))
rtl_bundle0.css()
- self.assertEquals(len(self._any_ira_for_bundle('css', lang='ar_SY')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('css', lang='ar_SY')), 1)
rtl_version0 = rtl_bundle0.version
rtl_ira0 = self._any_ira_for_bundle('css', lang='ar_SY')
@@ -328,23 +328,23 @@ class TestJavascriptAssetsBundle(FileTouchable):
rtl_bundle1 = self._get_asset(self.cssbundle_xmlid, env=self.env(context={'lang': 'ar_SY'}))
rtl_bundle1.css()
- self.assertEquals(len(self._any_ira_for_bundle('css', lang='ar_SY')), 1)
+ self.assertEqual(len(self._any_ira_for_bundle('css', lang='ar_SY')), 1)
rtl_version1 = rtl_bundle1.version
rtl_ira1 = self._any_ira_for_bundle('css', lang='ar_SY')
rtl_date1 = rtl_ira1.create_date
- self.assertEquals(rtl_version0, rtl_version1)
- self.assertEquals(rtl_date0, rtl_date1)
+ self.assertEqual(rtl_version0, rtl_version1)
+ self.assertEqual(rtl_date0, rtl_date1)
# Checks rtl and ltr bundles are different
- self.assertNotEquals(ltr_ira1.id, rtl_ira1.id)
+ self.assertNotEqual(ltr_ira1.id, rtl_ira1.id)
# Check two bundles are available, one for ltr and one for rtl
css_bundles = self.env['ir.attachment'].search([
('url', '=like', '/web/content/%-%/{0}%.{1}'.format(self.cssbundle_xmlid, 'css'))
])
- self.assertEquals(len(css_bundles), 2)
+ self.assertEqual(len(css_bundles), 2)
def test_17_css_bundle_date_invalidation(self):
""" Checks that both css bundles are invalidated when one of its assets' modification date is changed
@@ -371,8 +371,8 @@ class TestJavascriptAssetsBundle(FileTouchable):
ltr_last_modified1 = ltr_bundle1.last_modified
ltr_version1 = ltr_bundle1.version
ltr_ira1 = self._any_ira_for_bundle('css')
- self.assertNotEquals(ltr_last_modified0, ltr_last_modified1)
- self.assertNotEquals(ltr_version0, ltr_version1)
+ self.assertNotEqual(ltr_last_modified0, ltr_last_modified1)
+ self.assertNotEqual(ltr_version0, ltr_version1)
rtl_bundle1 = self._get_asset(self.cssbundle_xmlid, env=self.env(context={'lang': 'ar_SY'}))
@@ -380,17 +380,17 @@ class TestJavascriptAssetsBundle(FileTouchable):
rtl_last_modified1 = rtl_bundle1.last_modified
rtl_version1 = rtl_bundle1.version
rtl_ira1 = self._any_ira_for_bundle('css', lang='ar_SY')
- self.assertNotEquals(rtl_last_modified0, rtl_last_modified1)
- self.assertNotEquals(rtl_version0, rtl_version1)
+ self.assertNotEqual(rtl_last_modified0, rtl_last_modified1)
+ self.assertNotEqual(rtl_version0, rtl_version1)
# Checks rtl and ltr bundles are different
- self.assertNotEquals(ltr_ira1.id, rtl_ira1.id)
+ self.assertNotEqual(ltr_ira1.id, rtl_ira1.id)
# check if the previous attachment is correctly cleaned
css_bundles = self.env['ir.attachment'].search([
('url', '=like', '/web/content/%-%/{0}%.{1}'.format(self.cssbundle_xmlid, 'css'))
])
- self.assertEquals(len(css_bundles), 2)
+ self.assertEqual(len(css_bundles), 2)
def test_18_css_bundle_content_invalidation(self):
""" Checks that a bundle is invalidated when its content is modified by adding a file to
@@ -410,7 +410,7 @@ class TestJavascriptAssetsBundle(FileTouchable):
css_bundles = self.env['ir.attachment'].search([
('url', '=like', '/web/content/%-%/{0}%.{1}'.format(self.cssbundle_xmlid, 'css'))
])
- self.assertEquals(len(css_bundles), 2)
+ self.assertEqual(len(css_bundles), 2)
view_arch = """
<data>
@@ -433,8 +433,8 @@ class TestJavascriptAssetsBundle(FileTouchable):
ltr_version1 = ltr_bundle1.version
ltr_ira1 = self._any_ira_for_bundle('css')
- self.assertNotEquals(ltr_files0, ltr_files1)
- self.assertNotEquals(ltr_version0, ltr_version1)
+ self.assertNotEqual(ltr_files0, ltr_files1)
+ self.assertNotEqual(ltr_version0, ltr_version1)
rtl_bundle1 = self._get_asset(self.cssbundle_xmlid, env=self.env(context={'check_view_ids': view.ids, 'lang': 'ar_SY'}))
rtl_bundle1.css()
@@ -442,17 +442,17 @@ class TestJavascriptAssetsBundle(FileTouchable):
rtl_version1 = rtl_bundle1.version
rtl_ira1 = self._any_ira_for_bundle('css', lang='ar_SY')
- self.assertNotEquals(rtl_files0, rtl_files1)
- self.assertNotEquals(rtl_version0, rtl_version1)
+ self.assertNotEqual(rtl_files0, rtl_files1)
+ self.assertNotEqual(rtl_version0, rtl_version1)
# Checks rtl and ltr bundles are different
- self.assertNotEquals(ltr_ira1.id, rtl_ira1.id)
+ self.assertNotEqual(ltr_ira1.id, rtl_ira1.id)
# check if the previous attachment are correctly cleaned
css_bundles = self.env['ir.attachment'].search([
('url', '=like', '/web/content/%-%/{0}%.{1}'.format(self.cssbundle_xmlid, 'css'))
])
- self.assertEquals(len(css_bundles), 2)
+ self.assertEqual(len(css_bundles), 2)
def test_19_css_in_debug_assets(self):
""" Checks that a bundle rendered in debug mode(assets) with right to left language direction stores css files in assets bundle.
@@ -468,12 +468,12 @@ class TestJavascriptAssetsBundle(FileTouchable):
css_bundle = self.env['ir.attachment'].search([
('url', '=', '/test_assetsbundle/static/src/css/test_cssfile1/rtl/{0}.css'.format(self.cssbundle_xmlid))
])
- self.assertEquals(len(css_bundle), 1)
+ self.assertEqual(len(css_bundle), 1)
def test_20_exteral_lib_assets(self):
html = self.env['ir.ui.view'].render_template('test_assetsbundle.template2')
attachments = self.env['ir.attachment'].search([('url', '=like', '/web/content/%-%/test_assetsbundle.bundle4.%')])
- self.assertEquals(len(attachments), 2)
+ self.assertEqual(len(attachments), 2)
self.assertEqual(html.strip(), ("""<!DOCTYPE html>
<html>
<head>
@@ -492,7 +492,7 @@ class TestJavascriptAssetsBundle(FileTouchable):
def test_21_exteral_lib_assets_debug_mode(self):
html = self.env['ir.ui.view'].render_template('test_assetsbundle.template2', {"debug": "assets"})
attachments = self.env['ir.attachment'].search([('url', '=like', '/web/content/%-%/test_assetsbundle.bundle4.%')])
- self.assertEquals(len(attachments), 0)
+ self.assertEqual(len(attachments), 0)
self.assertEqual(html.strip(), ("""<!DOCTYPE html>
<html>
<head>
@@ -578,8 +578,8 @@ class TestAssetsBundleWithIRAMock(FileTouchable):
def _bundle(self, asset, should_create, should_unlink):
self.counter.clear()
asset.to_node(debug='assets')
- self.assertEquals(self.counter['create'], int(should_create))
- self.assertEquals(self.counter['unlink'], int(should_unlink))
+ self.assertEqual(self.counter['create'], int(should_create))
+ self.assertEqual(self.counter['unlink'], int(should_unlink))
def test_01_debug_mode_assets(self):
""" Checks that the ir.attachments records created for compiled assets in debug mode
diff --git a/odoo/addons/test_lint/tests/test_pylint.py b/odoo/addons/test_lint/tests/test_pylint.py
index 8e5e69fa351..9e7b3ab0397 100644
--- a/odoo/addons/test_lint/tests/test_pylint.py
+++ b/odoo/addons/test_lint/tests/test_pylint.py
@@ -30,6 +30,7 @@ class TestPyLint(TransactionCase):
'unreachable',
'mixed-indentation',
+ 'deprecated-method',
]
BAD_FUNCTIONS = [
@@ -43,7 +44,7 @@ class TestPyLint(TransactionCase):
] + list(tools.SUPPORTED_DEBUGGER)
def _skip_test(self, reason):
- _logger.warn(reason)
+ _logger.warning(reason)
self.skipTest(reason)
def test_pylint(self):
diff --git a/odoo/addons/test_new_api/tests/test_company_checks.py b/odoo/addons/test_new_api/tests/test_company_checks.py
index ad2697e2b1e..72540028824 100644
--- a/odoo/addons/test_new_api/tests/test_company_checks.py
+++ b/odoo/addons/test_new_api/tests/test_company_checks.py
@@ -71,4 +71,3 @@ class TestCompanyCheck(common.TransactionCase):
'parent_id': self.parent_company_b_id.id,
'company_id': self.company_b.id,
})
-
diff --git a/odoo/addons/test_performance/tests/test_performance.py b/odoo/addons/test_performance/tests/test_performance.py
index bfd2cdcee37..1db2e33f6d2 100644
--- a/odoo/addons/test_performance/tests/test_performance.py
+++ b/odoo/addons/test_performance/tests/test_performance.py
@@ -176,7 +176,7 @@ class TestPerformance(TransactionCase):
# This write() will raise because of the unique index if the unlink() is
# not performed before the create()
rec.write({'line_ids': [(5,)] + [(0, 0, {'value': val}) for val in range(6)]})
- self.assertEquals(len(rec.line_ids), 6)
+ self.assertEqual(len(rec.line_ids), 6)
@mute_logger('odoo.models.unlink')
@users('__system__', 'demo')
diff --git a/odoo/addons/test_testing_utilities/tests/test_form_impl.py b/odoo/addons/test_testing_utilities/tests/test_form_impl.py
index c0e5093fd3b..838103157b6 100644
--- a/odoo/addons/test_testing_utilities/tests/test_form_impl.py
+++ b/odoo/addons/test_testing_utilities/tests/test_form_impl.py
@@ -42,7 +42,7 @@ class TestBasic(TransactionCase):
def test_required(self):
f = Form(self.env['test_testing_utilities.a'])
# f1 no default & no value => should fail
- with self.assertRaisesRegexp(AssertionError, 'f1 is a required field'):
+ with self.assertRaisesRegex(AssertionError, 'f1 is a required field'):
f.save()
# set f1 and unset f2 => should work
f.f1 = '1'
diff --git a/odoo/addons/test_translation_import/tests/test_term_count.py b/odoo/addons/test_translation_import/tests/test_term_count.py
index 7526049f4e8..fb98e78661f 100644
--- a/odoo/addons/test_translation_import/tests/test_term_count.py
+++ b/odoo/addons/test_translation_import/tests/test_term_count.py
@@ -151,7 +151,7 @@ class TestTermCount(common.TransactionCase):
def test_import_from_po_file(self):
"""Test the import from a single po file works"""
with file_open('test_translation_import/i18n/tlh.po', 'rb') as f:
- po_file = base64.encodestring(f.read())
+ po_file = base64.encodebytes(f.read())
import_tlh = self.env["base.language.import"].create({
'name': 'Klingon',
@@ -174,7 +174,7 @@ class TestTermCount(common.TransactionCase):
def test_lazy_translation(self):
"""Test the import from a single po file works"""
with file_open('test_translation_import/i18n/tlh.po', 'rb') as f:
- po_file = base64.encodestring(f.read())
+ po_file = base64.encodebytes(f.read())
import_tlh = self.env["base.language.import"].create({
'name': 'Klingon',
@@ -200,7 +200,7 @@ class TestTermCount(common.TransactionCase):
def test_import_from_csv_file(self):
"""Test the import from a single CSV file works"""
with file_open('test_translation_import/i18n/dot.csv', 'rb') as f:
- po_file = base64.encodestring(f.read())
+ po_file = base64.encodebytes(f.read())
import_tlh = self.env["base.language.import"].create({
'name': 'Dothraki',
diff --git a/odoo/api.py b/odoo/api.py
index 4015156f1a3..dd0010db9f5 100644
--- a/odoo/api.py
+++ b/odoo/api.py
@@ -43,7 +43,7 @@ import logging
from collections import defaultdict, Mapping
from contextlib import contextmanager
from copy import deepcopy
-from inspect import getargspec
+from inspect import signature
from pprint import pformat
from weakref import WeakSet
@@ -280,7 +280,7 @@ def downgrade(method, value, self, args, kwargs):
if not spec:
return value
_, convert, _ = spec
- if convert and len(getargspec(convert).args) > 1:
+ if convert and len(signature(convert).parameters) > 1:
return convert(self, value, *args, **kwargs)
elif convert:
return convert(value)
diff --git a/odoo/exceptions.py b/odoo/exceptions.py
index 37fde75a845..75e8749b028 100644
--- a/odoo/exceptions.py
+++ b/odoo/exceptions.py
@@ -22,7 +22,7 @@ class except_orm(Exception):
def __init__(self, name, value=None):
if type(self) == except_orm:
caller = frame_codeinfo(currentframe(), 1)
- _logger.warn('except_orm is deprecated. Please use specific exceptions like UserError or AccessError. Caller: %s:%s', *caller)
+ _logger.warning('except_orm is deprecated. Please use specific exceptions like UserError or AccessError. Caller: %s:%s', *caller)
self.name = name
self.value = value
self.args = (name, value)
diff --git a/odoo/http.py b/odoo/http.py
index 9e0da1768fc..31bb46683f5 100644
--- a/odoo/http.py
+++ b/odoo/http.py
@@ -509,9 +509,14 @@ def route(route=None, **kw):
@functools.wraps(f)
def response_wrap(*args, **kw):
# if controller cannot be called with extra args (utm, debug, ...), call endpoint ignoring them
- spec = inspect.getargspec(f)
- if not spec.keywords:
- ignored = ['<%s=%s>' % (k, kw.pop(k)) for k in list(kw) if k not in spec.args]
+ params = inspect.signature(f).parameters.values()
+ is_kwargs = lambda p: p.kind == inspect.Parameter.VAR_KEYWORD
+ if not any(is_kwargs(p) for p in params): # missing **kw
+ is_keyword_compatible = lambda p: p.kind in (
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
+ inspect.Parameter.KEYWORD_ONLY)
+ fargs = {p.name for p in params if is_keyword_compatible(p)}
+ ignored = ['<%s=%s>' % (k, kw.pop(k)) for k in list(kw) if k not in fargs]
if ignored:
_logger.info("<function %s.%s> called ignoring args %s" % (f.__module__, f.__name__, ', '.join(ignored)))
@@ -529,7 +534,7 @@ def route(route=None, **kw):
response.set_default()
return response
- _logger.warn("<function %s.%s> returns an invalid response type for an http request" % (f.__module__, f.__name__))
+ _logger.warning("<function %s.%s> returns an invalid response type for an http request" % (f.__module__, f.__name__))
return response
response_wrap.routing = routing
response_wrap.original_func = f
@@ -779,10 +784,10 @@ class HttpRequest(WebRequest):
token = self.params.pop('csrf_token', None)
if not self.validate_csrf(token):
if token is not None:
- _logger.warn("CSRF validation failed on path '%s'",
+ _logger.warning("CSRF validation failed on path '%s'",
request.httprequest.path)
else:
- _logger.warn("""No CSRF validation token provided for path '%s'
+ _logger.warning("""No CSRF validation token provided for path '%s'
Odoo URLs are CSRF-protected by default (when accessed with unsafe
HTTP methods). See
@@ -877,12 +882,12 @@ class ControllerType(type):
parent_routing_type = getattr(parent[0], k).original_func.routing_type if parent else routing_type or 'http'
if routing_type is not None and routing_type is not parent_routing_type:
routing_type = parent_routing_type
- _logger.warn("Subclass re-defines <function %s.%s.%s> with different type than original."
+ _logger.warning("Subclass re-defines <function %s.%s.%s> with different type than original."
" Will use original type: %r" % (cls.__module__, cls.__name__, k, parent_routing_type))
v.original_func.routing_type = routing_type or parent_routing_type
- spec = inspect.getargspec(v.original_func)
- first_arg = spec.args[1] if len(spec.args) >= 2 else None
+ sign = inspect.signature(v.original_func)
+ first_arg = list(sign.parameters)[1] if len(sign.parameters) >= 2 else None
if first_arg in ["req", "request"]:
v._first_arg_is_req = True
@@ -1343,7 +1348,7 @@ class Root(object):
# Check if session.db is legit
if db:
if db not in db_filter([db], httprequest=httprequest):
- _logger.warn("Logged into database '%s', but dbfilter "
+ _logger.warning("Logged into database '%s', but dbfilter "
"rejects it; logging session out.", db)
httprequest.session.logout()
db = None
diff --git a/odoo/models.py b/odoo/models.py
index 5ba3b364a9e..05c6912ed7f 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -1891,7 +1891,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
orderby_terms.append(' '.join(order_split))
else:
# Cannot order by a field that will not appear in the results (needs to be grouped or aggregated)
- _logger.warn('%s: read_group order by `%s` ignored, cannot sort on empty columns (not grouped/aggregated)',
+ _logger.warning('%s: read_group order by `%s` ignored, cannot sort on empty columns (not grouped/aggregated)',
self._name, order_part)
return groupby_terms, orderby_terms
diff --git a/odoo/modules/module.py b/odoo/modules/module.py
index 53f1ee6d77e..8d0dd5a7091 100644
--- a/odoo/modules/module.py
+++ b/odoo/modules/module.py
@@ -30,92 +30,14 @@ README = ['README.rst', 'README.md', 'README.txt']
_logger = logging.getLogger(__name__)
-# addons path as a list
-# ad_paths is a deprecated alias, please use odoo.addons.__path__
-@tools.lazy
-def ad_paths():
- _logger.warning('"odoo.modules.module.ad_paths" is a deprecated '
- 'proxy to "odoo.addons.__path__". Please consider '
- 'using the latter as the former is going to be '
- 'removed in the next version.',
- exc_info=DeprecationWarning(), stack_info=True)
- return odoo.addons.__path__
-hooked = False
-
# Modules already loaded
loaded = []
-class AddonsHook(object):
- """ Makes modules accessible through openerp.addons.* """
-
- def find_module(self, name, path=None):
- if name.startswith('openerp.addons.') and name.count('.') == 2:
- _logger.warning('"openerp.addons" is a deprecated alias to '
- '"odoo.addons". Please consider using the '
- 'latter as the former is going to be removed '
- 'in the next version.',
- exc_info=DeprecationWarning(), stack_info=True)
- return self
-
- def load_module(self, name):
- assert name not in sys.modules
-
- odoo_name = re.sub(r'^openerp.addons.(\w+)$', r'odoo.addons.\g<1>', name)
-
- odoo_module = sys.modules.get(odoo_name)
- if not odoo_module:
- odoo_module = importlib.import_module(odoo_name)
-
- sys.modules[name] = odoo_module
-
- return odoo_module
-
-# need to register loader with setuptools as Jinja relies on it when using
-# PackageLoader
-pkg_resources.register_loader_type(AddonsHook, pkg_resources.DefaultProvider)
-
-class OdooHook(object):
- """ Makes odoo package also available as openerp """
-
- def find_module(self, name, path=None):
- # openerp.addons.<identifier> should already be matched by AddonsHook,
- # only framework and subdirectories of modules should match
- if re.match(r'^openerp\b', name):
- _logger.warning('openerp is a deprecated alias to odoo. '
- 'Please consider using the latter as the '
- 'former is going to be removed in the next '
- 'version.',
- exc_info=DeprecationWarning(), stack_info=True)
- return self
-
- def load_module(self, name):
- assert name not in sys.modules
-
- canonical = re.sub(r'^openerp(.*)', r'odoo\g<1>', name)
-
- if canonical in sys.modules:
- mod = sys.modules[canonical]
- else:
- # probable failure: canonical execution calling old naming -> corecursion
- mod = importlib.import_module(canonical)
-
- # just set the original module at the new location. Don't proxy,
- # it breaks *-import (unless you can find how `from a import *` lists
- # what's supposed to be imported by `*`, and manage to override it)
- sys.modules[name] = mod
-
- return sys.modules[name]
-
def initialize_sys_path():
"""
- Setup an import-hook to be able to import OpenERP addons from the different
- addons paths.
-
- This ensures something like ``import crm`` (or even
- ``import odoo.addons.crm``) works even if the addons are not in the
- PYTHONPATH.
+ Setup the addons path ``odoo.addons.__path__`` with various defaults
+ and explicit directories.
"""
- global hooked
dd = os.path.normcase(tools.config.addons_data_dir)
if os.access(dd, os.R_OK) and dd not in odoo.addons.__path__:
@@ -131,11 +53,6 @@ def initialize_sys_path():
if base_path not in odoo.addons.__path__ and os.path.isdir(base_path):
odoo.addons.__path__.append(base_path)
- if not hooked:
- sys.meta_path.insert(0, OdooHook())
- sys.meta_path.insert(0, AddonsHook())
- hooked = True
-
def get_module_path(module, downloaded=False, display_warning=True):
"""Return the path of the given module.
@@ -439,7 +356,7 @@ def get_test_modules(module):
_logger.exception('Can not `import %s`.', module)
return []
if hasattr(mod, 'fast_suite') or hasattr(mod, 'checks'):
- _logger.warn(
+ _logger.warning(
"Found deprecated fast_suite or checks attribute in test module "
"%s. These have no effect in or after version 8.0.",
mod.__name__)
@@ -463,7 +380,10 @@ class OdooTestResult(unittest.result.TestResult):
(fn, lno, func, sinfo) (logger.findCaller format), see logger.log for
the other parameters.
"""
- logger = logging.getLogger((test or self).__module__) # test should be always set
+ test = test or self
+ if isinstance(test, unittest.case._SubTest) and test.test_case:
+ test = test.test_case
+ logger = logging.getLogger(test.__module__)
try:
caller_infos = caller_infos or logger.findCaller(stack_info)
except ValueError:
@@ -477,6 +397,8 @@ class OdooTestResult(unittest.result.TestResult):
logger.handle(record)
def getDescription(self, test):
+ if isinstance(test, unittest.case._SubTest):
+ return 'Subtest %s' % test._subDescription()
if isinstance(test, unittest.TestCase):
# since we have the module name in the logger, this will avoid to duplicate module info in log line
# we only apply this for TestCase since we can receive error handler or other special case
diff --git a/odoo/release.py b/odoo/release.py
index d0fba10e1c3..6997f807039 100644
--- a/odoo/release.py
+++ b/odoo/release.py
@@ -12,7 +12,7 @@ RELEASE_LEVELS_DISPLAY = {ALPHA: ALPHA,
# properly comparable using normal operarors, for example:
# (6,1,0,'beta',0) < (6,1,0,'candidate',1) < (6,1,0,'candidate',2)
# (6,1,0,'candidate',2) < (6,1,0,'final',0) < (6,1,2,'final',0)
-version_info = ('saas~12', 5, 0, FINAL, 0, '')
+version_info = (13, 0, 0, ALPHA, 1, '')
version = '.'.join(str(s) for s in version_info[:2]) + RELEASE_LEVELS_DISPLAY[version_info[3]] + str(version_info[4] or '') + version_info[5]
series = serie = major_version = '.'.join(str(s) for s in version_info[:2])
diff --git a/odoo/service/server.py b/odoo/service/server.py
index 15d3009e36b..3cc2010c061 100644
--- a/odoo/service/server.py
+++ b/odoo/service/server.py
@@ -657,7 +657,7 @@ class PreforkServer(CommonServer):
self.queue.append(sig)
self.pipe_ping(self.pipe)
else:
- _logger.warn("Dropping signal: %s", sig)
+ _logger.warning("Dropping signal: %s", sig)
def worker_spawn(self, klass, workers_registry):
self.generation += 1
diff --git a/odoo/service/wsgi_server.py b/odoo/service/wsgi_server.py
index 23acfefad03..c29d91a2fbc 100644
--- a/odoo/service/wsgi_server.py
+++ b/odoo/service/wsgi_server.py
@@ -121,8 +121,22 @@ def application_unproxied(environ, start_response):
# We never returned from the loop.
return werkzeug.exceptions.NotFound("No handler found.\n")(environ, start_response)
+try:
+ # werkzeug >= 0.15
+ from werkzeug.middleware.proxy_fix import ProxyFix as ProxyFix_
+ # 0.15 also supports port and prefix, but 0.14 only forwarded for, proto
+ # and host so replicate that
+ ProxyFix = lambda app: ProxyFix_(app, x_for=1, x_proto=1, x_host=1)
+except ImportError:
+ # werkzeug < 0.15
+ from werkzeug.contrib.fixers import ProxyFix
+
def application(environ, start_response):
+ # FIXME: is checking for the presence of HTTP_X_FORWARDED_HOST really useful?
+ # we're ignoring the user configuration, and that means we won't
+ # support the standardised Forwarded header once werkzeug supports
+ # it
if config['proxy_mode'] and 'HTTP_X_FORWARDED_HOST' in environ:
- return werkzeug.contrib.fixers.ProxyFix(application_unproxied)(environ, start_response)
+ return ProxyFix(application_unproxied)(environ, start_response)
else:
return application_unproxied(environ, start_response)
diff --git a/odoo/tools/cache.py b/odoo/tools/cache.py
index e23f7ea1124..2d50b51aa94 100644
--- a/odoo/tools/cache.py
+++ b/odoo/tools/cache.py
@@ -4,7 +4,7 @@
# decorator makes wrappers that have the same API as their wrapped function
from collections import defaultdict
from decorator import decorator
-from inspect import formatargspec, getargspec
+from inspect import signature
import logging
unsafe_eval = eval
@@ -64,7 +64,7 @@ class ormcache(object):
""" Determine the function that computes a cache key from arguments. """
if self.skiparg is None:
# build a string that represents function code and evaluate it
- args = formatargspec(*getargspec(self.method))[1:-1]
+ args = str(signature(self.method))[1:-1]
if self.args:
code = "lambda %s: (%s,)" % (args, ", ".join(self.args))
else:
@@ -90,7 +90,7 @@ class ormcache(object):
value = d[key] = self.method(*args, **kwargs)
return value
except TypeError:
- _logger.warn("cache lookup error on %r", key, exc_info=True)
+ _logger.warning("cache lookup error on %r", key, exc_info=True)
counter.err += 1
return self.method(*args, **kwargs)
@@ -113,9 +113,9 @@ class ormcache_context(ormcache):
""" Determine the function that computes a cache key from arguments. """
assert self.skiparg is None, "ormcache_context() no longer supports skiparg"
# build a string that represents function code and evaluate it
- spec = getargspec(self.method)
- args = formatargspec(*spec)[1:-1]
- cont_expr = "(context or {})" if 'context' in spec.args else "self._context"
+ sign = signature(self.method)
+ args = str(sign)[1:-1]
+ cont_expr = "(context or {})" if 'context' in sign.parameters else "self._context"
keys_expr = "tuple(%s.get(k) for k in %r)" % (cont_expr, self.keys)
if self.args:
code = "lambda %s: (%s, %s)" % (args, ", ".join(self.args), keys_expr)
@@ -142,13 +142,13 @@ class ormcache_multi(ormcache):
super(ormcache_multi, self).determine_key()
# key_multi computes the extra element added to the key
- spec = getargspec(self.method)
- args = formatargspec(*spec)[1:-1]
+ sign = signature(self.method)
+ args = str(sign)[1:-1]
code_multi = "lambda %s: %s" % (args, self.multi)
self.key_multi = unsafe_eval(code_multi)
# self.multi_pos is the position of self.multi in args
- self.multi_pos = spec.args.index(self.multi)
+ self.multi_pos = list(sign.parameters).index(self.multi)
def lookup(self, method, *args, **kwargs):
d, key0, counter = self.lru(args[0])
diff --git a/odoo/tools/convert.py b/odoo/tools/convert.py
index cd893d6ab1a..4fd29e57417 100644
--- a/odoo/tools/convert.py
+++ b/odoo/tools/convert.py
@@ -789,10 +789,10 @@ def convert_xml_import(cr, module, xmlfile, idref=None, mode='init', noupdate=Fa
_logger.exception("The XML file '%s' does not fit the required schema !", xmlfile.name)
if jingtrang:
p = subprocess.run(['pyjing', schema, xmlfile.name], stdout=subprocess.PIPE)
- _logger.warn(p.stdout.decode())
+ _logger.warning(p.stdout.decode())
else:
for e in relaxng.error_log:
- _logger.warn(e)
+ _logger.warning(e)
_logger.info("Install 'jingtrang' for more precise and useful validation messages.")
raise
diff --git a/odoo/tools/mail.py b/odoo/tools/mail.py
index 50ccd6b0375..1ca5e0dc325 100644
--- a/odoo/tools/mail.py
+++ b/odoo/tools/mail.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import base64
import collections
import logging
from lxml.html import clean
@@ -10,8 +11,7 @@ import socket
import threading
import time
-from email.header import decode_header, Header
-from email.utils import getaddresses, formataddr
+from email.utils import getaddresses
from lxml import etree
import odoo
@@ -438,6 +438,8 @@ single_email_re = re.compile(r"""^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6
mail_header_msgid_re = re.compile('<[^<>]+>')
+email_addr_escapes_re = re.compile(r'[\\"]')
+
def generate_tracking_message_id(res_id):
"""Returns a string that can be used in the Message-ID RFC822 header field
@@ -545,19 +547,41 @@ def email_escape_char(email_address):
""" Escape problematic characters in the given email address string"""
return email_address.replace('\\', '\\\\').replace('%', '\\%').replace('_', '\\_')
-# was mail_message.decode()
-def decode_smtp_header(smtp_header):
- """Returns unicode() string conversion of the given encoded smtp header
- text. email.header decode_header method return a decoded string and its
- charset for each decoded par of the header. This method unicodes the
- decoded header and join them in a complete string. """
- if isinstance(smtp_header, Header):
- smtp_header = ustr(smtp_header)
- if smtp_header:
- text = decode_header(smtp_header.replace('\r', ''))
- return ''.join([ustr(x[0], x[1]) for x in text])
- return u''
-
# was mail_thread.decode_header()
def decode_message_header(message, header, separator=' '):
- return separator.join(decode_smtp_header(h) for h in message.get_all(header, []) if h)
+ return separator.join(h for h in message.get_all(header, []) if h)
+
+def formataddr(pair):
+ """Takes a 2-tuple of the form (realname, email_address) and returns
+ the string value suitable for an RFC 2822 From, To or Cc header.
+
+ The email address is considered valid and is left unmodified.
+
+ If the first element of pair is falsy then only the email address
+ is returned.
+
+ >>> formataddr(('John Doe', 'johndoe@example.com'))
+ '"John Doe" <johndoe@example.com>'
+
+ >>> formataddr(('', 'johndoe@example.com'))
+ 'johndoe@example.com'
+ """
+ name, address = pair
+ address.encode('ascii')
+ if name:
+ try:
+ name.encode('ascii')
+ except UnicodeEncodeError:
+ # non-ascii name, transcode the name in a safe format
+ # rfc2047 - MIME Message Header Extensions for Non-ASCII Text
+ return "=?utf-8?b?{name}?= {addr}".format(
+ name=base64.b64encode(name.encode('utf-8')).decode('ascii'),
+ addr=address)
+ else:
+ # ascii name, escape it if needed
+ # rfc2822 - Internet Message Format
+ # #section-3.4 - Address Specification
+ return '"{name}" <{addr}>'.format(
+ name=email_addr_escapes_re.sub(r'\\\g<0>', name),
+ addr=address)
+ return address
diff --git a/odoo/tools/view_validation.py b/odoo/tools/view_validation.py
index f135e52dc3a..a20dc4d46c4 100644
--- a/odoo/tools/view_validation.py
+++ b/odoo/tools/view_validation.py
@@ -183,7 +183,7 @@ def relaxng(view_type):
return _relaxng_cache[view_type]
-@validate('calendar', 'diagram', 'graph', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'graph', 'pivot', 'search', 'tree', 'activity')
def schema_valid(arch, **kwargs):
""" Get RNG validator and validate RNG file."""
validator = relaxng(arch.tag)
@@ -288,7 +288,7 @@ def valid_type_in_col(arch, **kwargs):
)
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_alternative_image_text(arch, **kwargs):
"""An `img` tag must have an alt value."""
if arch.xpath('//img[not(@alt or @t-att-alt or @t-attf-alt)]'):
@@ -296,7 +296,7 @@ def valid_alternative_image_text(arch, **kwargs):
return True
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_alternative_icon_text(arch, **kwargs):
"""An icon with fa- class or in a button must have aria-label in its tag, parents, descendants or have text."""
valid_aria_attrs = {
@@ -338,7 +338,7 @@ def valid_alternative_icon_text(arch, **kwargs):
return True
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_title_icon(arch, **kwargs):
"""An icon with fa- class or in a button must have title in its tag, parents, descendants or have text."""
valid_title_attrs = {'title', 't-att-title', 't-attf-title'}
@@ -378,7 +378,7 @@ def valid_title_icon(arch, **kwargs):
return True
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_simili_button(arch, **kwargs):
"""A simili button must be tagged with "role='button'"."""
# Select elements with class 'btn'
@@ -391,7 +391,7 @@ def valid_simili_button(arch, **kwargs):
return True
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_simili_dropdown(arch, **kwargs):
"""A simili dropdown must be tagged with "role='menu'"."""
xpath = '//*[contains(concat(" ", @class, " "), " dropdown-menu ")'
@@ -403,7 +403,7 @@ def valid_simili_dropdown(arch, **kwargs):
return True
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_simili_progressbar(arch, **kwargs):
"""A simili progressbar must be tagged with "role='progressbar'" and have
aria-valuenow, aria-valuemin and aria-valuemax attributes."""
@@ -421,7 +421,7 @@ def valid_simili_progressbar(arch, **kwargs):
return True
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_dialog(arch, **kwargs):
"""A dialog must use role="dialog" and its header, body and footer contents must use <header/>, <main/> and <footer/>."""
# Select elements with class 'btn'
@@ -455,7 +455,7 @@ def valid_dialog(arch, **kwargs):
return True
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_simili_tabpanel(arch, **kwargs):
"""A tab panel with tab-pane class must have role="tabpanel"."""
# Select elements with class 'btn'
@@ -468,7 +468,7 @@ def valid_simili_tabpanel(arch, **kwargs):
return True
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_simili_tab(arch, **kwargs):
"""A tab link must have role="tab", a link to an id (without #) by aria-controls."""
# Select elements with class 'btn'
@@ -481,7 +481,7 @@ def valid_simili_tab(arch, **kwargs):
return True
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_simili_tablist(arch, **kwargs):
"""A tab list with class nav-tabs must have role="tablist"."""
# Select elements with class 'btn'
@@ -494,7 +494,7 @@ def valid_simili_tablist(arch, **kwargs):
return True
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_focusable_button(arch, **kwargs):
"""A simili button must be with a `button`, an `input` (with type `button`, `submit` or `reset`) or a `a` tag."""
xpath = '//*[contains(concat(" ", @class), " btn")'
@@ -520,7 +520,7 @@ def valid_focusable_button(arch, **kwargs):
return True
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_prohibited_none_role(arch, **kwargs):
"""A role can't be `none` or `presentation`. All your elements must be accessible with screen readers, describe it."""
xpath = '//*[@role="none" or @role="presentation"]'
@@ -529,7 +529,7 @@ def valid_prohibited_none_role(arch, **kwargs):
return True
-@validate('calendar', 'diagram', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
+@validate('calendar', 'form', 'graph', 'kanban', 'pivot', 'search', 'tree', 'activity')
def valid_alerts(arch, **kwargs):
"""An alert (class alert-*) must have an alert, alertdialog or status role. Please use alert and alertdialog only for what expects to stop any activity to be read immediatly."""
xpath = '//*[contains(concat(" ", @class), " alert-")'