From 10151fa62d3c48193a501e8a0a0feb8f821a1a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Laukavi=C4=8Dius?= Date: Fri, 8 Dec 2023 11:40:48 +0200 Subject: [PATCH 1/5] [REF] graphql_vuestorefront -> vuestorefront Renaming to have simpler name and be different from forked one. --- .../data/demo_products_men_clothing_1.xml | 929 ------------------ .../security/ir.model.access.csv | 4 - .../README.rst | 0 .../__init__.py | 0 .../__manifest__.py | 0 .../controllers/__init__.py | 0 .../controllers/main.py | 0 .../data/demo_product_attribute.xml | 0 .../data/demo_product_public_category.xml | 0 .../data/demo_products_men_clothing_1.xml | 929 ++++++++++++++++++ .../data/demo_products_men_clothing_2.xml | 350 +++---- .../data/demo_products_men_clothing_3.xml | 170 ++-- .../data/demo_products_men_clothing_4.xml | 364 +++---- .../data/demo_products_men_shoes.xml | 156 +-- .../data/demo_products_women_bags.xml | 300 +++--- .../data/demo_products_women_clothing.xml | 468 ++++----- .../data/demo_products_women_shoes.xml | 364 +++---- .../data/ir_config_parameter_data.xml | 0 .../data/ir_cron_data.xml | 2 +- .../data/mail_template.xml | 0 .../data/website_data.xml | 0 .../hooks.py | 0 .../models/__init__.py | 0 .../models/invalidate_cache.py | 0 .../models/ir_http.py | 0 .../models/payment_transaction.py | 0 .../models/product.py | 0 .../models/res_config_settings.py | 2 +- .../models/res_users.py | 2 +- .../models/website.py | 0 .../schema.py | 2 +- .../schemas/__init__.py | 0 .../schemas/address.py | 2 +- .../schemas/category.py | 2 +- .../schemas/contact_us.py | 2 +- .../schemas/country.py | 2 +- .../schemas/invoice.py | 2 +- .../schemas/mailing_list.py | 2 +- .../schemas/objects.py | 0 .../schemas/order.py | 2 +- .../schemas/payment.py | 4 +- .../schemas/product.py | 2 +- .../schemas/shop.py | 2 +- .../schemas/sign.py | 2 +- .../schemas/user_profile.py | 2 +- .../schemas/website.py | 2 +- .../schemas/wishlist.py | 2 +- vuestorefront/security/ir.model.access.csv | 4 + .../static/description/icon.png | Bin .../static/men/clothing/blazer/blazer-1.jpg | Bin .../static/men/clothing/blazer/blazer-2.jpg | Bin .../men/clothing/jackets/jackets-1-blue.jpg | Bin .../men/clothing/jackets/jackets-1-grey.jpg | Bin .../men/clothing/jackets/jackets-10-black.jpg | Bin .../men/clothing/jackets/jackets-11-blue.jpg | Bin .../men/clothing/jackets/jackets-12-black.jpg | Bin .../men/clothing/jackets/jackets-13-blue.jpg | Bin .../men/clothing/jackets/jackets-14-black.jpg | Bin .../men/clothing/jackets/jackets-15-beige.jpg | Bin .../men/clothing/jackets/jackets-16-grey.jpg | Bin .../men/clothing/jackets/jackets-17-beige.jpg | Bin .../men/clothing/jackets/jackets-18-grey.jpg | Bin .../men/clothing/jackets/jackets-19-blue.jpg | Bin .../men/clothing/jackets/jackets-2-blue.jpg | Bin .../men/clothing/jackets/jackets-20-brown.jpg | Bin .../men/clothing/jackets/jackets-21-blue.jpg | Bin .../men/clothing/jackets/jackets-22-black.jpg | Bin .../men/clothing/jackets/jackets-23-black.jpg | Bin .../men/clothing/jackets/jackets-24-beige.jpg | Bin .../men/clothing/jackets/jackets-25-red.jpg | Bin .../men/clothing/jackets/jackets-3-blue.jpg | Bin .../men/clothing/jackets/jackets-4-blue.jpg | Bin .../men/clothing/jackets/jackets-4-brown.jpg | Bin .../men/clothing/jackets/jackets-4-green.jpg | Bin .../men/clothing/jackets/jackets-5-red.jpg | Bin .../men/clothing/jackets/jackets-6-brown.jpg | Bin .../men/clothing/jackets/jackets-7-blue.jpg | Bin .../men/clothing/jackets/jackets-8-blue.jpg | Bin .../men/clothing/jackets/jackets-9-blue.jpg | Bin .../static/men/clothing/jeans/jeans-1.jpg | Bin .../static/men/clothing/jeans/jeans-2.jpg | Bin .../static/men/clothing/shirts/shirts-1.jpg | Bin .../static/men/clothing/shirts/shirts-2.jpg | Bin .../static/men/clothing/suits/suits-1.jpg | Bin .../static/men/clothing/suits/suits-2.jpg | Bin .../men/clothing/t-shirts/t-shirts-1.jpg | Bin .../men/clothing/t-shirts/t-shirts-2.jpg | Bin .../static/men/clothing/tops/tops-1.jpg | Bin .../static/men/clothing/tops/tops-2.jpg | Bin .../men/clothing/trousers/trousers-1.jpg | Bin .../men/clothing/trousers/trousers-2.jpg | Bin .../shoes/lace-up_shoes/lace-up_shoes-1.jpg | Bin .../shoes/lace-up_shoes/lace-up_shoes-2.jpg | Bin .../static/men/shoes/loafers/loafers-1.jpg | Bin .../static/men/shoes/loafers/loafers-2.jpg | Bin .../static/men/shoes/sneakers/sneakers-1.jpg | Bin .../static/men/shoes/sneakers/sneakers-2.jpg | Bin .../static/women/bags/clutches/clutches-1.jpg | Bin .../static/women/bags/clutches/clutches-2.jpg | Bin .../women/bags/clutches/clutches-3-blue.jpg | Bin .../women/bags/clutches/clutches-3-brown.jpg | Bin .../women/bags/clutches/clutches-3-pink.jpg | Bin .../women/bags/clutches/clutches-3-yellow.jpg | Bin .../static/women/bags/handbag/handbag-1.jpg | Bin .../static/women/bags/handbag/handbag-2.jpg | Bin .../static/women/bags/shopper/shopper-1.jpg | Bin .../static/women/bags/shopper/shopper-2.jpg | Bin .../women/bags/shopper/shopper-3-black.jpg | Bin .../women/bags/shopper/shopper-3-white.jpg | Bin .../bags/shoulder_bags/shoulder_bags-1.jpg | Bin .../bags/shoulder_bags/shoulder_bags-2.jpg | Bin .../static/women/bags/wallets/wallets-1.jpg | Bin .../static/women/bags/wallets/wallets-2.jpg | Bin .../static/women/clothing/blazer/blazer-1.jpg | Bin .../static/women/clothing/blazer/blazer-2.jpg | Bin .../women/clothing/dresses/dresses-1.jpg | Bin .../women/clothing/dresses/dresses-2.jpg | Bin .../women/clothing/jackets/jacket-1.jpg | Bin .../women/clothing/jackets/jacket-2.jpg | Bin .../static/women/clothing/jeans/jeans-1.jpg | Bin .../static/women/clothing/jeans/jeans-2.jpg | Bin .../static/women/clothing/shirts/shirts-1.jpg | Bin .../static/women/clothing/shirts/shirts-2.jpg | Bin .../static/women/clothing/skirts/skirts-1.jpg | Bin .../static/women/clothing/skirts/skirts-2.jpg | Bin .../women/clothing/t-shirts/t-shirts-1.jpg | Bin .../women/clothing/t-shirts/t-shirts-2.jpg | Bin .../static/women/clothing/tops/tops-1.jpg | Bin .../static/women/clothing/tops/tops-2.jpg | Bin .../women/clothing/trouser/trouser-1.jpg | Bin .../women/clothing/trouser/trouser-2.jpg | Bin .../women/shoes/ankle_boots/ankle_boots-1.jpg | Bin .../women/shoes/ankle_boots/ankle_boots-2.jpg | Bin .../women/shoes/ballerinas/ballerinas-1.jpg | Bin .../women/shoes/ballerinas/ballerinas-2.jpg | Bin .../static/women/shoes/boots/boots-1.jpg | Bin .../static/women/shoes/boots/boots-2.jpg | Bin .../static/women/shoes/loafers/loafers-1.jpg | Bin .../static/women/shoes/loafers/loafers-2.jpg | Bin .../static/women/shoes/pumps/pumps-1.jpg | Bin .../static/women/shoes/pumps/pumps-2.jpg | Bin .../static/women/shoes/sandals/sandals-1.jpg | Bin .../static/women/shoes/sandals/sandals-2.jpg | Bin .../women/shoes/sneakers/sneakers-1.jpg | Bin .../women/shoes/sneakers/sneakers-2.jpg | Bin .../views/product_views.xml | 0 .../views/res_config_settings_views.xml | 4 +- .../views/website_views.xml | 4 +- 148 files changed, 2042 insertions(+), 2042 deletions(-) delete mode 100644 graphql_vuestorefront/data/demo_products_men_clothing_1.xml delete mode 100644 graphql_vuestorefront/security/ir.model.access.csv rename {graphql_vuestorefront => vuestorefront}/README.rst (100%) rename {graphql_vuestorefront => vuestorefront}/__init__.py (100%) rename {graphql_vuestorefront => vuestorefront}/__manifest__.py (100%) rename {graphql_vuestorefront => vuestorefront}/controllers/__init__.py (100%) rename {graphql_vuestorefront => vuestorefront}/controllers/main.py (100%) rename {graphql_vuestorefront => vuestorefront}/data/demo_product_attribute.xml (100%) rename {graphql_vuestorefront => vuestorefront}/data/demo_product_public_category.xml (100%) create mode 100644 vuestorefront/data/demo_products_men_clothing_1.xml rename {graphql_vuestorefront => vuestorefront}/data/demo_products_men_clothing_2.xml (52%) rename {graphql_vuestorefront => vuestorefront}/data/demo_products_men_clothing_3.xml (52%) rename {graphql_vuestorefront => vuestorefront}/data/demo_products_men_clothing_4.xml (58%) rename {graphql_vuestorefront => vuestorefront}/data/demo_products_men_shoes.xml (58%) rename {graphql_vuestorefront => vuestorefront}/data/demo_products_women_bags.xml (57%) rename {graphql_vuestorefront => vuestorefront}/data/demo_products_women_clothing.xml (58%) rename {graphql_vuestorefront => vuestorefront}/data/demo_products_women_shoes.xml (58%) rename {graphql_vuestorefront => vuestorefront}/data/ir_config_parameter_data.xml (100%) rename {graphql_vuestorefront => vuestorefront}/data/ir_cron_data.xml (90%) rename {graphql_vuestorefront => vuestorefront}/data/mail_template.xml (100%) rename {graphql_vuestorefront => vuestorefront}/data/website_data.xml (100%) rename {graphql_vuestorefront => vuestorefront}/hooks.py (100%) rename {graphql_vuestorefront => vuestorefront}/models/__init__.py (100%) rename {graphql_vuestorefront => vuestorefront}/models/invalidate_cache.py (100%) rename {graphql_vuestorefront => vuestorefront}/models/ir_http.py (100%) rename {graphql_vuestorefront => vuestorefront}/models/payment_transaction.py (100%) rename {graphql_vuestorefront => vuestorefront}/models/product.py (100%) rename {graphql_vuestorefront => vuestorefront}/models/res_config_settings.py (97%) rename {graphql_vuestorefront => vuestorefront}/models/res_users.py (96%) rename {graphql_vuestorefront => vuestorefront}/models/website.py (100%) rename {graphql_vuestorefront => vuestorefront}/schema.py (96%) rename {graphql_vuestorefront => vuestorefront}/schemas/__init__.py (100%) rename {graphql_vuestorefront => vuestorefront}/schemas/address.py (99%) rename {graphql_vuestorefront => vuestorefront}/schemas/category.py (97%) rename {graphql_vuestorefront => vuestorefront}/schemas/contact_us.py (95%) rename {graphql_vuestorefront => vuestorefront}/schemas/country.py (97%) rename {graphql_vuestorefront => vuestorefront}/schemas/invoice.py (97%) rename {graphql_vuestorefront => vuestorefront}/schemas/mailing_list.py (99%) rename {graphql_vuestorefront => vuestorefront}/schemas/objects.py (100%) rename {graphql_vuestorefront => vuestorefront}/schemas/order.py (98%) rename {graphql_vuestorefront => vuestorefront}/schemas/payment.py (98%) rename {graphql_vuestorefront => vuestorefront}/schemas/product.py (99%) rename {graphql_vuestorefront => vuestorefront}/schemas/shop.py (99%) rename {graphql_vuestorefront => vuestorefront}/schemas/sign.py (98%) rename {graphql_vuestorefront => vuestorefront}/schemas/user_profile.py (96%) rename {graphql_vuestorefront => vuestorefront}/schemas/website.py (95%) rename {graphql_vuestorefront => vuestorefront}/schemas/wishlist.py (97%) create mode 100644 vuestorefront/security/ir.model.access.csv rename {graphql_vuestorefront => vuestorefront}/static/description/icon.png (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/blazer/blazer-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/blazer/blazer-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-1-blue.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-1-grey.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-10-black.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-11-blue.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-12-black.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-13-blue.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-14-black.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-15-beige.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-16-grey.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-17-beige.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-18-grey.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-19-blue.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-2-blue.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-20-brown.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-21-blue.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-22-black.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-23-black.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-24-beige.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-25-red.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-3-blue.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-4-blue.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-4-brown.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-4-green.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-5-red.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-6-brown.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-7-blue.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-8-blue.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jackets/jackets-9-blue.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jeans/jeans-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/jeans/jeans-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/shirts/shirts-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/shirts/shirts-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/suits/suits-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/suits/suits-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/t-shirts/t-shirts-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/t-shirts/t-shirts-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/tops/tops-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/tops/tops-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/trousers/trousers-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/clothing/trousers/trousers-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/shoes/lace-up_shoes/lace-up_shoes-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/shoes/lace-up_shoes/lace-up_shoes-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/shoes/loafers/loafers-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/shoes/loafers/loafers-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/shoes/sneakers/sneakers-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/men/shoes/sneakers/sneakers-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/clutches/clutches-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/clutches/clutches-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/clutches/clutches-3-blue.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/clutches/clutches-3-brown.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/clutches/clutches-3-pink.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/clutches/clutches-3-yellow.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/handbag/handbag-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/handbag/handbag-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/shopper/shopper-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/shopper/shopper-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/shopper/shopper-3-black.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/shopper/shopper-3-white.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/shoulder_bags/shoulder_bags-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/shoulder_bags/shoulder_bags-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/wallets/wallets-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/bags/wallets/wallets-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/blazer/blazer-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/blazer/blazer-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/dresses/dresses-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/dresses/dresses-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/jackets/jacket-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/jackets/jacket-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/jeans/jeans-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/jeans/jeans-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/shirts/shirts-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/shirts/shirts-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/skirts/skirts-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/skirts/skirts-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/t-shirts/t-shirts-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/t-shirts/t-shirts-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/tops/tops-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/tops/tops-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/trouser/trouser-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/clothing/trouser/trouser-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/ankle_boots/ankle_boots-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/ankle_boots/ankle_boots-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/ballerinas/ballerinas-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/ballerinas/ballerinas-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/boots/boots-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/boots/boots-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/loafers/loafers-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/loafers/loafers-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/pumps/pumps-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/pumps/pumps-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/sandals/sandals-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/sandals/sandals-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/sneakers/sneakers-1.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/static/women/shoes/sneakers/sneakers-2.jpg (100%) rename {graphql_vuestorefront => vuestorefront}/views/product_views.xml (100%) rename {graphql_vuestorefront => vuestorefront}/views/res_config_settings_views.xml (97%) rename {graphql_vuestorefront => vuestorefront}/views/website_views.xml (97%) diff --git a/graphql_vuestorefront/data/demo_products_men_clothing_1.xml b/graphql_vuestorefront/data/demo_products_men_clothing_1.xml deleted file mode 100644 index ceba7f5..0000000 --- a/graphql_vuestorefront/data/demo_products_men_clothing_1.xml +++ /dev/null @@ -1,929 +0,0 @@ - - - - - - - Daniele Alessandrini – Vest - - 165.00 - - 165.00 - product - - - - This is the product: Daniele Alessandrini - Vest - delivery - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DA01-01 - 165.00 - - - - DA01-02 - 165.00 - - - - DA01-03 - 165.00 - - - - DA01-04 - 165.00 - - - - DA01-05 - 165.00 - 170.00 - - - - DA01-06 - 165.00 - 170.00 - - - - - - - Leather jacket Bully dark blue - - 523.75 - - 523.75 - product - - - - This is the product: Leather jacket Bully dark blue - delivery - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - LJBD01-01 - 523.75 - - - - LJBD01-02 - 523.75 - - - - - - - Bomber Daniele Alessandrini blue - - 356.25 - - 356.25 - product - - - - This is the product: Bomber Daniele Alessandrini blue - delivery - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BDA01-01 - 523.75 - - - - BDA01-02 - 523.75 - - - - - - - Save the Duck – Casual Jacket - - 161.25 - - 161.25 - product - - - - This is the product: Save the Duck – Casual Jacket - delivery - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CJ01-01 - 161.25 - - - - CJ01-02 - 161.25 - - - - CJ01-03 - 161.25 - - - - CJ01-04 - 161.25 - - - - CJ01-05 - 161.25 - - - - CJ01-06 - 161.25 - - - - - - - Vest ”Naples” Moncler red - - 662.50 - - 662.50 - product - - - - This is the product: Vest ”Naples” Moncler red - delivery - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - VNMR01-01 - 662.50 - - - - VNMR01-02 - 662.50 - - - - VNMR01-03 - 662.50 - - - - VNMR01-04 - 662.50 - - - - - - - Moncler – Down Jacket “Jacob” - - 1218.75 - - 1218.75 - product - - - - This is the product: Moncler – Down Jacket “Jacob” - delivery - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MDJ01-01 - 1218.75 - - - - MDJ01-02 - 1218.75 - - - - MDJ01-03 - 1218.75 - - - - - - - - Casual jacket Aspesi blue - - 430.00 - - 430.00 - product - - - - This is the product: Casual jacket Aspesi blue - delivery - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CJB01-01 - 430.00 - - - - CJB01-02 - 430.00 - - - - - - - Casual jacket “Mahakali“ Peuterey blue - - 423.75 - - 423.75 - product - - - - This is the product: Casual jacket “Mahakali“ Peuterey blue - delivery - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Mahakali-01 - 423.75 - - - - Mahakali-02 - 423.75 - - - - - - - Casual jacket Invicta blue - - 173.75 - - 173.75 - product - - - - This is the product: Casual jacket Invicta blue - delivery - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Invicta-01 - 423.75 - - - - Invicta-02 - 423.75 - - - - - - - - Casual jacket ”Lyon” Moncler black - - 656.25 - - 656.25 - product - - - - This is the product: Casual jacket ”Lyon” Moncler black - delivery - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - LYON-01 - 656.25 - - - - LYON-02 - 656.25 - - - - - diff --git a/graphql_vuestorefront/security/ir.model.access.csv b/graphql_vuestorefront/security/ir.model.access.csv deleted file mode 100644 index 3fd1e22..0000000 --- a/graphql_vuestorefront/security/ir.model.access.csv +++ /dev/null @@ -1,4 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -graphql_vuestorefront.access_invalidate_cache,access_invalidate_cache,graphql_vuestorefront.model_invalidate_cache,base.group_user,1,1,1,1 -access_website_menu_image,access_website_menu_image,model_website_menu_image,,1,0,0,0 -access_website_menu_image_designer,access_website_menu_image_designer,graphql_vuestorefront.model_website_menu_image,website.group_website_designer,1,1,1,1 \ No newline at end of file diff --git a/graphql_vuestorefront/README.rst b/vuestorefront/README.rst similarity index 100% rename from graphql_vuestorefront/README.rst rename to vuestorefront/README.rst diff --git a/graphql_vuestorefront/__init__.py b/vuestorefront/__init__.py similarity index 100% rename from graphql_vuestorefront/__init__.py rename to vuestorefront/__init__.py diff --git a/graphql_vuestorefront/__manifest__.py b/vuestorefront/__manifest__.py similarity index 100% rename from graphql_vuestorefront/__manifest__.py rename to vuestorefront/__manifest__.py diff --git a/graphql_vuestorefront/controllers/__init__.py b/vuestorefront/controllers/__init__.py similarity index 100% rename from graphql_vuestorefront/controllers/__init__.py rename to vuestorefront/controllers/__init__.py diff --git a/graphql_vuestorefront/controllers/main.py b/vuestorefront/controllers/main.py similarity index 100% rename from graphql_vuestorefront/controllers/main.py rename to vuestorefront/controllers/main.py diff --git a/graphql_vuestorefront/data/demo_product_attribute.xml b/vuestorefront/data/demo_product_attribute.xml similarity index 100% rename from graphql_vuestorefront/data/demo_product_attribute.xml rename to vuestorefront/data/demo_product_attribute.xml diff --git a/graphql_vuestorefront/data/demo_product_public_category.xml b/vuestorefront/data/demo_product_public_category.xml similarity index 100% rename from graphql_vuestorefront/data/demo_product_public_category.xml rename to vuestorefront/data/demo_product_public_category.xml diff --git a/vuestorefront/data/demo_products_men_clothing_1.xml b/vuestorefront/data/demo_products_men_clothing_1.xml new file mode 100644 index 0000000..cb676d8 --- /dev/null +++ b/vuestorefront/data/demo_products_men_clothing_1.xml @@ -0,0 +1,929 @@ + + + + + + + Daniele Alessandrini – Vest + + 165.00 + + 165.00 + product + + + + This is the product: Daniele Alessandrini - Vest + delivery + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DA01-01 + 165.00 + + + + DA01-02 + 165.00 + + + + DA01-03 + 165.00 + + + + DA01-04 + 165.00 + + + + DA01-05 + 165.00 + 170.00 + + + + DA01-06 + 165.00 + 170.00 + + + + + + + Leather jacket Bully dark blue + + 523.75 + + 523.75 + product + + + + This is the product: Leather jacket Bully dark blue + delivery + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LJBD01-01 + 523.75 + + + + LJBD01-02 + 523.75 + + + + + + + Bomber Daniele Alessandrini blue + + 356.25 + + 356.25 + product + + + + This is the product: Bomber Daniele Alessandrini blue + delivery + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BDA01-01 + 523.75 + + + + BDA01-02 + 523.75 + + + + + + + Save the Duck – Casual Jacket + + 161.25 + + 161.25 + product + + + + This is the product: Save the Duck – Casual Jacket + delivery + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CJ01-01 + 161.25 + + + + CJ01-02 + 161.25 + + + + CJ01-03 + 161.25 + + + + CJ01-04 + 161.25 + + + + CJ01-05 + 161.25 + + + + CJ01-06 + 161.25 + + + + + + + Vest ”Naples” Moncler red + + 662.50 + + 662.50 + product + + + + This is the product: Vest ”Naples” Moncler red + delivery + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VNMR01-01 + 662.50 + + + + VNMR01-02 + 662.50 + + + + VNMR01-03 + 662.50 + + + + VNMR01-04 + 662.50 + + + + + + + Moncler – Down Jacket “Jacob” + + 1218.75 + + 1218.75 + product + + + + This is the product: Moncler – Down Jacket “Jacob” + delivery + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MDJ01-01 + 1218.75 + + + + MDJ01-02 + 1218.75 + + + + MDJ01-03 + 1218.75 + + + + + + + + Casual jacket Aspesi blue + + 430.00 + + 430.00 + product + + + + This is the product: Casual jacket Aspesi blue + delivery + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CJB01-01 + 430.00 + + + + CJB01-02 + 430.00 + + + + + + + Casual jacket “Mahakali“ Peuterey blue + + 423.75 + + 423.75 + product + + + + This is the product: Casual jacket “Mahakali“ Peuterey blue + delivery + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mahakali-01 + 423.75 + + + + Mahakali-02 + 423.75 + + + + + + + Casual jacket Invicta blue + + 173.75 + + 173.75 + product + + + + This is the product: Casual jacket Invicta blue + delivery + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Invicta-01 + 423.75 + + + + Invicta-02 + 423.75 + + + + + + + + Casual jacket ”Lyon” Moncler black + + 656.25 + + 656.25 + product + + + + This is the product: Casual jacket ”Lyon” Moncler black + delivery + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LYON-01 + 656.25 + + + + LYON-02 + 656.25 + + + + + diff --git a/graphql_vuestorefront/data/demo_products_men_clothing_2.xml b/vuestorefront/data/demo_products_men_clothing_2.xml similarity index 52% rename from graphql_vuestorefront/data/demo_products_men_clothing_2.xml rename to vuestorefront/data/demo_products_men_clothing_2.xml index e06f83e..eff3223 100644 --- a/graphql_vuestorefront/data/demo_products_men_clothing_2.xml +++ b/vuestorefront/data/demo_products_men_clothing_2.xml @@ -8,7 +8,7 @@ Casual Jacket Save the Duck dark blue - + 198.75 198.75 @@ -18,8 +18,8 @@ This is the product: Casual Jacket Save the Duck dark blue delivery - - + + @@ -41,32 +41,32 @@ @@ -75,20 +75,20 @@ CJSDDB-01 198.75 - + CJSDDB-02 198.75 - + Leather jacket D.r.o.w.s black - + 872.50 872.50 @@ -98,8 +98,8 @@ This is the product: Leather jacket D.r.o.w.s black delivery - - + + @@ -121,32 +121,32 @@ @@ -155,12 +155,12 @@ LJB3-01 872.50 - + LJB3-02 872.50 - + @@ -168,7 +168,7 @@ Moncler – Down Jacket “Ryan” - + 1162.50 1162.50 @@ -178,8 +178,8 @@ This is the product: Moncler – Down Jacket “Ryan” delivery - - + + @@ -201,32 +201,32 @@ @@ -235,19 +235,19 @@ RYAN-01 1162.50 - + RYAN-02 1162.50 - + Harris Wharf – Coat - + 598.75 598.75 @@ -257,8 +257,8 @@ This is the product: Harris Wharf – Coat delivery - - + + @@ -280,32 +280,32 @@ @@ -314,19 +314,19 @@ HARRISWARF-01 598.75 - + HARRISWARF-02 598.75 - + Casual jacket Michael Kors beige - + 373.75 373.75 @@ -336,8 +336,8 @@ This is the product: Casual jacket Michael Kors beige delivery - - + + @@ -359,32 +359,32 @@ @@ -393,13 +393,13 @@ MKB02-01 598.75 - + MKB02-02 598.75 - + @@ -407,7 +407,7 @@ Down jacket “Kathmandu“ Peuterey grey - + 411.25 411.25 @@ -417,8 +417,8 @@ This is the product: Down jacket “Kathmandu“ Peuterey grey delivery - - + + @@ -440,40 +440,40 @@ @@ -482,17 +482,17 @@ KATHMANDU-01 411.25 - + KATHMANDU-02 411.25 - + KATHMANDU-03 411.25 - + @@ -500,7 +500,7 @@ Coat Aspesi beige - + 536.25 536.25 @@ -510,8 +510,8 @@ This is the product: Coat Aspesi beige delivery - - + + @@ -533,32 +533,32 @@ @@ -567,12 +567,12 @@ CAD02-01 536.25 - + CAD02-02 536.25 - + @@ -580,7 +580,7 @@ Casual jacket Stone Island grey - + 837.50 837.50 @@ -590,8 +590,8 @@ This is the product: Casual jacket Stone Island grey delivery - - + + @@ -613,32 +613,32 @@ @@ -647,19 +647,19 @@ CJSIG-01 837.50 - + CJSIG-02 837.50 - + Jacket Doubleface “Sol Walk“ Luis Trenker blue - + 498.75 498.75 @@ -669,8 +669,8 @@ This is the product: Jacket Doubleface “Sol Walk“ Luis Trenker blue delivery - - + + @@ -692,32 +692,32 @@ @@ -726,19 +726,19 @@ SOLWALK-01 498.75 - + SOLWALK-02 498.75 - + Bully – Leather Jacket - + 497.50 497.50 @@ -748,8 +748,8 @@ This is the product: Bully – Leather Jacket delivery - - + + @@ -771,32 +771,32 @@ @@ -805,13 +805,13 @@ BULLY02-01 497.50 - + BULLY02-02 497.50 - + diff --git a/graphql_vuestorefront/data/demo_products_men_clothing_3.xml b/vuestorefront/data/demo_products_men_clothing_3.xml similarity index 52% rename from graphql_vuestorefront/data/demo_products_men_clothing_3.xml rename to vuestorefront/data/demo_products_men_clothing_3.xml index f47ee86..dc5624a 100644 --- a/graphql_vuestorefront/data/demo_products_men_clothing_3.xml +++ b/vuestorefront/data/demo_products_men_clothing_3.xml @@ -8,7 +8,7 @@ Coat Aspesi blue - + 536.25 536.25 @@ -18,8 +18,8 @@ This is the product: Coat Aspesi blue delivery - - + + @@ -41,32 +41,32 @@ @@ -75,12 +75,12 @@ CAB05-01 536.25 - + CAB05-02 536.25 - + @@ -88,7 +88,7 @@ Casual jacket Stone Island black - + 498.75 498.75 @@ -98,8 +98,8 @@ This is the product: Casual jacket Stone Island black delivery - - + + @@ -121,32 +121,32 @@ @@ -155,20 +155,20 @@ CJSIB06-01 498.75 - + CJSIB06-02 498.75 - + Vest Bully black - + 486.25 486.25 @@ -178,8 +178,8 @@ This is the product: Vest Bully black delivery - - + + @@ -201,32 +201,32 @@ @@ -235,19 +235,19 @@ VBB03-01 486.25 - + VBB03-02 486.25 - + Leather jacket Daniele Alessandrini beige - + 736.25 736.25 @@ -257,8 +257,8 @@ This is the product: Leather jacket Daniele Alessandrini beige delivery - - + + @@ -280,32 +280,32 @@ @@ -314,19 +314,19 @@ LJDAB02-01 736.25 - + LJDAB02-02 736.25 - + Jacket Doubleface “Sol Walk“ Luis Trenker red - + 498.75 498.75 @@ -336,8 +336,8 @@ This is the product: Jacket Doubleface “Sol Walk“ Luis Trenker red delivery - - + + @@ -359,32 +359,32 @@ @@ -393,12 +393,12 @@ JDSWLTR-01 498.75 - + JDSWLTR-02 498.75 - + diff --git a/graphql_vuestorefront/data/demo_products_men_clothing_4.xml b/vuestorefront/data/demo_products_men_clothing_4.xml similarity index 58% rename from graphql_vuestorefront/data/demo_products_men_clothing_4.xml rename to vuestorefront/data/demo_products_men_clothing_4.xml index 10c988f..9a04de4 100644 --- a/graphql_vuestorefront/data/demo_products_men_clothing_4.xml +++ b/vuestorefront/data/demo_products_men_clothing_4.xml @@ -8,7 +8,7 @@ Cardigan Kangra green - + 200.00 200.00 @@ -19,8 +19,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -37,28 +37,28 @@ @@ -76,7 +76,7 @@ Vest Tagliatore blue - + 206.25 206.25 @@ -87,8 +87,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -105,28 +105,28 @@ @@ -145,7 +145,7 @@ Shirt Barba blue - + 248.75 248.75 @@ -156,8 +156,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -174,28 +174,28 @@ @@ -213,7 +213,7 @@ Shirt Himons multi - + 148.75 148.75 @@ -224,8 +224,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -242,28 +242,28 @@ @@ -282,7 +282,7 @@ Chino Paolo Pecora grey - + 281.25 281.25 @@ -293,8 +293,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -311,28 +311,28 @@ @@ -350,7 +350,7 @@ Daniele Alessandrini – Casual hosen - + 218.75 218.75 @@ -361,8 +361,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -379,28 +379,28 @@ @@ -419,7 +419,7 @@ jeans Siviglia dark blue - + 243.75 243.75 @@ -430,8 +430,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -448,28 +448,28 @@ @@ -487,7 +487,7 @@ Jeans Closed grey - + 211.25 211.25 @@ -498,8 +498,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -516,28 +516,28 @@ @@ -556,7 +556,7 @@ Blazer Tagliatore brown - + 573.75 573.75 @@ -567,8 +567,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -585,28 +585,28 @@ @@ -624,7 +624,7 @@ Blazer Circolo 1901 blue - + 411.25 411.25 @@ -635,8 +635,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -653,28 +653,28 @@ @@ -693,7 +693,7 @@ Suit Mauro Grifoni grey - + 668.75 668.75 @@ -704,8 +704,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -722,28 +722,28 @@ @@ -761,7 +761,7 @@ Suit Tagliatore dark blue - + 862.50 862.50 @@ -772,8 +772,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -790,28 +790,28 @@ @@ -830,7 +830,7 @@ Shirt The Sartorialist light blue - + 186.25 186.25 @@ -841,8 +841,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -859,28 +859,28 @@ @@ -898,7 +898,7 @@ Shirt Daniele Alessandrini grey - + 198.75 198.75 @@ -909,8 +909,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -927,28 +927,28 @@ diff --git a/graphql_vuestorefront/data/demo_products_men_shoes.xml b/vuestorefront/data/demo_products_men_shoes.xml similarity index 58% rename from graphql_vuestorefront/data/demo_products_men_shoes.xml rename to vuestorefront/data/demo_products_men_shoes.xml index 46ca507..7d040e9 100644 --- a/graphql_vuestorefront/data/demo_products_men_shoes.xml +++ b/vuestorefront/data/demo_products_men_shoes.xml @@ -8,7 +8,7 @@ Sneaker – Lotto “Tokyo“ - + 137.50 137.50 @@ -19,8 +19,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -37,28 +37,28 @@ @@ -76,7 +76,7 @@ Sneakers “Spot“ Springa multi - + 186.25 186.25 @@ -87,8 +87,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -105,28 +105,28 @@ @@ -145,7 +145,7 @@ Lace up shoes Tods dark blue - + 462.50 462.50 @@ -156,8 +156,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -174,28 +174,28 @@ @@ -213,7 +213,7 @@ Lace up shoes Tods blue - + 362.50 362.50 @@ -224,8 +224,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -242,28 +242,28 @@ @@ -282,7 +282,7 @@ Mokassins “Daime“ Doucals brown - + 343.75 343.75 @@ -293,8 +293,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -311,28 +311,28 @@ @@ -350,7 +350,7 @@ Flip Flops “Top Mix“ Havaianas dark blue - + 27.50 27.50 @@ -361,8 +361,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -379,28 +379,28 @@ diff --git a/graphql_vuestorefront/data/demo_products_women_bags.xml b/vuestorefront/data/demo_products_women_bags.xml similarity index 57% rename from graphql_vuestorefront/data/demo_products_women_bags.xml rename to vuestorefront/data/demo_products_women_bags.xml index 112726e..9f4c72d 100644 --- a/graphql_vuestorefront/data/demo_products_women_bags.xml +++ b/vuestorefront/data/demo_products_women_bags.xml @@ -8,7 +8,7 @@ Michael Kors – Clutch “Daria” - + 281.25 281.25 @@ -19,8 +19,8 @@ This is the product: Michael Kors – Clutch “Daria” 578902-00 delivery - - + + @@ -37,20 +37,20 @@ @@ -64,7 +64,7 @@ Clutch ”Carol” Liebeskind black - + 198.75 198.75 @@ -75,8 +75,8 @@ This is the product: Clutch ”Carol” Liebeskind black 578902-00 delivery - - + + @@ -93,20 +93,20 @@ @@ -121,7 +121,7 @@ Clutch “Jet Set Travel” small Michael Kors - + 106.25 106.25 @@ -132,8 +132,8 @@ This is the product: Clutch “Jet Set Travel” small Michael Kors 578902-00 delivery - - + + @@ -155,68 +155,68 @@ @@ -225,49 +225,49 @@ CJSTMK-01 106.25 - + CJSTMK-02 106.25 - + CJSTMK-03 106.25 - + CJSTMK-04 106.25 - + CJSTMK-05 106.25 - + CJSTMK-06 106.25 - + CJSTMK-07 106.25 - + CJSTMK-08 106.25 - + - + 31.25 @@ -275,7 +275,7 @@ Bag Moschino Love black-white - + 190.00 190.00 @@ -286,8 +286,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -304,20 +304,20 @@ @@ -331,7 +331,7 @@ DKNY – Bag - + 372.50 372.50 @@ -342,8 +342,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -360,20 +360,20 @@ @@ -388,7 +388,7 @@ Moschino Love – Shopper - + 256.25 256.25 @@ -399,8 +399,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -417,20 +417,20 @@ @@ -445,7 +445,7 @@ Michael Kors – Shopper “Jet Set Travel” - + 343.75 343.75 @@ -456,8 +456,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -474,20 +474,20 @@ @@ -502,7 +502,7 @@ Bag “Jet Set Travel” Michael Kors - + 368.75 368.75 @@ -513,8 +513,8 @@ This is the product: Bag “Jet Set Travel” Michael Kors 578902-00 delivery - - + + @@ -536,32 +536,32 @@ @@ -570,15 +570,15 @@ BJSTMK-01 368.75 - + BJSTMK-01 368.75 - + - + 26.00 @@ -586,7 +586,7 @@ Guess – Hand bag “Nikki“ - + 173.75 173.75 @@ -597,8 +597,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -615,20 +615,20 @@ @@ -642,7 +642,7 @@ Guess – handtaschen “Carnivale“ - + 181.25 181.25 @@ -653,8 +653,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -671,20 +671,20 @@ @@ -699,7 +699,7 @@ Wallet “Pervinca“ Gabs white - + 102.50 102.50 @@ -710,8 +710,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -728,20 +728,20 @@ @@ -755,7 +755,7 @@ Gabs – Wallet “Gmoney” - + 106.25 106.25 @@ -766,8 +766,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -784,20 +784,20 @@ diff --git a/graphql_vuestorefront/data/demo_products_women_clothing.xml b/vuestorefront/data/demo_products_women_clothing.xml similarity index 58% rename from graphql_vuestorefront/data/demo_products_women_clothing.xml rename to vuestorefront/data/demo_products_women_clothing.xml index ae2b9db..c2a7d4a 100644 --- a/graphql_vuestorefront/data/demo_products_women_clothing.xml +++ b/vuestorefront/data/demo_products_women_clothing.xml @@ -8,7 +8,7 @@ Leather jacket Bully grey - + 372.50 372.50 @@ -19,8 +19,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -37,28 +37,28 @@ @@ -76,7 +76,7 @@ Leather jacket Bully brown - + 372.50 372.50 @@ -87,8 +87,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -105,28 +105,28 @@ @@ -145,7 +145,7 @@ Blazer Michael Kors brown - + 281.25 281.25 @@ -156,8 +156,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -174,28 +174,28 @@ @@ -213,7 +213,7 @@ Blazer Pinko yellow - + 337.50 337.50 @@ -224,8 +224,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -242,28 +242,28 @@ @@ -282,7 +282,7 @@ Pullover Moschino Cheap And Chic black - + 247.50 247.50 @@ -293,8 +293,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -311,28 +311,28 @@ @@ -350,7 +350,7 @@ Pullover Moschino Cheap And Chic black - + 247.50 247.50 @@ -361,8 +361,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -379,28 +379,28 @@ @@ -419,7 +419,7 @@ Shirt Aspesi white test - + 200.00 200.00 @@ -430,8 +430,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -448,28 +448,28 @@ @@ -487,7 +487,7 @@ Shirt Himons multi - + 148.75 148.75 @@ -498,8 +498,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -516,28 +516,28 @@ @@ -556,7 +556,7 @@ Shirt ”Paola” MU blue - + 231.25 231.25 @@ -567,8 +567,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -585,28 +585,28 @@ @@ -624,7 +624,7 @@ Shirt Himons light blue - + 123.75 123.75 @@ -635,8 +635,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -653,28 +653,28 @@ @@ -693,7 +693,7 @@ Biker jeans Pinko white - + 247.50 247.50 @@ -704,8 +704,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -722,28 +722,28 @@ @@ -761,7 +761,7 @@ jeans Michael Kors black - + 193.75 193.75 @@ -772,8 +772,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -790,28 +790,28 @@ @@ -830,7 +830,7 @@ Jogging Pants Moschino Cheap And Chic black - + 286.25 286.25 @@ -841,8 +841,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -859,28 +859,28 @@ @@ -898,7 +898,7 @@ Chino Pinko multi - + 287.50 287.50 @@ -909,8 +909,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -927,28 +927,28 @@ @@ -967,7 +967,7 @@ Skirt Ki 6? Who are you? blue - + 185.00 185.00 @@ -978,8 +978,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -996,28 +996,28 @@ @@ -1035,7 +1035,7 @@ Skirt Pinko beige - + 185.00 185.00 @@ -1046,8 +1046,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -1064,28 +1064,28 @@ @@ -1104,7 +1104,7 @@ Dress Ki 6? Who are you? black - + 206.25 206.25 @@ -1115,8 +1115,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -1133,28 +1133,28 @@ @@ -1172,7 +1172,7 @@ Dress Moschino Cheap And Chic multi - + 320.00 320.00 @@ -1183,8 +1183,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -1201,28 +1201,28 @@ diff --git a/graphql_vuestorefront/data/demo_products_women_shoes.xml b/vuestorefront/data/demo_products_women_shoes.xml similarity index 58% rename from graphql_vuestorefront/data/demo_products_women_shoes.xml rename to vuestorefront/data/demo_products_women_shoes.xml index c718e12..011382f 100644 --- a/graphql_vuestorefront/data/demo_products_women_shoes.xml +++ b/vuestorefront/data/demo_products_women_shoes.xml @@ -8,7 +8,7 @@ Sneakers “Jazz“ Saucony grey-green - + 118.75 118.75 @@ -19,8 +19,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -37,28 +37,28 @@ @@ -76,7 +76,7 @@ Sneakers Philippe Model grey - + 327.50 327.50 @@ -87,8 +87,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -105,28 +105,28 @@ @@ -145,7 +145,7 @@ Booclothing Lerews beige - + 186.25 186.25 @@ -156,8 +156,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -174,28 +174,28 @@ @@ -213,7 +213,7 @@ Booclothing Lerews black - + 186.25 186.25 @@ -224,8 +224,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -242,28 +242,28 @@ @@ -282,7 +282,7 @@ Booties Lemare black - + 231.25 231.25 @@ -293,8 +293,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -311,28 +311,28 @@ @@ -350,7 +350,7 @@ Booties Lemare brown - + 248.75 248.75 @@ -361,8 +361,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -379,28 +379,28 @@ @@ -419,7 +419,7 @@ Pumps “Okala“ Sam Edelman grey - + 186.25 186.25 @@ -430,8 +430,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -448,28 +448,28 @@ @@ -487,7 +487,7 @@ Pumps ”H228” Hogan blue - + 362.50 362.50 @@ -498,8 +498,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -516,28 +516,28 @@ @@ -556,7 +556,7 @@ Ballerina Liebeskind black-beige - + 123.75 123.75 @@ -567,8 +567,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -585,28 +585,28 @@ @@ -624,7 +624,7 @@ Ballerina Liebeskind multi - + 98.75 98.75 @@ -635,8 +635,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -653,28 +653,28 @@ @@ -693,7 +693,7 @@ Loafers Alberto Guardiani gold - + 343.75 343.75 @@ -704,8 +704,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -722,28 +722,28 @@ @@ -761,7 +761,7 @@ Slip-On Shoes Crime silver - + 161.25 161.25 @@ -772,8 +772,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -790,28 +790,28 @@ @@ -830,7 +830,7 @@ Sandals ”H257” Hogan beige - + 337.50 337.50 @@ -841,8 +841,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -859,28 +859,28 @@ @@ -898,7 +898,7 @@ sandalen “Georgie“ Sam Edelman brown - + 186.25 186.25 @@ -909,8 +909,8 @@ The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + @@ -927,28 +927,28 @@ diff --git a/graphql_vuestorefront/data/ir_config_parameter_data.xml b/vuestorefront/data/ir_config_parameter_data.xml similarity index 100% rename from graphql_vuestorefront/data/ir_config_parameter_data.xml rename to vuestorefront/data/ir_config_parameter_data.xml diff --git a/graphql_vuestorefront/data/ir_cron_data.xml b/vuestorefront/data/ir_cron_data.xml similarity index 90% rename from graphql_vuestorefront/data/ir_cron_data.xml rename to vuestorefront/data/ir_cron_data.xml index 7f98c1c..6258919 100644 --- a/graphql_vuestorefront/data/ir_cron_data.xml +++ b/vuestorefront/data/ir_cron_data.xml @@ -8,7 +8,7 @@ Request VSF Cache Invalidation - + code model.request_vsf_cache_invalidation() 5 diff --git a/graphql_vuestorefront/data/mail_template.xml b/vuestorefront/data/mail_template.xml similarity index 100% rename from graphql_vuestorefront/data/mail_template.xml rename to vuestorefront/data/mail_template.xml diff --git a/graphql_vuestorefront/data/website_data.xml b/vuestorefront/data/website_data.xml similarity index 100% rename from graphql_vuestorefront/data/website_data.xml rename to vuestorefront/data/website_data.xml diff --git a/graphql_vuestorefront/hooks.py b/vuestorefront/hooks.py similarity index 100% rename from graphql_vuestorefront/hooks.py rename to vuestorefront/hooks.py diff --git a/graphql_vuestorefront/models/__init__.py b/vuestorefront/models/__init__.py similarity index 100% rename from graphql_vuestorefront/models/__init__.py rename to vuestorefront/models/__init__.py diff --git a/graphql_vuestorefront/models/invalidate_cache.py b/vuestorefront/models/invalidate_cache.py similarity index 100% rename from graphql_vuestorefront/models/invalidate_cache.py rename to vuestorefront/models/invalidate_cache.py diff --git a/graphql_vuestorefront/models/ir_http.py b/vuestorefront/models/ir_http.py similarity index 100% rename from graphql_vuestorefront/models/ir_http.py rename to vuestorefront/models/ir_http.py diff --git a/graphql_vuestorefront/models/payment_transaction.py b/vuestorefront/models/payment_transaction.py similarity index 100% rename from graphql_vuestorefront/models/payment_transaction.py rename to vuestorefront/models/payment_transaction.py diff --git a/graphql_vuestorefront/models/product.py b/vuestorefront/models/product.py similarity index 100% rename from graphql_vuestorefront/models/product.py rename to vuestorefront/models/product.py diff --git a/graphql_vuestorefront/models/res_config_settings.py b/vuestorefront/models/res_config_settings.py similarity index 97% rename from graphql_vuestorefront/models/res_config_settings.py rename to vuestorefront/models/res_config_settings.py index 9a12931..8f0c2b2 100644 --- a/graphql_vuestorefront/models/res_config_settings.py +++ b/vuestorefront/models/res_config_settings.py @@ -27,7 +27,7 @@ class ResConfigSettings(models.TransientModel): related='website_id.vsf_mailing_list_id', readonly=False, required=True) # VSF Images - vsf_image_quality = fields.Integer('Quality', required=True) + vsf_image_quality = fields.Integer('Vue Storefront Image Quality', required=True) vsf_image_background_rgba = fields.Char('Background RGBA', required=True) vsf_image_resize_limit = fields.Integer('Resize Limit', required=True, help='Limit in pixels to resize image for width and height') diff --git a/graphql_vuestorefront/models/res_users.py b/vuestorefront/models/res_users.py similarity index 96% rename from graphql_vuestorefront/models/res_users.py rename to vuestorefront/models/res_users.py index db4434a..9b82e77 100644 --- a/graphql_vuestorefront/models/res_users.py +++ b/vuestorefront/models/res_users.py @@ -28,7 +28,7 @@ def api_action_reset_password(self): self.mapped('partner_id').signup_prepare(signup_type="reset", expiration=expiration) # send email to users with their signup url - template = self.env.ref('graphql_vuestorefront.website_reset_password_email') + template = self.env.ref('vuestorefront.website_reset_password_email') assert template._name == 'mail.template' diff --git a/graphql_vuestorefront/models/website.py b/vuestorefront/models/website.py similarity index 100% rename from graphql_vuestorefront/models/website.py rename to vuestorefront/models/website.py diff --git a/graphql_vuestorefront/schema.py b/vuestorefront/schema.py similarity index 96% rename from graphql_vuestorefront/schema.py rename to vuestorefront/schema.py index 4d3e13f..71685fb 100644 --- a/graphql_vuestorefront/schema.py +++ b/vuestorefront/schema.py @@ -5,7 +5,7 @@ import graphene from odoo.addons.graphql_base import OdooObjectType -from odoo.addons.graphql_vuestorefront.schemas import ( +from odoo.addons.vuestorefront.schemas import ( country, category, product, order, invoice, contact_us, user_profile, sign, address, wishlist, shop, payment, diff --git a/graphql_vuestorefront/schemas/__init__.py b/vuestorefront/schemas/__init__.py similarity index 100% rename from graphql_vuestorefront/schemas/__init__.py rename to vuestorefront/schemas/__init__.py diff --git a/graphql_vuestorefront/schemas/address.py b/vuestorefront/schemas/address.py similarity index 99% rename from graphql_vuestorefront/schemas/address.py rename to vuestorefront/schemas/address.py index 8af7111..d6744be 100644 --- a/graphql_vuestorefront/schemas/address.py +++ b/vuestorefront/schemas/address.py @@ -7,7 +7,7 @@ from odoo import _ from odoo.http import request -from odoo.addons.graphql_vuestorefront.schemas.objects import Partner +from odoo.addons.vuestorefront.schemas.objects import Partner def get_partner(env, partner_id, order, website): diff --git a/graphql_vuestorefront/schemas/category.py b/vuestorefront/schemas/category.py similarity index 97% rename from graphql_vuestorefront/schemas/category.py rename to vuestorefront/schemas/category.py index 94b997a..b5d49ff 100644 --- a/graphql_vuestorefront/schemas/category.py +++ b/vuestorefront/schemas/category.py @@ -4,7 +4,7 @@ import graphene -from odoo.addons.graphql_vuestorefront.schemas.objects import ( +from odoo.addons.vuestorefront.schemas.objects import ( SortEnum, Category ) diff --git a/graphql_vuestorefront/schemas/contact_us.py b/vuestorefront/schemas/contact_us.py similarity index 95% rename from graphql_vuestorefront/schemas/contact_us.py rename to vuestorefront/schemas/contact_us.py index bfc3d63..226b32c 100644 --- a/graphql_vuestorefront/schemas/contact_us.py +++ b/vuestorefront/schemas/contact_us.py @@ -4,7 +4,7 @@ import graphene -from odoo.addons.graphql_vuestorefront.schemas.objects import Lead +from odoo.addons.vuestorefront.schemas.objects import Lead class ContactUsParams(graphene.InputObjectType): diff --git a/graphql_vuestorefront/schemas/country.py b/vuestorefront/schemas/country.py similarity index 97% rename from graphql_vuestorefront/schemas/country.py rename to vuestorefront/schemas/country.py index 71d1d5c..f25ad88 100644 --- a/graphql_vuestorefront/schemas/country.py +++ b/vuestorefront/schemas/country.py @@ -4,7 +4,7 @@ import graphene -from odoo.addons.graphql_vuestorefront.schemas.objects import ( +from odoo.addons.vuestorefront.schemas.objects import ( SortEnum, Country ) diff --git a/graphql_vuestorefront/schemas/invoice.py b/vuestorefront/schemas/invoice.py similarity index 97% rename from graphql_vuestorefront/schemas/invoice.py rename to vuestorefront/schemas/invoice.py index f943a55..51425fd 100644 --- a/graphql_vuestorefront/schemas/invoice.py +++ b/vuestorefront/schemas/invoice.py @@ -7,7 +7,7 @@ from odoo.http import request from odoo import _ -from odoo.addons.graphql_vuestorefront.schemas.objects import ( +from odoo.addons.vuestorefront.schemas.objects import ( SortEnum, Invoice, get_document_with_check_access, get_document_count_with_check_access diff --git a/graphql_vuestorefront/schemas/mailing_list.py b/vuestorefront/schemas/mailing_list.py similarity index 99% rename from graphql_vuestorefront/schemas/mailing_list.py rename to vuestorefront/schemas/mailing_list.py index e8bafe4..6e0289e 100644 --- a/graphql_vuestorefront/schemas/mailing_list.py +++ b/vuestorefront/schemas/mailing_list.py @@ -7,7 +7,7 @@ from odoo.http import request from odoo import _ from odoo.addons.website_mass_mailing.controllers.main import MassMailController -from odoo.addons.graphql_vuestorefront.schemas.objects import ( +from odoo.addons.vuestorefront.schemas.objects import ( SortEnum, MailingContact, MailingList ) diff --git a/graphql_vuestorefront/schemas/objects.py b/vuestorefront/schemas/objects.py similarity index 100% rename from graphql_vuestorefront/schemas/objects.py rename to vuestorefront/schemas/objects.py diff --git a/graphql_vuestorefront/schemas/order.py b/vuestorefront/schemas/order.py similarity index 98% rename from graphql_vuestorefront/schemas/order.py rename to vuestorefront/schemas/order.py index 99e5880..a54217a 100644 --- a/graphql_vuestorefront/schemas/order.py +++ b/vuestorefront/schemas/order.py @@ -7,7 +7,7 @@ from odoo.http import request from odoo import _ -from odoo.addons.graphql_vuestorefront.schemas.objects import ( +from odoo.addons.vuestorefront.schemas.objects import ( SortEnum, OrderStage, InvoiceStatus, Order, ShippingMethod, get_document_with_check_access, get_document_count_with_check_access diff --git a/graphql_vuestorefront/schemas/payment.py b/vuestorefront/schemas/payment.py similarity index 98% rename from graphql_vuestorefront/schemas/payment.py rename to vuestorefront/schemas/payment.py index d236c4a..04dac97 100644 --- a/graphql_vuestorefront/schemas/payment.py +++ b/vuestorefront/schemas/payment.py @@ -11,8 +11,8 @@ from odoo.addons.payment import utils as payment_utils from odoo.addons.payment_adyen_vsf.const import CURRENCY_DECIMALS -from odoo.addons.graphql_vuestorefront.schemas.objects import PaymentProvider, PaymentTransaction -from odoo.addons.graphql_vuestorefront.schemas.shop import Cart, CartData +from odoo.addons.vuestorefront.schemas.objects import PaymentProvider, PaymentTransaction +from odoo.addons.vuestorefront.schemas.shop import Cart, CartData from odoo.addons.website_sale.controllers.main import PaymentPortal from odoo.addons.payment_adyen.controllers.main import AdyenController from odoo.addons.payment_adyen_vsf.controllers.main import AdyenControllerInherit diff --git a/graphql_vuestorefront/schemas/product.py b/vuestorefront/schemas/product.py similarity index 99% rename from graphql_vuestorefront/schemas/product.py rename to vuestorefront/schemas/product.py index 921717e..f0d637e 100644 --- a/graphql_vuestorefront/schemas/product.py +++ b/vuestorefront/schemas/product.py @@ -8,7 +8,7 @@ from odoo import _ from odoo.osv import expression -from odoo.addons.graphql_vuestorefront.schemas.objects import ( +from odoo.addons.vuestorefront.schemas.objects import ( SortEnum, Product, Attribute, AttributeValue ) diff --git a/graphql_vuestorefront/schemas/shop.py b/vuestorefront/schemas/shop.py similarity index 99% rename from graphql_vuestorefront/schemas/shop.py rename to vuestorefront/schemas/shop.py index 9dec15e..a2cc55d 100644 --- a/graphql_vuestorefront/schemas/shop.py +++ b/vuestorefront/schemas/shop.py @@ -3,7 +3,7 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene -from odoo.addons.graphql_vuestorefront.schemas.objects import Order, Partner +from odoo.addons.vuestorefront.schemas.objects import Order, Partner from odoo.addons.website_mass_mailing.controllers.main import MassMailController from odoo.http import request diff --git a/graphql_vuestorefront/schemas/sign.py b/vuestorefront/schemas/sign.py similarity index 98% rename from graphql_vuestorefront/schemas/sign.py rename to vuestorefront/schemas/sign.py index b0d0f96..2db8a2e 100644 --- a/graphql_vuestorefront/schemas/sign.py +++ b/vuestorefront/schemas/sign.py @@ -9,7 +9,7 @@ from odoo.http import request from odoo.exceptions import UserError from odoo.addons.auth_signup.models.res_users import SignupError -from odoo.addons.graphql_vuestorefront.schemas.objects import User +from odoo.addons.vuestorefront.schemas.objects import User from odoo.addons.website_mass_mailing.controllers.main import MassMailController diff --git a/graphql_vuestorefront/schemas/user_profile.py b/vuestorefront/schemas/user_profile.py similarity index 96% rename from graphql_vuestorefront/schemas/user_profile.py rename to vuestorefront/schemas/user_profile.py index 53713f3..ecf7848 100644 --- a/graphql_vuestorefront/schemas/user_profile.py +++ b/vuestorefront/schemas/user_profile.py @@ -7,7 +7,7 @@ from odoo import _ from odoo.http import request -from odoo.addons.graphql_vuestorefront.schemas.objects import Partner +from odoo.addons.vuestorefront.schemas.objects import Partner class UserProfileQuery(graphene.ObjectType): diff --git a/graphql_vuestorefront/schemas/website.py b/vuestorefront/schemas/website.py similarity index 95% rename from graphql_vuestorefront/schemas/website.py rename to vuestorefront/schemas/website.py index 07a3e39..d29f983 100644 --- a/graphql_vuestorefront/schemas/website.py +++ b/vuestorefront/schemas/website.py @@ -3,7 +3,7 @@ # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene -from odoo.addons.graphql_vuestorefront.schemas.objects import WebsiteMenu +from odoo.addons.vuestorefront.schemas.objects import WebsiteMenu class WebsiteQuery(graphene.ObjectType): diff --git a/graphql_vuestorefront/schemas/wishlist.py b/vuestorefront/schemas/wishlist.py similarity index 97% rename from graphql_vuestorefront/schemas/wishlist.py rename to vuestorefront/schemas/wishlist.py index e5bf160..41f92fd 100644 --- a/graphql_vuestorefront/schemas/wishlist.py +++ b/vuestorefront/schemas/wishlist.py @@ -8,7 +8,7 @@ from odoo import _ from odoo.addons.website_sale_wishlist.controllers.main import WebsiteSaleWishlist -from odoo.addons.graphql_vuestorefront.schemas.objects import WishlistItem +from odoo.addons.vuestorefront.schemas.objects import WishlistItem class WishlistItems(graphene.Interface): diff --git a/vuestorefront/security/ir.model.access.csv b/vuestorefront/security/ir.model.access.csv new file mode 100644 index 0000000..7a14206 --- /dev/null +++ b/vuestorefront/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +vuestorefront.access_invalidate_cache,access_invalidate_cache,vuestorefront.model_invalidate_cache,base.group_user,1,1,1,1 +access_website_menu_image,access_website_menu_image,model_website_menu_image,,1,0,0,0 +access_website_menu_image_designer,access_website_menu_image_designer,vuestorefront.model_website_menu_image,website.group_website_designer,1,1,1,1 diff --git a/graphql_vuestorefront/static/description/icon.png b/vuestorefront/static/description/icon.png similarity index 100% rename from graphql_vuestorefront/static/description/icon.png rename to vuestorefront/static/description/icon.png diff --git a/graphql_vuestorefront/static/men/clothing/blazer/blazer-1.jpg b/vuestorefront/static/men/clothing/blazer/blazer-1.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/blazer/blazer-1.jpg rename to vuestorefront/static/men/clothing/blazer/blazer-1.jpg diff --git a/graphql_vuestorefront/static/men/clothing/blazer/blazer-2.jpg b/vuestorefront/static/men/clothing/blazer/blazer-2.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/blazer/blazer-2.jpg rename to vuestorefront/static/men/clothing/blazer/blazer-2.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-1-blue.jpg b/vuestorefront/static/men/clothing/jackets/jackets-1-blue.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-1-blue.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-1-blue.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-1-grey.jpg b/vuestorefront/static/men/clothing/jackets/jackets-1-grey.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-1-grey.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-1-grey.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-10-black.jpg b/vuestorefront/static/men/clothing/jackets/jackets-10-black.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-10-black.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-10-black.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-11-blue.jpg b/vuestorefront/static/men/clothing/jackets/jackets-11-blue.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-11-blue.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-11-blue.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-12-black.jpg b/vuestorefront/static/men/clothing/jackets/jackets-12-black.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-12-black.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-12-black.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-13-blue.jpg b/vuestorefront/static/men/clothing/jackets/jackets-13-blue.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-13-blue.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-13-blue.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-14-black.jpg b/vuestorefront/static/men/clothing/jackets/jackets-14-black.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-14-black.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-14-black.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-15-beige.jpg b/vuestorefront/static/men/clothing/jackets/jackets-15-beige.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-15-beige.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-15-beige.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-16-grey.jpg b/vuestorefront/static/men/clothing/jackets/jackets-16-grey.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-16-grey.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-16-grey.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-17-beige.jpg b/vuestorefront/static/men/clothing/jackets/jackets-17-beige.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-17-beige.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-17-beige.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-18-grey.jpg b/vuestorefront/static/men/clothing/jackets/jackets-18-grey.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-18-grey.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-18-grey.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-19-blue.jpg b/vuestorefront/static/men/clothing/jackets/jackets-19-blue.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-19-blue.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-19-blue.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-2-blue.jpg b/vuestorefront/static/men/clothing/jackets/jackets-2-blue.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-2-blue.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-2-blue.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-20-brown.jpg b/vuestorefront/static/men/clothing/jackets/jackets-20-brown.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-20-brown.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-20-brown.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-21-blue.jpg b/vuestorefront/static/men/clothing/jackets/jackets-21-blue.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-21-blue.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-21-blue.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-22-black.jpg b/vuestorefront/static/men/clothing/jackets/jackets-22-black.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-22-black.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-22-black.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-23-black.jpg b/vuestorefront/static/men/clothing/jackets/jackets-23-black.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-23-black.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-23-black.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-24-beige.jpg b/vuestorefront/static/men/clothing/jackets/jackets-24-beige.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-24-beige.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-24-beige.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-25-red.jpg b/vuestorefront/static/men/clothing/jackets/jackets-25-red.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-25-red.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-25-red.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-3-blue.jpg b/vuestorefront/static/men/clothing/jackets/jackets-3-blue.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-3-blue.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-3-blue.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-4-blue.jpg b/vuestorefront/static/men/clothing/jackets/jackets-4-blue.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-4-blue.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-4-blue.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-4-brown.jpg b/vuestorefront/static/men/clothing/jackets/jackets-4-brown.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-4-brown.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-4-brown.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-4-green.jpg b/vuestorefront/static/men/clothing/jackets/jackets-4-green.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-4-green.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-4-green.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-5-red.jpg b/vuestorefront/static/men/clothing/jackets/jackets-5-red.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-5-red.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-5-red.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-6-brown.jpg b/vuestorefront/static/men/clothing/jackets/jackets-6-brown.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-6-brown.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-6-brown.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-7-blue.jpg b/vuestorefront/static/men/clothing/jackets/jackets-7-blue.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-7-blue.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-7-blue.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-8-blue.jpg b/vuestorefront/static/men/clothing/jackets/jackets-8-blue.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-8-blue.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-8-blue.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jackets/jackets-9-blue.jpg b/vuestorefront/static/men/clothing/jackets/jackets-9-blue.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jackets/jackets-9-blue.jpg rename to vuestorefront/static/men/clothing/jackets/jackets-9-blue.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jeans/jeans-1.jpg b/vuestorefront/static/men/clothing/jeans/jeans-1.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jeans/jeans-1.jpg rename to vuestorefront/static/men/clothing/jeans/jeans-1.jpg diff --git a/graphql_vuestorefront/static/men/clothing/jeans/jeans-2.jpg b/vuestorefront/static/men/clothing/jeans/jeans-2.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/jeans/jeans-2.jpg rename to vuestorefront/static/men/clothing/jeans/jeans-2.jpg diff --git a/graphql_vuestorefront/static/men/clothing/shirts/shirts-1.jpg b/vuestorefront/static/men/clothing/shirts/shirts-1.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/shirts/shirts-1.jpg rename to vuestorefront/static/men/clothing/shirts/shirts-1.jpg diff --git a/graphql_vuestorefront/static/men/clothing/shirts/shirts-2.jpg b/vuestorefront/static/men/clothing/shirts/shirts-2.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/shirts/shirts-2.jpg rename to vuestorefront/static/men/clothing/shirts/shirts-2.jpg diff --git a/graphql_vuestorefront/static/men/clothing/suits/suits-1.jpg b/vuestorefront/static/men/clothing/suits/suits-1.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/suits/suits-1.jpg rename to vuestorefront/static/men/clothing/suits/suits-1.jpg diff --git a/graphql_vuestorefront/static/men/clothing/suits/suits-2.jpg b/vuestorefront/static/men/clothing/suits/suits-2.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/suits/suits-2.jpg rename to vuestorefront/static/men/clothing/suits/suits-2.jpg diff --git a/graphql_vuestorefront/static/men/clothing/t-shirts/t-shirts-1.jpg b/vuestorefront/static/men/clothing/t-shirts/t-shirts-1.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/t-shirts/t-shirts-1.jpg rename to vuestorefront/static/men/clothing/t-shirts/t-shirts-1.jpg diff --git a/graphql_vuestorefront/static/men/clothing/t-shirts/t-shirts-2.jpg b/vuestorefront/static/men/clothing/t-shirts/t-shirts-2.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/t-shirts/t-shirts-2.jpg rename to vuestorefront/static/men/clothing/t-shirts/t-shirts-2.jpg diff --git a/graphql_vuestorefront/static/men/clothing/tops/tops-1.jpg b/vuestorefront/static/men/clothing/tops/tops-1.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/tops/tops-1.jpg rename to vuestorefront/static/men/clothing/tops/tops-1.jpg diff --git a/graphql_vuestorefront/static/men/clothing/tops/tops-2.jpg b/vuestorefront/static/men/clothing/tops/tops-2.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/tops/tops-2.jpg rename to vuestorefront/static/men/clothing/tops/tops-2.jpg diff --git a/graphql_vuestorefront/static/men/clothing/trousers/trousers-1.jpg b/vuestorefront/static/men/clothing/trousers/trousers-1.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/trousers/trousers-1.jpg rename to vuestorefront/static/men/clothing/trousers/trousers-1.jpg diff --git a/graphql_vuestorefront/static/men/clothing/trousers/trousers-2.jpg b/vuestorefront/static/men/clothing/trousers/trousers-2.jpg similarity index 100% rename from graphql_vuestorefront/static/men/clothing/trousers/trousers-2.jpg rename to vuestorefront/static/men/clothing/trousers/trousers-2.jpg diff --git a/graphql_vuestorefront/static/men/shoes/lace-up_shoes/lace-up_shoes-1.jpg b/vuestorefront/static/men/shoes/lace-up_shoes/lace-up_shoes-1.jpg similarity index 100% rename from graphql_vuestorefront/static/men/shoes/lace-up_shoes/lace-up_shoes-1.jpg rename to vuestorefront/static/men/shoes/lace-up_shoes/lace-up_shoes-1.jpg diff --git a/graphql_vuestorefront/static/men/shoes/lace-up_shoes/lace-up_shoes-2.jpg b/vuestorefront/static/men/shoes/lace-up_shoes/lace-up_shoes-2.jpg similarity index 100% rename from graphql_vuestorefront/static/men/shoes/lace-up_shoes/lace-up_shoes-2.jpg rename to vuestorefront/static/men/shoes/lace-up_shoes/lace-up_shoes-2.jpg diff --git a/graphql_vuestorefront/static/men/shoes/loafers/loafers-1.jpg b/vuestorefront/static/men/shoes/loafers/loafers-1.jpg similarity index 100% rename from graphql_vuestorefront/static/men/shoes/loafers/loafers-1.jpg rename to vuestorefront/static/men/shoes/loafers/loafers-1.jpg diff --git a/graphql_vuestorefront/static/men/shoes/loafers/loafers-2.jpg b/vuestorefront/static/men/shoes/loafers/loafers-2.jpg similarity index 100% rename from graphql_vuestorefront/static/men/shoes/loafers/loafers-2.jpg rename to vuestorefront/static/men/shoes/loafers/loafers-2.jpg diff --git a/graphql_vuestorefront/static/men/shoes/sneakers/sneakers-1.jpg b/vuestorefront/static/men/shoes/sneakers/sneakers-1.jpg similarity index 100% rename from graphql_vuestorefront/static/men/shoes/sneakers/sneakers-1.jpg rename to vuestorefront/static/men/shoes/sneakers/sneakers-1.jpg diff --git a/graphql_vuestorefront/static/men/shoes/sneakers/sneakers-2.jpg b/vuestorefront/static/men/shoes/sneakers/sneakers-2.jpg similarity index 100% rename from graphql_vuestorefront/static/men/shoes/sneakers/sneakers-2.jpg rename to vuestorefront/static/men/shoes/sneakers/sneakers-2.jpg diff --git a/graphql_vuestorefront/static/women/bags/clutches/clutches-1.jpg b/vuestorefront/static/women/bags/clutches/clutches-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/clutches/clutches-1.jpg rename to vuestorefront/static/women/bags/clutches/clutches-1.jpg diff --git a/graphql_vuestorefront/static/women/bags/clutches/clutches-2.jpg b/vuestorefront/static/women/bags/clutches/clutches-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/clutches/clutches-2.jpg rename to vuestorefront/static/women/bags/clutches/clutches-2.jpg diff --git a/graphql_vuestorefront/static/women/bags/clutches/clutches-3-blue.jpg b/vuestorefront/static/women/bags/clutches/clutches-3-blue.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/clutches/clutches-3-blue.jpg rename to vuestorefront/static/women/bags/clutches/clutches-3-blue.jpg diff --git a/graphql_vuestorefront/static/women/bags/clutches/clutches-3-brown.jpg b/vuestorefront/static/women/bags/clutches/clutches-3-brown.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/clutches/clutches-3-brown.jpg rename to vuestorefront/static/women/bags/clutches/clutches-3-brown.jpg diff --git a/graphql_vuestorefront/static/women/bags/clutches/clutches-3-pink.jpg b/vuestorefront/static/women/bags/clutches/clutches-3-pink.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/clutches/clutches-3-pink.jpg rename to vuestorefront/static/women/bags/clutches/clutches-3-pink.jpg diff --git a/graphql_vuestorefront/static/women/bags/clutches/clutches-3-yellow.jpg b/vuestorefront/static/women/bags/clutches/clutches-3-yellow.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/clutches/clutches-3-yellow.jpg rename to vuestorefront/static/women/bags/clutches/clutches-3-yellow.jpg diff --git a/graphql_vuestorefront/static/women/bags/handbag/handbag-1.jpg b/vuestorefront/static/women/bags/handbag/handbag-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/handbag/handbag-1.jpg rename to vuestorefront/static/women/bags/handbag/handbag-1.jpg diff --git a/graphql_vuestorefront/static/women/bags/handbag/handbag-2.jpg b/vuestorefront/static/women/bags/handbag/handbag-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/handbag/handbag-2.jpg rename to vuestorefront/static/women/bags/handbag/handbag-2.jpg diff --git a/graphql_vuestorefront/static/women/bags/shopper/shopper-1.jpg b/vuestorefront/static/women/bags/shopper/shopper-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/shopper/shopper-1.jpg rename to vuestorefront/static/women/bags/shopper/shopper-1.jpg diff --git a/graphql_vuestorefront/static/women/bags/shopper/shopper-2.jpg b/vuestorefront/static/women/bags/shopper/shopper-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/shopper/shopper-2.jpg rename to vuestorefront/static/women/bags/shopper/shopper-2.jpg diff --git a/graphql_vuestorefront/static/women/bags/shopper/shopper-3-black.jpg b/vuestorefront/static/women/bags/shopper/shopper-3-black.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/shopper/shopper-3-black.jpg rename to vuestorefront/static/women/bags/shopper/shopper-3-black.jpg diff --git a/graphql_vuestorefront/static/women/bags/shopper/shopper-3-white.jpg b/vuestorefront/static/women/bags/shopper/shopper-3-white.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/shopper/shopper-3-white.jpg rename to vuestorefront/static/women/bags/shopper/shopper-3-white.jpg diff --git a/graphql_vuestorefront/static/women/bags/shoulder_bags/shoulder_bags-1.jpg b/vuestorefront/static/women/bags/shoulder_bags/shoulder_bags-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/shoulder_bags/shoulder_bags-1.jpg rename to vuestorefront/static/women/bags/shoulder_bags/shoulder_bags-1.jpg diff --git a/graphql_vuestorefront/static/women/bags/shoulder_bags/shoulder_bags-2.jpg b/vuestorefront/static/women/bags/shoulder_bags/shoulder_bags-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/shoulder_bags/shoulder_bags-2.jpg rename to vuestorefront/static/women/bags/shoulder_bags/shoulder_bags-2.jpg diff --git a/graphql_vuestorefront/static/women/bags/wallets/wallets-1.jpg b/vuestorefront/static/women/bags/wallets/wallets-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/wallets/wallets-1.jpg rename to vuestorefront/static/women/bags/wallets/wallets-1.jpg diff --git a/graphql_vuestorefront/static/women/bags/wallets/wallets-2.jpg b/vuestorefront/static/women/bags/wallets/wallets-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/bags/wallets/wallets-2.jpg rename to vuestorefront/static/women/bags/wallets/wallets-2.jpg diff --git a/graphql_vuestorefront/static/women/clothing/blazer/blazer-1.jpg b/vuestorefront/static/women/clothing/blazer/blazer-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/blazer/blazer-1.jpg rename to vuestorefront/static/women/clothing/blazer/blazer-1.jpg diff --git a/graphql_vuestorefront/static/women/clothing/blazer/blazer-2.jpg b/vuestorefront/static/women/clothing/blazer/blazer-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/blazer/blazer-2.jpg rename to vuestorefront/static/women/clothing/blazer/blazer-2.jpg diff --git a/graphql_vuestorefront/static/women/clothing/dresses/dresses-1.jpg b/vuestorefront/static/women/clothing/dresses/dresses-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/dresses/dresses-1.jpg rename to vuestorefront/static/women/clothing/dresses/dresses-1.jpg diff --git a/graphql_vuestorefront/static/women/clothing/dresses/dresses-2.jpg b/vuestorefront/static/women/clothing/dresses/dresses-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/dresses/dresses-2.jpg rename to vuestorefront/static/women/clothing/dresses/dresses-2.jpg diff --git a/graphql_vuestorefront/static/women/clothing/jackets/jacket-1.jpg b/vuestorefront/static/women/clothing/jackets/jacket-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/jackets/jacket-1.jpg rename to vuestorefront/static/women/clothing/jackets/jacket-1.jpg diff --git a/graphql_vuestorefront/static/women/clothing/jackets/jacket-2.jpg b/vuestorefront/static/women/clothing/jackets/jacket-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/jackets/jacket-2.jpg rename to vuestorefront/static/women/clothing/jackets/jacket-2.jpg diff --git a/graphql_vuestorefront/static/women/clothing/jeans/jeans-1.jpg b/vuestorefront/static/women/clothing/jeans/jeans-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/jeans/jeans-1.jpg rename to vuestorefront/static/women/clothing/jeans/jeans-1.jpg diff --git a/graphql_vuestorefront/static/women/clothing/jeans/jeans-2.jpg b/vuestorefront/static/women/clothing/jeans/jeans-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/jeans/jeans-2.jpg rename to vuestorefront/static/women/clothing/jeans/jeans-2.jpg diff --git a/graphql_vuestorefront/static/women/clothing/shirts/shirts-1.jpg b/vuestorefront/static/women/clothing/shirts/shirts-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/shirts/shirts-1.jpg rename to vuestorefront/static/women/clothing/shirts/shirts-1.jpg diff --git a/graphql_vuestorefront/static/women/clothing/shirts/shirts-2.jpg b/vuestorefront/static/women/clothing/shirts/shirts-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/shirts/shirts-2.jpg rename to vuestorefront/static/women/clothing/shirts/shirts-2.jpg diff --git a/graphql_vuestorefront/static/women/clothing/skirts/skirts-1.jpg b/vuestorefront/static/women/clothing/skirts/skirts-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/skirts/skirts-1.jpg rename to vuestorefront/static/women/clothing/skirts/skirts-1.jpg diff --git a/graphql_vuestorefront/static/women/clothing/skirts/skirts-2.jpg b/vuestorefront/static/women/clothing/skirts/skirts-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/skirts/skirts-2.jpg rename to vuestorefront/static/women/clothing/skirts/skirts-2.jpg diff --git a/graphql_vuestorefront/static/women/clothing/t-shirts/t-shirts-1.jpg b/vuestorefront/static/women/clothing/t-shirts/t-shirts-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/t-shirts/t-shirts-1.jpg rename to vuestorefront/static/women/clothing/t-shirts/t-shirts-1.jpg diff --git a/graphql_vuestorefront/static/women/clothing/t-shirts/t-shirts-2.jpg b/vuestorefront/static/women/clothing/t-shirts/t-shirts-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/t-shirts/t-shirts-2.jpg rename to vuestorefront/static/women/clothing/t-shirts/t-shirts-2.jpg diff --git a/graphql_vuestorefront/static/women/clothing/tops/tops-1.jpg b/vuestorefront/static/women/clothing/tops/tops-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/tops/tops-1.jpg rename to vuestorefront/static/women/clothing/tops/tops-1.jpg diff --git a/graphql_vuestorefront/static/women/clothing/tops/tops-2.jpg b/vuestorefront/static/women/clothing/tops/tops-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/tops/tops-2.jpg rename to vuestorefront/static/women/clothing/tops/tops-2.jpg diff --git a/graphql_vuestorefront/static/women/clothing/trouser/trouser-1.jpg b/vuestorefront/static/women/clothing/trouser/trouser-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/trouser/trouser-1.jpg rename to vuestorefront/static/women/clothing/trouser/trouser-1.jpg diff --git a/graphql_vuestorefront/static/women/clothing/trouser/trouser-2.jpg b/vuestorefront/static/women/clothing/trouser/trouser-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/clothing/trouser/trouser-2.jpg rename to vuestorefront/static/women/clothing/trouser/trouser-2.jpg diff --git a/graphql_vuestorefront/static/women/shoes/ankle_boots/ankle_boots-1.jpg b/vuestorefront/static/women/shoes/ankle_boots/ankle_boots-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/ankle_boots/ankle_boots-1.jpg rename to vuestorefront/static/women/shoes/ankle_boots/ankle_boots-1.jpg diff --git a/graphql_vuestorefront/static/women/shoes/ankle_boots/ankle_boots-2.jpg b/vuestorefront/static/women/shoes/ankle_boots/ankle_boots-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/ankle_boots/ankle_boots-2.jpg rename to vuestorefront/static/women/shoes/ankle_boots/ankle_boots-2.jpg diff --git a/graphql_vuestorefront/static/women/shoes/ballerinas/ballerinas-1.jpg b/vuestorefront/static/women/shoes/ballerinas/ballerinas-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/ballerinas/ballerinas-1.jpg rename to vuestorefront/static/women/shoes/ballerinas/ballerinas-1.jpg diff --git a/graphql_vuestorefront/static/women/shoes/ballerinas/ballerinas-2.jpg b/vuestorefront/static/women/shoes/ballerinas/ballerinas-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/ballerinas/ballerinas-2.jpg rename to vuestorefront/static/women/shoes/ballerinas/ballerinas-2.jpg diff --git a/graphql_vuestorefront/static/women/shoes/boots/boots-1.jpg b/vuestorefront/static/women/shoes/boots/boots-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/boots/boots-1.jpg rename to vuestorefront/static/women/shoes/boots/boots-1.jpg diff --git a/graphql_vuestorefront/static/women/shoes/boots/boots-2.jpg b/vuestorefront/static/women/shoes/boots/boots-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/boots/boots-2.jpg rename to vuestorefront/static/women/shoes/boots/boots-2.jpg diff --git a/graphql_vuestorefront/static/women/shoes/loafers/loafers-1.jpg b/vuestorefront/static/women/shoes/loafers/loafers-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/loafers/loafers-1.jpg rename to vuestorefront/static/women/shoes/loafers/loafers-1.jpg diff --git a/graphql_vuestorefront/static/women/shoes/loafers/loafers-2.jpg b/vuestorefront/static/women/shoes/loafers/loafers-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/loafers/loafers-2.jpg rename to vuestorefront/static/women/shoes/loafers/loafers-2.jpg diff --git a/graphql_vuestorefront/static/women/shoes/pumps/pumps-1.jpg b/vuestorefront/static/women/shoes/pumps/pumps-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/pumps/pumps-1.jpg rename to vuestorefront/static/women/shoes/pumps/pumps-1.jpg diff --git a/graphql_vuestorefront/static/women/shoes/pumps/pumps-2.jpg b/vuestorefront/static/women/shoes/pumps/pumps-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/pumps/pumps-2.jpg rename to vuestorefront/static/women/shoes/pumps/pumps-2.jpg diff --git a/graphql_vuestorefront/static/women/shoes/sandals/sandals-1.jpg b/vuestorefront/static/women/shoes/sandals/sandals-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/sandals/sandals-1.jpg rename to vuestorefront/static/women/shoes/sandals/sandals-1.jpg diff --git a/graphql_vuestorefront/static/women/shoes/sandals/sandals-2.jpg b/vuestorefront/static/women/shoes/sandals/sandals-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/sandals/sandals-2.jpg rename to vuestorefront/static/women/shoes/sandals/sandals-2.jpg diff --git a/graphql_vuestorefront/static/women/shoes/sneakers/sneakers-1.jpg b/vuestorefront/static/women/shoes/sneakers/sneakers-1.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/sneakers/sneakers-1.jpg rename to vuestorefront/static/women/shoes/sneakers/sneakers-1.jpg diff --git a/graphql_vuestorefront/static/women/shoes/sneakers/sneakers-2.jpg b/vuestorefront/static/women/shoes/sneakers/sneakers-2.jpg similarity index 100% rename from graphql_vuestorefront/static/women/shoes/sneakers/sneakers-2.jpg rename to vuestorefront/static/women/shoes/sneakers/sneakers-2.jpg diff --git a/graphql_vuestorefront/views/product_views.xml b/vuestorefront/views/product_views.xml similarity index 100% rename from graphql_vuestorefront/views/product_views.xml rename to vuestorefront/views/product_views.xml diff --git a/graphql_vuestorefront/views/res_config_settings_views.xml b/vuestorefront/views/res_config_settings_views.xml similarity index 97% rename from graphql_vuestorefront/views/res_config_settings_views.xml rename to vuestorefront/views/res_config_settings_views.xml index 5a82854..ca08ffe 100644 --- a/graphql_vuestorefront/views/res_config_settings_views.xml +++ b/vuestorefront/views/res_config_settings_views.xml @@ -119,14 +119,14 @@ diff --git a/graphql_vuestorefront/views/website_views.xml b/vuestorefront/views/website_views.xml similarity index 97% rename from graphql_vuestorefront/views/website_views.xml rename to vuestorefront/views/website_views.xml index 5de69af..d0fed7b 100644 --- a/graphql_vuestorefront/views/website_views.xml +++ b/vuestorefront/views/website_views.xml @@ -120,7 +120,7 @@ website.menu tree,form {'default_is_footer': True, 'search_default_is_footer': 1, 'search_default_group_by_website_id': 1} - + current @@ -129,7 +129,7 @@ website.menu tree,form {'default_is_mega_menu': True, 'search_default_is_mega_menu': 1, 'search_default_group_by_website_id': 1} - + current From 4b2846ef61c06fff8ec48148b5d9e64adf4fcfce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Laukavi=C4=8Dius?= Date: Fri, 8 Dec 2023 11:41:31 +0200 Subject: [PATCH 2/5] [ADD] pre-commit hook --- .editorconfig | 20 +++++ .eslintrc.yml | 187 ++++++++++++++++++++++++++++++++++++++++ .isort.cfg | 13 +++ .pre-commit-config.yaml | 150 ++++++++++++++++++++++++++++++++ .prettierrc.yml | 9 ++ .pylintrc-mandatory | 96 +++++++++++++++++++++ .python-black | 1 + .secrets.baseline | 1 + .yamllint | 3 + pytest.ini | 7 ++ setup.cfg | 12 +++ 11 files changed, 499 insertions(+) create mode 100644 .editorconfig create mode 100644 .eslintrc.yml create mode 100644 .isort.cfg create mode 100644 .pre-commit-config.yaml create mode 100644 .prettierrc.yml create mode 100644 .pylintrc-mandatory create mode 100644 .python-black create mode 100644 .secrets.baseline create mode 100644 .yamllint create mode 100644 pytest.ini create mode 100644 setup.cfg diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..bfd7ac5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# Configuration for known file extensions +[*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{json,yml,yaml,rst,md}] +indent_size = 2 + +# Do not configure editor for libs and autogenerated content +[{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}] +charset = unset +end_of_line = unset +indent_size = unset +indent_style = unset +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000..9429bc6 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,187 @@ +env: + browser: true + es6: true + +# See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449 +parserOptions: + ecmaVersion: 2019 + +overrides: + - files: + - "**/*.esm.js" + parserOptions: + sourceType: module + +# Globals available in Odoo that shouldn't produce errorings +globals: + _: readonly + $: readonly + fuzzy: readonly + jQuery: readonly + moment: readonly + odoo: readonly + openerp: readonly + owl: readonly + +# Styling is handled by Prettier, so we only need to enable AST rules; +# see https://github.com/OCA/maintainer-quality-tools/pull/618#issuecomment-558576890 +rules: + accessor-pairs: warn + array-callback-return: warn + callback-return: warn + capitalized-comments: + - warn + - always + - ignoreConsecutiveComments: true + ignoreInlineComments: true + complexity: + - warn + - 15 + constructor-super: warn + dot-notation: warn + eqeqeq: warn + global-require: warn + handle-callback-err: warn + id-blacklist: warn + id-match: warn + init-declarations: error + max-depth: warn + max-nested-callbacks: warn + max-statements-per-line: warn + no-alert: warn + no-array-constructor: warn + no-caller: warn + no-case-declarations: warn + no-class-assign: warn + no-cond-assign: error + no-const-assign: error + no-constant-condition: warn + no-control-regex: warn + no-debugger: error + no-delete-var: warn + no-div-regex: warn + no-dupe-args: error + no-dupe-class-members: error + no-dupe-keys: error + no-duplicate-case: error + no-duplicate-imports: error + no-else-return: warn + no-empty-character-class: warn + no-empty-function: error + no-empty-pattern: error + no-empty: warn + no-eq-null: error + no-eval: error + no-ex-assign: error + no-extend-native: warn + no-extra-bind: warn + no-extra-boolean-cast: warn + no-extra-label: warn + no-fallthrough: warn + no-func-assign: error + no-global-assign: error + no-implicit-coercion: + - warn + - allow: ["~"] + no-implicit-globals: warn + no-implied-eval: warn + no-inline-comments: warn + no-inner-declarations: warn + no-invalid-regexp: warn + no-irregular-whitespace: warn + no-iterator: warn + no-label-var: warn + no-labels: warn + no-lone-blocks: warn + no-lonely-if: error + no-mixed-requires: error + no-multi-str: warn + no-native-reassign: error + no-negated-condition: warn + no-negated-in-lhs: error + no-new-func: warn + no-new-object: warn + no-new-require: warn + no-new-symbol: warn + no-new-wrappers: warn + no-new: warn + no-obj-calls: warn + no-octal-escape: warn + no-octal: warn + no-param-reassign: warn + no-path-concat: warn + no-process-env: warn + no-process-exit: warn + no-proto: warn + no-prototype-builtins: warn + no-redeclare: warn + no-regex-spaces: warn + no-restricted-globals: warn + no-restricted-imports: warn + no-restricted-modules: warn + no-restricted-syntax: warn + no-return-assign: error + no-script-url: warn + no-self-assign: warn + no-self-compare: warn + no-sequences: warn + no-shadow-restricted-names: warn + no-shadow: warn + no-sparse-arrays: warn + no-sync: warn + no-this-before-super: warn + no-throw-literal: warn + no-undef-init: warn + no-undef: error + no-unmodified-loop-condition: warn + no-unneeded-ternary: error + no-unreachable: error + no-unsafe-finally: error + no-unused-expressions: error + no-unused-labels: error + no-unused-vars: error + no-use-before-define: error + no-useless-call: warn + no-useless-computed-key: warn + no-useless-concat: warn + no-useless-constructor: warn + no-useless-escape: warn + no-useless-rename: warn + no-void: warn + no-with: warn + operator-assignment: [error, always] + prefer-const: warn + radix: warn + require-yield: warn + sort-imports: warn + spaced-comment: [error, always] + strict: [error, function] + use-isnan: error + valid-jsdoc: + - warn + - prefer: + arg: param + argument: param + augments: extends + constructor: class + exception: throws + func: function + method: function + prop: property + return: returns + virtual: abstract + yield: yields + preferType: + array: Array + bool: Boolean + boolean: Boolean + number: Number + object: Object + str: String + string: String + requireParamDescription: false + requireReturn: false + requireReturnDescription: false + requireReturnType: false + valid-typeof: warn + yoda: warn diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..0ec187e --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,13 @@ +[settings] +; see https://github.com/psf/black +multi_line_output=3 +include_trailing_comma=True +force_grid_wrap=0 +combine_as_imports=True +use_parentheses=True +line_length=88 +known_odoo=odoo +known_odoo_addons=odoo.addons +sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER +default_section=THIRDPARTY +ensure_newline_before_comments = True diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..2ff26fd --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,150 @@ +--- +exclude: | + (?x) + # Files and folders generated by bots, to avoid loops + ^setup/|/static/description/index\.html$| + # We don't want to mess with tool-generated files + .svg$| + # Ignore readmes + ^README\.md$| + ^README\.rst$| + # Library files can have extraneous formatting (even minimized) + /static/(src/)?lib/| + # Repos using Sphinx to generate docs don't need prettying + ^docs/_templates/.*\.html$| + # You don't usually want a bot to modify your legal texts + (LICENSE.*|COPYING.*)| + # Do not check symlinked submodule directories. + ^graphql_base/ +default_language_version: + python: python3 + node: "14.18.0" +repos: + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.4.1 + hooks: + - id: prettier + name: prettier (with plugin-xml) + additional_dependencies: + - "prettier@2.4.1" + - "@prettier/plugin-xml@1.1.0" + args: + - --plugin=@prettier/plugin-xml + - --xml-self-closing-space=false + files: \.(css|htm|html|js|json|jsx|less|md|scss|toml|ts|xml|yaml|yml)$ + - repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + additional_dependencies: ["flake8-bugbear==22.9.11"] + - repo: https://github.com/myint/autoflake + rev: v1.6.0 + hooks: + - id: autoflake + args: + - --expand-star-imports + - --ignore-init-module-imports + - --in-place + - --remove-all-unused-imports + - --remove-duplicate-keys + - --remove-unused-variables + - repo: https://github.com/asottile/pyupgrade + rev: v2.38.0 + hooks: + - id: pyupgrade + args: + - --keep-percent-format + - --py310-plus + - repo: https://github.com/PyCQA/isort + rev: 5.12.0 + hooks: + - id: isort + name: isort except __init__.py + args: + - --settings=. + exclude: /__init__\.py$ + - repo: https://github.com/PyCQA/pydocstyle + rev: 6.3.0 + hooks: + - id: pydocstyle + args: + - --config=setup.cfg + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.9.0 + hooks: + - id: python-no-eval + - id: python-no-log-warn + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + - repo: https://github.com/pre-commit/mirrors-eslint + rev: v8.23.1 + hooks: + - id: eslint + verbose: true + args: + - --color + - --fix + - repo: https://github.com/PyCQA/bandit + rev: 1.7.4 + hooks: + - id: bandit + entry: bandit + language: python + language_version: python3 + types: [python] + args: + - --ini=setup.cfg + - repo: https://github.com/Yelp/detect-secrets + rev: v1.4.0 + hooks: + - id: detect-secrets + - repo: https://github.com/adrienverge/yamllint + rev: v1.28.0 + hooks: + - id: yamllint + entry: yamllint + language: python + types: [file, yaml] + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: check-ast + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-shebang-scripts-are-executable + exclude: \.sh\.jinja2$|\.sh\.j2$ + - id: check-json + - id: check-xml + - id: check-yaml + args: + # won't load yaml, only validate it (workaround for ansible !vault) + - --unsafe + - id: check-case-conflict + - id: check-merge-conflict + # exclude files where underlines are not distinguishable from merge + # conflicts + exclude: /README\.rst$|^docs/.*\.rst$ + - id: check-symlinks + - id: debug-statements + # Fixers + - id: end-of-file-fixer + - id: fix-byte-order-marker + # To remove utf-8 encoding (and the like) in python files. + - id: fix-encoding-pragma + args: ["--remove"] + - id: mixed-line-ending + args: ["--fix=lf"] + - repo: https://github.com/OCA/pylint-odoo + rev: v8.0.17 + hooks: + - id: pylint_odoo + args: + - --rcfile=.pylintrc-mandatory + - repo: https://github.com/psf/black + rev: 22.8.0 + hooks: + - id: black + args: + - --config=.python-black diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 0000000..177e347 --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,9 @@ +# Defaults for all prettier-supported languages. +# Prettier will complete this with settings from .editorconfig file. +--- +bracketSpacing: false +printWidth: 88 +proseWrap: always +semi: true +trailingComma: "es5" +xmlWhitespaceSensitivity: "strict" diff --git a/.pylintrc-mandatory b/.pylintrc-mandatory new file mode 100644 index 0000000..14e6114 --- /dev/null +++ b/.pylintrc-mandatory @@ -0,0 +1,96 @@ + +[MASTER] +load-plugins=pylint_odoo +score=n + +[ODOOLINT] +readme_template_url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" +manifest_required_authors=Odoo Community Association (OCA) +manifest_required_keys=license +manifest_deprecated_keys=description,active +license_allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 +valid_odoo_versions=16.0 + +[MESSAGES CONTROL] +disable=all + +enable=anomalous-backslash-in-string, + api-one-deprecated, + api-one-multi-together, + assignment-from-none, + attribute-deprecated, + class-camelcase, + dangerous-default-value, + dangerous-view-replace-wo-priority, + development-status-allowed, + duplicate-id-csv, + duplicate-key, + duplicate-xml-fields, + duplicate-xml-record-id, + eval-referenced, + eval-used, + incoherent-interpreter-exec-perm, + license-allowed, + manifest-author-string, + manifest-deprecated-key, + manifest-required-key, + manifest-version-format, + method-compute, + method-inverse, + method-required-super, + method-search, + openerp-exception-warning, + pointless-statement, + pointless-string-statement, + print-used, + redundant-keyword-arg, + redundant-modulename-xml, + reimported, + relative-import, + return-in-init, + rst-syntax-error, + sql-injection, + too-few-format-args, + translation-field, + translation-required, + unreachable, + use-vim-comment, + wrong-tabs-instead-of-spaces, + xml-syntax-error, + attribute-string-redundant, + character-not-valid-in-resource-link, + consider-merging-classes-inherited, + context-overridden, + create-user-wo-reset-password, + dangerous-filter-wo-user, + dangerous-qweb-replace-wo-priority, + deprecated-data-xml-node, + deprecated-openerp-xml-node, + duplicate-po-message-definition, + except-pass, + file-not-used, + invalid-commit, + manifest-maintainers-list, + missing-newline-extrafiles, + missing-readme, + odoo-addons-relative-import, + old-api7-method-defined, + po-msgstr-variables, + po-syntax-error, + renamed-field-parameter, + resource-not-exist, + str-format-used, + test-folder-imported, + translation-contains-variable, + translation-positional-used, + unnecessary-utf8-coding-comment, + website-manifest-key-not-valid-uri, + xml-attribute-translatable, + xml-deprecated-qweb-directive, + xml-deprecated-tree-attribute, + external-request-timeout + +[REPORTS] +msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} +output-format=colorized +reports=no diff --git a/.python-black b/.python-black new file mode 100644 index 0000000..28f4e25 --- /dev/null +++ b/.python-black @@ -0,0 +1 @@ +[tool.black] diff --git a/.secrets.baseline b/.secrets.baseline new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.secrets.baseline @@ -0,0 +1 @@ +{} diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..56684ed --- /dev/null +++ b/.yamllint @@ -0,0 +1,3 @@ +rules: + comments: + min-spaces-from-content: 1 diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..a6cc9c1 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,7 @@ +[pytest] +filterwarnings = + #action:message:category:module:line + # Odoo has some old code and Python is complaining. + ignore:invalid escape sequence.*:DeprecationWarning + # mrbob uses outdated code. + ignore:The SafeConfigParser class has been renamed.*:DeprecationWarning diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..2f84f47 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,12 @@ +[pydocstyle] +ignore = D100,D101,D102,D103,D104,D107,D203,D213,D406,D407 +[flake8] +ignore = E203,W503 +max-line-length = 88 +per-file-ignores= + __init__.py:F401 +[bandit] +# B101: Make no sense that using assert is security issue. +# B410: https://github.com/tiran/defusedxml/issues/31 +# B404, B603, B607: to use subprocess +skips = B101,B404,B410,B603,B607 From 7ae84e638061f806bb4011e897c5b3ad8edf62ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrius=20Laukavi=C4=8Dius?= Date: Fri, 8 Dec 2023 16:09:51 +0200 Subject: [PATCH 3/5] [REF] apply pre-commit Applying pre-commit rules and fixing bad code according to it. --- .github/workflows/dev.yml | 8 +- payment_adyen_vsf/__init__.py | 1 - payment_adyen_vsf/__manifest__.py | 34 +- payment_adyen_vsf/const.py | 69 +- payment_adyen_vsf/controllers/__init__.py | 1 - payment_adyen_vsf/controllers/apple_pay.py | 25 +- payment_adyen_vsf/controllers/main.py | 341 ++++--- setup.cfg | 2 +- vuestorefront/__init__.py | 6 +- vuestorefront/__manifest__.py | 86 +- vuestorefront/controllers/__init__.py | 1 - vuestorefront/controllers/main.py | 257 +++-- .../data/demo_product_public_category.xml | 45 +- .../data/demo_products_men_clothing_1.xml | 786 +++++++++++---- .../data/demo_products_men_clothing_2.xml | 683 ++++++++++--- .../data/demo_products_men_clothing_3.xml | 337 +++++-- .../data/demo_products_men_clothing_4.xml | 714 ++++++++++---- .../data/demo_products_men_shoes.xml | 294 ++++-- .../data/demo_products_women_bags.xml | 678 ++++++++++--- .../data/demo_products_women_clothing.xml | 918 +++++++++++++----- .../data/demo_products_women_shoes.xml | 714 ++++++++++---- vuestorefront/data/mail_template.xml | 168 +++- vuestorefront/data/website_data.xml | 8 +- vuestorefront/hooks.py | 22 +- vuestorefront/models/__init__.py | 1 - vuestorefront/models/invalidate_cache.py | 109 ++- vuestorefront/models/ir_http.py | 133 ++- vuestorefront/models/payment_transaction.py | 7 +- vuestorefront/models/product.py | 241 +++-- vuestorefront/models/res_config_settings.py | 94 +- vuestorefront/models/res_users.py | 54 +- vuestorefront/models/website.py | 80 +- vuestorefront/schema.py | 37 +- vuestorefront/schemas/__init__.py | 1 - vuestorefront/schemas/address.py | 171 ++-- vuestorefront/schemas/category.py | 40 +- vuestorefront/schemas/contact_us.py | 25 +- vuestorefront/schemas/country.py | 25 +- vuestorefront/schemas/invoice.py | 47 +- vuestorefront/schemas/mailing_list.py | 154 ++- vuestorefront/schemas/objects.py | 283 ++++-- vuestorefront/schemas/order.py | 100 +- vuestorefront/schemas/payment.py | 214 ++-- vuestorefront/schemas/product.py | 158 +-- vuestorefront/schemas/shop.py | 109 ++- vuestorefront/schemas/sign.py | 110 ++- vuestorefront/schemas/user_profile.py | 18 +- vuestorefront/schemas/website.py | 45 +- vuestorefront/schemas/wishlist.py | 37 +- .../views/res_config_settings_views.xml | 94 +- vuestorefront/views/website_views.xml | 33 +- website_google_feed/README.rst | 2 + website_google_feed/__init__.py | 1 - website_google_feed/__manifest__.py | 31 +- website_google_feed/controllers/__init__.py | 1 - website_google_feed/controllers/main.py | 59 +- website_google_feed/models/__init__.py | 1 - website_google_feed/models/product.py | 44 +- website_google_feed/models/website.py | 5 +- website_google_feed/views/website_feed.xml | 22 +- 60 files changed, 6238 insertions(+), 2546 deletions(-) create mode 100644 website_google_feed/README.rst diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 86ed03f..40c38ee 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -2,12 +2,12 @@ name: Build VSF on: push: - branches: [ 16.0 ] + branches: [16.0] jobs: deployment: runs-on: self-hosted steps: - - run: | - echo "-------- Deploying https://vsfdemo16.labs.odoogap.com/ " - /home/egap/.scripts/update + - run: | + echo "-------- Deploying https://vsfdemo16.labs.odoogap.com/ " + /home/egap/.scripts/update diff --git a/payment_adyen_vsf/__init__.py b/payment_adyen_vsf/__init__.py index 0405454..1ee9576 100644 --- a/payment_adyen_vsf/__init__.py +++ b/payment_adyen_vsf/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). diff --git a/payment_adyen_vsf/__manifest__.py b/payment_adyen_vsf/__manifest__.py index 668f150..88da8af 100644 --- a/payment_adyen_vsf/__manifest__.py +++ b/payment_adyen_vsf/__manifest__.py @@ -1,31 +1,23 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). { # Application Information - 'name': 'Adyen Payment Acquirer to VSF', - 'category': 'Accounting/Payment Acquirers', - 'version': '16.0.1.0.1', - 'summary': 'Adyen Payment Acquirer: Adapting Adyen to VSF', - + "name": "Adyen Payment Acquirer to VSF", + "category": "Accounting/Payment Acquirers", + "version": "16.0.1.0.1", + "summary": "Adyen Payment Acquirer: Adapting Adyen to VSF", # Author - 'author': "OdooGap", - 'website': "https://www.odoogap.com/", - 'maintainer': 'OdooGap', - 'license': 'LGPL-3', - + "author": "OdooGap", + "website": "https://www.odoogap.com/", + "maintainer": "OdooGap", + "license": "LGPL-3", # Dependencies - 'depends': [ - 'payment', - 'payment_adyen' - ], - + "depends": ["payment", "payment_adyen"], # Views - 'data': [], - + "data": [], # Technical - 'installable': True, - 'application': False, - 'auto_install': False, + "installable": True, + "application": False, + "auto_install": False, } diff --git a/payment_adyen_vsf/const.py b/payment_adyen_vsf/const.py index b6c8b53..47c1e66 100644 --- a/payment_adyen_vsf/const.py +++ b/payment_adyen_vsf/const.py @@ -1,41 +1,46 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -from odoo.addons.payment_adyen.const import API_ENDPOINT_VERSIONS, CURRENCY_DECIMALS, RESULT_CODES_MAPPING +from odoo.addons.payment_adyen.const import API_ENDPOINT_VERSIONS, CURRENCY_DECIMALS # Endpoints of the API. -# See https://docs.adyen.com/api-explorer/#/CheckoutService/v67/overview for Checkout API -# See https://docs.adyen.com/api-explorer/#/Recurring/v49/overview for Recurring API -API_ENDPOINT_VERSIONS.update({ - '/payments/{}/reversals': 67, # Checkout API -}) +# See for Checkout API: +# https://docs.adyen.com/api-explorer/#/CheckoutService/v67/overview +# See for Recurring API: +# https://docs.adyen.com/api-explorer/#/Recurring/v49/overview +API_ENDPOINT_VERSIONS.update( + { + "/payments/{}/reversals": 67, # Checkout API + } +) # Adyen-specific mapping of currency codes in ISO 4217 format to the number of decimals. # Only currencies for which Adyen does not follow the ISO 4217 norm are listed here. # See https://docs.adyen.com/development-resources/currency-codes -CURRENCY_DECIMALS.update({ - "BHD": 3, - "DJF": 0, - "GNF": 0, - "JOD": 3, - "JPY": 0, - "KMF": 0, - "KRW": 0, - "KWD": 3, - "LYD": 3, - "OMR": 3, - "PYG": 0, - "RWF": 0, - "TND": 3, - "UGX": 0, - "VND": 0, - "VUV": 0, - "XAF": 0, - "XOF": 0, - "XPF": 0, - "USD": 2, - "EUR": 2, - "SEK": 2, - "DKK": 2, -}) +CURRENCY_DECIMALS.update( + { + "BHD": 3, + "DJF": 0, + "GNF": 0, + "JOD": 3, + "JPY": 0, + "KMF": 0, + "KRW": 0, + "KWD": 3, + "LYD": 3, + "OMR": 3, + "PYG": 0, + "RWF": 0, + "TND": 3, + "UGX": 0, + "VND": 0, + "VUV": 0, + "XAF": 0, + "XOF": 0, + "XPF": 0, + "USD": 2, + "EUR": 2, + "SEK": 2, + "DKK": 2, + } +) diff --git a/payment_adyen_vsf/controllers/__init__.py b/payment_adyen_vsf/controllers/__init__.py index 0aec262..cb3a0ef 100644 --- a/payment_adyen_vsf/controllers/__init__.py +++ b/payment_adyen_vsf/controllers/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). diff --git a/payment_adyen_vsf/controllers/apple_pay.py b/payment_adyen_vsf/controllers/apple_pay.py index 5f5d208..4affecb 100644 --- a/payment_adyen_vsf/controllers/apple_pay.py +++ b/payment_adyen_vsf/controllers/apple_pay.py @@ -1,25 +1,34 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). +import os + from odoo import http from odoo.http import request -import os class AppleMerchantIDController(http.Controller): - - @http.route('/.well-known/apple-developer-merchantid-domain-association', type='http', auth='public') + @http.route( + "/.well-known/apple-developer-merchantid-domain-association", + type="http", + auth="public", + ) def apple_merchant_id(self, **kw): - file_path = os.path.abspath(os.path.join( - os.path.dirname(__file__), '..', 'static', 'description', 'apple-developer-merchantid-domain-association') + file_path = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "..", + "static", + "description", + "apple-developer-merchantid-domain-association", + ) ) try: - with open(file_path, 'r') as file: + with open(file_path) as file: file_content = file.read() - headers = [('Content-Type', 'text/plain')] + headers = [("Content-Type", "text/plain")] return request.make_response(file_content, headers) # Return error 404 - Not Found diff --git a/payment_adyen_vsf/controllers/main.py b/payment_adyen_vsf/controllers/main.py index 9d9cf79..d4d139d 100644 --- a/payment_adyen_vsf/controllers/main.py +++ b/payment_adyen_vsf/controllers/main.py @@ -1,20 +1,20 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import logging import pprint -import werkzeug +import werkzeug from werkzeug import urls -from odoo import http, _ -from odoo.http import request +from odoo import _, http from odoo.exceptions import ValidationError +from odoo.http import request + from odoo.addons.payment import utils as payment_utils +from odoo.addons.payment.controllers.post_processing import PaymentPostProcessing from odoo.addons.payment_adyen import utils as adyen_utils from odoo.addons.payment_adyen.controllers.main import AdyenController -from odoo.addons.payment.controllers.post_processing import PaymentPostProcessing _logger = logging.getLogger(__name__) @@ -23,127 +23,182 @@ class AdyenControllerInherit(AdyenController): _webhook_url = AdyenController()._webhook_url - @http.route('/payment/adyen/payments', type='json', auth='public') + @http.route("/payment/adyen/payments", type="json", auth="public") def adyen_payments( - self, provider_id, reference, converted_amount, currency_id, partner_id, payment_method, - access_token, browser_info=None + self, + provider_id, + reference, + converted_amount, + currency_id, + partner_id, + payment_method, + access_token, + browser_info=None, ): - """ Make a payment request and process the feedback data. + """Make a payment request and process the feedback data. - :param int provider_id: The provider handling the transaction, as a `payment.provider` id + :param int provider_id: The provider handling the transaction, as + `payment.provider` id :param str reference: The reference of the transaction - :param int converted_amount: The amount of the transaction in minor units of the currency + :param int converted_amount: The amount of the transaction in minor + units of the currency :param int currency_id: The currency of the transaction, as a `res.currency` id :param int partner_id: The partner making the transaction, as a `res.partner` id - :param dict payment_method: The details of the payment method used for the transaction + :param dict payment_method: The details of the payment method used for + the transaction :param str access_token: The access token used to verify the provided values :param dict browser_info: The browser info to pass to Adyen :return: The JSON-formatted content of the response :rtype: dict """ - # Check that the transaction details have not been altered. This allows preventing users + # Check that the transaction details have not been altered. This allows + # preventing users # from validating transactions by paying less than agreed upon. if not payment_utils.check_access_token( - access_token, reference, converted_amount, partner_id + access_token, reference, converted_amount, partner_id ): - raise ValidationError("Adyen: " + _("Received tampered payment request data.")) + raise ValidationError(_("Adyen: Received tampered payment request data.")) # Make the payment request to Adyen - provider_sudo = request.env['payment.provider'].sudo().browse(provider_id).exists() - tx_sudo = request.env['payment.transaction'].sudo().search([('reference', '=', reference)]) + provider_sudo = ( + request.env["payment.provider"].sudo().browse(provider_id).exists() + ) + tx_sudo = ( + request.env["payment.transaction"] + .sudo() + .search([("reference", "=", reference)]) + ) shopper_ip = payment_utils.get_customer_ip_address() if tx_sudo.created_on_vsf: - if request.httprequest.headers.environ.get('HTTP_REAL_IP', False) and \ - request.httprequest.headers.environ['HTTP_REAL_IP']: - shopper_ip = request.httprequest.headers.environ['HTTP_REAL_IP'] + if ( + request.httprequest.headers.environ.get("HTTP_REAL_IP", False) + and request.httprequest.headers.environ["HTTP_REAL_IP"] + ): + shopper_ip = request.httprequest.headers.environ["HTTP_REAL_IP"] data = { - 'merchantAccount': provider_sudo.adyen_merchant_account, - 'amount': { - 'value': converted_amount, - 'currency': request.env['res.currency'].browse(currency_id).name, # ISO 4217 + "merchantAccount": provider_sudo.adyen_merchant_account, + "amount": { + "value": converted_amount, + "currency": request.env["res.currency"] + .browse(currency_id) + .name, # ISO 4217 }, - 'reference': reference, - 'paymentMethod': payment_method, - 'shopperReference': provider_sudo._adyen_compute_shopper_reference(partner_id), - 'recurringProcessingModel': 'CardOnFile', # Most susceptible to trigger a 3DS check - 'shopperIP': shopper_ip, - 'shopperInteraction': 'Ecommerce', - 'shopperEmail': tx_sudo.partner_email, - 'shopperName': adyen_utils.format_partner_name(tx_sudo.partner_name), - 'telephoneNumber': tx_sudo.partner_phone, - 'storePaymentMethod': tx_sudo.tokenize, # True by default on Adyen side + "reference": reference, + "paymentMethod": payment_method, + "shopperReference": provider_sudo._adyen_compute_shopper_reference( + partner_id + ), + # Most susceptible to trigger a 3DS check + "recurringProcessingModel": "CardOnFile", + "shopperIP": shopper_ip, + "shopperInteraction": "Ecommerce", + "shopperEmail": tx_sudo.partner_email, + "shopperName": adyen_utils.format_partner_name(tx_sudo.partner_name), + "telephoneNumber": tx_sudo.partner_phone, + "storePaymentMethod": tx_sudo.tokenize, # True by default on Adyen side # 'additionalData': { # 'allow3DS2': True # }, - 'channel': 'web', # Required to support 3DS - 'origin': provider_sudo.get_base_url(), # Required to support 3DS - 'browserInfo': browser_info, # Required to support 3DS - 'returnUrl': urls.url_join( + "channel": "web", # Required to support 3DS + "origin": provider_sudo.get_base_url(), # Required to support 3DS + "browserInfo": browser_info, # Required to support 3DS + "returnUrl": urls.url_join( provider_sudo.get_base_url(), - # Include the reference in the return url to be able to match it after redirection. - # The key 'merchantReference' is chosen on purpose to be the same as that returned + # Include the reference in the return url to be able to match it + # after redirection. + # The key 'merchantReference' is chosen on purpose to be the + # same as that returned # by the /payments endpoint of Adyen. - f'/payment/adyen/return?merchantReference={reference}' + f"/payment/adyen/return?merchantReference={reference}", ), **adyen_utils.include_partner_addresses(tx_sudo), } - # Force the capture delay on Adyen side if the provider is not configured for capturing + # Force the capture delay on Adyen side if the provider is not configured + # for capturing # payments manually. This is necessary because it's not possible to distinguish - # 'AUTHORISATION' events sent by Adyen with the merchant account's capture delay set to - # 'manual' from events with the capture delay set to 'immediate' or a number of hours. If - # the merchant account is configured to capture payments with a delay but the provider is - # not, we force the immediate capture to avoid considering authorized transactions as + # 'AUTHORISATION' events sent by Adyen with the merchant account's capture + # delay set to + # 'manual' from events with the capture delay set to 'immediate' or a number + # of hours. If + # the merchant account is configured to capture payments with a delay but + # the provider is + # not, we force the immediate capture to avoid considering authorized + # transactions as # captured on Odoo. if not provider_sudo.capture_manually: data.update(captureDelayHours=0) # Make the payment request to Adyen response_content = provider_sudo._adyen_make_request( - url_field_name='adyen_checkout_api_url', - endpoint='/payments', + url_field_name="adyen_checkout_api_url", + endpoint="/payments", payload=data, - method='POST' + method="POST", ) # Handle the payment request response _logger.info( "payment request response for transaction with reference %s:\n%s", - reference, pprint.pformat(response_content) + reference, + pprint.pformat(response_content), ) tx_sudo._handle_notification_data( - 'adyen', dict(response_content, merchantReference=reference), # Match the transaction + "adyen", + dict( + response_content, merchantReference=reference + ), # Match the transaction ) return response_content - @http.route('/payment/adyen/return', type='http', auth='public', csrf=False, save_session=False) + @http.route( + "/payment/adyen/return", + type="http", + auth="public", + csrf=False, + save_session=False, + ) def adyen_return_from_3ds_auth(self, **data): - """ Process the authentication data sent by Adyen after redirection from the 3DS1 page. - - The route is flagged with `save_session=False` to prevent Odoo from assigning a new session - to the user if they are redirected to this route with a POST request. Indeed, as the session - cookie is created without a `SameSite` attribute, some browsers that don't implement the - recommended default `SameSite=Lax` behavior will not include the cookie in the redirection - request from the payment provider to Odoo. As the redirection to the '/payment/status' page - will satisfy any specification of the `SameSite` attribute, the session of the user will be - retrieved and with it the transaction which will be immediately post-processed. - - :param dict data: The authentication result data. May include custom params sent to Adyen in - the request to allow matching the transaction when redirected here. + """Process the authentication data sent by Adyen. + + Its done after redirection from the 3DS1 page. + + The route is flagged with `save_session=False` to prevent Odoo + from assigning a new session + to the user if they are redirected to this route with a POST + request. Indeed, as the session + cookie is created without a `SameSite` attribute, some browsers + that don't implement the + recommended default `SameSite=Lax` behavior will not include the + cookie in the redirection + request from the payment provider to Odoo. As the redirection to + the '/payment/status' page + will satisfy any specification of the `SameSite` attribute, the + session of the user will be + retrieved and with it the transaction which will be immediately + post-processed. + + :param dict data: The authentication result data. May include custom + params sent to Adyen in the request to allow matching the + transaction when redirected here. """ - payment_transaction = data.get('merchantReference') and request.env['payment.transaction'].sudo().search( - [('reference', 'in', [data.get('merchantReference')])], limit=1 - ) + payment_transaction = data.get("merchantReference") and request.env[ + "payment.transaction" + ].sudo().search([("reference", "in", [data.get("merchantReference")])], limit=1) # Check the Order and respective website related with the transaction # Check the payment_return url for the success and error pages # Pass the transaction_id on the session sale_order_ids = payment_transaction.sale_order_ids.ids - sale_order = request.env['sale.order'].sudo().search([ - ('id', 'in', sale_order_ids), ('website_id', '!=', False) - ], limit=1) + sale_order = ( + request.env["sale.order"] + .sudo() + .search( + [("id", "in", sale_order_ids), ("website_id", "!=", False)], limit=1 + ) + ) # Get Website website = sale_order.website_id @@ -154,34 +209,46 @@ def adyen_return_from_3ds_auth(self, **data): request.session["__payment_monitored_tx_ids__"] = [payment_transaction.id] # Retrieve the transaction based on the reference included in the return url - tx_sudo = request.env['payment.transaction'].sudo()._get_tx_from_notification_data( - 'adyen', data + tx_sudo = ( + request.env["payment.transaction"] + .sudo() + ._get_tx_from_notification_data("adyen", data) ) - # Overwrite the operation to force the flow to 'redirect'. This is necessary because even - # thought Adyen is implemented as a direct payment provider, it will redirect the user out - # of Odoo in some cases. For instance, when a 3DS1 authentication is required, or for + # Overwrite the operation to force the flow to 'redirect'. This is + # necessary because even + # thought Adyen is implemented as a direct payment provider, it will + # redirect the user out + # of Odoo in some cases. For instance, when a 3DS1 authentication is + # required, or for # special payment methods that are not handled by the drop-in (e.g. Sofort). - tx_sudo.operation = 'online_redirect' + tx_sudo.operation = "online_redirect" - # Query and process the result of the additional actions that have been performed + # Query and process the result of the additional actions that have been + # performed _logger.info( - "handling redirection from Adyen for transaction with reference %s with data:\n%s", - tx_sudo.reference, pprint.pformat(data) + "handling redirection from Adyen for transaction with reference" + + " %s with data:\n%s", + tx_sudo.reference, + pprint.pformat(data), ) result = self.adyen_payment_details( tx_sudo.provider_id.id, - data['merchantReference'], + data["merchantReference"], { - 'details': { - 'redirectResult': data['redirectResult'], + "details": { + "redirectResult": data["redirectResult"], }, }, ) if payment_transaction.created_on_vsf: # For Redirect 3DS2 and MobilePay (Success flow) - if result and result.get('resultCode') and result['resultCode'] == 'Authorised': + if ( + result + and result.get("resultCode") + and result["resultCode"] == "Authorised" + ): # Confirm sale order PaymentPostProcessing().poll_status() @@ -189,81 +256,115 @@ def adyen_return_from_3ds_auth(self, **data): return werkzeug.utils.redirect(vsf_payment_success_return_url) # For Redirect 3DS2 and MobilePay (Cancel/Error flow) - elif result and result.get('resultCode') and result['resultCode'] in ['Refused', 'Cancelled']: + elif ( + result + and result.get("resultCode") + and result["resultCode"] in ["Refused", "Cancelled"] + ): return werkzeug.utils.redirect(vsf_payment_error_return_url) else: # Redirect the user to the status page - return request.redirect('/payment/status') + return request.redirect("/payment/status") - @http.route(_webhook_url, type='json', auth='public') + @http.route(_webhook_url, type="json", auth="public") def adyen_webhook(self): - """ Process the data sent by Adyen to the webhook based on the event code. + """Process the data sent by Adyen to the webhook based on the event code. - See https://docs.adyen.com/development-resources/webhooks/understand-notifications for the - exhaustive list of event codes. + See + https://docs.adyen.com/development-resources/webhooks + /understand-notifications + for the exhaustive list of event codes. :return: The '[accepted]' string to acknowledge the notification :rtype: str """ data = request.dispatcher.jsonrequest - for notification_item in data['notificationItems']: - notification_data = notification_item['NotificationRequestItem'] + for notification_item in data["notificationItems"]: + notification_data = notification_item["NotificationRequestItem"] _logger.info( - "notification received from Adyen with data:\n%s", pprint.pformat(notification_data) + "notification received from Adyen with data:\n%s", + pprint.pformat(notification_data), ) - PaymentTransaction = request.env['payment.transaction'] + PaymentTransaction = request.env["payment.transaction"] try: - payment_transaction = notification_data.get('merchantReference') and PaymentTransaction.sudo().search( - [('reference', 'in', [notification_data.get('merchantReference')])], limit=1 + payment_transaction = notification_data.get( + "merchantReference" + ) and PaymentTransaction.sudo().search( + [("reference", "in", [notification_data.get("merchantReference")])], + limit=1, ) # Check the integrity of the notification - tx_sudo = request.env['payment.transaction'].sudo()._get_tx_from_notification_data( - 'adyen', notification_data + tx_sudo = ( + request.env["payment.transaction"] + .sudo() + ._get_tx_from_notification_data("adyen", notification_data) ) self._verify_notification_signature(notification_data, tx_sudo) - # Check whether the event of the notification succeeded and reshape the notification + # Check whether the event of the notification succeeded and + # reshape the notification # data for parsing - success = notification_data['success'] == 'true' - event_code = notification_data['eventCode'] - if event_code == 'AUTHORISATION' and success: - notification_data['resultCode'] = 'Authorised' - elif event_code == 'CANCELLATION': - notification_data['resultCode'] = 'Cancelled' if success else 'Error' - elif event_code in ['REFUND', 'CAPTURE']: - notification_data['resultCode'] = 'Authorised' if success else 'Error' + success = notification_data["success"] == "true" + event_code = notification_data["eventCode"] + if event_code == "AUTHORISATION" and success: + notification_data["resultCode"] = "Authorised" + elif event_code == "CANCELLATION": + notification_data["resultCode"] = ( + "Cancelled" if success else "Error" + ) + elif event_code in ["REFUND", "CAPTURE"]: + notification_data["resultCode"] = ( + "Authorised" if success else "Error" + ) else: continue # Don't handle unsupported event codes and failed events - # Handle the notification data as if they were feedback of a S2S payment request - tx_sudo._handle_notification_data('adyen', notification_data) + # Handle the notification data as if they were feedback of a + # S2S payment request + tx_sudo._handle_notification_data("adyen", notification_data) # Case the transaction was created on vsf (Success flow) - if event_code == 'AUTHORISATION' and success and payment_transaction.created_on_vsf: - # Check the Order and respective website related with the transaction - # Check the payment_return url for the success and error pages + if ( + event_code == "AUTHORISATION" + and success + and payment_transaction.created_on_vsf + ): + # Check the Order and respective website related with the + # transaction Check the payment_return url for the success + # and error pages sale_order_ids = payment_transaction.sale_order_ids.ids - sale_order = request.env['sale.order'].sudo().search([ - ('id', 'in', sale_order_ids), ('website_id', '!=', False) - ], limit=1) + sale_order = ( + request.env["sale.order"] + .sudo() + .search( + [("id", "in", sale_order_ids), ("website_id", "!=", False)], + limit=1, + ) + ) # Get Website website = sale_order.website_id # Redirect to VSF - vsf_payment_success_return_url = website.vsf_payment_success_return_url + vsf_payment_success_return_url = ( + website.vsf_payment_success_return_url + ) - request.session["__payment_monitored_tx_ids__"] = [payment_transaction.id] + request.session["__payment_monitored_tx_ids__"] = [ + payment_transaction.id + ] # Confirm sale order PaymentPostProcessing().poll_status() return werkzeug.utils.redirect(vsf_payment_success_return_url) + # Acknowledge the notification to avoid getting spammed + except ValidationError: + _logger.exception( + "unable to handle the notification data; skipping to acknowledge" + ) - except ValidationError: # Acknowledge the notification to avoid getting spammed - _logger.exception("unable to handle the notification data; skipping to acknowledge") - - return '[accepted]' # Acknowledge the notification + return "[accepted]" # Acknowledge the notification diff --git a/setup.cfg b/setup.cfg index 2f84f47..ba0a5cc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [pydocstyle] -ignore = D100,D101,D102,D103,D104,D107,D203,D213,D406,D407 +ignore = D100,D101,D102,D103,D104,D106,D107,D203,D213,D406,D407 [flake8] ignore = E203,W503 max-line-length = 88 diff --git a/vuestorefront/__init__.py b/vuestorefront/__init__.py index b69bb5c..fd82b3d 100644 --- a/vuestorefront/__init__.py +++ b/vuestorefront/__init__.py @@ -1,10 +1,6 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). from . import controllers from . import models -from .hooks import ( - pre_init_hook_login_check, - post_init_hook_login_convert -) +from .hooks import pre_init_hook_login_check, post_init_hook_login_convert diff --git a/vuestorefront/__manifest__.py b/vuestorefront/__manifest__.py index cd2b361..66f1c0e 100644 --- a/vuestorefront/__manifest__.py +++ b/vuestorefront/__manifest__.py @@ -1,52 +1,50 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). { - 'name': 'Vue Storefront Api', - 'version': '16.0.1.0.0', - 'summary': 'Vue Storefront API', - 'description': """Vue Storefront API Integration""", - 'category': 'Website', - 'license': 'LGPL-3', - 'author': 'OdooGap', - 'website': 'https://www.odoogap.com/', - 'depends': [ - 'graphql_base', - 'website_sale_wishlist', - 'website_sale_delivery', - 'website_mass_mailing', - 'website_sale_loyalty', - 'auth_signup', - 'contacts', - 'crm', - 'theme_default', - 'payment_adyen_vsf', + "name": "Vue Storefront Api", + "version": "16.0.1.0.0", + "summary": "Vue Storefront API", + "category": "Website", + "license": "LGPL-3", + "author": "OdooGap", + "website": "https://www.odoogap.com/", + "depends": [ + "graphql_base", + "website_sale_wishlist", + "website_sale_delivery", + "website_mass_mailing", + "website_sale_loyalty", + "auth_signup", + "contacts", + "crm", + "theme_default", + "payment_adyen_vsf", ], - 'data': [ - 'security/ir.model.access.csv', - 'data/website_data.xml', - 'data/mail_template.xml', - 'data/ir_config_parameter_data.xml', - 'data/ir_cron_data.xml', - 'views/product_views.xml', - 'views/website_views.xml', - 'views/res_config_settings_views.xml', + "data": [ + "security/ir.model.access.csv", + "data/website_data.xml", + "data/mail_template.xml", + "data/ir_config_parameter_data.xml", + "data/ir_cron_data.xml", + "views/product_views.xml", + "views/website_views.xml", + "views/res_config_settings_views.xml", ], - 'demo': [ - 'data/demo_product_attribute.xml', - 'data/demo_product_public_category.xml', - 'data/demo_products_women_clothing.xml', - 'data/demo_products_women_shoes.xml', - 'data/demo_products_women_bags.xml', - 'data/demo_products_men_clothing_1.xml', - 'data/demo_products_men_clothing_2.xml', - 'data/demo_products_men_clothing_3.xml', - 'data/demo_products_men_clothing_4.xml', - 'data/demo_products_men_shoes.xml', + "demo": [ + "data/demo_product_attribute.xml", + "data/demo_product_public_category.xml", + "data/demo_products_women_clothing.xml", + "data/demo_products_women_shoes.xml", + "data/demo_products_women_bags.xml", + "data/demo_products_men_clothing_1.xml", + "data/demo_products_men_clothing_2.xml", + "data/demo_products_men_clothing_3.xml", + "data/demo_products_men_clothing_4.xml", + "data/demo_products_men_shoes.xml", ], - 'installable': True, - 'auto_install': False, - 'pre_init_hook': 'pre_init_hook_login_check', - 'post_init_hook': 'post_init_hook_login_convert', + "installable": True, + "auto_install": False, + "pre_init_hook": "pre_init_hook_login_check", + "post_init_hook": "post_init_hook_login_convert", } diff --git a/vuestorefront/controllers/__init__.py b/vuestorefront/controllers/__init__.py index fbf9bf7..fee1a97 100644 --- a/vuestorefront/controllers/__init__.py +++ b/vuestorefront/controllers/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). diff --git a/vuestorefront/controllers/main.py b/vuestorefront/controllers/main.py index 51e350a..cd7a474 100644 --- a/vuestorefront/controllers/main.py +++ b/vuestorefront/controllers/main.py @@ -1,18 +1,17 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -import os import json import logging +import os import pprint +from urllib.parse import urlparse from odoo import http -from odoo.addons.web.controllers.binary import Binary +from odoo.http import Response, request + from odoo.addons.graphql_base import GraphQLControllerMixin -from odoo.http import request, Response -from odoo.tools.safe_eval import safe_eval -from urllib.parse import urlparse +from odoo.addons.web.controllers.binary import Binary from ..schema import schema @@ -20,96 +19,164 @@ class VSFBinary(Binary): - @http.route(['/web/image', - '/web/image/', - '/web/image//', - '/web/image//x', - '/web/image//x/', - '/web/image///', - '/web/image////', - '/web/image////x', - '/web/image////x/', - '/web/image/', - '/web/image//', - '/web/image//x', - '/web/image//x/', - '/web/image/-', - '/web/image/-/', - '/web/image/-/x', - '/web/image/-/x/'], type='http', - auth="public") - def content_image(self, xmlid=None, model='ir.attachment', id=None, field='datas', - filename_field='name', unique=None, filename=None, mimetype=None, - download=None, width=0, height=0, crop=False, access_token=None, - **kwargs): - """ Validate width and height """ + @http.route( + [ + "/web/image", + "/web/image/", + "/web/image//", + "/web/image//x", + "/web/image//x/", + "/web/image///", + "/web/image////", + ( + "/web/image////" + + "x" + ), + ( + "/web/image///" + + "/x/" + ), + "/web/image/", + "/web/image//", + "/web/image//x", + "/web/image//x/", + "/web/image/-", + "/web/image/-/", + "/web/image/-/x", + ( + "/web/image/-/" + + "x/" + ), + ], + type="http", + auth="public", + ) + def content_image( + self, + xmlid=None, + model="ir.attachment", + id=None, + field="datas", + filename_field="name", + unique=None, + filename=None, + mimetype=None, + download=None, + width=0, + height=0, + crop=False, + access_token=None, + **kwargs, + ): + """Validate width and height.""" try: - ICP = request.env['ir.config_parameter'].sudo() - vsf_image_resize_limit = int(ICP.get_param('vsf_image_resize_limit', 1920)) - + ICP = request.env["ir.config_parameter"].sudo() + vsf_image_resize_limit = int(ICP.get_param("vsf_image_resize_limit", 1920)) + if width > vsf_image_resize_limit or height > vsf_image_resize_limit: return request.not_found() except Exception: return request.not_found() - return super(VSFBinary, self).content_image( - xmlid=xmlid, model=model, id=id, field=field, filename_field=filename_field, unique=unique, - filename=filename, mimetype=mimetype, download=download, width=width, height=height, crop=crop, - access_token=access_token, **kwargs) + return super().content_image( + xmlid=xmlid, + model=model, + id=id, + field=field, + filename_field=filename_field, + unique=unique, + filename=filename, + mimetype=mimetype, + download=download, + width=width, + height=height, + crop=crop, + access_token=access_token, + **kwargs, + ) class GraphQLController(http.Controller, GraphQLControllerMixin): - def _process_request(self, schema, data): # Set the vsf_debug_mode value that exist in the settings - ICP = http.request.env['ir.config_parameter'].sudo() - vsf_debug_mode = ICP.get_param('vsf_debug_mode', False) + ICP = http.request.env["ir.config_parameter"].sudo() + vsf_debug_mode = ICP.get_param("vsf_debug_mode", False) if vsf_debug_mode: try: request = http.request.httprequest - _logger.info('# ------------------------------- GRAPHQL: DEBUG MODE -------------------------------- #') - _logger.info('') - _logger.info('# ------------------------------------------------------- #') - _logger.info('# HEADERS #') - _logger.info('# ------------------------------------------------------- #') - _logger.info('\n%s', pprint.pformat(request.headers.environ)) - _logger.info('') - _logger.info('# ------------------------------------------------------- #') - _logger.info('# QUERY / MUTATION #') - _logger.info('# ------------------------------------------------------- #') - _logger.info('\n%s', data.get('query', None)) - _logger.info('') - _logger.info('# ------------------------------------------------------- #') - _logger.info('# ARGUMENTS #') - _logger.info('# ------------------------------------------------------- #') - _logger.info('\n%s', request.args.get('variables', None)) - _logger.info('') - _logger.info('# ------------------------------------------------------------------------------------ #') - except: - pass - return super(GraphQLController, self)._process_request(schema, data) + _logger.info( + "# ------------------------------- GRAPHQL: DEBUG MODE" + + " -------------------------------- #" + ) + _logger.info("") + _logger.info( + "# ------------------------------------------------------- #" + ) + _logger.info( + "# HEADERS #" + ) + _logger.info( + "# ------------------------------------------------------- #" + ) + _logger.info("\n%s", pprint.pformat(request.headers.environ)) + _logger.info("") + _logger.info( + "# ------------------------------------------------------- #" + ) + _logger.info( + "# QUERY / MUTATION #" + ) + _logger.info( + "# ------------------------------------------------------- #" + ) + _logger.info("\n%s", data.get("query", None)) + _logger.info("") + _logger.info( + "# ------------------------------------------------------- #" + ) + _logger.info( + "# ARGUMENTS #" + ) + _logger.info( + "# ------------------------------------------------------- #" + ) + _logger.info("\n%s", request.args.get("variables", None)) + _logger.info("") + _logger.info("# ----------------------------------------------------#") + except Exception as e: + _logger.error( + "Something went wrong processing request: %s", e, exc_info=True + ) + return super()._process_request(schema, data) def _set_website_context(self): """Set website context based on http_request_host header.""" try: - request_host = request.httprequest.headers.environ['HTTP_RESQUEST_HOST'] - website = request.env['website'].search([('domain', 'ilike', request_host)], limit=1) + request_host = request.httprequest.headers.environ["HTTP_RESQUEST_HOST"] + website = request.env["website"].search( + [("domain", "ilike", request_host)], limit=1 + ) if website: context = dict(request.context) - context.update({ - 'website_id': website.id, - 'lang': website.default_lang_id.code, - }) + context.update( + { + "website_id": website.id, + "lang": website.default_lang_id.code, + } + ) request.context = context request_uid = http.request.env.uid website_uid = website.sudo().user_id.id - if request_uid != website_uid \ - and request.env['res.users'].sudo().browse(request_uid).has_group('base.group_public'): + if request_uid != website_uid and request.env[ + "res.users" + ].sudo().browse(request_uid).has_group("base.group_public"): request.uid = website_uid - except: - pass + except Exception as e: + _logger.error( + "Something went wrong setting website context: %s", e, exc_info=True + ) # The GraphiQL route, providing an IDE for developers @http.route("/graphiql/vsf", auth="user") @@ -125,65 +192,71 @@ def graphql(self, **kwargs): self._set_website_context() return self._handle_graphql_request(schema.graphql_schema) - @http.route('/vsf/categories', type='http', auth='public', csrf=False) + @http.route("/vsf/categories", type="http", auth="public", csrf=False) def vsf_categories(self): self._set_website_context() - website = request.env['website'].get_current_website() + website = request.env["website"].get_current_website() categories = [] if website.default_lang_id: lang_code = website.default_lang_id.code - domain = [('website_slug', '!=', False)] + domain = [("website_slug", "!=", False)] - for category in request.env['product.public.category'].sudo().search(domain): + for category in ( + request.env["product.public.category"].sudo().search(domain) + ): category = category.with_context(lang=lang_code) categories.append(category.website_slug) return Response( json.dumps(categories), - headers={'Content-Type': 'application/json'}, + headers={"Content-Type": "application/json"}, ) - @http.route('/vsf/products', type='http', auth='public', csrf=False) + @http.route("/vsf/products", type="http", auth="public", csrf=False) def vsf_products(self): self._set_website_context() - website = request.env['website'].get_current_website() + website = request.env["website"].get_current_website() products = [] if website.default_lang_id: lang_code = website.default_lang_id.code - domain = [('website_published', '=', True), ('website_slug', '!=', False)] + domain = [("website_published", "=", True), ("website_slug", "!=", False)] - for product in request.env['product.template'].sudo().search(domain): + for product in request.env["product.template"].sudo().search(domain): product = product.with_context(lang=lang_code) url_parsed = urlparse(product.website_slug) name = os.path.basename(url_parsed.path) - path = product.website_slug.replace(name, '') + path = product.website_slug.replace(name, "") - products.append({ - 'name': name, - 'path': '{}:slug'.format(path), - }) + products.append( + { + "name": name, + "path": f"{path}:slug", + } + ) return Response( json.dumps(products), - headers={'Content-Type': 'application/json'}, + headers={"Content-Type": "application/json"}, ) - @http.route('/vsf/redirects', type='http', auth='public', csrf=False) + @http.route("/vsf/redirects", type="http", auth="public", csrf=False) def vsf_redirects(self): redirects = [] - for redirect in request.env['website.rewrite'].sudo().search([]): - redirects.append({ - 'from': redirect.url_from, - 'to': redirect.url_to, - }) + for redirect in request.env["website.rewrite"].sudo().search([]): + redirects.append( + { + "from": redirect.url_from, + "to": redirect.url_to, + } + ) return Response( json.dumps(redirects), - headers={'Content-Type': 'application/json'}, + headers={"Content-Type": "application/json"}, ) diff --git a/vuestorefront/data/demo_product_public_category.xml b/vuestorefront/data/demo_product_public_category.xml index 6e7d477..9cbdb4c 100644 --- a/vuestorefront/data/demo_product_public_category.xml +++ b/vuestorefront/data/demo_product_public_category.xml @@ -68,7 +68,10 @@ Shirts 33 - + T-shirts 33 @@ -93,7 +96,10 @@ Dresses 38 - + Swimwear 39 @@ -115,7 +121,10 @@ Boots 43 - + Ankle boots 44 @@ -130,7 +139,10 @@ Ballerinas 46 - + Lace-up shoes 47 @@ -145,7 +157,10 @@ Sandals 49 - + Winterboots 50 @@ -162,7 +177,10 @@ Clutches 53 - + Shoulder Bags 54 @@ -182,7 +200,10 @@ Wallets 57 - + Bucketbag/packbag 58 @@ -280,7 +301,10 @@ Boots 75 - + Lace-up shoes 76 @@ -327,7 +351,10 @@ Wallets 85 - + Bucketbag/packbag 86 diff --git a/vuestorefront/data/demo_products_men_clothing_1.xml b/vuestorefront/data/demo_products_men_clothing_1.xml index cb676d8..d101f9f 100644 --- a/vuestorefront/data/demo_products_men_clothing_1.xml +++ b/vuestorefront/data/demo_products_men_clothing_1.xml @@ -16,31 +16,60 @@ - This is the product: Daniele Alessandrini - Vest + This is the product: Daniele Alessandrini - Vest delivery - - + + - + - - - + + + - - - + + + - + - + }]" + /> - + }]" + /> DA01-01 165.00 - + DA01-02 165.00 - + DA01-03 165.00 - + DA01-04 165.00 - + DA01-05 165.00 170.00 - + DA01-06 165.00 170.00 - + @@ -141,31 +198,57 @@ - This is the product: Leather jacket Bully dark blue + This is the product: Leather jacket Bully dark blue delivery - - + + - + - - - + + + - - - + + + - + }]" + /> - + }]" + /> LJBD01-01 523.75 - + LJBD01-02 523.75 - + @@ -220,31 +315,57 @@ - This is the product: Bomber Daniele Alessandrini blue + This is the product: Bomber Daniele Alessandrini blue delivery - - + + - + - - - + + + - - - + + + - + }]" + /> - + }]" + /> BDA01-01 523.75 - + BDA01-02 523.75 - + @@ -299,31 +432,60 @@ - This is the product: Save the Duck – Casual Jacket + This is the product: Save the Duck – Casual Jacket delivery - - + + - + - - - + + + - - - + + + - + - + }]" + /> - + }]" + /> CJ01-01 161.25 - + CJ01-02 161.25 - + CJ01-03 161.25 - + CJ01-04 161.25 - + CJ01-05 161.25 - + CJ01-06 161.25 - + @@ -422,31 +612,57 @@ - This is the product: Vest ”Naples” Moncler red + This is the product: Vest ”Naples” Moncler red delivery - - + + - + - - - + + + - - - + + + - + }]" + /> - + }]" + /> VNMR01-01 662.50 - + VNMR01-02 662.50 - + VNMR01-03 662.50 - + VNMR01-04 662.50 - + @@ -527,31 +763,60 @@ - This is the product: Moncler – Down Jacket “Jacob” + This is the product: Moncler – Down Jacket “Jacob” delivery - - + + - + - - - + + + - - - + + + - + - + }]" + /> - + }]" + /> MDJ01-01 1218.75 - + MDJ01-02 1218.75 - + MDJ01-03 1218.75 - + @@ -620,31 +901,57 @@ - This is the product: Casual jacket Aspesi blue + This is the product: Casual jacket Aspesi blue delivery - - + + - + - - - + + + - - - + + + - + }]" + /> - + }]" + /> CJB01-01 430.00 - + CJB01-02 430.00 - + @@ -699,31 +1018,57 @@ - This is the product: Casual jacket “Mahakali“ Peuterey blue + This is the product: Casual jacket “Mahakali“ Peuterey blue delivery - - + + - + - - - + + + - - - + + + - + }]" + /> - + }]" + /> Mahakali-01 423.75 - + Mahakali-02 423.75 - + @@ -778,31 +1135,57 @@ - This is the product: Casual jacket Invicta blue + This is the product: Casual jacket Invicta blue delivery - - + + - + - - - + + + - - - + + + - + }]" + /> - + }]" + /> Invicta-01 423.75 - + Invicta-02 423.75 - + @@ -858,31 +1253,60 @@ - This is the product: Casual jacket ”Lyon” Moncler black + This is the product: Casual jacket ”Lyon” Moncler black delivery - - + + - + - - - + + + - - - + + + - + - + }]" + /> - + }]" + /> LYON-01 656.25 - + LYON-02 656.25 - + diff --git a/vuestorefront/data/demo_products_men_clothing_2.xml b/vuestorefront/data/demo_products_men_clothing_2.xml index eff3223..3340bb1 100644 --- a/vuestorefront/data/demo_products_men_clothing_2.xml +++ b/vuestorefront/data/demo_products_men_clothing_2.xml @@ -16,31 +16,57 @@ - This is the product: Casual Jacket Save the Duck dark blue + This is the product: Casual Jacket Save the Duck dark blue delivery - - + + - + - + - + - + - + - + }]" + /> - + }]" + /> CJSDDB-01 198.75 - + CJSDDB-02 198.75 - + @@ -96,31 +134,60 @@ - This is the product: Leather jacket D.r.o.w.s black + This is the product: Leather jacket D.r.o.w.s black delivery - - + + - + - + - + - + - + - + - + }]" + /> - + }]" + /> LJB3-01 872.50 - + LJB3-02 872.50 - + @@ -176,31 +255,57 @@ - This is the product: Moncler – Down Jacket “Ryan” + This is the product: Moncler – Down Jacket “Ryan” delivery - - + + - + - + - + - + - + - + }]" + /> - + }]" + /> RYAN-01 1162.50 - + RYAN-02 1162.50 - + @@ -257,29 +374,56 @@ This is the product: Harris Wharf – Coat delivery - - + + - + - + - + - + - + - + - + }]" + /> - + }]" + /> HARRISWARF-01 598.75 - + HARRISWARF-02 598.75 - + @@ -334,31 +490,60 @@ - This is the product: Casual jacket Michael Kors beige + This is the product: Casual jacket Michael Kors beige delivery - - + + - + - + - + - + - + - + - + }]" + /> - + }]" + /> MKB02-01 598.75 - + MKB02-02 598.75 - + @@ -415,31 +612,57 @@ - This is the product: Down jacket “Kathmandu“ Peuterey grey + This is the product: Down jacket “Kathmandu“ Peuterey grey delivery - - + + - + - + - + - + - + - + }]" + /> - + }]" + /> KATHMANDU-01 411.25 - + KATHMANDU-02 411.25 - + KATHMANDU-03 411.25 - + @@ -510,29 +749,56 @@ This is the product: Coat Aspesi beige delivery - - + + - + - + - + - + - + - + - + }]" + /> - + }]" + /> CAD02-01 536.25 - + CAD02-02 536.25 - + @@ -588,31 +866,57 @@ - This is the product: Casual jacket Stone Island grey + This is the product: Casual jacket Stone Island grey delivery - - + + - + - + - + - + - + - + }]" + /> - + }]" + /> CJSIG-01 837.50 - + CJSIG-02 837.50 - + @@ -667,31 +983,57 @@ - This is the product: Jacket Doubleface “Sol Walk“ Luis Trenker blue + This is the product: Jacket Doubleface “Sol Walk“ Luis Trenker blue delivery - - + + - + - + - + - + - + - + }]" + /> - + }]" + /> SOLWALK-01 498.75 - + SOLWALK-02 498.75 - + @@ -746,31 +1100,60 @@ - This is the product: Bully – Leather Jacket + This is the product: Bully – Leather Jacket delivery - - + + - + - + - + - + - + - + - + }]" + /> - + }]" + /> BULLY02-01 497.50 - + BULLY02-02 497.50 - + diff --git a/vuestorefront/data/demo_products_men_clothing_3.xml b/vuestorefront/data/demo_products_men_clothing_3.xml index dc5624a..97262bc 100644 --- a/vuestorefront/data/demo_products_men_clothing_3.xml +++ b/vuestorefront/data/demo_products_men_clothing_3.xml @@ -18,29 +18,53 @@ This is the product: Coat Aspesi blue delivery - - + + - + - + - + - + - + - + }]" + /> - + }]" + /> CAB05-01 536.25 - + CAB05-02 536.25 - + @@ -96,31 +132,60 @@ - This is the product: Casual jacket Stone Island black + This is the product: Casual jacket Stone Island black delivery - - + + - + - + - + - + - + - + - + }]" + /> - + }]" + /> CJSIB06-01 498.75 - + CJSIB06-02 498.75 - + @@ -178,29 +255,56 @@ This is the product: Vest Bully black delivery - - + + - + - + - + - + - + - + - + }]" + /> - + }]" + /> VBB03-01 486.25 - + VBB03-02 486.25 - + @@ -255,31 +371,60 @@ - This is the product: Leather jacket Daniele Alessandrini beige + This is the product: Leather jacket Daniele Alessandrini beige delivery - - + + - + - + - + - + - + - + - + }]" + /> - + }]" + /> LJDAB02-01 736.25 - + LJDAB02-02 736.25 - + @@ -334,31 +491,57 @@ - This is the product: Jacket Doubleface “Sol Walk“ Luis Trenker red + This is the product: Jacket Doubleface “Sol Walk“ Luis Trenker red delivery - - + + - + - + - + - + - + - + }]" + /> - + }]" + /> JDSWLTR-01 498.75 - + JDSWLTR-02 498.75 - + diff --git a/vuestorefront/data/demo_products_men_clothing_4.xml b/vuestorefront/data/demo_products_men_clothing_4.xml index 9a04de4..09c021a 100644 --- a/vuestorefront/data/demo_products_men_clothing_4.xml +++ b/vuestorefront/data/demo_products_men_clothing_4.xml @@ -16,27 +16,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -84,27 +111,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -153,27 +207,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -221,27 +302,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -290,27 +398,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -358,27 +493,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -427,27 +589,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -495,27 +684,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -564,27 +780,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -632,27 +875,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -701,27 +971,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -769,27 +1066,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -838,27 +1162,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -906,27 +1257,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> diff --git a/vuestorefront/data/demo_products_men_shoes.xml b/vuestorefront/data/demo_products_men_shoes.xml index 7d040e9..ab9e484 100644 --- a/vuestorefront/data/demo_products_men_shoes.xml +++ b/vuestorefront/data/demo_products_men_shoes.xml @@ -16,27 +16,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -84,27 +111,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -153,27 +207,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -221,27 +302,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -290,27 +398,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -358,27 +493,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> diff --git a/vuestorefront/data/demo_products_women_bags.xml b/vuestorefront/data/demo_products_women_bags.xml index 9f4c72d..db94d7a 100644 --- a/vuestorefront/data/demo_products_women_bags.xml +++ b/vuestorefront/data/demo_products_women_bags.xml @@ -16,27 +16,50 @@ - This is the product: Michael Kors – Clutch “Daria” + This is the product: Michael Kors – Clutch “Daria” 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -72,27 +99,50 @@ - This is the product: Clutch ”Carol” Liebeskind black + This is the product: Clutch ”Carol” Liebeskind black 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -129,32 +183,61 @@ - This is the product: Clutch “Jet Set Travel” small Michael Kors + This is the product: Clutch “Jet Set Travel” small Michael Kors 578902-00 delivery - - + + - + - + - + - + - + - + - + }]" + /> - + }]" + /> CJSTMK-01 106.25 - + CJSTMK-02 106.25 - + CJSTMK-03 106.25 - + CJSTMK-04 106.25 - + CJSTMK-05 106.25 - + CJSTMK-06 106.25 - + CJSTMK-07 106.25 - + CJSTMK-08 106.25 - + - + 31.25 @@ -283,27 +405,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -339,27 +488,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -396,27 +572,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -453,27 +656,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -510,32 +740,61 @@ - This is the product: Bag “Jet Set Travel” Michael Kors + This is the product: Bag “Jet Set Travel” Michael Kors 578902-00 delivery - - + + - + - + - + - + - + - + - + }]" + /> - + }]" + /> BJSTMK-01 368.75 - + BJSTMK-01 368.75 - + - + 26.00 @@ -594,27 +868,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -650,27 +951,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -707,27 +1035,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> @@ -763,27 +1118,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - + - + - + - + },]" + /> - + },]" + /> diff --git a/vuestorefront/data/demo_products_women_clothing.xml b/vuestorefront/data/demo_products_women_clothing.xml index c2a7d4a..c5b5c6d 100644 --- a/vuestorefront/data/demo_products_women_clothing.xml +++ b/vuestorefront/data/demo_products_women_clothing.xml @@ -16,27 +16,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -84,27 +111,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -153,27 +207,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -221,27 +302,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -290,27 +398,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -358,27 +493,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -427,27 +589,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -495,27 +684,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -564,27 +780,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -632,27 +875,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -701,27 +971,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -769,27 +1066,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -838,27 +1162,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -906,27 +1257,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -975,27 +1353,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -1043,27 +1448,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -1112,27 +1544,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -1180,27 +1639,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> diff --git a/vuestorefront/data/demo_products_women_shoes.xml b/vuestorefront/data/demo_products_women_shoes.xml index 011382f..00741ff 100644 --- a/vuestorefront/data/demo_products_women_shoes.xml +++ b/vuestorefront/data/demo_products_women_shoes.xml @@ -16,27 +16,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -84,27 +111,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -153,27 +207,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -221,27 +302,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -290,27 +398,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -358,27 +493,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -427,27 +589,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -495,27 +684,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -564,27 +780,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -632,27 +875,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -701,27 +971,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -769,27 +1066,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -838,27 +1162,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> @@ -906,27 +1257,50 @@ - The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. + The Karissa V-Neck Tee features a semi-fitted shape that's flattering for every figure. You can hit the gym with confidence while it hugs curves and hides common "problem" areas. Find stunning women's cocktail dresses and party dresses. 578902-00 delivery - - + + - + - - - + + + - + - + },]" + /> - + },]" + /> diff --git a/vuestorefront/data/mail_template.xml b/vuestorefront/data/mail_template.xml index c416352..a1e54e6 100644 --- a/vuestorefront/data/mail_template.xml +++ b/vuestorefront/data/mail_template.xml @@ -10,37 +10,68 @@ Website Reset Password Password reset - "{{ object.company_id.name }}" <{{ (object.company_id.email or user.email) }}> + "{{ object.company_id.name }}" <{{ (object.company_id.email or user.email) }}> {{ object.email_formatted }} - +
- +
- +
-
- Your Account + Your Account
- - Marc Demo + + Marc Demo
- +
-
+
+
@@ -49,19 +80,34 @@
- +
-
+
- Dear Marc Demo, + Dear Marc Demo,

