822 lines
32 KiB
Python
822 lines
32 KiB
Python
import json
|
|
import re
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from odoo.tests import common, tagged
|
|
|
|
from odoo.addons.export_json.controller import main as controller_module
|
|
from odoo.addons.export_json.controller.main import JsonExportFormat
|
|
|
|
|
|
@tagged("export_json")
|
|
class TestExportJson(common.HttpCase):
|
|
def setUp(self):
|
|
self.partner = self.env["res.partner"].create(
|
|
{
|
|
"name": "TEST",
|
|
"email": "test@test.fr",
|
|
"type": "contact",
|
|
"child_ids": [
|
|
(
|
|
0,
|
|
None,
|
|
{
|
|
"name": "test child 1",
|
|
"email": "test_child_1@test.fr",
|
|
"type": "delivery",
|
|
},
|
|
),
|
|
(
|
|
0,
|
|
None,
|
|
{
|
|
"name": "test child 2",
|
|
"email": "test_child_2@test.fr",
|
|
"type": "invoice",
|
|
},
|
|
),
|
|
],
|
|
}
|
|
)
|
|
|
|
self.data_dict = {
|
|
"model": "res.partner",
|
|
"fields": [
|
|
{"name": "id", "label": "External ID"},
|
|
{"name": "name", "label": "Name"},
|
|
{"name": "email", "label": "Email"},
|
|
{"name": "type", "label": "Type"},
|
|
{"name": "child_ids/name", "label": "Name"},
|
|
{"name": "child_ids/email", "label": "Email"},
|
|
{"name": "child_ids/type", "label": "Type"},
|
|
],
|
|
"ids": [self.partner.id],
|
|
"domain": [["type", "in", ("contact", "delivery")]],
|
|
"groupby": [],
|
|
"context": {
|
|
"lang": "en_US",
|
|
"tz": "Europe/Paris",
|
|
"uid": 2,
|
|
"allowed_company_ids": [1],
|
|
},
|
|
"import_compat": True,
|
|
}
|
|
self.basic_expect_dict = {
|
|
"id": self.partner.id,
|
|
"name": "TEST",
|
|
"email": "test@test.fr",
|
|
"type": "contact",
|
|
"child_ids": [
|
|
{
|
|
"name": "test child 1",
|
|
"email": "test_child_1@test.fr",
|
|
"type": "delivery",
|
|
},
|
|
{
|
|
"name": "test child 2",
|
|
"email": "test_child_2@test.fr",
|
|
"type": "invoice",
|
|
},
|
|
],
|
|
}
|
|
|
|
self.expected_list_dict_merge = [
|
|
{
|
|
"amount_total": 300.0,
|
|
"amount_untaxed": 250.0,
|
|
"company_id": "My Company",
|
|
"currency_id": "EUR",
|
|
"invoice_status": "invoice_status",
|
|
"invoice_status_FR": "Rien à facturer",
|
|
"invoice_status_US": "Nothing to Bill",
|
|
"order_line": [
|
|
{
|
|
"company_id": "My Company",
|
|
"currency_id": "EUR",
|
|
"display_type": "Section",
|
|
"name": "Exmple de section",
|
|
"state": "order_line/state",
|
|
"state_FR": "Bon de commande fournisseur",
|
|
"state_US": "Purchase Order",
|
|
"tax_calculation_rounding_method": "order_line/tax_calculation_rounding_method",
|
|
"tax_calculation_rounding_method_FR": "Arrondir à la ligne",
|
|
"tax_calculation_rounding_method_US": "Round per Line",
|
|
"taxes_id": [],
|
|
},
|
|
{
|
|
"company_id": "My Company",
|
|
"currency_id": "EUR",
|
|
"name": "Produit test 2",
|
|
"price_subtotal": 75.0,
|
|
"price_unit": 75.0,
|
|
"product_id": "order_line/product_id",
|
|
"product_id_FR": "Produit test 2",
|
|
"product_id_US": "ENGLISH Produit test 2",
|
|
"product_type": "order_line/product_type",
|
|
"product_type_FR": "Biens",
|
|
"product_type_US": "Goods",
|
|
"product_uom": "order_line/product_uom",
|
|
"product_uom_FR": "Unité(s)",
|
|
"product_uom_US": "Units",
|
|
"product_uom_category_id": "order_line/product_uom_category_id",
|
|
"product_uom_category_id_FR": "Unité",
|
|
"product_uom_category_id_US": "Unit",
|
|
"state": "order_line/state",
|
|
"state_FR": "Bon de commande fournisseur",
|
|
"state_US": "Purchase Order",
|
|
"tax_calculation_rounding_method": "order_line/tax_calculation_rounding_method",
|
|
"tax_calculation_rounding_method_FR": "Arrondir à la ligne",
|
|
"tax_calculation_rounding_method_US": "Round per Line",
|
|
"taxes_id": ["20%"],
|
|
},
|
|
],
|
|
"payment_term_id": "payment_term_id",
|
|
"payment_term_id_FR": "21 jours ",
|
|
"payment_term_id_US": "21 Days",
|
|
"tax_country_id": "tax_country_id",
|
|
"tax_country_id_FR": "États-Unis",
|
|
"tax_country_id_US": "United States",
|
|
}
|
|
]
|
|
|
|
self.data_str = str(self.data_dict)
|
|
self.data = json.dumps(self.data_dict)
|
|
|
|
self.json_export_format = JsonExportFormat()
|
|
self.mock_request = MagicMock()
|
|
self.mock_request.env = self.env
|
|
super().setUp()
|
|
|
|
def helper_get_csrf_token(self):
|
|
resp = self.url_open("/web/login")
|
|
html = resp.content.decode()
|
|
match = re.search(r'csrf_token:\s*"([^"]+)"', html)
|
|
return match and match.group(1) or False
|
|
|
|
def test_generate_export_json(self):
|
|
# Perform the json export
|
|
fields = [field["name"] for field in self.data_dict["fields"]]
|
|
model = self.env[self.data_dict["model"]]
|
|
|
|
with patch.object(controller_module, "request", self.mock_request):
|
|
result_json = self.json_export_format.perform_json_export(
|
|
self.data_dict["domain"], fields, self.data_dict["ids"], model
|
|
)
|
|
|
|
dict_result_json = json.loads(result_json)
|
|
self.assertDictEqual(
|
|
self.basic_expect_dict,
|
|
dict_result_json[0],
|
|
"Check if json export is equal to expected data",
|
|
)
|
|
|
|
def test_perform_json_export_with_lang(self):
|
|
self.env["res.lang"]._activate_lang("fr_FR")
|
|
|
|
self.data_dict["fields"].append({"name": "title/name", "label": "Title"})
|
|
self.data_dict["fields"].append({"name": "country_id/name", "label": "Country"})
|
|
fields = [field["name"] for field in self.data_dict["fields"]]
|
|
model = self.env[self.data_dict["model"]]
|
|
|
|
# Ajouts de traductions
|
|
title = (
|
|
self.env["res.partner.title"]
|
|
.with_context(lang="en_US")
|
|
.create(
|
|
{
|
|
"name": "Doctor",
|
|
}
|
|
)
|
|
)
|
|
title.with_context(lang="fr_FR").name = "Docteur"
|
|
self.partner.title = title
|
|
|
|
self.env.ref("base.es").with_context(lang="fr_FR").name = "Espagne"
|
|
self.partner.country_id = self.env.ref("base.es")
|
|
|
|
with patch.object(controller_module, "request", self.mock_request):
|
|
result_json = self.json_export_format.perform_json_export(
|
|
self.data_dict["domain"],
|
|
fields,
|
|
self.data_dict["ids"],
|
|
model,
|
|
langs_code=["fr_FR", "en_US"],
|
|
)
|
|
|
|
dict_result_json = json.loads(result_json)
|
|
expected_dict = {
|
|
"id": self.partner.id,
|
|
"name": "TEST",
|
|
"email": "test@test.fr",
|
|
"type": "contact",
|
|
"title": {
|
|
"name": "title/name",
|
|
"name_FR": "Docteur",
|
|
"name_US": "Doctor",
|
|
},
|
|
"country_id": {
|
|
"name": "country_id/name",
|
|
"name_FR": "Espagne",
|
|
"name_US": "Spain",
|
|
},
|
|
"child_ids": [
|
|
{
|
|
"name": "test child 1",
|
|
"email": "test_child_1@test.fr",
|
|
"type": "delivery",
|
|
},
|
|
{
|
|
"name": "test child 2",
|
|
"email": "test_child_2@test.fr",
|
|
"type": "invoice",
|
|
},
|
|
],
|
|
}
|
|
self.assertDictEqual(expected_dict, dict_result_json[0], "Le json doit contenir les traductions")
|
|
|
|
def test_convert_simple_list(self):
|
|
"""La fonction _convert_simple_list prend en entrée une liste de clée, correspondant
|
|
à des noms de champs, et retourne une liste de dict{"name": nom_de_champ}"""
|
|
res_partner_fields_names = list(self.partner._fields.keys())
|
|
|
|
res = self.json_export_format._convert_simple_list(res_partner_fields_names)
|
|
|
|
self.assertEqual(
|
|
len(res_partner_fields_names),
|
|
len(res),
|
|
"Tous les noms de champs du modèle res.partner doivent être retourné sous la forme {'name': nom_de_champ}",
|
|
)
|
|
|
|
# Si on ajoute un dict dans les noms de champ du res_partner, le dict doit être retourné tel quel, lors
|
|
# du passage de la fonction _convert_simple_list
|
|
|
|
mock_dict = {
|
|
"random_key": "random_value",
|
|
"random_key1": "random_value1",
|
|
"random_key2": "random_value2",
|
|
}
|
|
res_partner_fields_names.append(mock_dict)
|
|
|
|
res = self.json_export_format._convert_simple_list(res_partner_fields_names)
|
|
|
|
self.assertEqual(len(res_partner_fields_names), len(res))
|
|
self.assertIn(
|
|
mock_dict,
|
|
res,
|
|
"Le dict ajouté dans les champs de res.partner est bien retourner telquel dans la list",
|
|
)
|
|
|
|
def test_get_country_code(self):
|
|
"""La fonction prend un paramètre un string de lang, elle doit retourner la deuxième partie du code
|
|
(séparé par le premier '_' du string)"""
|
|
|
|
for test_val, expected in [
|
|
("fr_FR", "FR"),
|
|
("_fr_FR", "fr"),
|
|
("FR_", ""),
|
|
("", ""),
|
|
]:
|
|
self.assertEqual(self.json_export_format.get_country_code(test_val), expected)
|
|
|
|
def test_add_unique_vals(self):
|
|
"""La fonction prend en paramètre un dict, et retourne un set de valeur unique.
|
|
La fonction stringify les list et les dicts, si dans le dict d'entrée, des valeurs sont
|
|
des dicts ou des lists."""
|
|
|
|
mock_dict_same_value = {
|
|
"random_key": "random_value",
|
|
"random_key1": "random_value",
|
|
"random_key2": "random_value",
|
|
}
|
|
res = self.json_export_format.add_unique_vals(mock_dict_same_value)
|
|
|
|
self.assertEqual(
|
|
len(res),
|
|
1,
|
|
"Le dict d'entrée contenait 3 clés valeurs, mais les valeurs liées aux clés étaient identiques.",
|
|
)
|
|
self.assertIn("random_value", res)
|
|
|
|
mock_dict_different_value = {
|
|
"random_key": "random_value",
|
|
"random_key1": "random_value1",
|
|
"random_key2": "random_value",
|
|
}
|
|
res = self.json_export_format.add_unique_vals(mock_dict_different_value)
|
|
self.assertEqual(len(res), 2)
|
|
for expected in ["random_value", "random_value1"]:
|
|
self.assertIn(expected, res)
|
|
|
|
mock_dict_with_dict_list = {
|
|
"random_key": {"an_other": "one"},
|
|
"random_key1": [10, 20, 40],
|
|
"random_key2": [10, 20, 40],
|
|
}
|
|
res = self.json_export_format.add_unique_vals(mock_dict_with_dict_list)
|
|
|
|
self.assertEqual(len(res), 2)
|
|
for expected in ["{'an_other': 'one'}", "[10, 20, 40]"]:
|
|
self.assertIn(expected, res)
|
|
|
|
self.assertEqual(0, len(self.json_export_format.add_unique_vals({})))
|
|
|
|
def test_retrieve_all_keys(self):
|
|
test_dict = {
|
|
"fr_FR": {
|
|
"champ_1": "valeur",
|
|
"champ_2": "valeur",
|
|
"champ_3": "valeur",
|
|
"champ_4": "valeur",
|
|
},
|
|
"zn_CN": {
|
|
"champ_5": "valeur",
|
|
"champ_6": "valeur",
|
|
"champ_3": "valeur",
|
|
"champ_4": "valeur",
|
|
},
|
|
}
|
|
|
|
res = self.json_export_format.retrieve_all_keys(test_dict)
|
|
for expected in [
|
|
"champ_1",
|
|
"champ_2",
|
|
"champ_3",
|
|
"champ_4",
|
|
"champ_5",
|
|
"champ_6",
|
|
]:
|
|
self.assertIn(expected, res)
|
|
|
|
other_dict = {
|
|
"champ_1": "valeur",
|
|
"champ_2": "valeur",
|
|
"champ_3": "valeur",
|
|
}
|
|
with self.assertRaises(AssertionError):
|
|
self.json_export_format.retrieve_all_keys(other_dict)
|
|
|
|
def test_process_keys_without_parent_key(self):
|
|
test_dict = {
|
|
"fr_FR": {
|
|
"champ_1": "valeur",
|
|
"champ_2": "valeur",
|
|
"champ_3": "valeur",
|
|
"champ_4": "valeur",
|
|
},
|
|
"zn_CN": {
|
|
"champ_5": "valeur",
|
|
"champ_6": "valeur",
|
|
"champ_3": "valeur",
|
|
"champ_4": "valeur",
|
|
},
|
|
}
|
|
|
|
merged_dict = {}
|
|
all_keys = self.json_export_format.retrieve_all_keys(test_dict)
|
|
|
|
self.json_export_format.process_keys(all_keys, test_dict, merged_dict)
|
|
|
|
for expected in [
|
|
"champ_1",
|
|
"champ_2",
|
|
"champ_3",
|
|
"champ_4",
|
|
"champ_5",
|
|
"champ_6",
|
|
]:
|
|
self.assertIn(
|
|
expected,
|
|
merged_dict,
|
|
"Chaque champ doit être inclus dans le dictionnaire final, qui fusionne"
|
|
"l'ensemble des champs des dictionnaires d'entrées, par langue.",
|
|
)
|
|
|
|
self.json_export_format.process_keys(all_keys, test_dict, merged_dict)
|
|
|
|
# Si pour un même nom de champ donné, la valeur est différente entre des langues, les deux clés concaténées
|
|
# du suffixe du code la langue,
|
|
# doivent être présent dans le dict final.
|
|
test_dict_2 = {
|
|
"fr_FR": {
|
|
"champ_1": "valeur",
|
|
"champ_2": "valeur",
|
|
"champ_3": "VALEUR EN FRANÇAIS",
|
|
"champ_4": "valeur",
|
|
},
|
|
"zn_CN": {
|
|
"champ_5": "valeur",
|
|
"champ_6": "valeur",
|
|
"champ_3": "VALEUR PAS EN FRANÇAIS",
|
|
"champ_4": "valeur",
|
|
},
|
|
}
|
|
self.json_export_format.process_keys(all_keys, test_dict_2, merged_dict)
|
|
for expected in [
|
|
"champ_1",
|
|
"champ_2",
|
|
"champ_3",
|
|
"champ_4",
|
|
"champ_5",
|
|
"champ_6",
|
|
"champ_3_FR",
|
|
"champ_3_CN",
|
|
]:
|
|
self.assertIn(
|
|
expected,
|
|
merged_dict,
|
|
"Chaque champ doit être inclus dans le dictionnaire final, qui fusionne"
|
|
"l'ensemble des champs des dictionnaires d'entrées, par langue.",
|
|
)
|
|
self.assertEqual(merged_dict.get("champ_3_FR"), "VALEUR EN FRANÇAIS")
|
|
self.assertEqual(merged_dict.get("champ_3_CN"), "VALEUR PAS EN FRANÇAIS")
|
|
|
|
def test_process_keys_with_parent_key(self):
|
|
test_dict = {
|
|
"fr_FR": {
|
|
"champ_1": "valeur",
|
|
"champ_2": "valeur",
|
|
"champ_3": {
|
|
"cle_dict_imbrique_1": "valeur1",
|
|
"cle_dict_imbrique_2": "valeur2",
|
|
"cle_dict_imbrique_3": [
|
|
"element_liste_imbriqueFR",
|
|
"element_autre_listeFR",
|
|
],
|
|
},
|
|
},
|
|
"zn_CN": {
|
|
"champ_1": "valeur",
|
|
"champ_2": "valeur",
|
|
"champ_3": {
|
|
"cle_dict_imbrique_1": "valeur1489",
|
|
"cle_dict_imbrique_2": "valeur24996",
|
|
"cle_dict_imbrique_3": [
|
|
"element_liste_imbrique",
|
|
"element_autre_liste",
|
|
],
|
|
},
|
|
},
|
|
}
|
|
merged_dict = {}
|
|
all_keys = self.json_export_format.retrieve_all_keys(test_dict)
|
|
|
|
self.json_export_format.process_keys(all_keys, test_dict, merged_dict, "champ_3")
|
|
|
|
# Le dict attendu doit contenir toutes les valeurs de chaque langues, même si les valeurs sont eux mêmes dans
|
|
# des dict ou dans des listes. Un regroupement doit être effectué autour du champ_3 :
|
|
# le dict du champ_3 doit contenir la clé original de chaque dict (cle_dict_imbrique_1, cle_dict_imbrique_2,
|
|
# cle_dict_imbrique_3)
|
|
# avec pour valeurs le chemin absolu de la valeur (champ_3/cle_dict_imbrique_1, champ_3/cle_dict_imbrique_2,
|
|
# champ_3/cle_dict_imbrique_3)
|
|
|
|
expected_dict = {
|
|
"champ_1": "valeur",
|
|
"champ_2": "valeur",
|
|
"champ_3": {
|
|
"cle_dict_imbrique_1": "champ_3/cle_dict_imbrique_1",
|
|
"cle_dict_imbrique_1_CN": "valeur1489",
|
|
"cle_dict_imbrique_1_FR": "valeur1",
|
|
"cle_dict_imbrique_2": "champ_3/cle_dict_imbrique_2",
|
|
"cle_dict_imbrique_2_CN": "valeur24996",
|
|
"cle_dict_imbrique_2_FR": "valeur2",
|
|
"cle_dict_imbrique_3": "champ_3/cle_dict_imbrique_3",
|
|
"cle_dict_imbrique_3_CN": [
|
|
"element_liste_imbrique",
|
|
"element_autre_liste",
|
|
],
|
|
"cle_dict_imbrique_3_FR": [
|
|
"element_liste_imbriqueFR",
|
|
"element_autre_listeFR",
|
|
],
|
|
},
|
|
}
|
|
self.assertDictEqual(expected_dict, merged_dict)
|
|
|
|
def test_process_keys_nested_dict(self):
|
|
test_dict = {
|
|
"fr_FR": {
|
|
"champ_1": [
|
|
{
|
|
"cle_dict_imbrique_1": "valeur1",
|
|
"cle_dict_imbrique_2": "valeur2",
|
|
"cle_dict_imbrique_3": [
|
|
"element_liste_imbriqueFR",
|
|
"element_autre_listeFR",
|
|
],
|
|
},
|
|
{
|
|
"cle_dict_imbrique_1": "valeur44",
|
|
"cle_dict_imbrique_2": "valeur4858",
|
|
"cle_dict_imbrique_3": [
|
|
"element_liste_imbriqueFR",
|
|
"element_autre_liste",
|
|
],
|
|
},
|
|
],
|
|
"champ_2": "valeur",
|
|
},
|
|
"zn_CN": {
|
|
"champ_1": [
|
|
{
|
|
"cle_dict_imbrique_1": "valeur1",
|
|
"cle_dict_imbrique_2": "valeur2",
|
|
"cle_dict_imbrique_3": [
|
|
"element_liste_imbriqueFR",
|
|
"element_autre_listeFR",
|
|
],
|
|
},
|
|
{
|
|
"cle_dict_imbrique_1": "valeur44",
|
|
"cle_dict_imbrique_2": "valeur4858",
|
|
"cle_dict_imbrique_3": [
|
|
"element_liste_imbrique",
|
|
"element_autre_liste",
|
|
],
|
|
},
|
|
],
|
|
},
|
|
}
|
|
merged_dict = {}
|
|
all_keys = self.json_export_format.retrieve_all_keys(test_dict)
|
|
self.json_export_format.process_keys(all_keys, test_dict, merged_dict)
|
|
|
|
expected_dict = {
|
|
"champ_1": [
|
|
{
|
|
"cle_dict_imbrique_1": "valeur1",
|
|
"cle_dict_imbrique_2": "valeur2",
|
|
"cle_dict_imbrique_3": [
|
|
"element_liste_imbriqueFR",
|
|
"element_autre_listeFR",
|
|
],
|
|
},
|
|
{
|
|
"cle_dict_imbrique_1": "valeur44",
|
|
"cle_dict_imbrique_2": "valeur4858",
|
|
"cle_dict_imbrique_3": "champ_1/cle_dict_imbrique_3",
|
|
"cle_dict_imbrique_3_CN": [
|
|
"element_liste_imbrique",
|
|
"element_autre_liste",
|
|
],
|
|
"cle_dict_imbrique_3_FR": [
|
|
"element_liste_imbriqueFR",
|
|
"element_autre_liste",
|
|
],
|
|
},
|
|
],
|
|
"champ_2": "valeur",
|
|
}
|
|
self.assertDictEqual(expected_dict, merged_dict)
|
|
|
|
def test_merge_list_of_dicts_without_lang(self):
|
|
self.assertEqual(
|
|
[],
|
|
self.json_export_format._merge_list_of_dicts({}),
|
|
"Sans langue, cette fonction ne doit rien faire et retourner une liste vide",
|
|
)
|
|
self.assertEqual(
|
|
[],
|
|
self.json_export_format.merge_multilingual_dicts({}),
|
|
"Même chose, sans langue, pas de dict",
|
|
)
|
|
|
|
def test_merge_multilingual_dicts(self):
|
|
test_dict = {
|
|
"en_US": [
|
|
{
|
|
"amount_total": 300.0,
|
|
"amount_untaxed": 250.0,
|
|
"company_id": "My Company",
|
|
"currency_id": "EUR",
|
|
"invoice_status": "Nothing to Bill",
|
|
"order_line": [
|
|
{
|
|
"company_id": "My Company",
|
|
"currency_id": "EUR",
|
|
"display_type": "Section",
|
|
"name": "Exmple de section",
|
|
"state": "Purchase Order",
|
|
"tax_calculation_rounding_method": "Round per Line",
|
|
"taxes_id": [],
|
|
},
|
|
{
|
|
"company_id": "My Company",
|
|
"currency_id": "EUR",
|
|
"name": "Produit test 2",
|
|
"price_subtotal": 75.0,
|
|
"price_unit": 75.0,
|
|
"product_type": "Goods",
|
|
"product_id": "ENGLISH Produit test 2",
|
|
"product_uom": "Units",
|
|
"product_uom_category_id": "Unit",
|
|
"state": "Purchase Order",
|
|
"tax_calculation_rounding_method": "Round per Line",
|
|
"taxes_id": ["20%"],
|
|
},
|
|
],
|
|
"payment_term_id": "21 Days",
|
|
"tax_country_id": "United States",
|
|
}
|
|
],
|
|
"fr_FR": [
|
|
{
|
|
"amount_total": 300.0,
|
|
"amount_untaxed": 250.0,
|
|
"company_id": "My Company",
|
|
"currency_id": "EUR",
|
|
"invoice_status": "Rien à facturer",
|
|
"order_line": [
|
|
{
|
|
"company_id": "My Company",
|
|
"currency_id": "EUR",
|
|
"display_type": "Section",
|
|
"name": "Exmple de section",
|
|
"state": "Bon de commande fournisseur",
|
|
"tax_calculation_rounding_method": "Arrondir à la ligne",
|
|
"taxes_id": [],
|
|
},
|
|
{
|
|
"company_id": "My Company",
|
|
"currency_id": "EUR",
|
|
"name": "Produit test 2",
|
|
"price_subtotal": 75.0,
|
|
"price_unit": 75.0,
|
|
"product_id": "Produit test 2",
|
|
"product_type": "Biens",
|
|
"product_uom": "Unité(s)",
|
|
"product_uom_category_id": "Unité",
|
|
"state": "Bon de commande fournisseur",
|
|
"tax_calculation_rounding_method": "Arrondir à la ligne",
|
|
"taxes_id": ["20%"],
|
|
},
|
|
],
|
|
"payment_term_id": "21 jours ",
|
|
"tax_country_id": "États-Unis",
|
|
}
|
|
],
|
|
}
|
|
self.assertDictEqual(
|
|
self.expected_list_dict_merge[0],
|
|
self.json_export_format.merge_multilingual_dicts(test_dict)[0],
|
|
)
|
|
|
|
def test_format_data(self):
|
|
fields = self.expected_list_dict_merge
|
|
res_json = self.json_export_format.format_data(fields)
|
|
self.assertTrue(json.loads(res_json), "Le résultat de la fonction doit être au format .json")
|
|
|
|
fields[0].update({"mock_bytes_key": b"mock_bytes_value"})
|
|
res_json = self.json_export_format.format_data(fields)
|
|
|
|
list_dict_from_json = json.loads(res_json)
|
|
bytes_key = list_dict_from_json[0].get("mock_bytes_key")
|
|
self.assertEqual(
|
|
"data:image/png;base64,mock_bytes_value",
|
|
bytes_key,
|
|
"La présence d'une clé contenant une valeur en bytes, dans les champs d'entrée, doit obligatoirement"
|
|
"être modifié en sorti dans le json, pour y inclure un préfixe.",
|
|
)
|
|
|
|
def test_define_parser(self):
|
|
res_partner_fields_names = list(self.partner._fields.keys())
|
|
set_fields_names = set(res_partner_fields_names)
|
|
|
|
# Ajouts de faux nom de champs qui sont concaténés.
|
|
res_partner_fields_names.append("company_id/name")
|
|
res_partner_fields_names.append("company_id/street1")
|
|
res_partner_fields_names.append("company_id/siret")
|
|
|
|
res_partner_fields_names.append("country_id/currency_id/name")
|
|
res_partner_fields_names.append("country_id/currency_id/iso_numeric")
|
|
converted_fields_name = self.json_export_format._convert_simple_list(res_partner_fields_names)
|
|
|
|
parser = self.json_export_format.define_parser(converted_fields_name)
|
|
|
|
self.assertTrue(
|
|
set_fields_names.issubset(parser),
|
|
"Tous les champs du modèle res.partner doivent être présent dans le parser.",
|
|
)
|
|
expected_tuple_1 = ("company_id", ["name", "street1", "siret"])
|
|
expected_tuple_2 = ("country_id", [("currency_id", ["name", "iso_numeric"])])
|
|
for expected_tuple in [expected_tuple_1, expected_tuple_2]:
|
|
self.assertIn(
|
|
expected_tuple,
|
|
parser,
|
|
"Un tuple nom_de_champ et liste de nom de champ (qui peut lui même contenir un tuple avec "
|
|
"la même combinaison), doit être présent, pour les champs de type M2O, avec un accès à un "
|
|
"des champs du M2O",
|
|
)
|
|
|
|
def test_add_list_or_dict_on_merge_item(self):
|
|
# Faire la version avec un parent_key qui est un dict
|
|
test_dict = {
|
|
"fr_FR": {
|
|
"champ_2": "valeur",
|
|
"champ_3": {
|
|
"cle_dict_imbrique_1": "valeur1",
|
|
"cle_dict_imbrique_2": "valeur2",
|
|
"cle_dict_imbrique_3": [
|
|
"element_liste_imbriqueFR",
|
|
"element_autre_listeFR",
|
|
],
|
|
},
|
|
},
|
|
"zn_CN": {
|
|
"champ_2": "valeur",
|
|
"champ_3": {
|
|
"cle_dict_imbrique_1": "valeur1489",
|
|
"cle_dict_imbrique_2": "valeur24996",
|
|
"cle_dict_imbrique_3": [
|
|
"element_liste_imbrique",
|
|
"element_autre_liste",
|
|
],
|
|
},
|
|
},
|
|
}
|
|
merged_dict = {}
|
|
all_keys = self.json_export_format.retrieve_all_keys(test_dict)
|
|
|
|
# On simule le fait que le champ_2 soit le parent key de tout le dict d'entrée "test_dict".
|
|
# La clé doit apparaître dans l'arborescence de chaque champ imbriquée.
|
|
self.json_export_format.process_keys(all_keys, test_dict, merged_dict, "champ_2")
|
|
|
|
expected_dict = {
|
|
"champ_2": "valeur",
|
|
"champ_3": {
|
|
"cle_dict_imbrique_2": "champ_2/champ_3/cle_dict_imbrique_2",
|
|
"cle_dict_imbrique_2_FR": "valeur2",
|
|
"cle_dict_imbrique_2_CN": "valeur24996",
|
|
"cle_dict_imbrique_3": "champ_2/champ_3/cle_dict_imbrique_3",
|
|
"cle_dict_imbrique_3_FR": [
|
|
"element_liste_imbriqueFR",
|
|
"element_autre_listeFR",
|
|
],
|
|
"cle_dict_imbrique_3_CN": [
|
|
"element_liste_imbrique",
|
|
"element_autre_liste",
|
|
],
|
|
"cle_dict_imbrique_1": "champ_2/champ_3/cle_dict_imbrique_1",
|
|
"cle_dict_imbrique_1_FR": "valeur1",
|
|
"cle_dict_imbrique_1_CN": "valeur1489",
|
|
},
|
|
}
|
|
self.assertDictEqual(expected_dict, merged_dict)
|
|
|
|
def test_index_json_export_route(self):
|
|
# Fonction appelé par le controller qui gère l'export au format json (bouton "Action" > "Exporter")
|
|
self.authenticate("admin", "admin")
|
|
csrf = self.helper_get_csrf_token()
|
|
|
|
url = "/web/export/json"
|
|
payload = {
|
|
"csrf_token": csrf,
|
|
"data": json.dumps(self.data_dict),
|
|
}
|
|
|
|
response = self.url_open(url, payload)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertDictEqual(self.basic_expect_dict, response.json()[0])
|
|
|
|
def test_formats_export_formats(self):
|
|
# On vérifie que la pop-up d'export ajoute bien la coche json dans les options de format de sortie.
|
|
self.authenticate("admin", "admin")
|
|
url = "/web/export/formats"
|
|
payload = {"jsonrpc": "2.0", "method": "call", "params": {}, "id": 1}
|
|
|
|
response = self.url_open(
|
|
url,
|
|
data=json.dumps(payload),
|
|
headers={"Content-Type": "application/json"},
|
|
)
|
|
result_format = response.json().get("result")
|
|
self.assertIn(
|
|
{"label": "JSON", "tag": "json"},
|
|
result_format,
|
|
"Le nouveau format json doit être disponible enexport",
|
|
)
|
|
|
|
def test_json_export_on_none_ordinary_table(self):
|
|
fields = [field["name"] for field in self.data_dict["fields"]]
|
|
model = self.env[self.data_dict["model"]]
|
|
with patch("odoo.models.BaseModel._is_an_ordinary_table", return_value=False):
|
|
with patch.object(controller_module, "request", self.mock_request):
|
|
result_json = self.json_export_format.perform_json_export(
|
|
self.data_dict["domain"], fields, self.data_dict["ids"], model
|
|
)
|
|
|
|
res_dict = json.loads(result_json)
|
|
self.basic_expect_dict.pop("id")
|
|
self.assertDictEqual(
|
|
res_dict[0],
|
|
self.basic_expect_dict,
|
|
"Seul l'id ne doit pas être récupérer, "
|
|
"si la table associé au modèle ne correspond pas à une table classique."
|
|
"Toutes les autres informations doivent être récupérés normalement.",
|
|
)
|
|
|
|
def test_property_extension(self):
|
|
self.assertEqual(self.json_export_format.extension, ".json")
|
|
|
|
def test_property_content_type(self):
|
|
self.assertEqual(self.json_export_format.content_type, "text/json;charset=utf8")
|