docs/docs/divers/devops/odoo/devmodule.md
Florian du Garage Num caa7c989a7 odoo 16 setup
2024-01-26 23:06:13 +01:00

24 KiB

Créer son module pour Odoo

Configurer l'environnement de déploiement

Il nous faut ajouter certaines librairies python au projet.

Pour cela nous allons:

  • ajouter un fichier requirements.txt au dossier etc
  • ajouter un script entrypoint.sh qui sera lancé par le compose

Fichier etc/requirements.txt

??? note "requirements.txt" astor python-stdnum>=1.18 python-magic requests_oauthlib factur-x unicodecsv email-validator py3o.template py3o.formats pypdf2 python-jose plotly==5.4.0

Fichier entrypoint.sh

??? note "entrypoint.sh" ``` #!/bin/bash

set -e

# set the postgres database host, port, user and password according to the environment
# and pass them as arguments to the odoo process if not present in the config file
: ${HOST:=${DB_PORT_5432_TCP_ADDR:='db'}}
: ${PORT:=${DB_PORT_5432_TCP_PORT:=5432}}
: ${USER:=${DB_ENV_POSTGRES_USER:=${POSTGRES_USER:='odoo'}}}
: ${PASSWORD:=${DB_ENV_POSTGRES_PASSWORD:=${POSTGRES_PASSWORD:='odoo'}}}

# install python packages
pip3 install pip --upgrade
pip3 install -r /etc/odoo/requirements.txt

# sed -i 's|raise werkzeug.exceptions.BadRequest(msg)|self.jsonrequest = {}|g' /usr/lib/python3/dist-packages/odoo/http.py

DB_ARGS=()
function check_config() {
    param="$1"
    value="$2"
    if grep -q -E "^\s*\b${param}\b\s*=" "$ODOO_RC" ; then       
        value=$(grep -E "^\s*\b${param}\b\s*=" "$ODOO_RC" |cut -d " " -f3|sed 's/["\n\r]//g')
    fi;
    DB_ARGS+=("--${param}")
    DB_ARGS+=("${value}")
}
check_config "db_host" "$HOST"
check_config "db_port" "$PORT"
check_config "db_user" "$USER"
check_config "db_password" "$PASSWORD"

case "$1" in
    -- | odoo)
        shift
        if [[ "$1" == "scaffold" ]] ; then
            exec odoo "$@"
        else
            wait-for-psql.py ${DB_ARGS[@]} --timeout=30
            exec odoo "$@" "${DB_ARGS[@]}"
        fi
        ;;
    -*)
        wait-for-psql.py ${DB_ARGS[@]} --timeout=30
        exec odoo "$@" "${DB_ARGS[@]}"
        ;;
    *)
        exec "$@"
esac

exit 1
```

Créer un script d'installation automatisée

Ce script python va permettre d'automatiser l'installation des modules de base