A password reset was requested for the Odoo account linked to this email. - You may change your password by following this link which will remain valid during 24 hours:
-
-
+
@@ -88,28 +139,53 @@
- +
- @@ -125,13 +201,21 @@
- YourCompany + YourCompany
- +1 650-123-4567 - + + +1 650-123-4567 + | - - info@yourcompany.com + + info@yourcompany.com - + | - - http://www.example.com + + http://www.example.com
- +
diff --git a/vuestorefront/data/website_data.xml b/vuestorefront/data/website_data.xml index 1b9ba28..dd3bab3 100644 --- a/vuestorefront/data/website_data.xml +++ b/vuestorefront/data/website_data.xml @@ -9,8 +9,12 @@ - http://localhost:3000/checkout/thank-you - http://localhost:3000/payment-fail + http://localhost:3000/checkout/thank-you + http://localhost:3000/payment-fail diff --git a/vuestorefront/hooks.py b/vuestorefront/hooks.py index 1af8d04..54dc637 100644 --- a/vuestorefront/hooks.py +++ b/vuestorefront/hooks.py @@ -1,20 +1,20 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -from odoo import api, SUPERUSER_ID, _ +from odoo import SUPERUSER_ID, _, api from odoo.exceptions import ValidationError def pre_init_hook_login_check(cr): - """ - This hook will see if exists any conflict between Portal logins, before the module is installed + """Check if there are any conflict between portal logins. + + Check is done before module is installed. """ env = api.Environment(cr, SUPERUSER_ID, {}) check_users = [] - users = env['res.users'].search([]) + users = env["res.users"].search([]) for user in users: - if user.login and user.has_group('base.group_portal'): + if user.login and user.has_group("base.group_portal"): login = user.login.lower() if login not in check_users: check_users.append(login) @@ -25,11 +25,9 @@ def pre_init_hook_login_check(cr): def post_init_hook_login_convert(cr, registry): - """ - After the module is installed, set Portal Logins to lowercase - """ + """Set Portal Logins to lowercase.""" env = api.Environment(cr, SUPERUSER_ID, {}) - users = env['res.users'].search([]) + users = env["res.users"].search([]) for user in users: - if user.login and user.has_group('base.group_portal'): - user.login = user.login.lower() \ No newline at end of file + if user.login and user.has_group("base.group_portal"): + user.login = user.login.lower() diff --git a/vuestorefront/models/__init__.py b/vuestorefront/models/__init__.py index 4b9a538..fca3668 100644 --- a/vuestorefront/models/__init__.py +++ b/vuestorefront/models/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). diff --git a/vuestorefront/models/invalidate_cache.py b/vuestorefront/models/invalidate_cache.py index 4fd180e..ce94fc2 100644 --- a/vuestorefront/models/invalidate_cache.py +++ b/vuestorefront/models/invalidate_cache.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). @@ -6,24 +5,27 @@ from datetime import datetime import requests -from odoo import models, fields, api + +from odoo import api, fields, models _logger = logging.getLogger(__name__) class InvalidateCache(models.Model): - _name = 'invalidate.cache' - _description = 'VSF Invalidate Cache' + _name = "invalidate.cache" + _description = "VSF Invalidate Cache" - res_model = fields.Char('Res Model', required=True, index=True) - res_id = fields.Integer('Res ID', required=True) + res_model = fields.Char(required=True, index=True) + res_id = fields.Integer("Res ID", required=True) def init(self): super().init() - self.env.cr.execute(""" + self.env.cr.execute( + """ CREATE INDEX IF NOT EXISTS invalidate_cache_find_idx ON invalidate_cache(res_model, res_id); - """) + """ + ) @api.model def find_invalidate_cache(self, res_model, res_id): @@ -34,15 +36,18 @@ def find_invalidate_cache(self, res_model, res_id): WHERE res_model=%s AND res_id=%s LIMIT 1; """ - params = (res_model, res_id,) + params = ( + res_model, + res_id, + ) cr.execute(query, params) return cr.fetchone() @api.model def create_invalidate_cache(self, res_model, res_ids): - ICP = self.env['ir.config_parameter'].sudo() - cache_invalidation_enable = ICP.get_param('vsf_cache_invalidation', False) + ICP = self.env["ir.config_parameter"].sudo() + cache_invalidation_enable = ICP.get_param("vsf_cache_invalidation", False) if not cache_invalidation_enable: return False @@ -50,76 +55,96 @@ def create_invalidate_cache(self, res_model, res_ids): for res_id in res_ids: if not self.find_invalidate_cache(res_model, res_id): query = """ - INSERT INTO invalidate_cache(res_model, res_id, create_date, write_date, create_uid, write_uid) + INSERT INTO + invalidate_cache( + res_model, + res_id, + create_date, + write_date, + create_uid, + write_uid + ) VALUES(%s, %s, %s, %s, %s, %s); """ now = datetime.now() uid = self.env.user.id - params = (res_model, res_id, now, now, uid, uid,) + params = ( + res_model, + res_id, + now, + now, + uid, + uid, + ) self.env.cr.execute(query, params) @api.model def delete_invalidate_cache(self, ids): - if len(ids) == 1: - ids = '({})'.format(ids[0]) - else: - ids = tuple(ids) - query = """ DELETE FROM invalidate_cache - WHERE id IN {}; - """.format(ids) - - self.env.cr.execute(query) + WHERE id IN %s + """ + self.env.cr.execute(query, (tuple(ids),)) @api.model def request_cache_invalidation(self, url, key, tags): if url and key and tags: try: - requests.get(url, params={'key': key, 'tags': tags}, timeout=5) + requests.get(url, params={"key": key, "tags": tags}, timeout=5) except Exception as e: _logger.error(e) self.env.cr.rollback() @api.model def request_vsf_cache_invalidation(self): - ICP = self.env['ir.config_parameter'].sudo() - url = ICP.get_param('vsf_cache_invalidation_url', False) - key = ICP.get_param('vsf_cache_invalidation_key', False) + ICP = self.env["ir.config_parameter"].sudo() + url = ICP.get_param("vsf_cache_invalidation_url", False) + key = ICP.get_param("vsf_cache_invalidation_key", False) models = [ { - 'name': 'product.template', - 'tags_method': '_get_product_tags', + "name": "product.template", + "tags_method": "_get_product_tags", }, { - 'name': 'product.public.category', - 'tags_method': '_get_category_tags', + "name": "product.public.category", + "tags_method": "_get_category_tags", }, ] for model in models: - invalidate_caches = self.env['invalidate.cache'].search([('res_model', '=', model['name'])]) + invalidate_caches = self.env["invalidate.cache"].search( + [("res_model", "=", model["name"])] + ) if invalidate_caches: - res_ids = invalidate_caches.mapped('res_id') - tags = getattr(self, model['tags_method'])(res_ids) + res_ids = invalidate_caches.mapped("res_id") + tags = getattr(self, model["tags_method"])(res_ids) self.delete_invalidate_cache(invalidate_caches.ids) self.request_cache_invalidation(url, key, tags) - self.env.cr.commit() + # FIXME: this probably should not be done. + self.env.cr.commit() # pylint: disable=invalid-commit def _get_product_tags(self, product_ids): - tags = ','.join(f'P{product_id}' for product_id in product_ids) - category_ids = self.env['product.template'].search( - [('id', 'in', product_ids)]).mapped('public_categ_slug_ids').ids + tags = ",".join(f"P{product_id}" for product_id in product_ids) + category_ids = ( + self.env["product.template"] + .search([("id", "in", product_ids)]) + .mapped("public_categ_slug_ids") + .ids + ) if category_ids: - tags += ',' + ','.join(f'C{category_id}' for category_id in category_ids) + tags += "," + ",".join(f"C{category_id}" for category_id in category_ids) return tags def _get_category_tags(self, product_ids): - tags = ','.join(f'P{product_id}' for product_id in product_ids) - category_ids = self.env['product.template'].search( - [('id', 'in', product_ids)]).mapped('public_categ_slug_ids').ids + tags = ",".join(f"P{product_id}" for product_id in product_ids) + category_ids = ( + self.env["product.template"] + .search([("id", "in", product_ids)]) + .mapped("public_categ_slug_ids") + .ids + ) if category_ids: - tags += ',' + ','.join(f'C{category_id}' for category_id in category_ids) + tags += "," + ",".join(f"C{category_id}" for category_id in category_ids) return tags diff --git a/vuestorefront/models/ir_http.py b/vuestorefront/models/ir_http.py index 2d04da7..903fed7 100644 --- a/vuestorefront/models/ir_http.py +++ b/vuestorefront/models/ir_http.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). @@ -7,6 +6,7 @@ import io from PIL.WebPImagePlugin import Image + from odoo import api, http, models from odoo.http import request from odoo.tools import image_process @@ -14,65 +14,119 @@ class Http(models.AbstractModel): - _inherit = 'ir.http' + _inherit = "ir.http" @api.model - def _content_image(self, xmlid=None, model='ir.attachment', res_id=None, field='datas', - filename_field='name', unique=None, filename=None, mimetype=None, download=None, - width=0, height=0, crop=False, quality=0, access_token=None, **kwargs): - if filename and filename.endswith(('jpeg', 'jpg')): - request.image_format = 'jpeg' - - return super(Http, self)._content_image(xmlid=xmlid, model=model, res_id=res_id, field=field, - filename_field=filename_field, unique=unique, filename=filename, - mimetype=mimetype, download=download, width=width, height=height, - crop=crop, quality=quality, access_token=access_token, **kwargs) + def _content_image( + self, + xmlid=None, + model="ir.attachment", + res_id=None, + field="datas", + filename_field="name", + unique=None, + filename=None, + mimetype=None, + download=None, + width=0, + height=0, + crop=False, + quality=0, + access_token=None, + **kwargs, + ): + if filename and filename.endswith(("jpeg", "jpg")): + request.image_format = "jpeg" + + return super()._content_image( + xmlid=xmlid, + model=model, + res_id=res_id, + field=field, + filename_field=filename_field, + unique=unique, + filename=filename, + mimetype=mimetype, + download=download, + width=width, + height=height, + crop=crop, + quality=quality, + access_token=access_token, + **kwargs, + ) @api.model - def _content_image_get_response(self, status, headers, image_base64, model='ir.attachment', - field='datas', download=None, width=0, height=0, crop=False, quality=0): - """ Center image in background with color, resize, compress and convert image to webp or jpeg """ + def _content_image_get_response( + self, + status, + headers, + image_base64, + model="ir.attachment", + field="datas", + download=None, + width=0, + height=0, + crop=False, + quality=0, + ): + """Handle image for content. + + Center image in background with color, resize, compress and convert + image to webp or jpeg. + """ if status == 200 and image_base64 and width and height: try: # Accepts jpeg and webp, defaults to webp if none found - if hasattr(request, 'image_format'): + if hasattr(request, "image_format"): image_format = request.image_format else: - image_format = 'webp' + image_format = "webp" width = int(width) height = int(height) - ICP = request.env['ir.config_parameter'].sudo() + ICP = request.env["ir.config_parameter"].sudo() image_base64 = image_process(image_base64, size=(width, height)) - img = Image.open(io.BytesIO(codecs.decode(image_base64, 'base64'))) - if img.mode != 'RGBA': - img = img.convert('RGBA') + img = Image.open(io.BytesIO(codecs.decode(image_base64, "base64"))) + if img.mode != "RGBA": + img = img.convert("RGBA") # Get background color from settings try: - background_rgba = safe_eval(ICP.get_param('vsf_image_background_rgba', '(255, 255, 255, 255)')) - except: + background_rgba = safe_eval( + ICP.get_param( + "vsf_image_background_rgba", "(255, 255, 255, 255)" + ) + ) + # FIXME: this should handle only specific exceptions, not + # all. + except Exception: background_rgba = (255, 255, 255, 255) # Create a new background, merge the background with the image centered img_w, img_h = img.size - if image_format == 'jpeg': - background = Image.new('RGB', (width, height), background_rgba[:3]) + if image_format == "jpeg": + background = Image.new("RGB", (width, height), background_rgba[:3]) else: - background = Image.new('RGBA', (width, height), background_rgba) + background = Image.new("RGBA", (width, height), background_rgba) bg_w, bg_h = background.size offset = ((bg_w - img_w) // 2, (bg_h - img_h) // 2) background.paste(img, offset) # Get compression quality from settings - quality = ICP.get_param('vsf_image_quality', 100) + quality = ICP.get_param("vsf_image_quality", 100) stream = io.BytesIO() - if image_format == 'jpeg': + if image_format == "jpeg": background.save(stream, format=image_format.upper(), subsampling=0) else: - background.save(stream, format=image_format.upper(), quality=quality, subsampling=0) + background.save( + stream, + format=image_format.upper(), + quality=quality, + subsampling=0, + ) image_base64 = base64.b64encode(stream.getvalue()) except Exception: @@ -80,9 +134,9 @@ def _content_image_get_response(self, status, headers, image_base64, model='ir.a # Replace Content-Type by generating a new list of headers new_headers = [] - for index, header in enumerate(headers): - if header[0] == 'Content-Type': - new_headers.append(('Content-Type', f'image/{image_format}')) + for header in headers: + if header[0] == "Content-Type": + new_headers.append(("Content-Type", f"image/{image_format}")) else: new_headers.append(header) @@ -94,6 +148,15 @@ def _content_image_get_response(self, status, headers, image_base64, model='ir.a return response # Fallback to super function - return super(Http, self)._content_image_get_response( - status, headers, image_base64, model=model, field=field, download=download, width=width, height=height, - crop=crop, quality=quality) + return super()._content_image_get_response( + status, + headers, + image_base64, + model=model, + field=field, + download=download, + width=width, + height=height, + crop=crop, + quality=quality, + ) diff --git a/vuestorefront/models/payment_transaction.py b/vuestorefront/models/payment_transaction.py index 97dfd6f..85e21fb 100644 --- a/vuestorefront/models/payment_transaction.py +++ b/vuestorefront/models/payment_transaction.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -from odoo import models, api, fields, tools, _ +from odoo import fields, models class PaymentTransactionInherit(models.Model): - _inherit = 'payment.transaction' + _inherit = "payment.transaction" - created_on_vsf = fields.Boolean(string='Created on Vsf?', default=False) + created_on_vsf = fields.Boolean(string="Created on Vsf?", default=False) diff --git a/vuestorefront/models/product.py b/vuestorefront/models/product.py index 526110a..a80523f 100644 --- a/vuestorefront/models/product.py +++ b/vuestorefront/models/product.py @@ -1,16 +1,16 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import json -import requests -from odoo import models, fields, api, tools, _ -from odoo.addons.http_routing.models.ir_http import slug, slugify + +from odoo import _, api, fields, models from odoo.exceptions import ValidationError +from odoo.addons.http_routing.models.ir_http import slug, slugify + class ProductTemplate(models.Model): - _inherit = 'product.template' + _inherit = "product.template" def _get_public_categ_slug(self, category_ids, category): category_ids.append(category.id) @@ -20,9 +20,9 @@ def _get_public_categ_slug(self, category_ids, category): return category_ids - @api.depends('public_categ_ids') + @api.depends("public_categ_ids") def _compute_public_categ_slug_ids(self): - """ To allow search of website_slug on parent categories """ + """Compute to allow search of website_slug on parent categories.""" cr = self.env.cr for product in self: @@ -31,20 +31,32 @@ def _compute_public_categ_slug_ids(self): for category in product.public_categ_ids: category_ids = product._get_public_categ_slug(category_ids, category) - cr.execute(""" + cr.execute( + """ DELETE FROM product_template_product_public_category_slug_rel WHERE product_template_id=%s; - """, (product.id,)) + """, + (product.id,), + ) for category_id in list(dict.fromkeys(category_ids)): - cr.execute(""" - INSERT INTO product_template_product_public_category_slug_rel(product_template_id, product_public_category_id) + cr.execute( + """ + INSERT INTO + product_template_product_public_category_slug_rel( + product_template_id, product_public_category_id + ) VALUES(%s, %s); - """, (product.id, category_id,)) - - @api.depends('name') + """, + ( + product.id, + category_id, + ), + ) + + @api.depends("name") def _compute_website_slug(self): - langs = self.env['res.lang'].search([]) + langs = self.env["res.lang"].search([]) for product in self: for lang in langs: @@ -53,69 +65,100 @@ def _compute_website_slug(self): if not product.id: product.website_slug = None else: - prefix = '/product' - slug_name = slugify(product.name or '').strip().strip('-') - product.website_slug = '{}/{}-{}'.format(prefix, slug_name, product.id) + prefix = "/product" + slug_name = slugify(product.name or "").strip().strip("-") + product.website_slug = f"{prefix}/{slug_name}-{product.id}" - @api.depends('product_variant_ids') + @api.depends("product_variant_ids") def _compute_variant_attribute_value_ids(self): - """ - Used to filter attribute values on the website. - This method computes a list of attribute values from variants of published products. - This will ensure that the available attribute values on the website filtering will return results. - By default, Odoo only shows attributes that will return results but doesn't consider that a particular + """Compute attribute values. + + This method computes a list of attribute values from variants of + published products. + This will ensure that the available attribute values on the website + filtering will return results. + By default, Odoo only shows attributes that will return results but + doesn't consider that a particular attribute value may not have a variant. """ for product in self: variants = product.product_variant_ids - attribute_values = variants.\ - mapped('product_template_attribute_value_ids').\ - mapped('product_attribute_value_id') + attribute_values = variants.mapped( + "product_template_attribute_value_ids" + ).mapped("product_attribute_value_id") product.variant_attribute_value_ids = [(6, 0, attribute_values.ids)] - variant_attribute_value_ids = fields.Many2many('product.attribute.value', - 'product_template_variant_product_attribute_value_rel', - compute='_compute_variant_attribute_value_ids', - store=True, readonly=True) - website_slug = fields.Char('Website Slug', compute='_compute_website_slug', store=True, readonly=True, - translate=True) - public_categ_slug_ids = fields.Many2many('product.public.category', - 'product_template_product_public_category_slug_rel', - compute='_compute_public_categ_slug_ids', - store=True, readonly=True) - json_ld = fields.Char('JSON-LD') + variant_attribute_value_ids = fields.Many2many( + "product.attribute.value", + "product_template_variant_product_attribute_value_rel", + compute="_compute_variant_attribute_value_ids", + store=True, + readonly=True, + ) + website_slug = fields.Char( + compute="_compute_website_slug", + store=True, + readonly=True, + translate=True, + ) + public_categ_slug_ids = fields.Many2many( + "product.public.category", + "product_template_product_public_category_slug_rel", + compute="_compute_public_categ_slug_ids", + store=True, + readonly=True, + ) + json_ld = fields.Char("JSON-LD") def write(self, vals): - res = super(ProductTemplate, self).write(vals) - self.env['invalidate.cache'].create_invalidate_cache(self._name, self.ids) + res = super().write(vals) + self.env["invalidate.cache"].create_invalidate_cache(self._name, self.ids) return res def unlink(self): - self.env['invalidate.cache'].create_invalidate_cache(self._name, self.ids) - return super(ProductTemplate, self).unlink() - - def _get_combination_info(self, combination=False, product_id=False, add_qty=1, pricelist=False, - parent_combination=False, only_template=False): - """ Add discount value and percentage based """ - combination_info = super(ProductTemplate, self)._get_combination_info( - combination=combination, product_id=product_id, add_qty=add_qty, pricelist=pricelist, - parent_combination=parent_combination, only_template=only_template) + self.env["invalidate.cache"].create_invalidate_cache(self._name, self.ids) + return super().unlink() + + def _get_combination_info( + self, + combination=False, + product_id=False, + add_qty=1, + pricelist=False, + parent_combination=False, + only_template=False, + ): + """Add discount value and percentage based.""" + combination_info = super()._get_combination_info( + combination=combination, + product_id=product_id, + add_qty=add_qty, + pricelist=pricelist, + parent_combination=parent_combination, + only_template=only_template, + ) discount = 0.00 discount_perc = 0 - if combination_info['has_discounted_price'] and product_id: - discount = combination_info['list_price'] - combination_info['price'] - discount_perc = combination_info['list_price'] and (discount * 100 / combination_info['list_price']) or 0 + if combination_info["has_discounted_price"] and product_id: + discount = combination_info["list_price"] - combination_info["price"] + discount_perc = ( + combination_info["list_price"] + and (discount * 100 / combination_info["list_price"]) + or 0 + ) if discount_perc: discount_perc = int(round(discount_perc, 0)) if not discount_perc: discount_perc = 1 - combination_info.update({ - 'discount': round(discount, 2), - 'discount_perc': discount_perc, - }) + combination_info.update( + { + "discount": round(discount, 2), + "discount_perc": discount_perc, + } + ) return combination_info @@ -125,15 +168,15 @@ def get_json_ld(self): return self.json_ld env = self.env - website = env['website'].get_current_website() - base_url = env['ir.config_parameter'].sudo().get_param('web.base.url', '') - if base_url and base_url[-1:] == '/': + website = env["website"].get_current_website() + base_url = env["ir.config_parameter"].sudo().get_param("web.base.url", "") + if base_url and base_url[-1:] == "/": base_url = base_url[:-1] # Get list of images images = list() if self.image_1920: - images.append('%s/web/image/product.product/%s/image' % (base_url, self.id)) + images.append("%s/web/image/product.product/%s/image" % (base_url, self.id)) json_ld = { "@context": "https://schema.org/", @@ -142,16 +185,18 @@ def get_json_ld(self): "image": images, "offers": { "@type": "Offer", - "url": "%s/product/%s" % (website.domain or '', slug(self)), + "url": "%s/product/%s" % (website.domain or "", slug(self)), "priceCurrency": self.currency_id.name, "price": self.list_price, "itemCondition": "https://schema.org/NewCondition", "availability": "https://schema.org/InStock", "seller": { "@type": "Organization", - "name": website and website.display_name or self.env.user.company_id.display_name - } - } + "name": website + and website.display_name + or self.env.user.company_id.display_name, + }, + }, } if self.description_sale: @@ -164,7 +209,7 @@ def get_json_ld(self): class ProductProduct(models.Model): - _inherit = 'product.product' + _inherit = "product.product" def get_json_ld(self): self.ensure_one() @@ -172,15 +217,15 @@ def get_json_ld(self): return self.json_ld env = self.env - website = env['website'].get_current_website() - base_url = env['ir.config_parameter'].sudo().get_param('web.base.url', '') - if base_url and base_url[-1:] == '/': + website = env["website"].get_current_website() + base_url = env["ir.config_parameter"].sudo().get_param("web.base.url", "") + if base_url and base_url[-1:] == "/": base_url = base_url[:-1] # Get list of images images = list() if self.image_1920: - images.append('%s/web/image/product.product/%s/image' % (base_url, self.id)) + images.append("%s/web/image/product.product/%s/image" % (base_url, self.id)) json_ld = { "@context": "https://schema.org/", @@ -189,16 +234,18 @@ def get_json_ld(self): "image": images, "offers": { "@type": "Offer", - "url": "%s/product/%s" % (website.domain or '', slug(self)), + "url": "%s/product/%s" % (website.domain or "", slug(self)), "priceCurrency": self.currency_id.name, "price": self.list_price, "itemCondition": "https://schema.org/NewCondition", "availability": "https://schema.org/InStock", "seller": { "@type": "Organization", - "name": website and website.display_name or self.env.user.company_id.display_name - } - } + "name": website + and website.display_name + or self.env.user.company_id.display_name, + }, + }, } if self.description_sale: @@ -211,55 +258,63 @@ def get_json_ld(self): class ProductPublicCategory(models.Model): - _inherit = 'product.public.category' + _inherit = "product.public.category" def _validate_website_slug(self): for category in self.filtered(lambda c: c.website_slug): - if category.website_slug[0] != '/': - raise ValidationError(_('Slug should start with /')) - - if self.search([('website_slug', '=', category.website_slug), ('id', '!=', category.id)], limit=1): - raise ValidationError(_('Slug is already in use: {}'.format(category.website_slug))) - - website_slug = fields.Char('Website Slug', translate=True, copy=False) - json_ld = fields.Char('JSON-LD') + if category.website_slug[0] != "/": + raise ValidationError(_("Slug should start with /")) + + if self.search( + [ + ("website_slug", "=", category.website_slug), + ("id", "!=", category.id), + ], + limit=1, + ): + raise ValidationError( + _(f"Slug is already in use: {category.website_slug}") + ) + + website_slug = fields.Char(translate=True, copy=False) + json_ld = fields.Char("JSON-LD") @api.model def create(self, vals): - rec = super(ProductPublicCategory, self).create(vals) + rec = super().create(vals) if rec.website_slug: rec._validate_website_slug() else: - rec.website_slug = '/category/{}'.format(rec.id) + rec.website_slug = f"/category/{rec.id}" return rec def write(self, vals): - res = super(ProductPublicCategory, self).write(vals) - if vals.get('website_slug', False): + res = super().write(vals) + if vals.get("website_slug", False): self._validate_website_slug() - self.env['invalidate.cache'].create_invalidate_cache(self._name, self.ids) + self.env["invalidate.cache"].create_invalidate_cache(self._name, self.ids) return res def unlink(self): - self.env['invalidate.cache'].create_invalidate_cache(self._name, self.ids) - return super(ProductPublicCategory, self).unlink() + self.env["invalidate.cache"].create_invalidate_cache(self._name, self.ids) + return super().unlink() def get_json_ld(self): self.ensure_one() if self.json_ld: return self.json_ld - website = self.env['website'].get_current_website() - base_url = website.domain or '' - if base_url and base_url[-1] == '/': + website = self.env["website"].get_current_website() + base_url = website.domain or "" + if base_url and base_url[-1] == "/": base_url = base_url[:-1] json_ld = { "@context": "https://schema.org", "@type": "CollectionPage", - "url": '{}{}'.format(base_url, self.website_slug or ''), + "url": "{}{}".format(base_url, self.website_slug or ""), "name": self.display_name, } diff --git a/vuestorefront/models/res_config_settings.py b/vuestorefront/models/res_config_settings.py index 8f0c2b2..ad02583 100644 --- a/vuestorefront/models/res_config_settings.py +++ b/vuestorefront/models/res_config_settings.py @@ -1,69 +1,83 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import uuid -from odoo import api, fields, models, _ + +from odoo import _, api, fields, models from odoo.exceptions import ValidationError -from odoo.tools.safe_eval import safe_eval class ResConfigSettings(models.TransientModel): - _inherit = 'res.config.settings' + _inherit = "res.config.settings" - vsf_debug_mode = fields.Boolean('Debug Mode') + vsf_debug_mode = fields.Boolean("Debug Mode") vsf_payment_success_return_url = fields.Char( - 'Payment Success Return Url', related='website_id.vsf_payment_success_return_url', readonly=False, - required=True + "Payment Success Return Url", + related="website_id.vsf_payment_success_return_url", + readonly=False, + required=True, ) vsf_payment_error_return_url = fields.Char( - 'Payment Error Return Url', related='website_id.vsf_payment_error_return_url', readonly=False, - required=True + "Payment Error Return Url", + related="website_id.vsf_payment_error_return_url", + readonly=False, + required=True, + ) + vsf_cache_invalidation = fields.Boolean("Cache Invalidation") + vsf_cache_invalidation_key = fields.Char("Cache Invalidation Key", required=True) + vsf_cache_invalidation_url = fields.Char("Cache Invalidation Url", required=True) + vsf_mailing_list_id = fields.Many2one( + "mailing.list", + "Newsletter", + domain=[("is_public", "=", True)], + related="website_id.vsf_mailing_list_id", + readonly=False, + required=True, ) - vsf_cache_invalidation = fields.Boolean('Cache Invalidation') - vsf_cache_invalidation_key = fields.Char('Cache Invalidation Key', required=True) - vsf_cache_invalidation_url = fields.Char('Cache Invalidation Url', required=True) - vsf_mailing_list_id = fields.Many2one('mailing.list', 'Newsletter', domain=[('is_public', '=', True)], - related='website_id.vsf_mailing_list_id', readonly=False, required=True) # VSF Images - vsf_image_quality = fields.Integer('Vue Storefront Image Quality', required=True) - vsf_image_background_rgba = fields.Char('Background RGBA', required=True) - vsf_image_resize_limit = fields.Integer('Resize Limit', required=True, - help='Limit in pixels to resize image for width and height') + vsf_image_quality = fields.Integer("Vue Storefront Image Quality", required=True) + vsf_image_background_rgba = fields.Char("Background RGBA", required=True) + vsf_image_resize_limit = fields.Integer( + "Resize Limit", + required=True, + help="Limit in pixels to resize image for width and height", + ) def get_values(self): - res = super(ResConfigSettings, self).get_values() - ICP = self.env['ir.config_parameter'].sudo() + res = super().get_values() + ICP = self.env["ir.config_parameter"].sudo() res.update( - vsf_debug_mode=ICP.get_param('vsf_debug_mode'), - vsf_cache_invalidation=ICP.get_param('vsf_cache_invalidation'), - vsf_cache_invalidation_key=ICP.get_param('vsf_cache_invalidation_key'), - vsf_cache_invalidation_url=ICP.get_param('vsf_cache_invalidation_url'), - vsf_image_quality=int(ICP.get_param('vsf_image_quality', 100)), - vsf_image_background_rgba=ICP.get_param('vsf_image_background_rgba', '(255, 255, 255, 255)'), - vsf_image_resize_limit=int(ICP.get_param('vsf_image_resize_limit', 1920)), + vsf_debug_mode=ICP.get_param("vsf_debug_mode"), + vsf_cache_invalidation=ICP.get_param("vsf_cache_invalidation"), + vsf_cache_invalidation_key=ICP.get_param("vsf_cache_invalidation_key"), + vsf_cache_invalidation_url=ICP.get_param("vsf_cache_invalidation_url"), + vsf_image_quality=int(ICP.get_param("vsf_image_quality", 100)), + vsf_image_background_rgba=ICP.get_param( + "vsf_image_background_rgba", "(255, 255, 255, 255)" + ), + vsf_image_resize_limit=int(ICP.get_param("vsf_image_resize_limit", 1920)), ) return res def set_values(self): if self.vsf_image_quality < 0 or self.vsf_image_quality > 100: - raise ValidationError(_('Invalid image quality percentage.')) + raise ValidationError(_("Invalid image quality percentage.")) if self.vsf_image_resize_limit < 0: - raise ValidationError(_('Invalid image resize limit.')) + raise ValidationError(_("Invalid image resize limit.")) - super(ResConfigSettings, self).set_values() - ICP = self.env['ir.config_parameter'].sudo() - ICP.set_param('vsf_debug_mode', self.vsf_debug_mode) - ICP.set_param('vsf_cache_invalidation', self.vsf_cache_invalidation) - ICP.set_param('vsf_cache_invalidation_key', self.vsf_cache_invalidation_key) - ICP.set_param('vsf_cache_invalidation_url', self.vsf_cache_invalidation_url) - ICP.set_param('vsf_image_quality', self.vsf_image_quality) - ICP.set_param('vsf_image_background_rgba', self.vsf_image_background_rgba) - ICP.set_param('vsf_image_resize_limit', self.vsf_image_resize_limit) + super().set_values() + ICP = self.env["ir.config_parameter"].sudo() + ICP.set_param("vsf_debug_mode", self.vsf_debug_mode) + ICP.set_param("vsf_cache_invalidation", self.vsf_cache_invalidation) + ICP.set_param("vsf_cache_invalidation_key", self.vsf_cache_invalidation_key) + ICP.set_param("vsf_cache_invalidation_url", self.vsf_cache_invalidation_url) + ICP.set_param("vsf_image_quality", self.vsf_image_quality) + ICP.set_param("vsf_image_background_rgba", self.vsf_image_background_rgba) + ICP.set_param("vsf_image_resize_limit", self.vsf_image_resize_limit) @api.model def create_vsf_cache_invalidation_key(self): - ICP = self.env['ir.config_parameter'].sudo() - ICP.set_param('vsf_cache_invalidation_key', str(uuid.uuid4())) + ICP = self.env["ir.config_parameter"].sudo() + ICP.set_param("vsf_cache_invalidation_key", str(uuid.uuid4())) diff --git a/vuestorefront/models/res_users.py b/vuestorefront/models/res_users.py index 9b82e77..a5b9861 100644 --- a/vuestorefront/models/res_users.py +++ b/vuestorefront/models/res_users.py @@ -1,58 +1,70 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import logging +from odoo import _, models +from odoo.exceptions import UserError from odoo.http import request -from odoo import api, models, _ + from odoo.addons.auth_signup.models.res_partner import now -from odoo.exceptions import UserError _logger = logging.getLogger(__name__) class ResUsers(models.Model): - _inherit = 'res.users' + _inherit = "res.users" def api_action_reset_password(self): - """ create signup token for each user, and send their signup url by email """ + """Create signup token for each user, and send their signup url by email.""" if self.filtered(lambda user: not user.active): raise UserError(_("You cannot perform this action on an archived user.")) # prepare reset password signup - create_mode = bool(self.env.context.get('create_user')) + create_mode = bool(self.env.context.get("create_user")) # no time limit for initial invitation, only for reset password expiration = False if create_mode else now(days=+1) - self.mapped('partner_id').signup_prepare(signup_type="reset", expiration=expiration) + self.mapped("partner_id").signup_prepare( + signup_type="reset", expiration=expiration + ) # send email to users with their signup url - template = self.env.ref('vuestorefront.website_reset_password_email') + template = self.env.ref("vuestorefront.website_reset_password_email") - assert template._name == 'mail.template' + assert template._name == "mail.template" - website = request.env['website'].get_current_website() - domain = website.domain or '' - if domain and domain[-1] == '/': + website = request.env["website"].get_current_website() + domain = website.domain or "" + if domain and domain[-1] == "/": domain = domain[:-1] email_values = { - 'email_cc': False, - 'auto_delete': True, - 'recipient_ids': [], - 'partner_ids': [], - 'scheduled_date': False, + "email_cc": False, + "auto_delete": True, + "recipient_ids": [], + "partner_ids": [], + "scheduled_date": False, } for user in self: token = user.signup_token signup_url = "%s/forgot-password/new-password?token=%s" % (domain, token) if not user.email: - raise UserError(_("Cannot send email: user %s has no email address.", user.name)) - email_values['email_to'] = user.email + raise UserError( + _("Cannot send email: user %s has no email address.", user.name) + ) + email_values["email_to"] = user.email with self.env.cr.savepoint(): force_send = not create_mode template.with_context(lang=user.lang, signup_url=signup_url).send_mail( - user.id, force_send=force_send, raise_exception=True, email_values=email_values) - _logger.info("Password reset email sent for user <%s> to <%s>", user.login, user.email) + user.id, + force_send=force_send, + raise_exception=True, + email_values=email_values, + ) + _logger.info( + "Password reset email sent for user <%s> to <%s>", + user.login, + user.email, + ) diff --git a/vuestorefront/models/website.py b/vuestorefront/models/website.py index c7d3b73..3295d59 100644 --- a/vuestorefront/models/website.py +++ b/vuestorefront/models/website.py @@ -1,45 +1,51 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). +import logging + import requests -from odoo import models, fields, api + +from odoo import api, fields, models + +_logger = logging.getLogger(__name__) class Website(models.Model): - _inherit = 'website' + _inherit = "website" vsf_payment_success_return_url = fields.Char( - 'Payment Success Return Url', required=True, translate=True, default='Dummy' + "Payment Success Return Url", required=True, translate=True, default="Dummy" ) vsf_payment_error_return_url = fields.Char( - 'Payment Error Return Url', required=True, translate=True, default='Dummy' + "Payment Error Return Url", required=True, translate=True, default="Dummy" + ) + vsf_mailing_list_id = fields.Many2one( + "mailing.list", "Newsletter", domain=[("is_public", "=", True)] ) - vsf_mailing_list_id = fields.Many2one('mailing.list', 'Newsletter', domain=[('is_public', '=', True)]) @api.model def enable_b2c_reset_password(self): - """ Enable sign up and reset password on default website """ - website = self.env.ref('website.default_website', raise_if_not_found=False) + """Enable sign up and reset password on default website.""" + website = self.env.ref("website.default_website", raise_if_not_found=False) if website: - website.auth_signup_uninvited = 'b2c' + website.auth_signup_uninvited = "b2c" - ICP = self.env['ir.config_parameter'].sudo() - ICP.set_param('auth_signup.invitation_scope', 'b2c') - ICP.set_param('auth_signup.reset_password', True) + ICP = self.env["ir.config_parameter"].sudo() + ICP.set_param("auth_signup.invitation_scope", "b2c") + ICP.set_param("auth_signup.reset_password", True) class WebsiteRewrite(models.Model): - _inherit = 'website.rewrite' + _inherit = "website.rewrite" def _get_vsf_tags(self): - tags = 'WR%s' % self.id + tags = "WR%s" % self.id return tags def _vsf_request_cache_invalidation(self): - ICP = self.env['ir.config_parameter'].sudo() - url = ICP.get_param('vsf_cache_invalidation_url', False) - key = ICP.get_param('vsf_cache_invalidation_key', False) + ICP = self.env["ir.config_parameter"].sudo() + url = ICP.get_param("vsf_cache_invalidation_url", False) + key = ICP.get_param("vsf_cache_invalidation_key", False) if url and key: try: @@ -47,42 +53,46 @@ def _vsf_request_cache_invalidation(self): tags = website_rewrite._get_vsf_tags() # Make the GET request to the /cache-invalidate - requests.get(url, params={'key': key, 'tags': tags}, timeout=5) - except: - pass + requests.get(url, params={"key": key, "tags": tags}, timeout=5) + except Exception: + _logger.error( + "Something went wrong processing cache invalidation", exc_info=True + ) def write(self, vals): - res = super(WebsiteRewrite, self).write(vals) + res = super().write(vals) self._vsf_request_cache_invalidation() return res def unlink(self): self._vsf_request_cache_invalidation() - return super(WebsiteRewrite, self).unlink() + return super().unlink() class WebsiteMenu(models.Model): - _inherit = 'website.menu' + _inherit = "website.menu" - is_footer = fields.Boolean('Is Footer', default=False) - menu_image_ids = fields.One2many('website.menu.image', 'menu_id', string='Menu Images') + is_footer = fields.Boolean(default=False) + menu_image_ids = fields.One2many( + "website.menu.image", "menu_id", string="Menu Images" + ) is_mega_menu = fields.Boolean(store=True) class WebsiteMenuImage(models.Model): - _name = 'website.menu.image' - _description = 'Website Menu Image' + _name = "website.menu.image" + _description = "Website Menu Image" def _default_sequence(self): menu = self.search([], limit=1, order="sequence DESC") return menu.sequence or 0 - menu_id = fields.Many2one('website.menu', 'Website Menu', required=True) + menu_id = fields.Many2one("website.menu", "Website Menu", required=True) sequence = fields.Integer(default=_default_sequence) - image = fields.Image(string='Image', required=True) - tag = fields.Char('Tag') - title = fields.Char('Title') - subtitle = fields.Char('Subtitle') - text_color = fields.Char('Text Color (Hex)', help='#111000') - button_text = fields.Char('Button Text') - button_url = fields.Char('Button URL') + image = fields.Image(required=True) + tag = fields.Char() + title = fields.Char() + subtitle = fields.Char() + text_color = fields.Char("Text Color (Hex)", help="#111000") + button_text = fields.Char() + button_url = fields.Char("Button URL") diff --git a/vuestorefront/schema.py b/vuestorefront/schema.py index 71685fb..fa0ad26 100644 --- a/vuestorefront/schema.py +++ b/vuestorefront/schema.py @@ -1,15 +1,25 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene from odoo.addons.graphql_base import OdooObjectType -from odoo.addons.vuestorefront.schemas import ( - country, category, product, order, - invoice, contact_us, user_profile, sign, - address, wishlist, shop, payment, - mailing_list, website, + +from .schemas import ( + address, + category, + contact_us, + country, + invoice, + mailing_list, + order, + payment, + product, + shop, + sign, + user_profile, + website, + wishlist, ) @@ -51,7 +61,16 @@ class Mutation( schema = graphene.Schema( query=Query, mutation=Mutation, - types=[country.CountryList, category.CategoryList, product.ProductList, product.ProductVariantData, order.OrderList, - invoice.InvoiceList, wishlist.WishlistData, shop.CartData, mailing_list.MailingContactList, - mailing_list.MailingListList] + types=[ + country.CountryList, + category.CategoryList, + product.ProductList, + product.ProductVariantData, + order.OrderList, + invoice.InvoiceList, + wishlist.WishlistData, + shop.CartData, + mailing_list.MailingContactList, + mailing_list.MailingListList, + ], ) diff --git a/vuestorefront/schemas/__init__.py b/vuestorefront/schemas/__init__.py index 0c2cbef..ea3806d 100644 --- a/vuestorefront/schemas/__init__.py +++ b/vuestorefront/schemas/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). diff --git a/vuestorefront/schemas/address.py b/vuestorefront/schemas/address.py index d6744be..1ded337 100644 --- a/vuestorefront/schemas/address.py +++ b/vuestorefront/schemas/address.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). @@ -7,39 +6,46 @@ from odoo import _ from odoo.http import request -from odoo.addons.vuestorefront.schemas.objects import Partner + +from .objects import Partner def get_partner(env, partner_id, order, website): if not order: - raise GraphQLError(_('Shopping cart not found.')) + raise GraphQLError(_("Shopping cart not found.")) - ResPartner = env['res.partner'].with_context(show_address=1).sudo() + ResPartner = env["res.partner"].with_context(show_address=1).sudo() partner = ResPartner.browse(partner_id) # Is public user - if not order.partner_id.user_ids or order.partner_id.id == website.user_id.sudo().partner_id.id: + if ( + not order.partner_id.user_ids + or order.partner_id.id == website.user_id.sudo().partner_id.id + ): partner_id = order.partner_id.id else: partner_id = env.user.partner_id.commercial_partner_id.id # Addresses that belong to this user - shippings = ResPartner.search([ - ("id", "child_of", partner_id), - '|', ("type", "in", ["delivery", "invoice"]), - ("id", "=", partner_id) - ]) + shippings = ResPartner.search( + [ + ("id", "child_of", partner_id), + "|", + ("type", "in", ["delivery", "invoice"]), + ("id", "=", partner_id), + ] + ) # Validate if the address exists and if the user has access to this address if not partner or not partner.exists() or partner.id not in shippings.ids: - raise GraphQLError(_('Address not found.')) + raise GraphQLError(_("Address not found.")) return partner class AddressEnum(graphene.Enum): - Billing = 'invoice' - Shipping = 'delivery' + Billing = "invoice" + Shipping = "delivery" class AddressFilterInput(graphene.InputObjectType): @@ -49,29 +55,34 @@ class AddressFilterInput(graphene.InputObjectType): class AddressQuery(graphene.ObjectType): addresses = graphene.List( graphene.NonNull(Partner), - filter=graphene.Argument(AddressFilterInput, default_value={}) + filter=graphene.Argument(AddressFilterInput, default_value={}), ) @staticmethod def resolve_addresses(self, info, filter): env = info.context["env"] - ResPartner = env['res.partner'].with_context(show_address=1).sudo() - website = env['website'].get_current_website() + ResPartner = env["res.partner"].with_context(show_address=1).sudo() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order() if not order: - raise GraphQLError(_('Shopping cart not found.')) + raise GraphQLError(_("Shopping cart not found.")) # Is public user - if not order.partner_id.user_ids or order.partner_id.id == website.user_id.sudo().partner_id.id: + if ( + not order.partner_id.user_ids + or order.partner_id.id == website.user_id.sudo().partner_id.id + ): partner_id = order.partner_id.id else: partner_id = env.user.partner_id.commercial_partner_id.id # Get all addresses of a specific addressType - delivery or/and shipping - if filter.get('address_type'): - types = [address_type.value for address_type in filter.get('address_type', [])] + if filter.get("address_type"): + types = [ + address_type.value for address_type in filter.get("address_type", []) + ] domain = [ ("id", "child_of", partner_id), @@ -81,11 +92,12 @@ def resolve_addresses(self, info, filter): else: domain = [ ("id", "child_of", partner_id), - '|', ("type", "in", ['delivery', 'invoice']), + "|", + ("type", "in", ["delivery", "invoice"]), ("id", "=", partner_id), ] - return ResPartner.search(domain, order='id desc') + return ResPartner.search(domain, order="id desc") class AddAddressInput(graphene.InputObjectType): @@ -131,24 +143,24 @@ class Arguments: @staticmethod def mutate(self, info, type, address): env = info.context["env"] - ResPartner = env['res.partner'].sudo().with_context(tracking_disable=True) - website = env['website'].get_current_website() + ResPartner = env["res.partner"].sudo().with_context(tracking_disable=True) + website = env["website"].get_current_website() request.website = website order = website.sale_get_order() if not order: - raise GraphQLError(_('Shopping cart not found.')) + raise GraphQLError(_("Shopping cart not found.")) values = { - 'name': address.get('name'), - 'street': address.get('street'), - 'street2': address.get('street2'), - 'phone': address.get('phone'), - 'zip': address.get('zip'), - 'city': address.get('city'), - 'state_id': address.get('state_id', False), - 'country_id': address.get('country_id', False), - 'email': address.get('email', False), + "name": address.get("name"), + "street": address.get("street"), + "street2": address.get("street2"), + "phone": address.get("phone"), + "zip": address.get("zip"), + "city": address.get("city"), + "state_id": address.get("state_id", False), + "country_id": address.get("country_id", False), + "email": address.get("email", False), } partner_id = order.partner_id.id @@ -156,20 +168,20 @@ def mutate(self, info, type, address): # Check public user if partner_id == website.user_id.sudo().partner_id.id: # Create main contact - values['type'] = 'contact' + values["type"] = "contact" partner_id = ResPartner.create(values).id order.partner_id = partner_id - values['type'] = type.value - values['parent_id'] = partner_id + values["type"] = type.value + values["parent_id"] = partner_id # Create the new shipping or invoice address partner = ResPartner.create(values) # Update order with the new shipping or invoice address - if values['type'] == 'invoice': + if values["type"] == "invoice": order.partner_invoice_id = partner.id - elif values['type'] == 'delivery': + elif values["type"] == "delivery": order.partner_shipping_id = partner.id # Trigger the change of fiscal position when the shipping address is modified @@ -187,35 +199,36 @@ class Arguments: @staticmethod def mutate(self, info, address): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order() - partner = get_partner(env, address['id'], order, website) + partner = get_partner(env, address["id"], order, website) values = {} - if address.get('name'): - values.update({'name': address['name']}) - if address.get('street'): - values.update({'street': address['street']}) - if address.get('street2'): - values.update({'street2': address['street2']}) - if address.get('phone'): - values.update({'phone': address['phone']}) - if address.get('zip'): - values.update({'zip': address['zip']}) - if address.get('city'): - values.update({'city': address['city']}) - if address.get('state_id'): - values.update({'state_id': address['state_id']}) - if address.get('country_id'): - values.update({'country_id': address['country_id']}) - - # Trigger the change of fiscal position when the shipping address is modified + if address.get("name"): + values.update({"name": address["name"]}) + if address.get("street"): + values.update({"street": address["street"]}) + if address.get("street2"): + values.update({"street2": address["street2"]}) + if address.get("phone"): + values.update({"phone": address["phone"]}) + if address.get("zip"): + values.update({"zip": address["zip"]}) + if address.get("city"): + values.update({"city": address["city"]}) + if address.get("state_id"): + values.update({"state_id": address["state_id"]}) + if address.get("country_id"): + values.update({"country_id": address["country_id"]}) + + # Trigger the change of fiscal position when the shipping address + # is modified order._compute_fiscal_position_id() - if address.get('email'): - values.update({'email': address['email']}) + if address.get("email"): + values.update({"email": address["email"]}) if values: partner.write(values) @@ -232,11 +245,11 @@ class Arguments: @staticmethod def mutate(self, info, address): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order() - partner = get_partner(env, address['id'], order, website) + partner = get_partner(env, address["id"], order, website) if not partner.parent_id: raise GraphQLError(_("You can't delete the primary address.")) @@ -247,7 +260,8 @@ def mutate(self, info, address): if order.partner_shipping_id.id == partner.id: order.partner_shipping_id = partner.parent_id.id - # Archive address, safer than delete since this address could be in use by other object + # Archive address, safer than delete since this address could be in use by + # other object partner.active = False # Trigger the change of fiscal position when the shipping address is modified @@ -266,16 +280,16 @@ class Arguments: @staticmethod def mutate(self, info, type, address): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order() - partner = get_partner(env, address['id'], order, website) + partner = get_partner(env, address["id"], order, website) # Update order with the new shipping or invoice address - if type.value == 'invoice': + if type.value == "invoice": order.partner_invoice_id = partner.id - elif type.value == 'delivery': + elif type.value == "delivery": order.partner_shipping_id = partner.id # Trigger the change of fiscal position when the shipping address is modified @@ -285,9 +299,18 @@ def mutate(self, info, type, address): class AddressMutation(graphene.ObjectType): - add_address = AddAddress.Field(description='Add new billing or shipping address and set it on the shopping cart.') - update_address = UpdateAddress.Field(description="Update a billing or shipping address and set it on the shopping " - "cart.") - delete_address = DeleteAddress.Field(description='Delete a billing or shipping address.') - select_address = SelectAddress.Field(description="Select a billing or shipping address to be used on the shopping " - "cart.") + add_address = AddAddress.Field( + description="Add new billing or shipping address and set it on the " + + "shopping cart." + ) + update_address = UpdateAddress.Field( + description="Update a billing or shipping address and " # nosec: B608 + + "set it on the shopping cart." + ) + delete_address = DeleteAddress.Field( + description="Delete a billing or shipping address." + ) + select_address = SelectAddress.Field( + description="Select a billing or shipping address to be used on the " + + "shopping cart." + ) diff --git a/vuestorefront/schemas/category.py b/vuestorefront/schemas/category.py index b5d49ff..f86506c 100644 --- a/vuestorefront/schemas/category.py +++ b/vuestorefront/schemas/category.py @@ -1,23 +1,20 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene -from odoo.addons.vuestorefront.schemas.objects import ( - SortEnum, Category -) +from .objects import Category, SortEnum def get_search_order(sort): - sorting = '' + sorting = "" for field, val in sort.items(): if sorting: - sorting += ', ' - sorting += '%s %s' % (field, val.value) + sorting += ", " + sorting += "%s %s" % (field, val.value) if not sorting: - sorting = 'sequence ASC, id ASC' + sorting = "sequence ASC, id ASC" return sorting @@ -53,21 +50,21 @@ class CategoryQuery(graphene.ObjectType): current_page=graphene.Int(default_value=1), page_size=graphene.Int(default_value=20), search=graphene.String(default_value=False), - sort=graphene.Argument(CategorySortInput, default_value={}) + sort=graphene.Argument(CategorySortInput, default_value={}), ) @staticmethod def resolve_category(self, info, id=None, slug=None): - env = info.context['env'] - Category = env['product.public.category'] + env = info.context["env"] + Category = env["product.public.category"] - domain = env['website'].get_current_website().website_domain() + domain = env["website"].get_current_website().website_domain() if id: - domain += [('id', '=', id)] + domain += [("id", "=", id)] category = Category.search(domain, limit=1) elif slug: - domain += [('website_slug', '=', slug)] + domain += [("website_slug", "=", slug)] category = Category.search(domain, limit=1) else: category = Category @@ -78,18 +75,18 @@ def resolve_category(self, info, id=None, slug=None): def resolve_categories(self, info, filter, current_page, page_size, search, sort): env = info.context["env"] order = get_search_order(sort) - domain = env['website'].get_current_website().website_domain() + domain = env["website"].get_current_website().website_domain() if search: for srch in search.split(" "): - domain += [('name', 'ilike', srch)] + domain += [("name", "ilike", srch)] - if filter.get('id'): - domain += [('id', 'in', filter['id'])] + if filter.get("id"): + domain += [("id", "in", filter["id"])] # Parent if is a Top Category - if filter.get('parent'): - domain += [('parent_id', '=', False)] + if filter.get("parent"): + domain += [("parent_id", "=", False)] # First offset is 0 but first page is 1 if current_page > 1: @@ -100,5 +97,6 @@ def resolve_categories(self, info, filter, current_page, page_size, search, sort ProductPublicCategory = env["product.public.category"] total_count = ProductPublicCategory.search_count(domain) categories = ProductPublicCategory.search( - domain, limit=page_size, offset=offset, order=order) + domain, limit=page_size, offset=offset, order=order + ) return CategoryList(categories=categories, total_count=total_count) diff --git a/vuestorefront/schemas/contact_us.py b/vuestorefront/schemas/contact_us.py index 226b32c..435cf53 100644 --- a/vuestorefront/schemas/contact_us.py +++ b/vuestorefront/schemas/contact_us.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene -from odoo.addons.vuestorefront.schemas.objects import Lead +from .objects import Lead class ContactUsParams(graphene.InputObjectType): @@ -24,23 +23,25 @@ class Arguments: @staticmethod def mutate(self, info, contactus): - env = info.context['env'] + env = info.context["env"] data = { - 'contact_name': contactus['name'], - 'email_from': contactus['email'], - 'phone': contactus['phone'], - 'name': contactus['subject'], - 'description': contactus['message'], + "contact_name": contactus["name"], + "email_from": contactus["email"], + "phone": contactus["phone"], + "name": contactus["subject"], + "description": contactus["message"], } # If Contact Us have one Company Name - if contactus.get('company'): - company = {'partner_name': contactus['company']} + if contactus.get("company"): + company = {"partner_name": contactus["company"]} data.update(company) - return env['crm.lead'].sudo().create(data) + return env["crm.lead"].sudo().create(data) class ContactUsMutation(graphene.ObjectType): - contact_us = ContactUs.Field(description='Creates a new lead with the contact information.') + contact_us = ContactUs.Field( + description="Creates a new lead with the contact information." + ) diff --git a/vuestorefront/schemas/country.py b/vuestorefront/schemas/country.py index f25ad88..4694ed9 100644 --- a/vuestorefront/schemas/country.py +++ b/vuestorefront/schemas/country.py @@ -1,26 +1,23 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene -from odoo.addons.vuestorefront.schemas.objects import ( - SortEnum, Country -) +from .objects import Country, SortEnum def get_search_order(sort): - sorting = '' + sorting = "" for field, val in sort.items(): if sorting: - sorting += ', ' - sorting += '%s %s' % (field, val.value) + sorting += ", " + sorting += "%s %s" % (field, val.value) # Add id as last factor, so we can consistently get the same results if sorting: - sorting += ', id ASC' + sorting += ", id ASC" else: - sorting = 'id ASC' + sorting = "id ASC" return sorting @@ -55,12 +52,12 @@ class CountryQuery(graphene.ObjectType): current_page=graphene.Int(default_value=1), page_size=graphene.Int(default_value=20), search=graphene.String(default_value=False), - sort=graphene.Argument(CountrySortInput, default_value={}) + sort=graphene.Argument(CountrySortInput, default_value={}), ) @staticmethod def resolve_country(self, info, id): - return info.context['env']['res.country'].search([('id', '=', id)], limit=1) + return info.context["env"]["res.country"].search([("id", "=", id)], limit=1) @staticmethod def resolve_countries(self, info, filter, current_page, page_size, search, sort): @@ -70,10 +67,10 @@ def resolve_countries(self, info, filter, current_page, page_size, search, sort) if search: for srch in search.split(" "): - domain += [('name', 'ilike', srch)] + domain += [("name", "ilike", srch)] - if filter.get('id'): - domain += [('id', '=', filter['id'])] + if filter.get("id"): + domain += [("id", "=", filter["id"])] # First offset is 0 but first page is 1 if current_page > 1: diff --git a/vuestorefront/schemas/invoice.py b/vuestorefront/schemas/invoice.py index 51425fd..38d155b 100644 --- a/vuestorefront/schemas/invoice.py +++ b/vuestorefront/schemas/invoice.py @@ -1,31 +1,32 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene from graphql import GraphQLError -from odoo.http import request + from odoo import _ +from odoo.http import request -from odoo.addons.vuestorefront.schemas.objects import ( - SortEnum, Invoice, +from .objects import ( + Invoice, + SortEnum, + get_document_count_with_check_access, get_document_with_check_access, - get_document_count_with_check_access ) def get_search_order(sort): - sorting = '' + sorting = "" for field, val in sort.items(): if sorting: - sorting += ', ' - sorting += '%s %s' % (field, val.value) + sorting += ", " + sorting += "%s %s" % (field, val.value) # Add id as last factor, so we can consistently get the same results if sorting: - sorting += ', id ASC' + sorting += ", id ASC" else: - sorting = 'id ASC' + sorting = "id ASC" return sorting @@ -57,14 +58,16 @@ class InvoiceQuery(graphene.ObjectType): Invoices, current_page=graphene.Int(default_value=1), page_size=graphene.Int(default_value=10), - sort=graphene.Argument(InvoiceSortInput, default_value={}) + sort=graphene.Argument(InvoiceSortInput, default_value={}), ) @staticmethod def resolve_invoice(self, info, id): - AccountMove = info.context["env"]['account.move'] - error_msg = 'Invoice does not exist.' - invoice = get_document_with_check_access(AccountMove, [('id', '=', id)], error_msg=error_msg) + AccountMove = info.context["env"]["account.move"] + error_msg = "Invoice does not exist." + invoice = get_document_with_check_access( + AccountMove, [("id", "=", id)], error_msg=error_msg + ) if not invoice: raise GraphQLError(_(error_msg)) return invoice.sudo() @@ -76,7 +79,7 @@ def resolve_invoices(self, info, current_page, page_size, sort): partner = user.partner_id sort_order = get_search_order(sort) domain = [ - ('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]) + ("message_partner_ids", "child_of", [partner.commercial_partner_id.id]) ] # First offset is 0 but first page is 1 @@ -86,7 +89,15 @@ def resolve_invoices(self, info, current_page, page_size, sort): offset = 0 AccountMove = env["account.move"] - invoices = get_document_with_check_access(AccountMove, domain, sort_order, page_size, offset, - error_msg='Invoice does not exist.') + invoices = get_document_with_check_access( + AccountMove, + domain, + sort_order, + page_size, + offset, + error_msg="Invoice does not exist.", + ) total_count = get_document_count_with_check_access(AccountMove, domain) - return InvoiceList(invoices=invoices and invoices.sudo() or invoices, total_count=total_count) + return InvoiceList( + invoices=invoices and invoices.sudo() or invoices, total_count=total_count + ) diff --git a/vuestorefront/schemas/mailing_list.py b/vuestorefront/schemas/mailing_list.py index 6e0289e..3f29471 100644 --- a/vuestorefront/schemas/mailing_list.py +++ b/vuestorefront/schemas/mailing_list.py @@ -1,29 +1,33 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene from graphql import GraphQLError -from odoo.http import request + from odoo import _ +from odoo.http import request + from odoo.addons.website_mass_mailing.controllers.main import MassMailController -from odoo.addons.vuestorefront.schemas.objects import ( - SortEnum, MailingContact, MailingList -) + +from .objects import MailingContact, MailingList, SortEnum + + +def predicate_maillist_id(maillist_id): + return lambda mail: mail.list_id.id == maillist_id def get_search_order(sort): - sorting = '' + sorting = "" for field, val in sort.items(): if sorting: - sorting += ', ' - sorting += '%s %s' % (field, val.value) + sorting += ", " + sorting += "%s %s" % (field, val.value) # Add id as last factor, so we can consistently get the same results if sorting: - sorting += ', id ASC' + sorting += ", id ASC" else: - sorting = 'id ASC' + sorting = "id ASC" return sorting @@ -32,6 +36,7 @@ def get_search_order(sort): # Mailing Contacts # # --------------------- # + class MailingContactFilterInput(graphene.InputObjectType): id = graphene.Int() @@ -57,22 +62,24 @@ class MailingContactQuery(graphene.ObjectType): current_page=graphene.Int(default_value=1), page_size=graphene.Int(default_value=20), search=graphene.String(default_value=False), - sort=graphene.Argument(MailingContactSortInput, default_value={}) + sort=graphene.Argument(MailingContactSortInput, default_value={}), ) @staticmethod - def resolve_mailing_contacts(self, info, filter, current_page, page_size, search, sort): - env = info.context['env'] + def resolve_mailing_contacts( + self, info, filter, current_page, page_size, search, sort + ): + env = info.context["env"] order = get_search_order(sort) - domain = [('email', '=', env.user.email)] + domain = [("email", "=", env.user.email)] if search: for srch in search.split(" "): - domain += [('name', 'ilike', srch)] + domain += [("name", "ilike", srch)] - if filter.get('id', False): - domain += [('id', '=', filter['id'])] + if filter.get("id", False): + domain += [("id", "=", filter["id"])] # First offset is 0 but first page is 1 if current_page > 1: @@ -80,16 +87,21 @@ def resolve_mailing_contacts(self, info, filter, current_page, page_size, search else: offset = 0 - MailingContact = env['mailing.contact'].sudo() + MailingContact = env["mailing.contact"].sudo() total_count = MailingContact.search_count(domain) - mailing_contacts = MailingContact.search(domain, limit=page_size, offset=offset, order=order) - return MailingContactList(mailing_contacts=mailing_contacts, total_count=total_count) + mailing_contacts = MailingContact.search( + domain, limit=page_size, offset=offset, order=order + ) + return MailingContactList( + mailing_contacts=mailing_contacts, total_count=total_count + ) # --------------------- # # Mailing List # # --------------------- # + class MailingListFilterInput(graphene.InputObjectType): id = graphene.Int() @@ -120,25 +132,31 @@ class MailingListQuery(graphene.ObjectType): current_page=graphene.Int(default_value=1), page_size=graphene.Int(default_value=20), search=graphene.String(default_value=False), - sort=graphene.Argument(MailingListSortInput, default_value={}) + sort=graphene.Argument(MailingListSortInput, default_value={}), ) @staticmethod def resolve_mailing_list(self, info, id): - return info.context['env']['mailing.list'].sudo().search([('id', '=', id), ('is_public', '=', True)], limit=1) + return ( + info.context["env"]["mailing.list"] + .sudo() + .search([("id", "=", id), ("is_public", "=", True)], limit=1) + ) @staticmethod - def resolve_mailing_lists(self, info, filter, current_page, page_size, search, sort): + def resolve_mailing_lists( + self, info, filter, current_page, page_size, search, sort + ): env = info.context["env"] order = get_search_order(sort) - domain = [('is_public', '=', True)] + domain = [("is_public", "=", True)] if search: for srch in search.split(" "): - domain += [('name', 'ilike', srch)] + domain += [("name", "ilike", srch)] - if filter.get('id', False): - domain += [('id', '=', filter['id'])] + if filter.get("id", False): + domain += [("id", "=", filter["id"])] # First offset is 0 but first page is 1 if current_page > 1: @@ -148,7 +166,9 @@ def resolve_mailing_lists(self, info, filter, current_page, page_size, search, s MailingList = env["mailing.list"].sudo() total_count = MailingList.search_count(domain) - mailing_lists = MailingList.search(domain, limit=page_size, offset=offset, order=order) + mailing_lists = MailingList.search( + domain, limit=page_size, offset=offset, order=order + ) return MailingListList(mailing_lists=mailing_lists, total_count=total_count) @@ -160,11 +180,13 @@ class Arguments: @staticmethod def mutate(self, info, email): - env = info.context['env'] - website = env['website'].get_current_website() + env = info.context["env"] + website = env["website"].get_current_website() if website.vsf_mailing_list_id: - MassMailController().subscribe(website.vsf_mailing_list_id.id, email, 'email') + MassMailController().subscribe( + website.vsf_mailing_list_id.id, email, "email" + ) return NewsletterSubscribe(subscribed=True) return NewsletterSubscribe(subscribed=False) @@ -183,7 +205,7 @@ class Arguments: @staticmethod def mutate(self, info, mailings): - env = info.context['env'] + env = info.context["env"] user = request.env.user # Company name @@ -200,43 +222,69 @@ def mutate(self, info, mailings): else: country_id = False - mailing_contact = env['mailing.contact'].sudo().search([('email', '=', user.email)], limit=1) + mailing_contact = ( + env["mailing.contact"].sudo().search([("email", "=", user.email)], limit=1) + ) for mailing in mailings: - maillist_id = mailing['mailinglistId'] - optout = mailing['optout'] + maillist_id = mailing["mailinglistId"] + optout = mailing["optout"] - mailing_list = env['mailing.list'].sudo().search([('id', '=', maillist_id)], limit=1) + mailing_list = ( + env["mailing.list"].sudo().search([("id", "=", maillist_id)], limit=1) + ) if not mailing_list: - raise GraphQLError(_('Maillist does not exist.')) + raise GraphQLError(_("Maillist does not exist.")) if mailing_contact: - line = mailing_contact.subscription_list_ids.filtered(lambda mail: mail.list_id.id == maillist_id) + line = mailing_contact.subscription_list_ids.filtered( + predicate_maillist_id(maillist_id) + ) - if not mailing_contact.company_name or (company_name and mailing_contact.company_name != company_name): - mailing_contact.update({'company_name': company_name}) + if not mailing_contact.company_name or ( + company_name and mailing_contact.company_name != company_name + ): + mailing_contact.update({"company_name": company_name}) - if not mailing_contact.country_id or (country_id and mailing_contact.country_id != country_id): - mailing_contact.update({'country_id': country_id}) + if not mailing_contact.country_id or ( + country_id and mailing_contact.country_id != country_id + ): + mailing_contact.update({"country_id": country_id}) if line: - line.update({'opt_out': optout}) + line.update({"opt_out": optout}) else: mailing_contact.write( - {'subscription_list_ids': [(0, 0, {'list_id': mailing_list.id, 'opt_out': optout})], }) + { + "subscription_list_ids": [ + (0, 0, {"list_id": mailing_list.id, "opt_out": optout}) + ], + } + ) else: - mailing_contact = env['mailing.contact'].sudo().create({ - 'name': user.name, - 'country_id': country_id, - 'email': user.email, - 'company_name': company_name, - 'subscription_list_ids': [(0, 0, {'list_id': mailing_list.id, 'opt_out': optout})], - }) + mailing_contact = ( + env["mailing.contact"] + .sudo() + .create( + { + "name": user.name, + "country_id": country_id, + "email": user.email, + "company_name": company_name, + "subscription_list_ids": [ + (0, 0, {"list_id": mailing_list.id, "opt_out": optout}) + ], + } + ) + ) return mailing_contact class NewsletterSubscribeMutation(graphene.ObjectType): - newsletter_subscribe = NewsletterSubscribe.Field(description='Subscribe to newsletter.') + newsletter_subscribe = NewsletterSubscribe.Field( + description="Subscribe to newsletter." + ) user_add_multiple_mailing = UserAddMultipleMailing.Field( - description='Create or Update Multiple Mailing Contact information') + description="Create or Update Multiple Mailing Contact information" + ) diff --git a/vuestorefront/schemas/objects.py b/vuestorefront/schemas/objects.py index 97a515a..42eb7a8 100644 --- a/vuestorefront/schemas/objects.py +++ b/vuestorefront/schemas/objects.py @@ -1,65 +1,114 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene from graphene.types import generic from graphql import GraphQLError -from odoo import SUPERUSER_ID, _ -from odoo.addons.http_routing.models.ir_http import slugify -from odoo.addons.graphql_base import OdooObjectType +from odoo import SUPERUSER_ID, _ from odoo.exceptions import AccessError from odoo.http import request +from odoo.addons.graphql_base import OdooObjectType +from odoo.addons.http_routing.models.ir_http import slugify # --------------------- # # ENUMS # # --------------------- # -AddressType = graphene.Enum('AddressType', [('Contact', 'contact'), ('InvoiceAddress', 'invoice'), - ('DeliveryAddress', 'delivery'), ('OtherAddress', 'other'), - ('PrivateAddress', 'private')]) - -VariantCreateMode = graphene.Enum('VariantCreateMode', [('Instantly', 'always'), ('Dynamically', 'dynamically'), - ('NeverOption', 'no_variant')]) - -FilterVisibility = graphene.Enum('FilterVisibility', [('Visible', 'visible'), ('Hidden', 'hidden')]) - -OrderStage = graphene.Enum('OrderStage', [('Quotation', 'draft'), ('QuotationSent', 'sent'), - ('SalesOrder', 'sale'), ('Locked', 'done'), ('Cancelled', 'cancel')]) - -InvoiceStatus = graphene.Enum('InvoiceStatus', [('UpsellingOpportunity', 'upselling'), ('FullyInvoiced', 'invoiced'), - ('ToInvoice', 'to invoice'), ('NothingtoInvoice', 'no')]) - -InvoiceState = graphene.Enum('InvoiceState', [('Draft', 'draft'), ('Posted', 'posted'), ('Cancelled', 'cancel')]) - -PaymentTransactionState = graphene.Enum('PaymentTransactionState', [('Draft', 'draft'), ('Pending', 'pending'), - ('Authorized', 'authorized'), ('Confirmed', 'done'), - ('Canceled', 'cancel'), ('Error', 'error')]) +AddressType = graphene.Enum( + "AddressType", + [ + ("Contact", "contact"), + ("InvoiceAddress", "invoice"), + ("DeliveryAddress", "delivery"), + ("OtherAddress", "other"), + ("PrivateAddress", "private"), + ], +) + +VariantCreateMode = graphene.Enum( + "VariantCreateMode", + [ + ("Instantly", "always"), + ("Dynamically", "dynamically"), + ("NeverOption", "no_variant"), + ], +) + +FilterVisibility = graphene.Enum( + "FilterVisibility", [("Visible", "visible"), ("Hidden", "hidden")] +) + +OrderStage = graphene.Enum( + "OrderStage", + [ + ("Quotation", "draft"), + ("QuotationSent", "sent"), + ("SalesOrder", "sale"), + ("Locked", "done"), + ("Cancelled", "cancel"), + ], +) + +InvoiceStatus = graphene.Enum( + "InvoiceStatus", + [ + ("UpsellingOpportunity", "upselling"), + ("FullyInvoiced", "invoiced"), + ("ToInvoice", "to invoice"), + ("NothingtoInvoice", "no"), + ], +) + +InvoiceState = graphene.Enum( + "InvoiceState", [("Draft", "draft"), ("Posted", "posted"), ("Cancelled", "cancel")] +) + +PaymentTransactionState = graphene.Enum( + "PaymentTransactionState", + [ + ("Draft", "draft"), + ("Pending", "pending"), + ("Authorized", "authorized"), + ("Confirmed", "done"), + ("Canceled", "cancel"), + ("Error", "error"), + ], +) class SortEnum(graphene.Enum): - ASC = 'ASC' - DESC = 'DESC' + ASC = "ASC" + DESC = "DESC" # --------------------- # # Functions # # --------------------- # -def get_document_with_check_access(model, domain=[], order=None, limit=20, offset=0, access_token=None, - error_msg='This document does not exist.'): + +def get_document_with_check_access( + model, + domain=None, + order=None, + limit=20, + offset=0, + access_token=None, + error_msg="This document does not exist.", +): + if domain is None: + domain = [] if access_token: model = model.sudo() - domain = [('access_token', '=', access_token)] + domain = [("access_token", "=", access_token)] document = model.search(domain, order=order, limit=limit, offset=offset) document_sudo = document.with_user(SUPERUSER_ID).exists() if document and not document_sudo: raise GraphQLError(_(error_msg)) try: - document.check_access_rights('read') - document.check_access_rule('read') + document.check_access_rights("read") + document.check_access_rule("read") except AccessError: return [] return document_sudo @@ -67,21 +116,23 @@ def get_document_with_check_access(model, domain=[], order=None, limit=20, offse def get_document_count_with_check_access(model, domain): try: - model.check_access_rights('read') - model.check_access_rule('read') + model.check_access_rights("read") + model.check_access_rule("read") except AccessError: return 0 return model.search_count(domain) def get_product_pricing_info(env, product): - website = env['website'].get_current_website() + website = env["website"].get_current_website() pricelist = website.get_current_pricelist() - return product and product._get_combination_info_variant(pricelist=pricelist) or None + return ( + product and product._get_combination_info_variant(pricelist=pricelist) or None + ) def product_is_in_wishlist(env, product): - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website return product._is_in_wishlist() @@ -90,6 +141,7 @@ def product_is_in_wishlist(env, product): # Objects # # --------------------- # + class Lead(OdooObjectType): id = graphene.Int(required=True) name = graphene.String() @@ -160,7 +212,7 @@ def resolve_state(self, info): return self.state_id or None def resolve_image(self, info): - return '/web/image/res.company/{}/image_1920'.format(self.id) + return f"/web/image/res.company/{self.id}/image_1920" class Pricelist(OdooObjectType): @@ -207,7 +259,9 @@ def resolve_address_type(self, info): return self.type or None def resolve_billing_address(self, info): - billing_address = self.child_ids.filtered(lambda a: a.type and a.type == 'invoice') + billing_address = self.child_ids.filtered( + lambda a: a.type and a.type == "invoice" + ) return billing_address and billing_address[0] or None def resolve_company(self, info): @@ -220,15 +274,18 @@ def resolve_parent_id(self, info): return self.parent_id or None def resolve_image(self, info): - return '/web/image/res.partner/{}/image_1920'.format(self.id) + return f"/web/image/res.partner/{self.id}/image_1920" def resolve_public_pricelist(self, info): - website = self.env['website'].get_current_website() + website = self.env["website"].get_current_website() partner = website.user_id.sudo().partner_id - return partner.last_website_so_id.pricelist_id or partner.property_product_pricelist + return ( + partner.last_website_so_id.pricelist_id + or partner.property_product_pricelist + ) def resolve_current_pricelist(self, info): - website = self.env['website'].get_current_website() + website = self.env["website"].get_current_website() return website.get_current_pricelist() @@ -282,7 +339,9 @@ class AttributeValue(OdooObjectType): display_type = graphene.String() html_color = graphene.String() search = graphene.String() - price_extra = graphene.Float(description='Not use in the return Attributes List of the Products Query') + price_extra = graphene.Float( + description="Not use in the return Attributes List of the Products Query" + ) attribute = graphene.Field(lambda: Attribute) def resolve_id(self, info): @@ -291,7 +350,7 @@ def resolve_id(self, info): def resolve_search(self, info): attribute_id = self.attribute_id.id attribute_value_id = self.id - return '{}-{}'.format(attribute_id, attribute_value_id) or None + return f"{attribute_id}-{attribute_value_id}" or None def resolve_attribute(self, info): return self.attribute_id or None @@ -326,7 +385,7 @@ def resolve_id(self, info): return self.id or None def resolve_image(self, info): - return '/web/image/product.image/{}/image_1920'.format(self.id) + return f"/web/image/product.image/{self.id}/image_1920" def resolve_image_filename(self, info): return slugify(self.name) @@ -376,28 +435,44 @@ class Product(OdooObjectType): alternative_products = graphene.List(graphene.NonNull(lambda: Product)) accessory_products = graphene.List(graphene.NonNull(lambda: Product)) # Specific to use in Product Variant - combination_info_variant = generic.GenericScalar(description='Specific to Product Variant') - variant_price = graphene.Float(description='Specific to Product Variant') - variant_price_after_discount = graphene.Float(description='Specific to Product Variant') - variant_has_discounted_price = graphene.Boolean(description='Specific to Product Variant') - is_variant_possible = graphene.Boolean(description='Specific to Product Variant') - variant_attribute_values = graphene.List(graphene.NonNull(lambda: AttributeValue), - description='Specific to Product Variant') - product_template = graphene.Field((lambda: Product), description='Specific to Product Variant') + combination_info_variant = generic.GenericScalar( + description="Specific to Product Variant" + ) + variant_price = graphene.Float(description="Specific to Product Variant") + variant_price_after_discount = graphene.Float( + description="Specific to Product Variant" + ) + variant_has_discounted_price = graphene.Boolean( + description="Specific to Product Variant" + ) + is_variant_possible = graphene.Boolean(description="Specific to Product Variant") + variant_attribute_values = graphene.List( + graphene.NonNull(lambda: AttributeValue), + description="Specific to Product Variant", + ) + product_template = graphene.Field( + (lambda: Product), description="Specific to Product Variant" + ) # Specific to use in Product Template - combination_info = generic.GenericScalar(description='Specific to Product Template') - price = graphene.Float(description='Specific to Product Template') - attribute_values = graphene.List(graphene.NonNull(lambda: AttributeValue), - description='Specific to Product Template') - product_variants = graphene.List(graphene.NonNull(lambda: Product), description='Specific to Product Template') - first_variant = graphene.Field((lambda: Product), description='Specific to use in Product Template') + combination_info = generic.GenericScalar(description="Specific to Product Template") + price = graphene.Float(description="Specific to Product Template") + attribute_values = graphene.List( + graphene.NonNull(lambda: AttributeValue), + description="Specific to Product Template", + ) + product_variants = graphene.List( + graphene.NonNull(lambda: Product), description="Specific to Product Template" + ) + first_variant = graphene.Field( + (lambda: Product), description="Specific to use in Product Template" + ) json_ld = generic.GenericScalar() def resolve_type_id(self, info): - if self.detailed_type == 'product': - return 'simple' + if self.detailed_type == "product": + return "simple" else: - return 'configurable' + return "configurable" def resolve_visibility(self, info): if self.website_published: @@ -430,22 +505,28 @@ def resolve_meta_description(self, info): return self.website_meta_description or None def resolve_image(self, info): - return '/web/image/{}/{}/image_1920'.format(self._name, self.id) + return f"/web/image/{self._name}/{self.id}/image_1920" def resolve_small_image(self, info): - return '/web/image/{}/{}/image_128'.format(self._name, self.id) + return f"/web/image/{self._name}/{self.id}/image_128" def resolve_image_filename(self, info): return slugify(self.name) def resolve_thumbnail(self, info): - return '/web/image/{}/{}/image_512'.format(self._name, self.id) + return f"/web/image/{self._name}/{self.id}/image_512" def resolve_categories(self, info): - website = self.env['website'].get_current_website() + website = self.env["website"].get_current_website() if website: - return self.public_categ_ids.filtered( - lambda c: not c.website_id or c.website_id and c.website_id.id == website.id) or None + return ( + self.public_categ_ids.filtered( + lambda c: not c.website_id + or c.website_id + and c.website_id.id == website.id + ) + or None + ) return self.public_categ_ids or None def resolve_allow_out_of_stock(self, info): @@ -466,10 +547,12 @@ def resolve_is_in_wishlist(self, info): return bool(is_in_wishlist) def resolve_media_gallery(self, info): - if self._name == 'product.template': + if self._name == "product.template": return self.product_template_image_ids or None else: - return self.product_template_image_ids + self.product_variant_image_ids or None + return ( + self.product_template_image_ids + self.product_variant_image_ids or None + ) def resolve_qty(self, info): return self.free_qty @@ -492,17 +575,17 @@ def resolve_combination_info_variant(self, info): def resolve_variant_price(self, info): env = info.context["env"] pricing_info = get_product_pricing_info(env, self) - return pricing_info['list_price'] or None + return pricing_info["list_price"] or None def resolve_variant_price_after_discount(self, info): env = info.context["env"] pricing_info = get_product_pricing_info(env, self) - return pricing_info['price'] or None + return pricing_info["price"] or None def resolve_variant_has_discounted_price(self, info): env = info.context["env"] pricing_info = get_product_pricing_info(env, self) - return pricing_info['has_discounted_price'] + return pricing_info["has_discounted_price"] def resolve_is_variant_possible(self, info): return self._is_variant_possible() @@ -591,13 +674,21 @@ def resolve_quantity(self, info): def resolve_gift_card(self, info): gift_card = None - if self.coupon_id and self.coupon_id.program_type and self.coupon_id.program_type == 'gift_card': + if ( + self.coupon_id + and self.coupon_id.program_type + and self.coupon_id.program_type == "gift_card" + ): gift_card = self.coupon_id return gift_card def resolve_coupon(self, info): coupon = None - if self.coupon_id and self.coupon_id.program_type and self.coupon_id.program_type == 'coupons': + if ( + self.coupon_id + and self.coupon_id.program_type + and self.coupon_id.program_type == "coupons" + ): coupon = self.coupon_id return coupon @@ -619,10 +710,12 @@ class ShippingMethod(OdooObjectType): product = graphene.Field(lambda: Product) def resolve_price(self, info): - website = self.env['website'].get_current_website() + website = self.env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=True) - return self.rate_shipment(order)['price'] if self.free_over else self.fixed_price + return ( + self.rate_shipment(order)["price"] if self.free_over else self.fixed_price + ) def resolve_product(self, info): return self.product_id or None @@ -697,26 +790,38 @@ def resolve_transactions(self, info): def resolve_last_transaction(self, info): if self.transaction_ids: - return self.transaction_ids.sorted(key=lambda r: r.create_date, reverse=True)[0] + return self.transaction_ids.sorted( + key=lambda r: r.create_date, reverse=True + )[0] return None def resolve_amount_subtotal(self, info): - subtotal_lines = self.order_line.filtered(lambda l: not l.is_reward_line) - return sum(subtotal_lines.mapped('price_total')) - self.amount_delivery + subtotal_lines = self.order_line.filtered(lambda r: not r.is_reward_line) + return sum(subtotal_lines.mapped("price_total")) - self.amount_delivery def resolve_amount_discounts(self, info): return self.reward_amount def resolve_amount_gift_cards(self, info): - return sum(self.order_line.filtered( - lambda l: l.coupon_id and l.coupon_id.program_type and - l.coupon_id.program_type == 'gift_card').mapped('price_total')) + return sum( + self.order_line.filtered( + lambda r: r.coupon_id + and r.coupon_id.program_type + and r.coupon_id.program_type == "gift_card" + ).mapped("price_total") + ) def resolve_coupons(self, info): - return self.applied_coupon_ids.filtered(lambda c: c.program_type == 'coupons') or None + return ( + self.applied_coupon_ids.filtered(lambda c: c.program_type == "coupons") + or None + ) def resolve_gift_cards(self, info): - return self.applied_coupon_ids.filtered(lambda c: c.program_type == 'gift_card') or None + return ( + self.applied_coupon_ids.filtered(lambda c: c.program_type == "gift_card") + or None + ) def resolve_cart_quantity(self, info): return self.cart_quantity or None @@ -802,7 +907,7 @@ class PaymentIcon(OdooObjectType): image = graphene.String() def resolve_image(self, info): - return '/web/image/payment.icon/{}/image'.format(self.id) + return f"/web/image/payment.icon/{self.id}/image" class PaymentProvider(OdooObjectType): @@ -835,7 +940,9 @@ class MailingContact(OdooObjectType): name = graphene.String() email = graphene.String() company_name = graphene.String() - subscription_list = graphene.List(graphene.NonNull(lambda: MailingContactSubscription)) + subscription_list = graphene.List( + graphene.NonNull(lambda: MailingContactSubscription) + ) def resolve_country(self, info): return self.country_id or None @@ -893,4 +1000,4 @@ class WebsiteMenuImage(OdooObjectType): button_url = graphene.String() def resolve_image(self, info): - return '/web/image/website.menu.image/{}/image'.format(self.id) + return f"/web/image/website.menu.image/{self.id}/image" diff --git a/vuestorefront/schemas/order.py b/vuestorefront/schemas/order.py index a54217a..95c4a42 100644 --- a/vuestorefront/schemas/order.py +++ b/vuestorefront/schemas/order.py @@ -1,31 +1,35 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene from graphql import GraphQLError -from odoo.http import request + from odoo import _ +from odoo.http import request -from odoo.addons.vuestorefront.schemas.objects import ( - SortEnum, OrderStage, InvoiceStatus, Order, ShippingMethod, +from .objects import ( + InvoiceStatus, + Order, + OrderStage, + ShippingMethod, + SortEnum, + get_document_count_with_check_access, get_document_with_check_access, - get_document_count_with_check_access ) def get_search_order(sort): - sorting = '' + sorting = "" for field, val in sort.items(): if sorting: - sorting += ', ' - sorting += '%s %s' % (field, val.value) + sorting += ", " + sorting += "%s %s" % (field, val.value) # Add id as last factor, so we can consistently get the same results if sorting: - sorting += ', id ASC' + sorting += ", id ASC" else: - sorting = 'id ASC' + sorting = "id ASC" return sorting @@ -63,17 +67,17 @@ class OrderQuery(graphene.ObjectType): filter=graphene.Argument(OrderFilterInput, default_value={}), current_page=graphene.Int(default_value=1), page_size=graphene.Int(default_value=10), - sort=graphene.Argument(OrderSortInput, default_value={}) - ) - delivery_methods = graphene.List( - graphene.NonNull(ShippingMethod) + sort=graphene.Argument(OrderSortInput, default_value={}), ) + delivery_methods = graphene.List(graphene.NonNull(ShippingMethod)) @staticmethod def resolve_order(self, info, id): - SaleOrder = info.context['env']['sale.order'] - error_msg = 'Sale Order does not exist.' - order = get_document_with_check_access(SaleOrder, [('id', '=', id)], error_msg=error_msg) + SaleOrder = info.context["env"]["sale.order"] + error_msg = "Sale Order does not exist." + order = get_document_with_check_access( + SaleOrder, [("id", "=", id)], error_msg=error_msg + ) if not order: raise GraphQLError(_(error_msg)) return order.sudo() @@ -85,20 +89,22 @@ def resolve_orders(self, info, filter, current_page, page_size, sort): partner = user.partner_id sort_order = get_search_order(sort) domain = [ - ('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]), + ("message_partner_ids", "child_of", [partner.commercial_partner_id.id]), ] # Filter by stages or default to sales and done - if filter.get('stages', False): - stages = [stage.value for stage in filter['stages']] - domain += [('state', 'in', stages)] + if filter.get("stages", False): + stages = [stage.value for stage in filter["stages"]] + domain += [("state", "in", stages)] else: - domain += [('state', 'in', ['sale', 'done'])] + domain += [("state", "in", ["sale", "done"])] # Filter by invoice status - if filter.get('invoice_status', False): - invoice_status = [invoice_status.value for invoice_status in filter['invoice_status']] - domain += [('invoice_status', 'in', invoice_status)] + if filter.get("invoice_status", False): + invoice_status = [ + invoice_status.value for invoice_status in filter["invoice_status"] + ] + domain += [("invoice_status", "in", invoice_status)] # First offset is 0 but first page is 1 if current_page > 1: @@ -107,16 +113,24 @@ def resolve_orders(self, info, filter, current_page, page_size, sort): offset = 0 SaleOrder = env["sale.order"] - orders = get_document_with_check_access(SaleOrder, domain, sort_order, page_size, offset, - error_msg='Sale Order does not exist.') + orders = get_document_with_check_access( + SaleOrder, + domain, + sort_order, + page_size, + offset, + error_msg="Sale Order does not exist.", + ) total_count = get_document_count_with_check_access(SaleOrder, domain) - return OrderList(orders=orders and orders.sudo() or orders, total_count=total_count) + return OrderList( + orders=orders and orders.sudo() or orders, total_count=total_count + ) @staticmethod def resolve_delivery_methods(self, info): - """ Get all shipping/delivery methods """ - env = info.context['env'] - website = env['website'].get_current_website() + """Get all shipping/delivery methods.""" + env = info.context["env"] + website = env["website"].get_current_website() request.website = website order = website.sale_get_order() return order._get_delivery_methods() @@ -131,20 +145,22 @@ class Arguments: @staticmethod def mutate(self, info, promo): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=1) coupon_status = order._try_apply_code(promo) - if 'error' in coupon_status: - raise GraphQLError(coupon_status['error']) + if "error" in coupon_status: + raise GraphQLError(coupon_status["error"]) # Apply Coupon order._update_programs_and_rewards() order._auto_apply_rewards() order.action_open_reward_wizard() - return ApplyCoupon(error=coupon_status.get('error') or coupon_status.get('not_found')) + return ApplyCoupon( + error=coupon_status.get("error") or coupon_status.get("not_found") + ) class ApplyGiftCard(graphene.Mutation): @@ -156,22 +172,24 @@ class Arguments: @staticmethod def mutate(self, info, promo): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=1) gift_card_status = order._try_apply_code(promo) - if 'error' in gift_card_status: - raise GraphQLError(gift_card_status['error']) + if "error" in gift_card_status: + raise GraphQLError(gift_card_status["error"]) # Apply Coupon order._update_programs_and_rewards() order._auto_apply_rewards() order.action_open_reward_wizard() - return ApplyGiftCard(error=gift_card_status.get('error') or gift_card_status.get('not_found')) + return ApplyGiftCard( + error=gift_card_status.get("error") or gift_card_status.get("not_found") + ) class OrderMutation(graphene.ObjectType): - apply_coupon = ApplyCoupon.Field(description='Apply Coupon') - apply_gift_card = ApplyGiftCard.Field(description='Apply Gift Card') + apply_coupon = ApplyCoupon.Field(description="Apply Coupon") + apply_gift_card = ApplyGiftCard.Field(description="Apply Gift Card") diff --git a/vuestorefront/schemas/payment.py b/vuestorefront/schemas/payment.py index 04dac97..e5a9526 100644 --- a/vuestorefront/schemas/payment.py +++ b/vuestorefront/schemas/payment.py @@ -1,21 +1,22 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene from graphene.types import generic from graphql import GraphQLError + from odoo import _ from odoo.http import request from odoo.osv import expression from odoo.addons.payment import utils as payment_utils -from odoo.addons.payment_adyen_vsf.const import CURRENCY_DECIMALS -from odoo.addons.vuestorefront.schemas.objects import PaymentProvider, PaymentTransaction -from odoo.addons.vuestorefront.schemas.shop import Cart, CartData -from odoo.addons.website_sale.controllers.main import PaymentPortal from odoo.addons.payment_adyen.controllers.main import AdyenController +from odoo.addons.payment_adyen_vsf.const import CURRENCY_DECIMALS from odoo.addons.payment_adyen_vsf.controllers.main import AdyenControllerInherit +from odoo.addons.website_sale.controllers.main import PaymentPortal + +from .objects import PaymentProvider, PaymentTransaction +from .shop import Cart, CartData class PaymentQuery(graphene.ObjectType): @@ -31,7 +32,7 @@ class PaymentQuery(graphene.ObjectType): PaymentTransaction, required=True, id=graphene.Int(default_value=None), - reference=graphene.String(default_value=None) + reference=graphene.String(default_value=None), ) payment_confirmation = graphene.Field( Cart, @@ -40,71 +41,87 @@ class PaymentQuery(graphene.ObjectType): @staticmethod def resolve_payment_provider(self, info, id): env = info.context["env"] - PaymentProvider = env['payment.provider'].sudo() - website = env['website'].get_current_website() + PaymentProvider = env["payment.provider"].sudo() + website = env["website"].get_current_website() request.website = website domain = [ - ('id', '=', id), - ('state', 'in', ['enabled', 'test']), + ("id", "=", id), + ("state", "in", ["enabled", "test"]), ] payment_provider = PaymentProvider.search(domain, limit=1) if not payment_provider: - raise GraphQLError(_('Payment provider does not exist.')) + raise GraphQLError(_("Payment provider does not exist.")) return payment_provider @staticmethod def resolve_payment_providers(self, info): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order() - domain = expression.AND([ - ['&', ('state', 'in', ['enabled', 'test']), ('company_id', '=', order.company_id.id)], - ['|', ('website_id', '=', False), ('website_id', '=', website.id)], - ['|', ('available_country_ids', '=', False), ('available_country_ids', 'in', [order.partner_id.country_id.id])] - ]) - return env['payment.provider'].sudo().search(domain) + domain = expression.AND( + [ + [ + "&", + ("state", "in", ["enabled", "test"]), + ("company_id", "=", order.company_id.id), + ], + ["|", ("website_id", "=", False), ("website_id", "=", website.id)], + [ + "|", + ("available_country_ids", "=", False), + ("available_country_ids", "in", [order.partner_id.country_id.id]), + ], + ] + ) + return env["payment.provider"].sudo().search(domain) @staticmethod def resolve_payment_transaction(self, info, id, reference): env = info.context["env"] - PaymentTransaction = env['payment.transaction'] + PaymentTransaction = env["payment.transaction"] if id: - payment_transaction = PaymentTransaction.sudo().search([('id', '=', id)], limit=1) + payment_transaction = PaymentTransaction.sudo().search( + [("id", "=", id)], limit=1 + ) elif reference: - payment_transaction = PaymentTransaction.sudo().search([('reference', '=', reference)], limit=1) + payment_transaction = PaymentTransaction.sudo().search( + [("reference", "=", reference)], limit=1 + ) else: payment_transaction = None if not payment_transaction: - raise GraphQLError(_('Payment Transaction does not exist.')) + raise GraphQLError(_("Payment Transaction does not exist.")) return payment_transaction @staticmethod def resolve_payment_confirmation(self, info): env = info.context["env"] - PaymentTransaction = env['payment.transaction'] - Order = env['sale.order'] + PaymentTransaction = env["payment.transaction"] + Order = env["sale.order"] # Pass in the session the sale_order created in vsf - payment_transaction_id = request.session.get('__payment_monitored_tx_ids__') + payment_transaction_id = request.session.get("__payment_monitored_tx_ids__") if payment_transaction_id and payment_transaction_id[0]: - payment_transaction = PaymentTransaction.sudo().search([('id', '=', payment_transaction_id[0])], limit=1) + payment_transaction = PaymentTransaction.sudo().search( + [("id", "=", payment_transaction_id[0])], limit=1 + ) sale_order_id = payment_transaction.sale_order_ids.ids[0] if sale_order_id: - order = Order.sudo().search([('id', '=', sale_order_id)], limit=1) + order = Order.sudo().search([("id", "=", sale_order_id)], limit=1) if order.exists(): return CartData(order=order) - raise GraphQLError(_('Cart does not exist')) + raise GraphQLError(_("Cart does not exist")) class MakeGiftCardPayment(graphene.Mutation): @@ -113,7 +130,7 @@ class MakeGiftCardPayment(graphene.Mutation): @staticmethod def mutate(self, info): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order() tx = order.get_portal_last_transaction() @@ -126,13 +143,16 @@ def mutate(self, info): class PaymentMutation(graphene.ObjectType): - make_gift_card_payment = MakeGiftCardPayment.Field(description='Pay the order only with gift card.') + make_gift_card_payment = MakeGiftCardPayment.Field( + description="Pay the order only with gift card." + ) # -------------------------------- # # Adyen Payment # # -------------------------------- # + class AdyenProviderInfoResult(graphene.ObjectType): adyen_provider_info = generic.GenericScalar() @@ -162,19 +182,19 @@ class Arguments: @staticmethod def mutate(self, info, provider_id): env = info.context["env"] - PaymentProvider = env['payment.provider'].sudo() - website = env['website'].get_current_website() + PaymentProvider = env["payment.provider"].sudo() + website = env["website"].get_current_website() request.website = website domain = [ - ('id', '=', provider_id), - ('state', 'in', ['enabled', 'test']), + ("id", "=", provider_id), + ("state", "in", ["enabled", "test"]), ] payment_provider_id = PaymentProvider.search(domain, limit=1) if not payment_provider_id: - raise GraphQLError(_('Payment Provider does not exist.')) + raise GraphQLError(_("Payment Provider does not exist.")) - if not payment_provider_id.code == 'adyen': + if not payment_provider_id.code == "adyen": raise GraphQLError(_('Payment Provider "Adyen" does not exist.')) adyen_provider_info = AdyenController().adyen_provider_info( @@ -193,27 +213,27 @@ class Arguments: @staticmethod def mutate(self, info, provider_id): env = info.context["env"] - PaymentProvider = env['payment.provider'].sudo() - website = env['website'].get_current_website() + PaymentProvider = env["payment.provider"].sudo() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order() domain = [ - ('id', '=', provider_id), - ('state', 'in', ['enabled', 'test']), + ("id", "=", provider_id), + ("state", "in", ["enabled", "test"]), ] payment_provider_id = PaymentProvider.search(domain, limit=1) if not payment_provider_id: - raise GraphQLError(_('Payment Provider does not exist.')) + raise GraphQLError(_("Payment Provider does not exist.")) - if not payment_provider_id.code == 'adyen': + if not payment_provider_id.code == "adyen": raise GraphQLError(_('Payment Provider "Adyen" does not exist.')) adyen_payment_methods = AdyenController().adyen_payment_methods( provider_id=payment_provider_id.id, amount=order.amount_total, currency_id=order.currency_id.id, - partner_id=order.partner_id.id + partner_id=order.partner_id.id, ) return AdyenPaymentMethodsResult(adyen_payment_methods=adyen_payment_methods) @@ -228,21 +248,21 @@ class Arguments: @staticmethod def mutate(self, info, provider_id): env = info.context["env"] - PaymentProvider = env['payment.provider'].sudo() - PaymentTransaction = env['payment.transaction'].sudo() - website = env['website'].get_current_website() + PaymentProvider = env["payment.provider"].sudo() + PaymentTransaction = env["payment.transaction"].sudo() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order() domain = [ - ('id', '=', provider_id), - ('state', 'in', ['enabled', 'test']), + ("id", "=", provider_id), + ("state", "in", ["enabled", "test"]), ] payment_provider_id = PaymentProvider.search(domain, limit=1) if not payment_provider_id: - raise GraphQLError(_('Payment Provider does not exist.')) + raise GraphQLError(_("Payment Provider does not exist.")) - if not payment_provider_id.code == 'adyen': + if not payment_provider_id.code == "adyen": raise GraphQLError(_('Payment Provider "Adyen" does not exist.')) transaction = PaymentPortal().shop_payment_transaction( @@ -252,12 +272,14 @@ def mutate(self, info, provider_id): amount=order.amount_total, currency_id=order.currency_id.id, partner_id=order.partner_id.id, - flow='direct', + flow="direct", tokenization_requested=False, - landing_route='/shop/payment/validate' + landing_route="/shop/payment/validate", ) - transaction_id = PaymentTransaction.search([('reference', '=', transaction['reference'])], limit=1) + transaction_id = PaymentTransaction.search( + [("reference", "=", transaction["reference"])], limit=1 + ) # Update the field created_on_vsf transaction_id.created_on_vsf = True @@ -270,38 +292,54 @@ class Arguments: provider_id = graphene.Int(required=True) transaction_reference = graphene.String(required=True) access_token = graphene.String(required=True) - payment_method = generic.GenericScalar(required=True, description='Return state.data.paymentMethod') - browser_info = generic.GenericScalar(required=True, description='Return state.data.browserInfo') + payment_method = generic.GenericScalar( + required=True, description="Return state.data.paymentMethod" + ) + browser_info = generic.GenericScalar( + required=True, description="Return state.data.browserInfo" + ) Output = AdyenPaymentsResult @staticmethod - def mutate(self, info, provider_id, transaction_reference, access_token, payment_method, browser_info): + def mutate( + self, + info, + provider_id, + transaction_reference, + access_token, + payment_method, + browser_info, + ): env = info.context["env"] - PaymentProvider = env['payment.provider'].sudo() - PaymentTransaction = env['payment.transaction'].sudo() - website = env['website'].get_current_website() + PaymentProvider = env["payment.provider"].sudo() + PaymentTransaction = env["payment.transaction"].sudo() + website = env["website"].get_current_website() request.website = website domain = [ - ('id', '=', provider_id), - ('state', 'in', ['enabled', 'test']), + ("id", "=", provider_id), + ("state", "in", ["enabled", "test"]), ] payment_provider_id = PaymentProvider.search(domain, limit=1) if not payment_provider_id: - raise GraphQLError(_('Payment Provider does not exist.')) + raise GraphQLError(_("Payment Provider does not exist.")) - if not payment_provider_id.code == 'adyen': + if not payment_provider_id.code == "adyen": raise GraphQLError(_('Payment Provider "Adyen" does not exist.')) - transaction = PaymentTransaction.search([('reference', '=', transaction_reference)], limit=1) + transaction = PaymentTransaction.search( + [("reference", "=", transaction_reference)], limit=1 + ) if not transaction: - raise GraphQLError(_('Payment transaction does not exist.')) + raise GraphQLError(_("Payment transaction does not exist.")) converted_amount = payment_utils.to_minor_currency_units( transaction.amount, transaction.currency_id, - arbitrary_decimal_number=CURRENCY_DECIMALS.get(transaction.currency_id.name, 2) + arbitrary_decimal_number=CURRENCY_DECIMALS.get( + transaction.currency_id.name, 2 + ), ) # Create Payment @@ -313,7 +351,7 @@ def mutate(self, info, provider_id, transaction_reference, access_token, payment partner_id=transaction.partner_id.id, payment_method=payment_method, access_token=access_token, - browser_info=browser_info + browser_info=browser_info, ) return AdyenPaymentsResult(adyen_payments=adyen_payment) @@ -323,46 +361,56 @@ class AdyenPaymentDetails(graphene.Mutation): class Arguments: provider_id = graphene.Int(required=True) transaction_reference = graphene.String(required=True) - payment_details = generic.GenericScalar(required=True, description='Return state.data') + payment_details = generic.GenericScalar( + required=True, description="Return state.data" + ) Output = AdyenPaymentDetailsResult @staticmethod def mutate(self, info, provider_id, transaction_reference, payment_details): env = info.context["env"] - PaymentProvider = env['payment.provider'].sudo() - PaymentTransaction = env['payment.transaction'].sudo() - website = env['website'].get_current_website() + PaymentProvider = env["payment.provider"].sudo() + PaymentTransaction = env["payment.transaction"].sudo() + website = env["website"].get_current_website() request.website = website domain = [ - ('id', '=', provider_id), - ('state', 'in', ['enabled', 'test']), + ("id", "=", provider_id), + ("state", "in", ["enabled", "test"]), ] payment_provider_id = PaymentProvider.search(domain, limit=1) if not payment_provider_id: - raise GraphQLError(_('Payment Provider does not exist.')) + raise GraphQLError(_("Payment Provider does not exist.")) - if not payment_provider_id.code == 'adyen': + if not payment_provider_id.code == "adyen": raise GraphQLError(_('Payment Provider "Adyen" does not exist.')) - transaction = PaymentTransaction.search([('reference', '=', transaction_reference)], limit=1) + transaction = PaymentTransaction.search( + [("reference", "=", transaction_reference)], limit=1 + ) if not transaction: - raise GraphQLError(_('Payment transaction does not exist.')) + raise GraphQLError(_("Payment transaction does not exist.")) # Submit the details adyen_payment_details = AdyenController().adyen_payment_details( provider_id=payment_provider_id.id, reference=transaction.reference, - payment_details=payment_details + payment_details=payment_details, ) return AdyenPaymentDetailsResult(adyen_payment_details=adyen_payment_details) class AdyenPaymentMutation(graphene.ObjectType): - adyen_provider_info = AdyenProviderInfo.Field(description='Get Adyen Provider Info.') - adyen_payment_methods = AdyenPaymentMethods.Field(description='Get Adyen Payment Methods.') - adyen_transaction = AdyenTransaction.Field(description='Create Adyen Transaction') - adyen_payments = AdyenPayments.Field(description='Make Adyen Payment request.') - adyen_payment_details = AdyenPaymentDetails.Field(description='Submit the Adyen Payment Details.') + adyen_provider_info = AdyenProviderInfo.Field( + description="Get Adyen Provider Info." + ) + adyen_payment_methods = AdyenPaymentMethods.Field( + description="Get Adyen Payment Methods." + ) + adyen_transaction = AdyenTransaction.Field(description="Create Adyen Transaction") + adyen_payments = AdyenPayments.Field(description="Make Adyen Payment request.") + adyen_payment_details = AdyenPaymentDetails.Field( + description="Submit the Adyen Payment Details." + ) diff --git a/vuestorefront/schemas/product.py b/vuestorefront/schemas/product.py index f0d637e..4ef56ae 100644 --- a/vuestorefront/schemas/product.py +++ b/vuestorefront/schemas/product.py @@ -1,84 +1,93 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene from graphql import GraphQLError -from odoo.http import request + from odoo import _ +from odoo.http import request from odoo.osv import expression -from odoo.addons.vuestorefront.schemas.objects import ( - SortEnum, Product, Attribute, AttributeValue -) +from .objects import Attribute, AttributeValue, Product, SortEnum def get_search_order(sort): - sorting = '' + sorting = "" for field, val in sort.items(): if sorting: - sorting += ', ' - if field == 'price': - sorting += 'list_price %s' % val.value + sorting += ", " + if field == "price": + sorting += "list_price %s" % val.value else: - sorting += '%s %s' % (field, val.value) + sorting += "%s %s" % (field, val.value) # Add id as last factor, so we can consistently get the same results if sorting: - sorting += ', id ASC' + sorting += ", id ASC" else: - sorting = 'id ASC' + sorting = "id ASC" return sorting def get_search_domain(env, search, **kwargs): # Only get published products - domains = [env['website'].get_current_website().sale_product_domain()] + domains = [env["website"].get_current_website().sale_product_domain()] # Filter with ids - if kwargs.get('ids', False): - domains.append([('id', 'in', kwargs['ids'])]) + if kwargs.get("ids", False): + domains.append([("id", "in", kwargs["ids"])]) # Filter with Category ID - if kwargs.get('category_id', False): - domains.append([('public_categ_ids', 'child_of', kwargs['category_id'])]) + if kwargs.get("category_id", False): + domains.append([("public_categ_ids", "child_of", kwargs["category_id"])]) # Filter with Category Slug - if kwargs.get('category_slug', False): - domains.append([('public_categ_slug_ids.website_slug', '=', kwargs['category_slug'])]) + if kwargs.get("category_slug", False): + domains.append( + [("public_categ_slug_ids.website_slug", "=", kwargs["category_slug"])] + ) # Filter With Name - if kwargs.get('name', False): - name = kwargs['name'] + if kwargs.get("name", False): + name = kwargs["name"] for n in name.split(" "): - domains.append([('name', 'ilike', n)]) + domains.append([("name", "ilike", n)]) if search: for srch in search.split(" "): - domains.append([ - '|', '|', ('name', 'ilike', srch), ('description_sale', 'like', srch), ('default_code', 'like', srch)]) + domains.append( + [ + "|", + "|", + ("name", "ilike", srch), + ("description_sale", "like", srch), + ("default_code", "like", srch), + ] + ) partial_domain = domains.copy() # Product Price Filter - if kwargs.get('min_price', False): - domains.append([('list_price', '>=', float(kwargs['min_price']))]) - if kwargs.get('max_price', False): - domains.append([('list_price', '<=', float(kwargs['max_price']))]) + if kwargs.get("min_price", False): + domains.append([("list_price", ">=", float(kwargs["min_price"]))]) + if kwargs.get("max_price", False): + domains.append([("list_price", "<=", float(kwargs["max_price"]))]) # Deprecated: filter with Attribute Value - if kwargs.get('attribute_value_id', False): - domains.append([('attribute_line_ids.value_ids', 'in', kwargs['attribute_value_id'])]) + if kwargs.get("attribute_value_id", False): + domains.append( + [("attribute_line_ids.value_ids", "in", kwargs["attribute_value_id"])] + ) # Filter with Attribute Value - if kwargs.get('attrib_values', False): + if kwargs.get("attrib_values", False): attributes = {} attributes_domain = [] - for value in kwargs['attrib_values']: + for value in kwargs["attrib_values"]: try: - value = value.split('-') + value = value.split("-") if len(value) != 2: continue @@ -92,8 +101,8 @@ def get_search_domain(env, search, **kwargs): attributes[attribute_id].append(attribute_value_id) - for key, value in attributes.items(): - attributes_domain.append([('attribute_line_ids.value_ids', 'in', value)]) + for value in attributes.values(): + attributes_domain.append([("attribute_line_ids.value_ids", "in", value)]) attributes_domain = expression.AND(attributes_domain) domains.append(attributes_domain) @@ -102,7 +111,7 @@ def get_search_domain(env, search, **kwargs): def get_product_list(env, current_page, page_size, search, sort, **kwargs): - Product = env['product.template'].sudo() + Product = env["product.template"].sudo() domain, partial_domain = get_search_domain(env, search, **kwargs) # First offset is 0 but first page is 1 @@ -113,17 +122,20 @@ def get_product_list(env, current_page, page_size, search, sort, **kwargs): order = get_search_order(sort) products = Product.search(domain, order=order) - # If attribute values are selected, we need to get the full list of attribute values and prices + # If attribute values are selected, we need to get the full list of + # attribute values and prices if domain == partial_domain: - attribute_values = products.mapped('variant_attribute_value_ids') - prices = products.mapped('list_price') + attribute_values = products.mapped("variant_attribute_value_ids") + prices = products.mapped("list_price") else: without_attributes_products = Product.search(partial_domain) - attribute_values = without_attributes_products.mapped('variant_attribute_value_ids') - prices = without_attributes_products.mapped('list_price') + attribute_values = without_attributes_products.mapped( + "variant_attribute_value_ids" + ) + prices = without_attributes_products.mapped("list_price") total_count = len(products) - products = products[offset:offset + page_size] + products = products[offset : offset + page_size] if prices: return products, total_count, attribute_values, min(prices), max(prices) return products, total_count, attribute_values, 0.0, 0.0 @@ -189,7 +201,7 @@ class ProductQuery(graphene.ObjectType): current_page=graphene.Int(default_value=1), page_size=graphene.Int(default_value=20), search=graphene.String(default_value=False), - sort=graphene.Argument(ProductSortInput, default_value={}) + sort=graphene.Argument(ProductSortInput, default_value={}), ) attribute = graphene.Field( Attribute, @@ -200,7 +212,7 @@ class ProductQuery(graphene.ObjectType): ProductVariant, required=True, product_template_id=graphene.Int(), - combination_id=graphene.List(graphene.Int) + combination_id=graphene.List(graphene.Int), ) @staticmethod @@ -209,16 +221,16 @@ def resolve_product(self, info, id=None, slug=None, barcode=None): Product = env["product.template"].sudo() if id: - product = Product.search([('id', '=', id)], limit=1) + product = Product.search([("id", "=", id)], limit=1) elif slug: - product = Product.search([('website_slug', '=', slug)], limit=1) + product = Product.search([("website_slug", "=", slug)], limit=1) elif barcode: - product = Product.search([('barcode', '=', barcode)], limit=1) + product = Product.search([("barcode", "=", barcode)], limit=1) else: product = Product if product: - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website if not product.can_access_from_current_website(): product = Product @@ -228,49 +240,61 @@ def resolve_product(self, info, id=None, slug=None, barcode=None): @staticmethod def resolve_products(self, info, filter, current_page, page_size, search, sort): env = info.context["env"] - products, total_count, attribute_values,min_price, max_price = get_product_list( - env, current_page, page_size, search, sort, **filter) - return ProductList(products=products, total_count=total_count, attribute_values=attribute_values, - min_price=min_price, max_price=max_price) + ( + products, + total_count, + attribute_values, + min_price, + max_price, + ) = get_product_list(env, current_page, page_size, search, sort, **filter) + return ProductList( + products=products, + total_count=total_count, + attribute_values=attribute_values, + min_price=min_price, + max_price=max_price, + ) @staticmethod def resolve_attribute(self, info, id): - return info.context["env"]["product.attribute"].search([('id', '=', id)], limit=1) + return info.context["env"]["product.attribute"].search( + [("id", "=", id)], limit=1 + ) @staticmethod def resolve_product_variant(self, info, product_template_id, combination_id): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website pricelist = website.get_current_pricelist() - product_template = env['product.template'].browse(product_template_id) - combination = env['product.template.attribute.value'].browse(combination_id) + product_template = env["product.template"].browse(product_template_id) + combination = env["product.template.attribute.value"].browse(combination_id) variant_info = product_template._get_combination_info(combination, pricelist) - product = env['product.product'].browse(variant_info['product_id']) + product = env["product.product"].browse(variant_info["product_id"]) # Condition to verify if Product exist if not product: - raise GraphQLError(_('Product does not exist')) + raise GraphQLError(_("Product does not exist")) is_combination_possible = product_template._is_combination_possible(combination) # Condition to Verify if Product is active or if combination exist if not product.active or not is_combination_possible: - variant_info['is_combination_possible'] = False + variant_info["is_combination_possible"] = False else: - variant_info['is_combination_possible'] = True + variant_info["is_combination_possible"] = True return ProductVariantData( product=product, - product_template_id=variant_info['product_template_id'], - display_name=variant_info['display_name'], - display_image=variant_info['display_image'], - price=variant_info['price'], - list_price=variant_info['list_price'], - has_discounted_price=variant_info['has_discounted_price'], - is_combination_possible=variant_info['is_combination_possible'] + product_template_id=variant_info["product_template_id"], + display_name=variant_info["display_name"], + display_image=variant_info["display_image"], + price=variant_info["price"], + list_price=variant_info["list_price"], + has_discounted_price=variant_info["has_discounted_price"], + is_combination_possible=variant_info["is_combination_possible"], ) diff --git a/vuestorefront/schemas/shop.py b/vuestorefront/schemas/shop.py index a2cc55d..ecb2016 100644 --- a/vuestorefront/schemas/shop.py +++ b/vuestorefront/schemas/shop.py @@ -1,12 +1,18 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene -from odoo.addons.vuestorefront.schemas.objects import Order, Partner -from odoo.addons.website_mass_mailing.controllers.main import MassMailController + from odoo.http import request +from odoo.addons.website_mass_mailing.controllers.main import MassMailController + +from .objects import Order, Partner + + +def predicate_order_line_id(line_id): + return lambda r: r.id == line_id + class Cart(graphene.Interface): order = graphene.Field(Order) @@ -25,14 +31,14 @@ class ShoppingCartQuery(graphene.ObjectType): @staticmethod def resolve_cart(self, info): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=True) - if order and order.state != 'draft': - request.session['sale_order_id'] = None + if order and order.state != "draft": + request.session["sale_order_id"] = None order = website.sale_get_order(force_create=True) if order: - order.order_line.filtered(lambda l: not l.product_id.active).unlink() + order.order_line.filtered(lambda r: not r.product_id.active).unlink() return CartData(order=order) @@ -46,11 +52,11 @@ class Arguments: @staticmethod def mutate(self, info, product_id, quantity): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=1) # Forcing the website_id to be passed to the Order - order.write({'website_id': website.id}) + order.write({"website_id": website.id}) order._cart_update(product_id=product_id, add_qty=quantity) return CartData(order=order) @@ -65,13 +71,15 @@ class Arguments: @staticmethod def mutate(self, info, line_id, quantity): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=1) line = order.order_line.filtered(lambda rec: rec.id == line_id) # Reset Warning Stock Message always before a new update line.shop_warning = "" - order._cart_update(product_id=line.product_id.id, line_id=line.id, set_qty=quantity) + order._cart_update( + product_id=line.product_id.id, line_id=line.id, set_qty=quantity + ) return CartData(order=order) @@ -84,7 +92,7 @@ class Arguments: @staticmethod def mutate(self, info, line_id): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=1) line = order.order_line.filtered(lambda rec: rec.id == line_id) @@ -98,7 +106,7 @@ class CartClear(graphene.Mutation): @staticmethod def mutate(self, info): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=1) order.order_line.sudo().unlink() @@ -114,7 +122,7 @@ class Arguments: @staticmethod def mutate(self, info, shipping_method_id): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=1) @@ -127,6 +135,7 @@ def mutate(self, info, shipping_method_id): # Additional Mutations that can be useful # # ---------------------------------------------------# + class ProductInput(graphene.InputObjectType): id = graphene.Int(required=True) quantity = graphene.Int(required=True) @@ -146,14 +155,14 @@ class Arguments: @staticmethod def mutate(self, info, products): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=1) # Forcing the website_id to be passed to the Order - order.write({'website_id': website.id}) + order.write({"website_id": website.id}) for product in products: - product_id = product['id'] - quantity = product['quantity'] + product_id = product["id"] + quantity = product["quantity"] order._cart_update(product_id=product_id, add_qty=quantity) return CartData(order=order) @@ -167,16 +176,18 @@ class Arguments: @staticmethod def mutate(self, info, lines): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=1) for line in lines: - line_id = line['id'] - quantity = line['quantity'] - line = order.order_line.filtered(lambda rec: rec.id == line_id) + line_id = line["id"] + quantity = line["quantity"] + line = order.order_line.filtered(predicate_order_line_id(line_id)) # Reset Warning Stock Message always before a new update line.shop_warning = "" - order._cart_update(product_id=line.product_id.id, line_id=line.id, set_qty=quantity) + order._cart_update( + product_id=line.product_id.id, line_id=line.id, set_qty=quantity + ) return CartData(order=order) @@ -189,11 +200,11 @@ class Arguments: @staticmethod def mutate(self, info, line_ids): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=1) for line_id in line_ids: - line = order.order_line.filtered(lambda rec: rec.id == line_id) + line = order.order_line.filtered(predicate_order_line_id(line_id)) line.unlink() return CartData(order=order) @@ -208,34 +219,38 @@ class Arguments: @staticmethod def mutate(self, info, name, email, subscribe_newsletter): - env = info.context['env'] - website = env['website'].get_current_website() + env = info.context["env"] + website = env["website"].get_current_website() request.website = website order = website.sale_get_order(force_create=1) data = { - 'name': name, - 'email': email, + "name": name, + "email": email, } partner = order.partner_id # Is public user if partner.id == website.user_id.sudo().partner_id.id: - partner = env['res.partner'].sudo().create(data) - - order.write({ - 'partner_id': partner.id, - 'partner_invoice_id': partner.id, - 'partner_shipping_id': partner.id, - }) + partner = env["res.partner"].sudo().create(data) + + order.write( + { + "partner_id": partner.id, + "partner_invoice_id": partner.id, + "partner_shipping_id": partner.id, + } + ) else: partner.write(data) # Subscribe to newsletter if subscribe_newsletter: if website.vsf_mailing_list_id: - MassMailController().subscribe(website.vsf_mailing_list_id.id, email, 'email') + MassMailController().subscribe( + website.vsf_mailing_list_id.id, email, "email" + ) return partner @@ -245,8 +260,18 @@ class ShopMutation(graphene.ObjectType): cart_update_item = CartUpdateItem.Field(description="Update Item") cart_remove_item = CartRemoveItem.Field(description="Remove Item") cart_clear = CartClear.Field(description="Cart Clear") - cart_add_multiple_items = CartAddMultipleItems.Field(description="Add Multiple Items") - cart_update_multiple_items = CartUpdateMultipleItems.Field(description="Update Multiple Items") - cart_remove_multiple_items = CartRemoveMultipleItems.Field(description="Remove Multiple Items") - set_shipping_method = SetShippingMethod.Field(description="Set Shipping Method on Cart") - create_update_partner = CreateUpdatePartner.Field(description="Create or update a partner for guest checkout") + cart_add_multiple_items = CartAddMultipleItems.Field( + description="Add Multiple Items" + ) + cart_update_multiple_items = CartUpdateMultipleItems.Field( + description="Update Multiple Items" + ) + cart_remove_multiple_items = CartRemoveMultipleItems.Field( + description="Remove Multiple Items" + ) + set_shipping_method = SetShippingMethod.Field( + description="Set Shipping Method on Cart" + ) + create_update_partner = CreateUpdatePartner.Field( + description="Create or update a partner for guest checkout" + ) diff --git a/vuestorefront/schemas/sign.py b/vuestorefront/schemas/sign.py index 2db8a2e..2425672 100644 --- a/vuestorefront/schemas/sign.py +++ b/vuestorefront/schemas/sign.py @@ -1,17 +1,19 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene from graphql import GraphQLError + import odoo from odoo import _ -from odoo.http import request from odoo.exceptions import UserError +from odoo.http import request + from odoo.addons.auth_signup.models.res_users import SignupError -from odoo.addons.vuestorefront.schemas.objects import User from odoo.addons.website_mass_mailing.controllers.main import MassMailController +from .objects import User + class Login(graphene.Mutation): class Arguments: @@ -23,8 +25,8 @@ class Arguments: @staticmethod def mutate(self, info, email, password, subscribe_newsletter): - env = info.context['env'] - website = env['website'].get_current_website() + env = info.context["env"] + website = env["website"].get_current_website() request.website = website # Set email in lowercase @@ -34,11 +36,13 @@ def mutate(self, info, email, password, subscribe_newsletter): uid = request.session.authenticate(request.session.db, email, password) # Subscribe Newsletter if website and website.vsf_mailing_list_id and subscribe_newsletter: - MassMailController().subscribe(website.vsf_mailing_list_id.id, email, 'email') - return env['res.users'].sudo().browse(uid) + MassMailController().subscribe( + website.vsf_mailing_list_id.id, email, "email" + ) + return env["res.users"].sudo().browse(uid) except odoo.exceptions.AccessDenied as e: if e.args == odoo.exceptions.AccessDenied().args: - raise GraphQLError(_('Wrong email or password.')) + raise GraphQLError(_("Wrong email or password.")) else: raise GraphQLError(_(e.args[0])) @@ -64,29 +68,33 @@ class Arguments: @staticmethod def mutate(self, info, name, email, password, subscribe_newsletter): - env = info.context['env'] - website = env['website'].get_current_website() + env = info.context["env"] + website = env["website"].get_current_website() request.website = website # Set email in lowercase email = email.lower() data = { - 'name': name, - 'login': email, - 'password': password, + "name": name, + "login": email, + "password": password, } - if env['res.users'].sudo().search([('login', '=', data['login'])], limit=1): - raise GraphQLError(_('Another user is already registered using this email address.')) + if env["res.users"].sudo().search([("login", "=", data["login"])], limit=1): + raise GraphQLError( + _("Another user is already registered using this email address.") + ) - env['res.users'].sudo().signup(data) + env["res.users"].sudo().signup(data) # Subscribe Newsletter if website and website.vsf_mailing_list_id and subscribe_newsletter: - MassMailController().subscribe(website.vsf_mailing_list_id.id, email, 'email') + MassMailController().subscribe( + website.vsf_mailing_list_id.id, email, "email" + ) - return env['res.users'].sudo().search([('login', '=', data['login'])], limit=1) + return env["res.users"].sudo().search([("login", "=", data["login"])], limit=1) class ResetPassword(graphene.Mutation): @@ -97,18 +105,18 @@ class Arguments: @staticmethod def mutate(self, info, email): - env = info.context['env'] - ResUsers = env['res.users'].sudo() - create_user = info.context.get('create_user', False) + env = info.context["env"] + ResUsers = env["res.users"].sudo() + create_user = info.context.get("create_user", False) # Set email in lowercase email = email.lower() - user = ResUsers.search([('login', '=', email)]) + user = ResUsers.search([("login", "=", email)]) if not user: - user = ResUsers.search([('email', '=', email)]) + user = ResUsers.search([("email", "=", email)]) if len(user) != 1: - raise GraphQLError(_('Invalid email.')) + raise GraphQLError(_("Invalid email.")) try: user.with_context(create_user=create_user).api_action_reset_password() @@ -116,7 +124,7 @@ def mutate(self, info, email): except UserError as e: raise GraphQLError(e.name or e.value) except SignupError: - raise GraphQLError(_('Could not reset your password.')) + raise GraphQLError(_("Could not reset your password.")) except Exception as e: raise GraphQLError(str(e)) @@ -130,21 +138,21 @@ class Arguments: @staticmethod def mutate(self, info, token, new_password): - env = info.context['env'] + env = info.context["env"] data = { - 'password': new_password, + "password": new_password, } - ResUsers = env['res.users'].sudo() + ResUsers = env["res.users"].sudo() try: login, password = ResUsers.signup(data, token) - return ResUsers.search([('login', '=', login)], limit=1) + return ResUsers.search([("login", "=", login)], limit=1) except UserError as e: raise GraphQLError(e.args[0]) except SignupError: - raise GraphQLError(_('Could not change your password.')) + raise GraphQLError(_("Could not change your password.")) except Exception as e: raise GraphQLError(str(e)) @@ -158,34 +166,50 @@ class Arguments: @staticmethod def mutate(self, info, current_password, new_password): - env = info.context['env'] - website = env['website'].get_current_website() + env = info.context["env"] + website = env["website"].get_current_website() request.website = website website_user = website.user_id if env.uid: - user = env['res.users'].sudo().search([('id', '=', env.uid), ('active', 'in', [True, False])], limit=1) + user = ( + env["res.users"] + .sudo() + .search( + [("id", "=", env.uid), ("active", "in", [True, False])], limit=1 + ) + ) # Prevent "Public User" to be Updated if user and user.id and user.id == website_user.id: - raise GraphQLError(_('Partner cannot be updated.')) + raise GraphQLError(_("Partner cannot be updated.")) try: user._check_credentials(current_password, env) user.change_password(current_password, new_password) env.cr.commit() - request.session.authenticate(request.session.db, user.login, new_password) + request.session.authenticate( + request.session.db, user.login, new_password + ) return user except odoo.exceptions.AccessDenied: - raise GraphQLError(_('Incorrect password.')) + raise GraphQLError(_("Incorrect password.")) else: - raise GraphQLError(_('You must be logged in.')) + raise GraphQLError(_("You must be logged in.")) class SignMutation(graphene.ObjectType): - login = Login.Field(description='Authenticate user with email and password and retrieves token.') - logout = Logout.Field(description='Logout user') - register = Register.Field(description='Register a new user with email, name and password.') - reset_password = ResetPassword.Field(description="Send change password url to user's email.") - change_password = ChangePassword.Field(description="Set new user's password with the token from the change " - "password url received in the email.") + login = Login.Field( + description="Authenticate user with email and password and retrieves token." + ) + logout = Logout.Field(description="Logout user") + register = Register.Field( + description="Register a new user with email, name and password." + ) + reset_password = ResetPassword.Field( + description="Send change password url to user's email." + ) + change_password = ChangePassword.Field( + description="Set new user's password with the token from the change " + "password url received in the email." + ) update_password = UpdatePassword.Field(description="Update user password.") diff --git a/vuestorefront/schemas/user_profile.py b/vuestorefront/schemas/user_profile.py index ecf7848..707b8d0 100644 --- a/vuestorefront/schemas/user_profile.py +++ b/vuestorefront/schemas/user_profile.py @@ -1,13 +1,13 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene from graphql import GraphQLError + from odoo import _ from odoo.http import request -from odoo.addons.vuestorefront.schemas.objects import Partner +from .objects import Partner class UserProfileQuery(graphene.ObjectType): @@ -19,13 +19,13 @@ class UserProfileQuery(graphene.ObjectType): @staticmethod def resolve_partner(self, info): uid = request.session.uid - user = info.context['env']['res.users'].sudo().browse(uid) + user = info.context["env"]["res.users"].sudo().browse(uid) if user: partner = user.partner_id if not partner: - raise GraphQLError(_('Partner does not exist.')) + raise GraphQLError(_("Partner does not exist.")) else: - raise GraphQLError(_('User does not exist.')) + raise GraphQLError(_("User does not exist.")) return partner @@ -45,22 +45,22 @@ class Arguments: @staticmethod def mutate(self, info, myaccount): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website user = request.env.user website_user = website.user_id # Prevent "Public User" to be Updated if user.id == website_user.id: - raise GraphQLError(_('Partner cannot be updated.')) + raise GraphQLError(_("Partner cannot be updated.")) partner = user.partner_id if partner: partner.write(myaccount) else: - raise GraphQLError(_('Partner does not exist.')) + raise GraphQLError(_("Partner does not exist.")) return partner class UserProfileMutation(graphene.ObjectType): - update_my_account = UpdateMyAccount.Field(description='Update MyAccount') + update_my_account = UpdateMyAccount.Field(description="Update MyAccount") diff --git a/vuestorefront/schemas/website.py b/vuestorefront/schemas/website.py index d29f983..d337b9a 100644 --- a/vuestorefront/schemas/website.py +++ b/vuestorefront/schemas/website.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene -from odoo.addons.vuestorefront.schemas.objects import WebsiteMenu +from .objects import WebsiteMenu class WebsiteQuery(graphene.ObjectType): @@ -19,42 +18,42 @@ class WebsiteQuery(graphene.ObjectType): @staticmethod def resolve_website_menu(self, info): - env = info.context['env'] - website = env['website'].get_current_website() + env = info.context["env"] + website = env["website"].get_current_website() domain = [ - ('website_id', '=', website.id), - ('is_visible', '=', True), - ('is_footer', '=', False), - ('is_mega_menu', '=', False), + ("website_id", "=", website.id), + ("is_visible", "=", True), + ("is_footer", "=", False), + ("is_mega_menu", "=", False), ] - return env['website.menu'].search(domain) + return env["website.menu"].search(domain) @staticmethod def resolve_website_mega_menu(self, info): - env = info.context['env'] - website = env['website'].get_current_website() + env = info.context["env"] + website = env["website"].get_current_website() domain = [ - ('website_id', '=', website.id), - ('is_visible', '=', True), - ('is_footer', '=', False), - ('is_mega_menu', '=', True), + ("website_id", "=", website.id), + ("is_visible", "=", True), + ("is_footer", "=", False), + ("is_mega_menu", "=", True), ] - return env['website.menu'].search(domain) + return env["website.menu"].search(domain) @staticmethod def resolve_website_footer(self, info): - env = info.context['env'] - website = env['website'].get_current_website() + env = info.context["env"] + website = env["website"].get_current_website() domain = [ - ('website_id', '=', website.id), - ('is_visible', '=', True), - ('is_footer', '=', True), - ('is_mega_menu', '=', False), + ("website_id", "=", website.id), + ("is_visible", "=", True), + ("is_footer", "=", True), + ("is_mega_menu", "=", False), ] - return env['website.menu'].search(domain) + return env["website.menu"].search(domain) diff --git a/vuestorefront/schemas/wishlist.py b/vuestorefront/schemas/wishlist.py index 41f92fd..f392c92 100644 --- a/vuestorefront/schemas/wishlist.py +++ b/vuestorefront/schemas/wishlist.py @@ -1,14 +1,15 @@ -# -*- coding: utf-8 -*- # Copyright 2023 ODOOGAP/PROMPTEQUATION LDA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). import graphene from graphql import GraphQLError -from odoo.http import request + from odoo import _ +from odoo.http import request from odoo.addons.website_sale_wishlist.controllers.main import WebsiteSaleWishlist -from odoo.addons.vuestorefront.schemas.objects import WishlistItem + +from .objects import WishlistItem class WishlistItems(graphene.Interface): @@ -28,11 +29,11 @@ class WishlistQuery(graphene.ObjectType): @staticmethod def resolve_wishlist_items(root, info): - """ Get current user wishlist items """ - env = info.context['env'] - website = env['website'].get_current_website() + """Get current user wishlist items.""" + env = info.context["env"] + website = env["website"].get_current_website() request.website = website - wishlist_items = env['product.wishlist'].current() + wishlist_items = env["product.wishlist"].current() total_count = len(wishlist_items) return WishlistData(wishlist_items=wishlist_items, total_count=total_count) @@ -46,37 +47,39 @@ class Arguments: @staticmethod def mutate(self, info, product_id): env = info.context["env"] - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website - values = env['product.wishlist'].with_context(display_default_code=False).current() + values = ( + env["product.wishlist"].with_context(display_default_code=False).current() + ) if values.filtered(lambda v: v.product_id.id == product_id): - raise GraphQLError(_('Product already exists in the Wishlist.')) + raise GraphQLError(_("Product already exists in the Wishlist.")) WebsiteSaleWishlist().add_to_wishlist(product_id) - wishlist_items = env['product.wishlist'].current() + wishlist_items = env["product.wishlist"].current() total_count = len(wishlist_items) return WishlistData(wishlist_items=wishlist_items, total_count=total_count) class WishlistRemoveItem(graphene.Mutation): class Arguments: - wish_id = graphene.Int(required=True) + wish_id = graphene.Int(required=True) Output = WishlistData @staticmethod def mutate(self, info, wish_id): - env = info.context['env'] - Wishlist = env['product.wishlist'].sudo() + env = info.context["env"] + Wishlist = env["product.wishlist"].sudo() - wish_id = Wishlist.search([('id', '=', wish_id)], limit=1) + wish_id = Wishlist.search([("id", "=", wish_id)], limit=1) wish_id.unlink() - website = env['website'].get_current_website() + website = env["website"].get_current_website() request.website = website - wishlist_items = env['product.wishlist'].current() + wishlist_items = env["product.wishlist"].current() total_count = len(wishlist_items) return WishlistData(wishlist_items=wishlist_items, total_count=total_count) diff --git a/vuestorefront/views/res_config_settings_views.xml b/vuestorefront/views/res_config_settings_views.xml index ca08ffe..484706b 100644 --- a/vuestorefront/views/res_config_settings_views.xml +++ b/vuestorefront/views/res_config_settings_views.xml @@ -14,7 +14,10 @@

Vue Storefront

-
+
@@ -26,7 +29,10 @@
-
+
-
+
-
+
@@ -54,7 +66,11 @@
-
+
-
+
-
+
Powered by - Odoo + Odoo