diff --git a/.env.dist b/.env.dist index 4681b27..7c8879b 100644 --- a/.env.dist +++ b/.env.dist @@ -8,18 +8,26 @@ APP_URL=${APP_SCHEME}://${APP_URI} ENV=dist ODOO14_ADDITIONAL_ODOO_RC=encryption_key=${APP_DOMAIN} ODOO14_ADMIN_PASSWD=admin +ODOO14_DBFILTER=^.*$ ODOO14_DB_HOST=postgres ODOO14_DB_NAME=librezo ODOO14_DB_PASSWORD=odoo ODOO14_DB_USER=odoo ODOO14_LIST_DB=true +ODOO14_LOG_DB=False +ODOO14_LOG_HANDLER=:INFO +ODOO14_LOG_LEVEL=info +ODOO14_MARABUNTA_MODE=demo +ODOO14_MAX_CRON_THREADS=1 ODOO14_MODULES_AUTO_INSTALL_DISABLED=partner_autocomplete,iap,mail_bot,account_edi,account_edi_facturx,account_edi_ubl ODOO14_MODULES_AUTO_INSTALL_ENABLED=account_menu,base_technical_features,disable_odoo_online,web_no_bubble,web_responsive ODOO14_ODOO_BASE_URL=${APP_URL} ODOO14_ODOO_REPORT_URL=${APP_URL} +ODOO14_RUNNING_ENV=dev ODOO14_SERVER_WIDE_MODULES=web,module_change_auto_install ODOO14_SERVICE_8069_TAGS=urlprefix-${APP_URI} ODOO14_SERVICE_8072_TAGS=urlprefix-${APP_URI}longpolling/ +ODOO14_WORKERS=0 POSTGRES_DB=${ODOO14_DB_NAME} POSTGRES_PASSWORD=${ODOO14_DB_PASSWORD} POSTGRES_USER=${ODOO14_DB_USER} diff --git a/bin/config b/bin/config new file mode 100755 index 0000000..0bbcb36 --- /dev/null +++ b/bin/config @@ -0,0 +1,12 @@ +#!/opt/odoo/bin/python + +import click +import click_odoo + +@click.command() +@click_odoo.env_options(default_log_level="error") +def main(env): + env["res.company"].browse(1).write({"name": "Librezo"}) + +if __name__ == "__main__": + main() diff --git a/bin/csv_export b/bin/csv_export new file mode 100755 index 0000000..8ac1239 --- /dev/null +++ b/bin/csv_export @@ -0,0 +1,155 @@ +#!/opt/odoo/bin/python +# pylint: disable=print-used +import csv +import json +import os +import requests +from bs4 import BeautifulSoup +from datetime import datetime +from io import StringIO + +# Config +csv_export_db = os.getenv('ODOO_CSV_EXPORT_DB', 'odoo') +csv_export_dir = os.getenv('ODOO_CSV_EXPORT_DIR', 'csv') +csv_export_login = os.getenv('ODOO_CSV_EXPORT_USER', 'admin') +csv_export_password = os.getenv('ODOO_CSV_EXPORT_PASSWORD', 'admin') +csv_export_url = os.getenv('ODOO_CSV_EXPORT_URL', 'http://localhost:8069') + +PRODUCT_FIELD = [ + (".id", "openerp_id#key"), + ("code", "default_code"), + "name", + ("categ_id/.id", "categ_id|openerp_id"), + "standard_price", + ("tag_ids/.id", "tag_ids|openerp_id"), + "type", + ("taxes_id/description", "taxes_id|1|name"), + ("supplier_taxes_id/description", "supplier_taxes_id|1|name"), + "sale_ok", + "purchase_ok", + ("seller_ids/name/.id", "seller_ids|1|name|openerp_id"), + ("seller_ids/pricelist_ids/price", "seller_ids|1|price"), + "lst_price", +] + +TODO = [ + { + "filename": "product.tag.csv", + "model": "product.tag", + "fields": [ + (".id", "openerp_id#key"), + "name", + ], + }, + { + "filename": "product.category.csv", + "model": "product.category", + "fields": [ + (".id", "openerp_id#key"), + "name", + ("parent_id/.id", "parent_id|openerp_id"), + ], + "domain": [("name", "!=", "")], + }, + { + "filename": "res.partner.csv", + "model": "res.partner", + "fields": [ + (".id", "openerp_id#key"), + "name", + ("parent_id/.id", "parent_id|openerp_id"), + "is_company", + ("customer", "customer_rank"), + ("supplier", "supplier_rank"), + "type", + "street", + "street2", + "zip", + "city", + ("country_id/code", "country_id|code"), + "website", + "function", + "phone", + "mobile", + "email", + "lang", + "fax", + ], + "domain": [("supplier", "=", True)], + }, + { + "filename": "product.product.csv", + "model": "product.product", + "fields": PRODUCT_FIELD, + "domain": [("name", "!=", "")], + }, +] + +def main(): + print(csv_export_url) + print(csv_export_db) + print(csv_export_login) + session = requests.Session() + response = session.get( + "%s/web/login?db=%s" % (csv_export_url,csv_export_db), + ) + print(response.status_code) + print(response.headers) + soup = BeautifulSoup(response.text, 'lxml') + csrf_token = soup.select_one('input[name="csrf_token"]')['value'] + response = session.post( + "%s/web/login" % csv_export_url, + params={"login": csv_export_login, "password": csv_export_password, "csrf_token": csrf_token}, + ) + if response.status_code == 200: + if "Wrong login/password" in response.text: + raise Exception("ERROR: login failed due to wrong login/password, please check ODOO_CSV_EXPORT_USER and ODOO_CSV_EXPORT_PASSWORD environment variables") + else: + raise Exception("ERROR: unable to connect to odoo, did you set ODOO_CSV_EXPORT_URL, ODOO_CSV_EXPORT_USER and ODOO_CSV_EXPORT_PASSWORD environment variables ?") + + for todo in TODO: + start = datetime.now() + fields = [] + for field in todo["fields"]: + if isinstance(field, tuple): + fields.append({"name": field[0], "label": field[1]}) + else: + fields.append({"name": field, "label": field}) + response = session.get( + "%s/web/export/csv?data=foo&token=bar" % csv_export_url, + ) + if response.status_code == 404: + raise Exception("ERROR: unable to find export csv route, please install pattern-import-export addon") + print (response.text) + soup = BeautifulSoup(response.text, 'lxml') + csrf_token = soup.find('input',attrs = {'name':'csrf_token'})['value'] + print (csrf_token) + response = session.post( + "%s/web/export/csv" % csv_export_url, + params={ + "data": json.dumps( + { + "model": todo["model"], + "fields": fields, + "domain": todo.get("domain", []), + "ids": False, + "import_compat": False, + "context": {"lang": "fr_FR"}, + } + ), + "token": "foobar", + "csrf_token": csrf_token, + }, + ) + if response.status_code == 500: + raise Exception("ERROR: unable to retrieve csv, please check your import/export patterns") + text = response.text + + with open("%s/%s" % (csv_export_dir, todo["filename"]), "w") as output: + if todo["filename"] == "res.partner.csv": + text = text.replace('"True"', "1").replace('"False"', "0") + output.write(text) + print("File %s/%s exported in %s s" % (csv_export_dir, todo["filename"], (datetime.now() - start).seconds)) + +if __name__ == "__main__": + main() diff --git a/bin/csv_import b/bin/csv_import new file mode 100755 index 0000000..7cc1787 --- /dev/null +++ b/bin/csv_import @@ -0,0 +1,31 @@ +#!/opt/odoo/bin/python + +import base64 + +import click +import click_odoo + +TODO = [ + ("product.category.csv", "Product categories"), + ("product.tag.csv", "Product tags"), + ("res.partner.csv", "Partners"), + ("product.product.csv", "Products"), +] + +@click.command() +@click_odoo.env_options(default_log_level="error") +def main(env): + for filename, pattern_name in TODO: + with open(f"csv/{filename}", "rb") as f: + pattern = env["pattern.config"].search([("name", "=", pattern_name)]) + wizard = env["import.pattern.wizard"].create( + { + "pattern_config_id": pattern.id, + "import_file": base64.b64encode(f.read()), + "filename": filename, + } + ) + wizard.action_launch_import() + +if __name__ == "__main__": + main() diff --git a/bin/pattern_import b/bin/pattern_import new file mode 100755 index 0000000..f580941 --- /dev/null +++ b/bin/pattern_import @@ -0,0 +1,40 @@ +#!/opt/odoo/bin/python +# pylint: disable=print-used + +import click +import click_odoo +import yaml + +@click.command() +@click_odoo.env_options(default_log_level="error") +def main(env): + def _prepare_fields(field): + if "sub_pattern_config_id" in field: + pattern = env["pattern.config"].search( + [("name", "=", field.pop("sub_pattern_config_id"))] + ) + field["sub_pattern_config_id"] = pattern.id + return field + + with open("pattern.yml") as f: + for data in yaml.load(f, Loader=yaml.SafeLoader): + print("Import pattern", data["name"]) + pattern = env["pattern.config"].search([("name", "=", data["name"])]) + vals = { + "name": data["name"], + "resource": data["resource"], + "export_fields": [ + (0, 0, _prepare_fields(field)) for field in data["export_fields"] + ], + "export_format": "csv", + "tab_to_import": "match_name", + "process_multi": data.get("process_multi", True), + } + if pattern: + pattern.export_fields.unlink() + pattern.write(vals) + else: + env["pattern.config"].create(vals) + +if __name__ == "__main__": + main() diff --git a/csv/.gitignore b/csv/.gitignore new file mode 100644 index 0000000..afed073 --- /dev/null +++ b/csv/.gitignore @@ -0,0 +1 @@ +*.csv diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index ac47168..9738510 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -35,6 +35,11 @@ services: - MODULES_AUTO_INSTALL_DISABLED=${ODOO14_MODULES_AUTO_INSTALL_DISABLED} - MODULES_AUTO_INSTALL_ENABLED=${ODOO14_MODULES_AUTO_INSTALL_ENABLED} - ODOO_BASE_URL=${ODOO14_ODOO_BASE_URL} + - ODOO_CSV_EXPORT_DB=${ODOO14_ODOO_CSV_EXPORT_DB:-odoo} + - ODOO_CSV_EXPORT_DIR=${ODOO14_ODOO_CSV_EXPORT_DIR:-csv} + - ODOO_CSV_EXPORT_PASSWORD=${ODOO14_ODOO_CSV_EXPORT_PASSWORD:-admin} + - ODOO_CSV_EXPORT_URL=${ODOO14_ODOO_CSV_EXPORT_URL:-http://localhost:8069} + - ODOO_CSV_EXPORT_USER=${ODOO14_ODOO_CSV_EXPORT_USER:-admin} - ODOO_QUEUE_JOB_CHANNELS=${ODOO14_ODOO_QUEUE_JOB_CHANNELS:-root:1} - ODOO_REPORT_URL=${ODOO14_ODOO_REPORT_URL} - RUNNING_ENV=${ODOO14_RUNNING_ENV:-dev} diff --git a/docker/odoo14/Dockerfile b/docker/odoo14/Dockerfile index 0517d03..49c8aa4 100644 --- a/docker/odoo14/Dockerfile +++ b/docker/odoo14/Dockerfile @@ -161,6 +161,8 @@ RUN apk add --no-cache --virtual .build-deps \ | xargs -rt apk add --no-cache COPY --chown=${USER} *.yml /opt/odoo/ +COPY --chown=${USER} bin/ /opt/odoo/bin/ +COPY --chown=${USER} csv/ /opt/odoo/csv/ COPY --chown=${USER} entrypoint.d/ /opt/odoo/entrypoint.d/ COPY --chown=${USER} extra-addons/ /opt/odoo/extra-addons/ ENV ADDONS_PATH=${ADDONS_PATH},/opt/odoo/extra-addons diff --git a/pattern.yml b/pattern.yml new file mode 100644 index 0000000..70e1d56 --- /dev/null +++ b/pattern.yml @@ -0,0 +1,49 @@ +- name: Product categories + resource: product.category + process_multi: False + export_fields: + - name: id + is_key: True + - name: name + - name: parent_id/id +- name: Partners + resource: res.partner + export_fields: + - name: id + is_key: True + - name: name + - name: company_id/id + - name: parent_id/id + - name: is_company + - name: customer_rank + - name: siret + - name: supplier_rank + - name: type + - name: street + - name: street2 + - name: zip + - name: city + - name: country_id/code + - name: website + - name: function + - name: phone + - name: mobile + - name: email + - name: lang +- name: Products + resource: product.product + export_fields: + - name: id + is_key: True + - name: default_code + - name: name + - name: categ_id/id + - name: standard_price + - name: type + - name: taxes_id/description + - name: supplier_taxes_id/description + - name: sale_ok + - name: purchase_ok + - name: seller_ids/name/name + - name: seller_ids/price + - name: list_price