diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d1bb653b06..c0682511d0 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,7 +3,6 @@ exclude: |
# NOT INSTALLABLE ADDONS
^partner_contact_company/|
^product_online_category/|
- ^shopinvader/|
^shopinvader_algolia/|
^shopinvader_assortment/|
^shopinvader_auth_api_key/|
diff --git a/requirements.txt b/requirements.txt
index 1cfd61ad0e..a5b24c18aa 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,5 @@
# generated from manifests external_dependencies
+cerberus
openupgradelib
python-slugify
+unidecode
diff --git a/setup/shopinvader/odoo/addons/shopinvader b/setup/shopinvader/odoo/addons/shopinvader
new file mode 120000
index 0000000000..b6335c1331
--- /dev/null
+++ b/setup/shopinvader/odoo/addons/shopinvader
@@ -0,0 +1 @@
+../../../../shopinvader
\ No newline at end of file
diff --git a/setup/shopinvader/setup.py b/setup/shopinvader/setup.py
new file mode 100644
index 0000000000..28c57bb640
--- /dev/null
+++ b/setup/shopinvader/setup.py
@@ -0,0 +1,6 @@
+import setuptools
+
+setuptools.setup(
+ setup_requires=['setuptools-odoo'],
+ odoo_addon=True,
+)
diff --git a/shopinvader/__manifest__.py b/shopinvader/__manifest__.py
index a12f1b09cd..ce36c5e1df 100644
--- a/shopinvader/__manifest__.py
+++ b/shopinvader/__manifest__.py
@@ -5,13 +5,13 @@
{
"name": "Shopinvader",
"summary": "Shopinvader",
- "version": "14.0.5.24.12",
+ "version": "16.0.1.0.0",
"category": "e-commerce",
"website": "https://github.com/shopinvader/odoo-shopinvader",
"author": "Akretion",
"license": "AGPL-3",
"application": True,
- "installable": False,
+ "installable": True,
"external_dependencies": {"python": ["cerberus", "unidecode"], "bin": []},
"depends": [
"base_rest",
diff --git a/shopinvader/data/mail_activity_data.xml b/shopinvader/data/mail_activity_data.xml
index f8c64713e7..a29ebe12d6 100644
--- a/shopinvader/data/mail_activity_data.xml
+++ b/shopinvader/data/mail_activity_data.xml
@@ -1,11 +1,9 @@
-
-
-
- Shop - Validate customer
- fa-tasks
- 3
- warning
-
-
+
+
+ Shop - Validate customer
+ fa-tasks
+ 3
+ warning
+
diff --git a/shopinvader/demo/account_demo.xml b/shopinvader/demo/account_demo.xml
index d1551c3ea5..c178048512 100644
--- a/shopinvader/demo/account_demo.xml
+++ b/shopinvader/demo/account_demo.xml
@@ -59,7 +59,12 @@
+
+ ShopInvader Analytic Plan
+
+
ShopInvader Analytic
+
diff --git a/shopinvader/demo/email_demo.xml b/shopinvader/demo/email_demo.xml
index 1427cd2433..3886f77898 100644
--- a/shopinvader/demo/email_demo.xml
+++ b/shopinvader/demo/email_demo.xml
@@ -3,67 +3,67 @@
Cart notification
- ${(object.user_id.email or '')|safe}
- Cart notification ${object.name}
- ${object.partner_id.id}
+ {{ (object.user_id.email or '') }}
+ Cart notification {{ object.name }}
+ {{ object.partner_id.id }}
- ${object.partner_id.lang}
+ {{ object.partner_id.lang }}
Sale notification
- ${(object.user_id.email or '')|safe}
- Sale notification ${object.name}
- ${object.partner_id.id}
+ {{ (object.user_id.email or '') }}
+ Sale notification {{ object.name }}
+ {{ object.partner_id.id }}
- ${object.partner_id.lang}
+ {{ object.partner_id.lang }}
Invoice notification
- ${(object.user_id.email or '')|safe}
- Invoice notification ${object.number}
- ${object.partner_id.id}
+ {{ (object.user_id.email or '') }}
+ Invoice notification {{ object.name }}
+ {{ object.partner_id.id }}
- ${object.partner_id.lang}
+ {{ object.partner_id.lang }}
Welcome notification
- ${(object.user_id.email or '')|safe}
- Welcome notification ${object.name}
- ${object.id}
+ {{ (object.user_id.email or '') }}
+ Welcome notification {{ object.name }}
+ {{ object.id }}
- ${object.lang}
+ {{ object.lang }}
Customer updated notification
- ${(object.user_id.email or '')|safe}
- Notification ${object.name} - Customer modified
- ${object.id}
+ {{ (object.user_id.email or '') }}
+ Notification {{ object.name }} - Customer modified
+ {{ object.id }}
- ${object.lang}
+ {{ object.lang }}
Address created notification
- ${(object.user_id.email or '')|safe}
- Notification ${object.name} - Address created
- ${object.id}
+ {{ (object.user_id.email or '') }}
+ Notification {{ object.name }} - Address created
+ {{ object.id }}
- ${object.lang}
+ {{ object.lang }}
@@ -71,12 +71,12 @@
Address updated notification
- ${(object.user_id.email or '')|safe}
- Notification ${object.name} - Address modified
- ${object.id}
+ {{ (object.user_id.email or '') }}
+ Notification {{ object.name }} - Address modified
+ {{ object.id }}
- ${object.lang}
+ {{ object.lang }}
diff --git a/shopinvader/migrations/14.0.2.2.0/pre-migration.py b/shopinvader/migrations/14.0.2.2.0/pre-migration.py
deleted file mode 100644
index a175962295..0000000000
--- a/shopinvader/migrations/14.0.2.2.0/pre-migration.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
-
-from odoo.tools.sql import column_exists, rename_column
-
-
-def migrate(cr, version):
- if column_exists(cr, "res_partner", "shopinvader_enabled"):
- rename_column(cr, "res_partner", "shopinvader_enabled", "is_shopinvader_active")
diff --git a/shopinvader/migrations/14.0.4.0.0/post-migrate.py b/shopinvader/migrations/14.0.4.0.0/post-migrate.py
deleted file mode 100644
index 4036d96981..0000000000
--- a/shopinvader/migrations/14.0.4.0.0/post-migrate.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2020 ACSONE SA/NV
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-
-import logging
-
-from odoo import SUPERUSER_ID, api
-
-_logger = logging.getLogger(__name__)
-
-
-def migrate(cr, version):
- _logger.info("Install module shopinvader_auth_api_key")
- if not version:
- return
-
- env = api.Environment(cr, SUPERUSER_ID, {})
- module = env["ir.module.module"].search(
- [("name", "=", "shopinvader_auth_api_key"), ("state", "=", "uninstalled")]
- )
- if module:
- module.write({"state": "to install"})
- return
diff --git a/shopinvader/migrations/14.0.4.1.0/post-migrate.py b/shopinvader/migrations/14.0.4.1.0/post-migrate.py
deleted file mode 100644
index 1f808a7f47..0000000000
--- a/shopinvader/migrations/14.0.4.1.0/post-migrate.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2021 Camptocamp (http://www.camptocamp.com).
-# @author Iván Todorovich
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-
-from odoo import SUPERUSER_ID, api
-
-
-def _fix_multicompany_ir_rules(env):
- """Fix Multi-Company ir.rules
-
- These rules are set as noupdate=1, but they weren't migrated properly
- as they don't account for multiple companies set in context.
- """
- refs = [
- "shopinvader_backend_comp_rule",
- "shopinvader_category_comp_rule",
- "shopinvader_partner_comp_rule",
- "shopinvader_product_comp_rule",
- "shopinvader_variant_comp_rule",
- ]
- for ref in refs:
- xmlid = "shopinvader.{}".format(ref)
- rule = env.ref(xmlid, raise_if_not_found=False)
- if not rule:
- continue
- rule.domain_force = """
- [
- '|',
- ('company_id', '=', False),
- ('company_id', 'in', company_ids),
- ]
- """
-
-
-def migrate(cr, version):
- if not version:
- return
- with api.Environment.manage():
- env = api.Environment(cr, SUPERUSER_ID, {})
- _fix_multicompany_ir_rules(env)
diff --git a/shopinvader/migrations/14.0.5.14.1/post-migrate.py b/shopinvader/migrations/14.0.5.14.1/post-migrate.py
deleted file mode 100644
index 82b749f041..0000000000
--- a/shopinvader/migrations/14.0.5.14.1/post-migrate.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2022 ACSONE SA/NV ()
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-
-from odoo import SUPERUSER_ID, api
-
-
-def _upgrade_shopinvader_model_access(env):
- """
- Upgrade the "shopinvader partner binding edit" access rights
- (as it's not update)
- """
- sec_group = env.ref("shopinvader.group_shopinvader_partner_binding")
- env.ref("shopinvader.access_shopinvader_partner_edit").write(
- {
- "group_id": sec_group.id,
- }
- )
-
-
-def migrate(cr, version):
- if not version:
- return
- with api.Environment.manage():
- env = api.Environment(cr, SUPERUSER_ID, {})
- _upgrade_shopinvader_model_access(env)
diff --git a/shopinvader/models/product_product.py b/shopinvader/models/product_product.py
index 8c90bdbfde..81ae6adb6b 100644
--- a/shopinvader/models/product_product.py
+++ b/shopinvader/models/product_product.py
@@ -30,7 +30,7 @@ def _search_shopinvader_backend_ids(self, operator, value):
"Shopinvader binded",
compute="_compute_is_shopinvader_binded",
store=True,
- index=True,
+ index="btree",
help="Technical field to know if this product is related by a"
"(at least one) shopinvader backend",
)
diff --git a/shopinvader/models/product_template.py b/shopinvader/models/product_template.py
index 7e6333e1ba..7aa4d3ed12 100644
--- a/shopinvader/models/product_template.py
+++ b/shopinvader/models/product_template.py
@@ -37,13 +37,11 @@ def unlink(self):
record.shopinvader_bind_ids.unlink()
return super(ProductTemplate, self).unlink()
- @api.model
- def create(self, vals):
+ @api.model_create_multi
+ def create(self, vals_list):
"""Due to the order in which product.template, product.product,
and bindings, are created, this is to handle the case in which
a product.template + its bindings are created in one function call"""
- result = super().create(vals)
- bindings = result.shopinvader_bind_ids
- if bindings:
- bindings.active = True
+ result = super().create(vals_list)
+ result.mapped("shopinvader_bind_ids").active = True
return result
diff --git a/shopinvader/models/res_partner.py b/shopinvader/models/res_partner.py
index 70bf99214e..3db14c9775 100644
--- a/shopinvader/models/res_partner.py
+++ b/shopinvader/models/res_partner.py
@@ -59,7 +59,7 @@ def _is_partner_duplicate_prevented(self):
def _check_unique_email(self):
if not self._is_partner_duplicate_prevented():
return True
- self.env["res.partner"].flush(["email", "shopinvader_bind_ids"])
+ self.env["res.partner"].flush_model(["email", "shopinvader_bind_ids"])
self.env.cr.execute(
"""
SELECT
diff --git a/shopinvader/models/sale.py b/shopinvader/models/sale.py
index 744a4ec17e..759c0ed8a6 100644
--- a/shopinvader/models/sale.py
+++ b/shopinvader/models/sale.py
@@ -129,5 +129,5 @@ class SaleOrderLine(models.Model):
def reset_price_tax(self):
for line in self:
- line.product_id_change()
- line._onchange_discount()
+ line._compute_tax_id()
+ line._compute_discount()
diff --git a/shopinvader/models/shopinvader_backend.py b/shopinvader/models/shopinvader_backend.py
index e584629def..483c0c65f7 100644
--- a/shopinvader/models/shopinvader_backend.py
+++ b/shopinvader/models/shopinvader_backend.py
@@ -551,10 +551,10 @@ def _send_notification(self, notification, record):
("notification_type", "=", notification),
]
)
- description = _("Notify %s for %s,%s") % (
- notification,
- record._name,
- record.id,
+ description = _("Notify {notification} for {name},{id}").format(
+ notification=notification,
+ name=record._name,
+ id=record.id,
)
for notif in notifs:
notif.with_delay(description=description).send(record.id)
diff --git a/shopinvader/models/shopinvader_binding.py b/shopinvader/models/shopinvader_binding.py
index b8adcf23cd..bffe263b81 100644
--- a/shopinvader/models/shopinvader_binding.py
+++ b/shopinvader/models/shopinvader_binding.py
@@ -11,7 +11,7 @@ class ShopinvaderBinding(models.AbstractModel):
backend_id = fields.Many2one("shopinvader.backend", string="Backend", required=True)
company_id = fields.Many2one(
- related="backend_id.company_id", store=True, index=True
+ related="backend_id.company_id", store=True, index="btree"
)
external_id = fields.Char(string="External ID")
sync_date = fields.Datetime(string="Last synchronization date")
diff --git a/shopinvader/models/shopinvader_category.py b/shopinvader/models/shopinvader_category.py
index fe1dced815..55aecf09d2 100644
--- a/shopinvader/models/shopinvader_category.py
+++ b/shopinvader/models/shopinvader_category.py
@@ -23,7 +23,7 @@ class ShopinvaderCategory(models.Model):
"product.category",
required=True,
ondelete="cascade",
- index=True,
+ index="btree",
)
sequence = fields.Integer()
meta_description = fields.Char()
@@ -36,7 +36,7 @@ class ShopinvaderCategory(models.Model):
"Shopinvader Parent",
compute="_compute_parent_category",
store=True,
- index=True,
+ index="btree",
compute_sudo=True,
)
shopinvader_child_ids = fields.Many2many(
@@ -44,13 +44,14 @@ class ShopinvaderCategory(models.Model):
"Shopinvader Childs",
compute="_compute_child_category",
)
- level = fields.Integer(compute="_compute_level")
+ level = fields.Integer(compute="_compute_level", recursive=True)
active = fields.Boolean(
default=True,
compute="_compute_active",
store=True,
readonly=False,
)
+ automatic_url_key = fields.Char(recursive=True)
_sql_constraints = [
(
diff --git a/shopinvader/models/shopinvader_notification.py b/shopinvader/models/shopinvader_notification.py
index 5025c4090f..be581197e4 100644
--- a/shopinvader/models/shopinvader_notification.py
+++ b/shopinvader/models/shopinvader_notification.py
@@ -13,7 +13,6 @@ class ShopinvaderNotification(models.Model):
backend_id = fields.Many2one("shopinvader.backend", "Backend", required=False)
notification_type = fields.Selection(
selection="_selection_notification_type",
- string="Notification Type",
required=True,
)
model_id = fields.Many2one("ir.model", "Model", required=True, ondelete="cascade")
diff --git a/shopinvader/models/shopinvader_partner.py b/shopinvader/models/shopinvader_partner.py
index b38e15e2aa..ae8fa28b93 100644
--- a/shopinvader/models/shopinvader_partner.py
+++ b/shopinvader/models/shopinvader_partner.py
@@ -72,10 +72,12 @@ def _compute_role(self):
def _get_role(self):
return self.backend_id.customer_default_role
- @api.model
- def create(self, vals):
- vals = self._prepare_create_params(vals)
- return super(ShopinvaderPartner, self).create(vals)
+ @api.model_create_multi
+ def create(self, vals_list):
+ new_vals_list = []
+ for vals in vals_list:
+ new_vals_list.append(self._prepare_create_params(vals))
+ return super(ShopinvaderPartner, self).create(new_vals_list)
@api.model
def _prepare_create_params(self, vals):
diff --git a/shopinvader/models/shopinvader_product.py b/shopinvader/models/shopinvader_product.py
index 1cbad2eadf..6343766312 100644
--- a/shopinvader/models/shopinvader_product.py
+++ b/shopinvader/models/shopinvader_product.py
@@ -16,7 +16,7 @@ class ShopinvaderProduct(models.Model):
"product.template",
required=True,
ondelete="cascade",
- index=True,
+ index="btree",
check_company=True,
)
meta_description = fields.Char()
@@ -37,7 +37,6 @@ class ShopinvaderProduct(models.Model):
related="backend_id.use_shopinvader_product_name", store=True
)
shopinvader_name = fields.Char(
- string="Shopinvader Name",
help="Name for shopinvader, if not set the product name will be used.",
)
shopinvader_display_name = fields.Char(compute="_compute_name", readonly=True)
@@ -142,12 +141,13 @@ def _create_shopinvader_variant(self):
shopinv_variants |= shopinv_variant_obj.create(vals)
return shopinv_variants
- @api.model
- def create(self, vals):
- binding = super(ShopinvaderProduct, self).create(vals)
+ @api.model_create_multi
+ def create(self, vals_list):
+ bindings = super(ShopinvaderProduct, self).create(vals_list)
if self.env.context.get("map_children"):
- binding._create_shopinvader_variant()
- return binding
+ for binding in bindings:
+ binding._create_shopinvader_variant()
+ return bindings
def _get_url_keywords(self):
self.ensure_one()
diff --git a/shopinvader/models/shopinvader_variant.py b/shopinvader/models/shopinvader_variant.py
index 2fbe510120..f20979bfb0 100644
--- a/shopinvader/models/shopinvader_variant.py
+++ b/shopinvader/models/shopinvader_variant.py
@@ -27,14 +27,14 @@ class ShopinvaderVariant(models.Model):
"shopinvader.product",
required=True,
ondelete="cascade",
- index=True,
+ index="btree",
check_company=True,
)
tmpl_record_id = fields.Many2one(
string="Product template",
related="shopinvader_product_id.record_id",
store=True,
- index=True,
+ index="btree",
check_company=True,
)
record_id = fields.Many2one(
@@ -42,7 +42,7 @@ class ShopinvaderVariant(models.Model):
comodel_name="product.product",
required=True,
ondelete="cascade",
- index=True,
+ index="btree",
check_company=True,
)
variant_count = fields.Integer(
@@ -217,7 +217,7 @@ def _get_price(
# Always filter taxes by the company
taxes = product.taxes_id.filtered(lambda tax: tax.company_id == company)
# Apply fiscal position
- taxes = fposition.map_tax(taxes, product) if fposition else taxes
+ taxes = fposition.map_tax(taxes) if fposition else taxes
# Set context. Some of the methods used here depend on these values
product_context = dict(
self.env.context,
@@ -228,9 +228,11 @@ def _get_price(
)
product = product.with_context(**product_context)
pricelist = pricelist.with_context(**product_context) if pricelist else None
- # If we have a pricelist, use product.price as it already accounts
- # for pricelist rules and quantity (in context)
- price_unit = product.price if pricelist else product.lst_price
+ price_unit = (
+ pricelist._get_product_price(product, qty, date=date)
+ if pricelist
+ else product.lst_price
+ )
price_unit = AccountTax._fix_tax_included_price_company(
price_unit, product.taxes_id, taxes, company
)
@@ -246,26 +248,12 @@ def _get_price(
# Handle pricelists.discount_policy == "without_discount"
if pricelist and pricelist.discount_policy == "without_discount":
# Get the price rule
- price_unit, rule_id = pricelist.get_product_price_rule(
- product, qty, None, date=date
+ price_unit, rule_id = pricelist._get_product_price_rule(
+ product, qty, date=date
)
# Get the price before applying the pricelist
- SaleOrderLine = self.env["sale.order.line"].with_context(**product_context)
- original_price_unit, currency = SaleOrderLine._get_real_price_currency(
- product, rule_id, qty, product.uom_id, pricelist.id
- )
+ original_price_unit = product.lst_price
price_dp = self.env["decimal.precision"].precision_get("Product Price")
- # Convert currency if necessary
- if (
- not float_is_zero(original_price_unit, precision_digits=price_dp)
- and pricelist.currency_id != currency
- ):
- original_price_unit = currency._convert(
- original_price_unit,
- pricelist.currency_id,
- company or self.env.company,
- fields.Date.today(),
- )
# Compute discount
if not float_is_zero(
original_price_unit, precision_digits=price_dp
@@ -297,7 +285,11 @@ def _compute_main_product(self):
# Respect same order.
order_by = [x.strip() for x in self.env["product.product"]._order.split(",")]
backends = self.mapped("backend_id")
- fields_to_read = ["shopinvader_product_id", "backend_id", "lang_id"] + order_by
+ fields_to_read = [
+ "shopinvader_product_id",
+ "backend_id",
+ "lang_id",
+ ] + [f.split(" ")[0] for f in order_by]
product_ids = self.mapped("shopinvader_product_id").ids
# Use sudo to bypass permissions (we don't care)
_variants = self.sudo().search(
@@ -315,22 +307,23 @@ def _compute_main_product(self):
)
def pick_1st_variant(variants):
- # NOTE: if the order is changed by adding `asc/desc` this can be broken
- # but it's very unlikely that the default order for product.product
- # will be changed.
def get_value(record, key):
if record[key] is False and self._fields[key].type in ("char", "text"):
return ""
else:
return record[key]
- ordered = sorted(
- variants, key=lambda var: [get_value(var, x) for x in order_by]
- )
- return ordered[0].get("id") if ordered else None
+ for order_key in reversed(order_by):
+ order_key_split = order_key.split(" ")
+ reverse = len(order_key_split) > 1 and order_key_split[1] == "desc"
+ variants.sort(
+ key=lambda var: get_value(var, order_key_split[0]),
+ reverse=reverse,
+ )
+ return variants[0].get("id") if variants else None
main_by_product = {
- product: pick_1st_variant(tuple(variants))
+ product: pick_1st_variant(list(variants))
for product, variants in var_by_product
}
for record in self:
diff --git a/shopinvader/services/abstract_download.py b/shopinvader/services/abstract_download.py
index b672633318..381811c628 100644
--- a/shopinvader/services/abstract_download.py
+++ b/shopinvader/services/abstract_download.py
@@ -69,10 +69,10 @@ def _get_binary_content(self, target, params=None):
target_report_def = self._get_report_action(target, params=params)
report_name = target_report_def.get("report_name")
report_type = target_report_def.get("report_type")
- report = self._get_report(report_name, report_type)
- content, extension = report._render(
- target.ids, data={"report_type": report_type}
+ content, extension = self.env["ir.actions.report"]._render(
+ report_name, target.ids, data={"report_type": report_type}
)
+ report = self._get_report(report_name, report_type)
filename = self._get_binary_content_filename(
target, report, extension, params=params
)
diff --git a/shopinvader/services/cart.py b/shopinvader/services/cart.py
index 6bc7770506..d48c160ce6 100644
--- a/shopinvader/services/cart.py
+++ b/shopinvader/services/cart.py
@@ -243,7 +243,7 @@ def _upgrade_cart_item_quantity(self, cart, item, params, action="replace"):
cart._cache["order_line"] = tuple(real_line_ids)
vals.update(new_values)
item.order_id.write({"order_line": [(1, item.id, vals)]})
- cart.recompute()
+ cart.flush_recordset()
def _do_clear_cart_cancel(self, cart):
"""
@@ -304,7 +304,7 @@ def _add_item(self, cart, params):
else:
with self.env.norecompute():
item = self._create_cart_line(cart, params)
- cart.recompute()
+ cart.flush_recordset()
return item
def _create_cart_line(self, cart, params):
@@ -513,6 +513,7 @@ def _prepare_cart_item(self, params, cart):
"product_uom_qty": params["item_qty"],
"order_id": cart.id,
"sequence": max(cart.order_line.mapped("sequence"), default=0) + 1,
+ "currency_id": cart.currency_id.id,
}
)
return _params
diff --git a/shopinvader/services/sale.py b/shopinvader/services/sale.py
index c5d2ef4521..3ff515d2de 100644
--- a/shopinvader/services/sale.py
+++ b/shopinvader/services/sale.py
@@ -142,7 +142,7 @@ def _is_cancel_allowed(self, sale):
def _cancel(self, sale, reset_to_cart=False):
if not self._is_cancel_allowed(sale):
raise UserError(_("This order cannot be cancelled"))
- sale.action_cancel()
+ sale.with_context(disable_cancel_warning=True).action_cancel()
if reset_to_cart:
sale.action_draft()
sale.typology = "cart"
diff --git a/shopinvader/services/service.py b/shopinvader/services/service.py
index 703ce0be94..c98eaf729a 100644
--- a/shopinvader/services/service.py
+++ b/shopinvader/services/service.py
@@ -97,7 +97,11 @@ def _scope_to_domain(self, scope):
domain.append((key, op, value))
return domain
except Exception as e:
- raise UserError(_("Invalid scope %s, error: %s") % (str(scope), str(e)))
+ raise UserError(
+ _("Invalid scope {scope}, error: {error}").format(
+ scope=str(scope), error=str(e)
+ )
+ ) from e
# Validator
def _default_validator_search(self):
@@ -171,7 +175,9 @@ def _get(self, _id):
record = self._exposed_model.search(domain)
if not record:
raise MissingError(
- _("The record %s %s does not exist") % (self._expose_model, _id)
+ _("The record {model} {id} does not exist").format(
+ model=self._expose_model, id=_id
+ )
)
else:
return record
diff --git a/shopinvader/tests/common.py b/shopinvader/tests/common.py
index f45c545277..ae117191df 100644
--- a/shopinvader/tests/common.py
+++ b/shopinvader/tests/common.py
@@ -4,12 +4,11 @@
# pylint: disable=method-required-super
from contextlib import contextmanager
-
-import mock
+from unittest import mock
from odoo import fields
from odoo.exceptions import MissingError
-from odoo.tests import SavepointCase
+from odoo.tests import TransactionCase
from odoo.addons.base_rest.controllers.main import RestController
from odoo.addons.base_rest.core import _rest_controllers_per_module
@@ -137,7 +136,7 @@ def _perform_created_job(self):
self._perform_job(job)
-class CommonCase(SavepointCase, CommonMixin):
+class CommonCase(TransactionCase, CommonMixin):
# by default disable tracking suite-wise, it's a time saver :)
tracking_disable = True
@@ -160,7 +159,7 @@ class ControllerTest(RestController):
# TODO FIXME
# It seem that setUpComponent / setUpRegistry loose stuff from
# the cache so we do an explicit flush here to avoid losing data
- cls.env["base"].flush()
+ cls.env["base"].flush_model()
cls.setUpComponent()
cls.setUpRegistry()
@@ -170,7 +169,7 @@ def tearDownClass(cls):
_rest_controllers_per_module["shopinvader"] = []
def setUp(self):
- SavepointCase.setUp(self)
+ TransactionCase.setUp(self)
CommonMixin.setUp(self)
shopinvader_response.set_testmode(True)
@@ -310,12 +309,12 @@ def _make_payment(self, invoice):
"""
self._ensure_posted(invoice)
ctx = {"active_ids": invoice.ids, "active_model": "account.move"}
- wizard_obj = self.register_payments_obj.with_context(ctx)
+ wizard_obj = self.register_payments_obj.with_context(**ctx)
register_payments = wizard_obj.create(
{
"payment_date": fields.Date.today(),
"journal_id": self.bank_journal_euro.id,
- "payment_method_id": self.payment_method_manual_in.id,
+ "payment_method_line_id": self.payment_method_line_manual_in.id,
}
)
register_payments._create_payments()
diff --git a/shopinvader/tests/test_cart.py b/shopinvader/tests/test_cart.py
index eeb0a752dc..85d89a3b1b 100644
--- a/shopinvader/tests/test_cart.py
+++ b/shopinvader/tests/test_cart.py
@@ -1,6 +1,7 @@
# Copyright 2017 Akretion (http://www.akretion.com).
# @author Sébastien BEAU
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+from datetime import timedelta
from odoo import fields
from odoo.tools import mute_logger
@@ -186,7 +187,7 @@ def test_cart_pricelist_apply(self):
"price_surcharge": -100,
"base_pricelist_id": first_pricelist.id,
"date_start": fields.Date.today(),
- "date_end": fields.Date.today(),
+ "date_end": fields.Date.today() + timedelta(days=1),
},
)
],
@@ -456,7 +457,7 @@ def test_cart_misc_data_update(self):
def test_writing_note(self):
res = self.service.dispatch("update", params={"note": "FOO"})
self.assertIn("note", res["data"])
- self.assertEqual("FOO", res["data"]["note"])
+ self.assertEqual("FOO
", res["data"]["note"])
class ConnectedCartNoTaxCase(CartCase):
diff --git a/shopinvader/tests/test_cart_item.py b/shopinvader/tests/test_cart_item.py
index 64a0d74780..bf90751239 100644
--- a/shopinvader/tests/test_cart_item.py
+++ b/shopinvader/tests/test_cart_item.py
@@ -170,18 +170,18 @@ def test_pricelist_product_price_unit_without_discount(self):
self.pricelist.discount_policy = "without_discount"
amount = self._test_pricelist_product()
# into the cart, the price must be the price without discount
- self.assertEqual(amount["price"], 16.5)
+ self.assertEqual(amount["price"], 33)
# but the total for the line into the cart info must be the price with
# discount
- self.assertEqual(amount["total"], 14.85)
+ self.assertEqual(amount["total"], 29.7)
def test_pricelist_product_price_unit_with_discount(self):
self.pricelist.discount_policy = "with_discount"
amount = self._test_pricelist_product()
# into the cart, the price must be the price with discount
- self.assertEqual(amount["price"], 14.85)
+ self.assertEqual(amount["price"], 29.7)
# same for the total
- self.assertEqual(amount["total"], 14.85)
+ self.assertEqual(amount["total"], 29.7)
def test_upgrade_last_update_date(self):
last_external_update_date = self._get_last_external_update_date(self.cart)
diff --git a/shopinvader/tests/test_invoice.py b/shopinvader/tests/test_invoice.py
index 1d1528a9f7..c90b5beed4 100644
--- a/shopinvader/tests/test_invoice.py
+++ b/shopinvader/tests/test_invoice.py
@@ -15,6 +15,9 @@ def setUpClass(cls):
cls.payment_method_manual_in = cls.env.ref(
"account.account_payment_method_manual_in"
)
+ cls.payment_method_line_manual_in = cls.env[
+ "account.payment.method.line"
+ ].search([("payment_method_id", "=", cls.payment_method_manual_in.id)], limit=1)
cls.bank_journal_euro = cls.journal_obj.create(
{"name": "Bank", "type": "bank", "code": "BNK627"}
)
diff --git a/shopinvader/tests/test_product.py b/shopinvader/tests/test_product.py
index 7ba0488256..ad041506d1 100644
--- a/shopinvader/tests/test_product.py
+++ b/shopinvader/tests/test_product.py
@@ -66,7 +66,7 @@ def test_product_price_rounding(self):
"percent_price": 50,
}
)
- self.variant.price = 423.4
+ self.variant.list_price = 423.4
self.assertEqual(self.shopinvader_variant.price["default"]["value"], 211.70)
@contextmanager
@@ -79,7 +79,7 @@ def _check_url(self, shopinvader_variants):
shopinv_variant_names = {r: r.name for r in shopinvader_variants}
shopinv_variant_urls = {r: r.url_url_ids for r in shopinvader_variants}
yield
- shopinvader_variants.refresh()
+ shopinvader_variants.invalidate_recordset()
for shopinv_variant in shopinvader_variants:
existing_urls = shopinv_variant_urls.get(shopinv_variant)
new_url = shopinv_variant.url_url_ids.filtered(
@@ -342,7 +342,7 @@ def test_product_category_with_two_lang(self):
lang = self._install_lang("base.lang_fr")
product = self.env.ref("product.product_product_4")
product.with_context(lang="fr_FR").name = "Bureau Personnalisable"
- product.flush()
+ product.flush_recordset()
self.backend.lang_ids |= lang
self.backend.bind_all_category()
self.backend.bind_all_product()
@@ -353,7 +353,7 @@ def test_product_category_with_two_lang(self):
if binding.lang_id.code == "fr_FR":
self.assertEqual(binding.url_key, "bureau-personnalisable")
elif binding.lang_id.code == "en_US":
- self.assertEqual(binding.url_key, "customizable-desk-config")
+ self.assertEqual(binding.url_key, "customizable-desk")
def test_create_product_binding1(self):
"""
@@ -770,11 +770,11 @@ def test_product_url1(self):
self.assertEqual(urls.model_id, bind_product)
bind_product.write({"active": False})
- bind_product.flush()
+ bind_product.flush_recordset()
self.assertEqual(urls.model_id, bind_categ)
bind_product.write({"active": True})
- bind_product.flush()
+ bind_product.flush_recordset()
self.assertEqual(urls.model_id, bind_product)
def test_product_url2(self):
@@ -834,11 +834,11 @@ def test_product_url2(self):
self.assertEqual(urls.model_id, bind_product)
bind_product.write({"active": False})
- bind_product.flush()
+ bind_product.flush_recordset()
self.assertEqual(urls.model_id, bind_categ2)
bind_product.write({"active": True})
- bind_product.flush()
+ bind_product.flush_recordset()
self.assertEqual(urls.model_id, bind_product)
@contextmanager
@@ -962,7 +962,8 @@ def test_main_product(self):
# change order
tmpl.product_variant_ids[0].default_code = "ZZZZZZZ"
tmpl.product_variant_ids[0].name = "ZZZZZZ"
- tmpl.product_variant_ids.invalidate_cache()
+ tmpl.product_variant_ids.invalidate_recordset()
+ invader_variants.invalidate_recordset()
main_variant1 = tmpl.product_variant_ids[0]
self.assertNotEqual(main_variant, main_variant1)
self.assertTrue(
diff --git a/shopinvader/tests/test_product_filter.py b/shopinvader/tests/test_product_filter.py
index 00fc1186a5..9421cd3378 100644
--- a/shopinvader/tests/test_product_filter.py
+++ b/shopinvader/tests/test_product_filter.py
@@ -86,7 +86,7 @@ def test_product_filter_field_constrains(self):
with self.assertRaises(exceptions.UserError) as err:
self.filter_on_field.write({"field_id": False})
self.assertEqual(
- err.exception.name,
+ err.exception.args[0],
"Product filter ID=%d is based on field: "
"requires a field!" % self.filter_on_field.id,
)
@@ -95,7 +95,7 @@ def test_product_filter_attr_constrains(self):
with self.assertRaises(exceptions.UserError) as err:
self.filter_on_attr.write({"variant_attribute_id": False})
self.assertEqual(
- err.exception.name,
+ err.exception.args[0],
"Product filter ID=%d is based on variant attribute: "
"requires an attribute!" % self.filter_on_attr.id,
)
diff --git a/shopinvader/tests/test_res_partner.py b/shopinvader/tests/test_res_partner.py
index bb2661ad1d..87ea1fe94b 100644
--- a/shopinvader/tests/test_res_partner.py
+++ b/shopinvader/tests/test_res_partner.py
@@ -5,10 +5,10 @@
from odoo.exceptions import ValidationError
-from odoo.addons.component.tests.common import SavepointComponentCase
+from odoo.addons.component.tests.common import TransactionComponentCase
-class TestResPartner(SavepointComponentCase):
+class TestResPartner(TransactionComponentCase):
@classmethod
def setUpClass(cls):
super(TestResPartner, cls).setUpClass()
diff --git a/shopinvader/tests/test_sale.py b/shopinvader/tests/test_sale.py
index 1fa2df6979..e6b6e455a6 100644
--- a/shopinvader/tests/test_sale.py
+++ b/shopinvader/tests/test_sale.py
@@ -20,6 +20,9 @@ def setUpClass(cls):
cls.payment_method_manual_in = cls.env.ref(
"account.account_payment_method_manual_in"
)
+ cls.payment_method_line_manual_in = cls.env[
+ "account.payment.method.line"
+ ].search([("payment_method_id", "=", cls.payment_method_manual_in.id)], limit=1)
cls.bank_journal_euro = cls.journal_obj.create(
{"name": "Bank", "type": "bank", "code": "BNK6278"}
)
diff --git a/shopinvader/tests/test_sale_cancel.py b/shopinvader/tests/test_sale_cancel.py
index eb64de07d3..02eaa34657 100644
--- a/shopinvader/tests/test_sale_cancel.py
+++ b/shopinvader/tests/test_sale_cancel.py
@@ -21,7 +21,7 @@ def test_sale_cancel_fail_if_delivered(self):
self.sale.action_confirm()
# deliver only 1 line
self.sale.order_line[0].write({"qty_delivered": 1})
- self.env["sale.order.line"].flush(records=self.sale.order_line)
+ self.sale.order_line.flush_recordset()
with self.assertRaises(UserError):
self.service.dispatch("cancel", self.sale.id)
self.assertEqual("sale", self.sale.typology)
diff --git a/shopinvader/tests/test_settings.py b/shopinvader/tests/test_settings.py
index bbe7e7a1c5..353bbaa40e 100644
--- a/shopinvader/tests/test_settings.py
+++ b/shopinvader/tests/test_settings.py
@@ -19,7 +19,7 @@
"Professor",
)
EXPECTED_GET_INDUSTRY = (
- "Administrative",
+ "Administrative/Utilities",
"Agriculture",
"Construction",
"Education",
@@ -27,7 +27,7 @@
"Entertainment",
"Extraterritorial",
"Finance/Insurance",
- "Food",
+ "Food/Hospitality",
"Health/Social",
"Households",
"IT/Communication",
@@ -37,7 +37,7 @@
"Public Administration",
"Real Estate",
"Scientific",
- "Transportation",
+ "Transportation/Logistics",
"Water supply",
"Wholesale/Retail",
)
diff --git a/shopinvader/tests/test_shopinvader_category.py b/shopinvader/tests/test_shopinvader_category.py
index c49f0f87d0..5039cab10a 100644
--- a/shopinvader/tests/test_shopinvader_category.py
+++ b/shopinvader/tests/test_shopinvader_category.py
@@ -1,11 +1,11 @@
# Copyright 2020 Camptocamp (http://www.camptocamp.com).
# @author Simone Orsi
-from odoo.addons.component.tests.common import SavepointComponentCase
+from odoo.addons.component.tests.common import TransactionComponentCase
from .common import CommonMixin
-class TestShopinvaderCategoryBase(SavepointComponentCase, CommonMixin):
+class TestShopinvaderCategoryBase(TransactionComponentCase, CommonMixin):
@classmethod
def setUpClass(cls):
super(TestShopinvaderCategoryBase, cls).setUpClass()
diff --git a/shopinvader/tests/test_shopinvader_partner.py b/shopinvader/tests/test_shopinvader_partner.py
index dc78f107cd..4a8f151edb 100644
--- a/shopinvader/tests/test_shopinvader_partner.py
+++ b/shopinvader/tests/test_shopinvader_partner.py
@@ -7,10 +7,10 @@
from odoo.tools import mute_logger
-from odoo.addons.component.tests.common import SavepointComponentCase
+from odoo.addons.component.tests.common import TransactionComponentCase
-class TestShopinvaderPartner(SavepointComponentCase):
+class TestShopinvaderPartner(TransactionComponentCase):
@classmethod
def setUpClass(cls):
super(TestShopinvaderPartner, cls).setUpClass()
@@ -124,7 +124,7 @@ def test_partner_no_duplicate_child(self):
}
)
self.assertEqual(partner, binding.record_id)
- partner.refresh()
+ partner.invalidate_recordset()
self.assertTrue(partner.child_ids)
self.assertEqual(1, len(partner.child_ids))
child = partner.child_ids
diff --git a/shopinvader/tests/test_shopinvader_partner_binding.py b/shopinvader/tests/test_shopinvader_partner_binding.py
index becc5d1a93..9f21a497c5 100644
--- a/shopinvader/tests/test_shopinvader_partner_binding.py
+++ b/shopinvader/tests/test_shopinvader_partner_binding.py
@@ -31,7 +31,7 @@ def test_binding1(self):
"active_model": self.partner._name,
}
)
- wizard_obj = self.binding_wiz_obj.with_context(context)
+ wizard_obj = self.binding_wiz_obj.with_context(**context)
fields_list = wizard_obj.fields_get().keys()
values = wizard_obj.default_get(fields_list)
values.update({"shopinvader_backend_id": self.backend.id})
@@ -40,7 +40,7 @@ def test_binding1(self):
wizard.binding_lines.write({"bind": False})
with self.assertRaises(exceptions.UserError) as e:
wizard.action_apply()
- self.assertIn("unbind is not implemented", e.exception.name)
+ self.assertIn("unbind is not implemented", e.exception.args[0])
shopinv_partner = self.partner._get_invader_partner(self.backend)
# As we set bind = False, we check if the binding is not executed.
self.assertFalse(shopinv_partner)
diff --git a/shopinvader/tests/test_shopinvader_variant_binding_wizard.py b/shopinvader/tests/test_shopinvader_variant_binding_wizard.py
index e648b2b2df..bf44549ed7 100644
--- a/shopinvader/tests/test_shopinvader_variant_binding_wizard.py
+++ b/shopinvader/tests/test_shopinvader_variant_binding_wizard.py
@@ -2,12 +2,12 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.tools import mute_logger
-from odoo.addons.component.tests.common import SavepointComponentCase
+from odoo.addons.component.tests.common import TransactionComponentCase
@mute_logger("odoo.models.unlink")
@mute_logger("odoo.addons.base.models.ir_model")
-class TestShopinvaderVariantBindingWizard(SavepointComponentCase):
+class TestShopinvaderVariantBindingWizard(TransactionComponentCase):
@classmethod
def setUpClass(cls):
super(TestShopinvaderVariantBindingWizard, cls).setUpClass()
diff --git a/shopinvader/tests/test_shopinvader_variant_seo_title.py b/shopinvader/tests/test_shopinvader_variant_seo_title.py
index 5bbd69eb7a..6c98ac0bea 100644
--- a/shopinvader/tests/test_shopinvader_variant_seo_title.py
+++ b/shopinvader/tests/test_shopinvader_variant_seo_title.py
@@ -46,7 +46,7 @@ def test_public_name_empty(self):
self.assertEqual(variant.seo_title, title)
self.assertEqual(variant.manual_seo_title, title)
# Invalidate cache to ensure data stay in memory
- self.shopinvader_variants.invalidate_cache()
+ self.shopinvader_variants.invalidate_recordset()
self._check_expected_seo_name(self.backend, self.shopinvader_variants)
def test_public_name_normal(self):
diff --git a/shopinvader/tests/test_utils.py b/shopinvader/tests/test_utils.py
index 799c2f4df6..89a19f7929 100644
--- a/shopinvader/tests/test_utils.py
+++ b/shopinvader/tests/test_utils.py
@@ -2,7 +2,7 @@
# @author Simone Orsi
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-import mock
+from unittest import mock
from odoo.addons.shopinvader import utils # pylint: disable=W7950
diff --git a/shopinvader/views/product_filter_view.xml b/shopinvader/views/product_filter_view.xml
index 65fc096873..42d1e9fbde 100644
--- a/shopinvader/views/product_filter_view.xml
+++ b/shopinvader/views/product_filter_view.xml
@@ -32,7 +32,7 @@
product.filter
-
+
diff --git a/shopinvader/views/shopinvader_cart_step_view.xml b/shopinvader/views/shopinvader_cart_step_view.xml
index 9ff56e4cd4..b0f5849cba 100644
--- a/shopinvader/views/shopinvader_cart_step_view.xml
+++ b/shopinvader/views/shopinvader_cart_step_view.xml
@@ -4,7 +4,7 @@
shopinvader.cart.step
-
+
diff --git a/shopinvader/views/shopinvader_category_view.xml b/shopinvader/views/shopinvader_category_view.xml
index d725123132..58bd7a72a7 100644
--- a/shopinvader/views/shopinvader_category_view.xml
+++ b/shopinvader/views/shopinvader_category_view.xml
@@ -74,7 +74,7 @@
shopinvader.category
-
+
diff --git a/shopinvader/views/shopinvader_partner_view.xml b/shopinvader/views/shopinvader_partner_view.xml
index 6eb81aff88..64f4d1cbfe 100644
--- a/shopinvader/views/shopinvader_partner_view.xml
+++ b/shopinvader/views/shopinvader_partner_view.xml
@@ -23,6 +23,7 @@
+
shopinvader.partner
-
+
+
shopinvader.variant.tree (in shopinvader)
shopinvader.variant
-
+
@@ -105,6 +105,7 @@
/>
+