??? note "init.py" ``` import odoorpc import subprocess import os import shutil import time

# Configuration
ODOO_URL = 'http://localhost:10014'
ODOO_DB = 'odoo'
ODOO_USERNAME = 'garagenum@gmail.com'
ODOO_PASSWORD = 'bellinux'

# Applications to install
APPS_TO_INSTALL = [
    'sale_management',
    'account',
    'crm',
    'website',
    'stock',
    'purchase',
    'project',
    'mass_mailing',
    'hr_expense',
    'hr_holidays',
    'hr_recruitment',
    'hr',
    'website_slides',
    'mail',
    'contacts',
    'calendar',
    'survey',
    'board',
    'mass_mailing_sms',
    'note',
    'website_forum',
    'hr_skills',
    'website_hr_recruitment',
    'hr_contract',
    'stock_account',
    'website_sms',
    'purchase_stock',
    'account_edi',
    'account_edi_facturx',
    'account_edi_ubl',
    'account_payment',
    'account_qr_code_sepa',
    'analytic',
    'attachment_indexation',
    'auth_oauth',
    'auth_signup',
    'auth_totp',
    'auth_totp_portal',
    'barcodes',
    'base',
    'base_automation',
    'base_iban',
    'base_import',
    'base_setup',
    'base_vat',
    'bus',
    'calendar_sms',
    'delivery',
    'digest',
    'fetchmail',
    'google_recaptcha',
    'hr_gamification',
    'hr_org_chart',
    'hr_skills_slides',
    'hr_skills_survey',
    'iap',
    'iap_mail',
    'l10n_fr',
    'link_tracker',
    'mail_bot',
    'mail_bot_hr',
    'mass_mailing_sale',
    'mass_mailing_slides',
    'payment',
    'payment_fix_register_token',
    'payment_transfer',
    'portal_rating',
    'procurement_jit',
    'product',
    'rating',
    'resource',
    'sale',
    'sale_expense',
    'sale_project',
    'sale_purchase',
    'sale_purchase_stock',
    'sale_stock',
    'sales_team',
    'sms',
    'snailmail',
    'snailmail_account',
    'social_media',
    'stock_sms',
    'uom',
    'utm',
    'web',
    'web_editor',
    'web_kanban_gauge',
    'web_tour',
    'web_unsplash',
    'website_form',
    'website_form_project',
    'website_links',
    'website_mail',
    'website_mass_mailing',
    'website_partner',
    'website_profile',
    'website_slides_forum',
    'website_slides_survey',
    'gamification',
    'portal',
    'http_routing',
    'phone_validation',
    'partner_autocomplete'
]

MODULES_TO_INSTALL = [
    { 'url': 'https://github.com/OCA/account-analytic',
      'apps': [
        {'name': 'account_move_update_analytic'},
        {'name': '/analytic_distribution_widget_remove_save'},
    ]},
    { 'url': 'https://github.com/OCA/account-budgeting',
      'apps': [
        {'name': 'account_budget_oca'},
    ]},     
    { 'url': 'https://github.com/OCA/account-closing',
    'apps': [
        {'name': 'account_cutoff_base'},
        {'name': 'account_invoice_start_end_dates'},
        {'name': 'account_cutoff_start_end_dates'},
        {'name': 'account_cutoff_accrual_picking'},
        {'name': 'account_cutoff_accrual_subscription'},
    ]},
    { 'url': 'https://github.com/OCA/account-financial-reporting',
      'apps': [
        {'name': 'account_financial_report'},
        {'name': 'mis_builder_cash_flow'},
    ]},             
    { 'url': 'https://github.com/OCA/account-financial-tools',
      'apps': [
        {'name': 'account_asset_management'},
        {'name': 'account_cash_deposit'},
        {'name': 'account_fiscal_year'},
        {'name': 'account_move_budget'},
        {'name': 'account_move_line_purchase_info'},
        {'name': 'account_move_line_sale_info'},
        {'name': 'account_move_template'},
        {'name': 'account_netting'},
        {'name': 'account_template_active'},
        {'name': 'account_usability'},
    ]},
    { 'url': 'https://github.com/OCA/account-invoice-reporting',
      'apps': [
        {'name': 'account_comment_template'},
        {'name': 'partner_time_to_pay'},
        {'name': 'account_invoice_line_report'},
    ]},
    { 'url': 'https://github.com/OCA/account-invoicing',
      'apps': [
        {'name': 'account_invoice_fixed_discount'},
        {'name': 'account_invoice_payment_term_date_due'},
        {'name': 'account_invoice_refund_line_selection'},
        {'name': 'account_invoice_refund_link'},
        {'name': 'account_menu_invoice_refund'},
        {'name': 'account_move_tier_validation'},
        {'name': 'account_tax_group_widget_base_amount'},
        {'name': 'partner_invoicing_mode'},
        {'name': 'partner_invoicing_mode_monthly'},
    ]},
    { 'url': 'https://github.com/OCA/account-financial-reporting',
      'apps': [
        {'name': 'account_financial_report'},
        {'name': 'mis_builder_cash_flow'},
    ]}, 
    { 'url': 'https://github.com/OCA/account-payment',
      'apps': [
        {'name': 'account_payment_notification'},
        {'name': 'account_due_list'},
    ]}, 
    {'url': 'https://github.com/OCA/account-reconcile',
    'apps': [  
        {'name': 'account_reconciliation_widget'},
        {'name': 'account_partner_reconcile'},
        {'name': 'account_move_base_import'},
        {'name': 'account_move_line_reconcile_manual'},
        {'name': 'account_reconcile_oca'},
        {'name': 'account_statement_base'},   
    ]},  
    {'url': 'https://github.com/OCA/agreement',
     'apps': [
        {'name': 'agreement'},
        {'name': 'agreement_legal'},
     ]},
    {'url': 'https://github.com/OCA/bank-payment',
    'apps': [
        {'name': 'account_payment_mode'},
    ]},
    {'url': 'https://github.com/OCA/bank-statement-import',
    'apps': [
        {'name': 'account_statement_import_base'},
        {'name': 'account_statement_import_file'},
        {'name': 'account_statement_import_online'},
        {'name': 'account_statement_import_sheet_file'},
    ]},
    {'url': 'https://github.com/OCA/community-data-files',
     'apps': [
        {'name': 'base_unece'},
        {'name': 'account_payment_unece'},
        {'name': 'account_tax_unece'},
        {'name': 'base_currency_iso_4217'},
        {'name': 'base_iso3166'},
        {'name': 'company_sanitary_registry'},
        {'name': 'uom_unece'},
    ]},
    {'url': 'https://github.com/OCA/data-protection',
     'apps': [
        {'name': 'privacy'},
        {'name': 'privacy_consent'},
    ]},
    {'url': 'https://github.com/OCA/dms',
     'apps': [
        {'name': 'dms'},
    ]},
    {'url': 'https://github.com/OCA/donation',
        'apps': [
        {'name': 'donation_base'},
        {'name': 'donation'},
        {'name': 'donation_bank_statement_oca'}
    ]},
    {'url': 'https://github.com/OCA/edi',
     'apps': [
        {'name': 'account_einvoice_generate'},
        {'name': 'account_invoice_facturx'},
        {'name': 'account_invoice_facturx_py3o'},
        {'name': 'base_business_document_import'},
        {'name': 'base_edi'},
        {'name': 'base_facturx'},
        {'name': 'pdf_helper'},
    ]},
    {'url': 'https://github.com/OCA/hr',
     'apps': [
        {'name': 'hr_course'},
        {'name': 'hr_employee_age'},
        {'name': 'hr_employee_birthday_mail'},
        {'name': 'hr_employee_calendar_planning'},
        {'name': 'hr_employee_document'},
        {'name': 'hr_employee_firstname'},
        {'name': 'hr_holidays_settings'},
        {'name': 'hr_personal_equipment_request'},
     ]}
    {'url': 'https://github.com/OCA/hr-expense',
        'apps': [
        {'name': 'hr_expense_cancel'},
        {'name': 'hr_expense_invoice'},
        {'name': 'hr_expense_payment'},
        {'name': 'hr_expense_tier_validation'},
    ]},
    {'url': 'https://github.com/OCA/hr-holidays',
     'apps': [
        {'name': 'hr_holidays_natural_period'},
        {'name': 'hr_holidays_public'},
     ]},
    {'url': 'https://github.com/OCA/knowledge',
     'apps': [
        {'name': 'document_knowledge'},
        {'name': 'document_page'},
        {'name': 'document_page_approval'},
        {'name': 'document_page_group'},
        {'name': 'document_page_reference'},
        {'name': 'document_page_tag'},
        {'name': 'document_url'}
     ]},
    { 'url': 'https://github.com/OCA/l10n-france',
    'apps': [
        {'name': 'l10n_fr_account_invoice_facturx'},
        {'name': 'l10n_fr_account_tax_unece'},
        {'name': 'l10n_fr_account_vat_return'},
        {'name': 'l10n_fr_account_vat_return_teledec'},
        {'name': 'l10n_fr_chorus_account'},
        {'name': 'l10n_fr_chorus_facturx'},
        {'name': 'l10n_fr_chorus_sale'},
        {'name': 'l10n_fr_department'},
        {'name': 'l10n_fr_department_oversea'},
        {'name': 'l10n_fr_fec_oca'},
        {'name': 'l10n_fr_hr_check_ssnid'},
        {'name': 'l10n_fr_mis_reports'},
        {'name': 'l10n_fr_oca},
        {'name': 'l10n_fr_siret'},
        {'name': 'l10n_fr_siret_lookup'},
    ]},        
    { 'url': 'https://github.com/OCA/mis-builder',
    'apps': [
        {'name': 'mis_builder'},
        {'name': 'mis_builder_budget'}
    ]},  
    {'url': 'https://github.com/OCA/partner-contact',
     'apps': [
        {'name': 'partner_address_street3'},
        {'name': 'partner_address_two_lines'},
        {'name': 'partner_company_type'},
        {'name': 'partner_contact_access_link'},
        {'name': 'partner_contact_address_default'},
        {'name': 'partner_email_check'},
        {'name': 'partner_email_duplicate_warn'},
        {'name': 'partner_firstname'},
        {'name': 'partner_mobile_duplicate_warn'},
        {'name': 'partner_subject_to_vat'},
        {'name': 'partner_vat_unique'},    
    ]},
    {'url': 'https://github.com/OCA/payroll',
     'apps': [
        {'name': 'payroll'},
        {'name': 'payroll_account'}
    ]},
    {'url': 'https://github.com/OCA/project',
     'apps': [
        {'name': 'project_department'},
        {'name': 'project_duplicate_subtask'},
        {'name': 'project_hr'},
        {'name': 'project_list'},
        {'name': 'project_parent'},
        {'name': 'project_parent_task_filter'},
        {'name': 'project_task_add_very_high'},
        {'name': 'project_task_link'},
        {'name': 'project_task_stage_mgmt'},
        {'name': 'project_template'},
        {'name': 'project_timeline'},
        {'name': 'project_timeline_hr_timesheet'},
        {'name': 'project_timesheet_time_control'},
    ]},
    {'url': 'https://github.com/OCA/reporting-engine',
      'apps': [
        {'name': 'bi_view_editor'},
        {'name': 'bi_view_editor_spreadsheet_dashboard'},
        {'name': 'report_py3o'},
        {'name': 'report_qweb_pdf_watermark'},
        {'name': 'report_wkhtmltopdf_param'},
    ]},
    {'url': 'https://github.com/OCA/sale-workflow',
     'apps': [
        {'name': 'partner_sale_pivot'},
        {'name': 'product_form_sale_link'},
        {'name': 'sale_advance_payment},
        {'name': 'sale_cancel_reason'},
        {'name': 'sale_delivery_state'},
        {'name': 'sale_discount_display_amount'},
        {'name': 'sale_fixed_discount'},
        {'name': 'sale_force_invoiced'},
        {'name': 'sale_order_general_discount'}
        {'name': 'sale_order_invoice_amount'},
        {'name': 'sale_order_line_menu'},
        {'name': 'sale_order_line_tag'},
        {'name': 'sale_order_revision'},
        {'name': 'sale_start_end_dates'},
        {'name': 'sale_substate'},
        {'name': 'sale_tier_validation'},
     ]},
    {'url': 'https://github.com/OCA/server-auth',
     'apps': [
        {'name': 'auth_oidc'},
     ]},
     {'url': 'https://github.com/OCA/server-brand',
      'apps': [
        {'name': 'disable_odoo_online'},
        {'name': 'hr_expense_remove_mobile_link'},
        {'name': 'portal_odoo_debranding'},
        {'name': 'remove_odoo_enterprise'},
      ]},
    {'url': 'https://github.com/OCA/server-tools',
     'apps': [
        {'name': 'attachment_unindex_content'},
        {'name': 'base_fontawesome'},
        {'name': 'base_name_search_improved'},
        {'name': 'base_view_inheritance_extension'},
        {'name': 'iap_alternative_provider'},
        {'name': 'module_auto_update'},
     ]},
    { 'url': 'https://github.com/OCA/server-ux',
    'apps': [
        {'name': 'base_cancel_confirm'},
        {'name': 'base_custom_filter'},
        {'name': 'base_menu_visibility_restriction'},
        {'name': 'base_optional_quick_create'},
        {'name': 'base_revision'},
        {'name': 'base_search_custom_field_filter'},
        {'name': 'base_substate'},
        {'name': 'base_technical_features'},
        {'name': 'base_tier_validation'},
        {'name': 'date_range'},
        {'name': 'date_range_account'},
        {'name': 'filter_multi_user'},
        {'name': 'multi_step_wizard'},
        {'name': 'user_all_groups'}
    ]}, 
    {'url': 'https://github.com/OCA/sign',
     'apps': [
        {'name': 'sign_oca'},
     ]}
    {'url': 'https://github.com/OCA/social',
    'apps': [
        {'name': 'base_search_mail_content'},
        {'name': 'email_template_qweb'},
        {'name': 'mail_activity_board'},
        {'name': 'mail_activity_done'},
        {'name': 'mail_attach_existing_attachment'},
        {'name': 'mail_composer_cc_bcc'},
        {'name': 'mail_debrand'},
        {'name': 'mail_layout_preview'},
        {'name': 'mail_inline_css'},
        {'name': 'mail_optional_follower_notification'},
        {'name': 'mail_tracking'},
        {'name': 'mail_tracking_mass_mailing'},
        {'name': 'mass_mailing_list_dynamic'},
        {'name': 'mass_mailing_partner'},
        {'name': 'mass_mailing_resend'},
        {'name': 'mass_mailing_unique'},
    ]},
    {'url': 'https://github.com/OCA/spreadsheet',
     'apps': [
        {'name': 'spreadsheet_dashboard_oca'},
        {'name': 'spreadsheet_oca'},
     ]},
    {'url': 'https://github.com/OCA/survey',
     'apps': [
        {'name': 'survey_question_type_five_star'},
    ]},
    {'url': 'https://github.com/OCA/timesheet',
     'apps': [
        {'name': 'hr_timesheet_begin_end'}
        {'name': 'hr_timesheet_sheet'},
        {'name': 'hr_timesheet_task_domain'},
        {'name': 'hr_timesheet_time_type'},
        {'name': '}
     ]}
    {'url': 'https://github.com/OCA/vertical-association',
     'apps': [
        {'name: 'web_advanced_search'},
        {'name': 'web_calendar_slot_duration'},
        {'name': 'web_chatter_position'},
        {'name': 'web_dark_mode'},
        {'name': 'web_dialog_size'},
        {'name': 'web_group_expand'},
        {'name': 'web_help'},
        {'name': 'web_ir_actions_act_window_page'},
        {'name': 'web_listview_range_select'},
        {'name': 'web_m2x_options'},
        {'name': 'web_no_bubble'},
        {'name': 'web_remember_tree_column_width'},
        {'name': 'web_responsive'},
        {'name': 'web_search_with_and'},
        {'name': 'web_theme_classic'},
        {'name': 'web_timeline'},
        {'name': 'web_tree_many2one_clickable'},
        {'name': 'web_widget_dropdown_dynamic'},
        {'name': 'web_widget_numeric_step'},
        {'name': 'web_widget_open_tab'},
        {'name': 'web_widget_plotly_chart'}
     ]},
    {'url': 'https://github.com/OCA/website',
     'apps': [
    {'name': 'website_odoo_debranding'},
      ]},
    {'url': 'https://github.com/OCA/website-cms',
     'apps': [
        {'name': 'cms_form'},
        {'name': 'cms_status_message'}
     ]}
    # {'url': 'https://git.legaragenumerique.fr/odoo/gn_odoo',
    # 'apps': [
    #     {'name': 'gn_discount'},
    #     {'name': 'gn_donations'}
    # ]},
]

def configure_folders():
    #os.makedirs('addons', exist_ok=True)
    subprocess.run(["setfacl", "-m", "u:101:rwx", './addons'], check=True)
    subprocess.run(["setfacl", "-d", "-m", "u:101:rwx", './addons'], check=True)
    subprocess.run(["setfacl", "-m", "u:101:rwx", './etc'], check=True)
    subprocess.run(["setfacl", "-d", "-m", "u:101:rwx", './etc'], check=True)

def merge_directories(src, dst):
    for item in os.listdir(src):
        if item != '.git':
            s = os.path.join(src, item)
            d = os.path.join(dst, item)
            if os.path.isdir(s):
                if not os.path.exists(d):
                    shutil.copytree(s, d)
                else:
                    merge_directories(s, d)
            else:
                shutil.copy2(s, d)

def connect_odoo():
    # Connect to the Odoo server
    odoo = odoorpc.ODOO('localhost', port=10014)
    odoo.login(ODOO_DB, ODOO_USERNAME, ODOO_PASSWORD)
    return odoo


def install_app(odoo, odoo_app):
    # Check if each module is installed or not
    print(f"Checking {odoo_app} installation...")
    module_id = odoo.env['ir.module.module'].search([('name', '=', odoo_app)])
    if module_id:
        module = odoo.env['ir.module.module'].browse(module_id)[0]
        if module.state not in ['installed', 'to upgrade']:
            time.sleep(0.3)
            module.button_immediate_install()
            print(f"Module {odoo_app} has been installed.")
        else:
            print(f"Module {odoo_app} is already installed.")
    else:
        print(f"Module {odoo_app} not found.")

def install_apps(odoo):
    for odoo_app in APPS_TO_INSTALL:
        install_app(odoo, odoo_app)
        time.sleep(1)

def dl_modules(group):
    repo_url = group['url'].rstrip('/')
    addons_path = "addons"

    sparse_checkout_lines = []
    for app in group['apps']:
        if app['name'] != None:
            app_dir = app['dir'] if 'dir' in app else app['name']
            sparse_checkout_lines.append(f"{app_dir}\n")

    clone_path = 'tmp' if group['apps'][0]['name'] != None else os.path.join('tmp', group['url'].split('/')[-1])
    clone_full_path = os.path.join(addons_path, clone_path)
    git_dir_path = os.path.join(clone_full_path, '.git')
    sparse_checkout_file_path = os.path.join(git_dir_path, 'info', 'sparse-checkout')
    branch_name = "16.0"

    print("starting git clone")
    subprocess.run(["git", "clone", "--filter=blob:none", "--sparse", "--branch", branch_name, "--depth", "1", repo_url, clone_path], cwd=addons_path, check=True)

    if not os.path.isdir(sparse_checkout_file_path):
        subprocess.run(["git", "config", "core.sparseCheckout", "true"], cwd=clone_full_path, check=True)

    with open(sparse_checkout_file_path, "w") as sparse_checkout_file:
        sparse_checkout_file.writelines(sparse_checkout_lines)

    print("starting git checkout")
    subprocess.run(["git", "checkout", branch_name], cwd=clone_full_path, check=True)

    src_path = os.path.join(addons_path, 'tmp')
    merge_directories(src_path, addons_path)
    shutil.rmtree(src_path)

    print(f"Modules {', '.join(app['name']  if app['name'] else group['url'] for app in group['apps'])} has been downloaded")

def update_module_list(odoo):
    ModuleUpdate = odoo.env['base.module.update']
    module_update_id = ModuleUpdate.create({})
    module_update = ModuleUpdate.browse(module_update_id)
    module_update.update_module()
    updated_modules = module_update.updated
    added_modules = module_update.added
    print(f"Updated modules: {updated_modules}, Added modules: {added_modules}")

def install_modules(odoo):
    for group in MODULES_TO_INSTALL:
        dl_modules(group)
    time.sleep(1)
    update_module_list(odoo)
    time.sleep(1)
    for group in MODULES_TO_INSTALL:
        for app in group['apps']:
            app_name = app['name'] if app['name'] != None else group['url'].split('/')[-1]
            install_app(odoo, app_name)

def main():
    configure_folders()
    odoo = connect_odoo()
    install_apps(odoo)
    time.sleep(3)
    install_modules(odoo)
    print("Script execution finished!")

main()

```