Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API] Permettre le remplacement d'une demande avec un ingrédient d'autre type #1514

Open
wants to merge 9 commits into
base: staging
Choose a base branch
from
33 changes: 14 additions & 19 deletions api/serializers/declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,18 +90,21 @@ def update(self, instance, validated_data):


ADDABLE_ELEMENT_FIELDS = (
"new",
"new_name",
"new_description",
"authorization_mode",
"fr_reason",
"fr_details",
"eu_reference_country",
"eu_legal_source",
"eu_details",
"new_description",
"new",
"request_private_notes",
"request_status",
"request_private_notes",
)

DECLARED_ELEMENT_SHARED_FIELDS = ADDABLE_ELEMENT_FIELDS + ("type",)


class DeclaredElementNestedField:
def create(self, validated_data):
Expand Down Expand Up @@ -137,17 +140,15 @@ class DeclaredPlantSerializer(DeclaredIngredientCommonSerializer):

class Meta:
model = DeclaredPlant
fields = ADDABLE_ELEMENT_FIELDS + (
fields = DECLARED_ELEMENT_SHARED_FIELDS + (
"id",
"declaration",
"element",
"new_name",
"active",
"used_part",
"unit",
"quantity",
"unit",
"preparation",
"type",
)


Expand All @@ -160,18 +161,16 @@ class DeclaredMicroorganismSerializer(DeclaredIngredientCommonSerializer):

class Meta:
model = DeclaredMicroorganism
fields = ADDABLE_ELEMENT_FIELDS + (
fields = DECLARED_ELEMENT_SHARED_FIELDS + (
"id",
"declaration",
"element",
"new_name",
"new_species",
"new_genre",
"active",
"activated",
"new_species",
"new_genre",
"strain",
"quantity",
"type",
)


Expand All @@ -185,16 +184,14 @@ class DeclaredIngredientSerializer(DeclaredIngredientCommonSerializer):

class Meta:
model = DeclaredIngredient
fields = ADDABLE_ELEMENT_FIELDS + (
fields = DECLARED_ELEMENT_SHARED_FIELDS + (
"id",
"declaration",
"element",
"new_name",
"new_type",
"active",
"new_type",
"quantity",
"unit",
"type",
)


Expand All @@ -207,15 +204,13 @@ class DeclaredSubstanceSerializer(DeclaredIngredientCommonSerializer):

class Meta:
model = DeclaredSubstance
fields = ADDABLE_ELEMENT_FIELDS + (
fields = DECLARED_ELEMENT_SHARED_FIELDS + (
"id",
"declaration",
"element",
"new_name",
"active",
"quantity",
"unit",
"type",
)


Expand Down
233 changes: 221 additions & 12 deletions api/tests/test_declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
VisaRoleFactory,
PlantSynonymFactory,
)
from data.models import Attachment, Declaration, DeclaredMicroorganism, DeclaredPlant, Snapshot
from data.models import Attachment, Declaration, DeclaredMicroorganism, DeclaredPlant, DeclaredSubstance, Snapshot

from .utils import authenticate

Expand Down Expand Up @@ -1924,32 +1924,149 @@ def test_replace_declared_plant(self):
)
self.assertEqual(response.status_code, status.HTTP_200_OK, response.json())
declared_plant.refresh_from_db()
self.assertEqual(declared_plant.plant, plant)
self.assertEqual(declared_plant.request_status, DeclaredPlant.AddableStatus.REPLACED)
self.assertFalse(declared_plant.new)
self.assertEqual(declared_plant.plant, plant)

@authenticate
def test_cannot_replace_element_different_type(self):
def test_can_replace_plant_request_with_microorganism(self):
"""
Pour reduire le scope de changements, temporairement bloque le remplacement d'une demande
avec un element d'un type different
Test de remplacement cross-type : plante vers microorganisme
Verifier que les données sont copiées, et les nouvelles données sont sauvegardées.
"""
InstructionRoleFactory(user=authenticate.user)

declaration = DeclarationFactory()
declared_plant = DeclaredPlantFactory(declaration=declaration)
declared_plant = DeclaredPlantFactory(
declaration=declaration, new_name="Test plant", new_description="Test description", new=True, quantity=10
)
self.assertEqual(declared_plant.request_status, DeclaredPlant.AddableStatus.REQUESTED)
microorganism = MicroorganismFactory()

response = self.client.post(
reverse("api:declared_element_replace", kwargs={"pk": declared_plant.id, "type": "plant"}),
{"element": {"id": microorganism.id, "type": "microorganism"}},
{
"element": {"id": microorganism.id, "type": "microorganism"},
"additional_fields": {
"strain": "Test strain",
"activated": False,
"quantity": 90,
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST, response.json())
declared_plant.refresh_from_db()
self.assertEqual(declared_plant.request_status, DeclaredPlant.AddableStatus.REQUESTED)
self.assertNotEqual(declared_plant.plant, microorganism)
self.assertEqual(response.status_code, status.HTTP_200_OK, response.json())
with self.assertRaises(DeclaredPlant.DoesNotExist):
DeclaredPlant.objects.get(id=declared_plant.id)

self.assertEqual(DeclaredMicroorganism.objects.count(), 1)
declared_microorganism = DeclaredMicroorganism.objects.get(declaration=declaration)

self.assertEqual(declared_microorganism.microorganism, microorganism)
self.assertEqual(declared_microorganism.request_status, DeclaredMicroorganism.AddableStatus.REPLACED)
self.assertFalse(declared_microorganism.new)

# est-ce que le nom est copié dans le champ espèce ?
self.assertEqual(declared_microorganism.new_species, "Test plant")
self.assertEqual(declared_microorganism.new_genre, "")
# est-ce que les nouveaux champs sont sauvegardés ?
self.assertEqual(declared_microorganism.strain, "Test strain")
self.assertEqual(declared_microorganism.activated, False)
self.assertEqual(declared_microorganism.quantity, 90)
# est-ce que les anciens champs sont sauvegardés ?
self.assertEqual(declared_microorganism.new_description, "Test description")

@authenticate
def test_can_replace_microorganism_request_with_plant(self):
"""
Test de remplacement cross-type : microoganisme vers plante
Verifier que les données sont copiées, et les nouvelles données sont sauvegardées.
"""
InstructionRoleFactory(user=authenticate.user)

declaration = DeclarationFactory()
declared_microorganism = DeclaredMicroorganismFactory(
declaration=declaration,
new_species="test",
new_genre="testing",
new_description="Test description",
new=True,
quantity=10,
)
self.assertEqual(declared_microorganism.request_status, DeclaredMicroorganism.AddableStatus.REQUESTED)
plant = PlantFactory()
used_part = PlantPartFactory()
unit = SubstanceUnitFactory()

response = self.client.post(
reverse("api:declared_element_replace", kwargs={"pk": declared_microorganism.id, "type": "microorganism"}),
{
"element": {"id": plant.id, "type": "plant"},
"additional_fields": {
"used_part": used_part.id,
"unit": unit.id,
"quantity": 90,
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK, response.json())
with self.assertRaises(DeclaredMicroorganism.DoesNotExist):
DeclaredMicroorganism.objects.get(id=declared_microorganism.id)

self.assertEqual(DeclaredPlant.objects.count(), 1)
declared_plant = DeclaredPlant.objects.get(declaration=declaration)

self.assertEqual(declared_plant.plant, plant)
self.assertEqual(declared_plant.request_status, DeclaredPlant.AddableStatus.REPLACED)
self.assertFalse(declared_plant.new)

# est-ce que l'espèce + genre sont copiés dans le champ nom ?
self.assertEqual(declared_plant.new_name, "test testing")
# est-ce que les nouveaux champs sont sauvegardés ?
self.assertEqual(declared_plant.used_part, used_part)
self.assertEqual(declared_plant.unit, unit)
self.assertEqual(declared_plant.quantity, 90)
# est-ce que les anciens champs sont sauvegardés ?
self.assertEqual(declared_plant.new_description, "Test description")

@authenticate
def test_can_replace_substance_request_with_plant(self):
"""
Test de remplacement cross-type : microoganisme vers plante
Verifier que les données complexes, comme unit, sont bien gardées si elles ne sont pas spécifiées.
"""
InstructionRoleFactory(user=authenticate.user)

declaration = DeclarationFactory()
unit = SubstanceUnitFactory()
declared_substance = DeclaredSubstanceFactory(
declaration=declaration, new_description="Test description", new=True, unit=unit
)
self.assertEqual(declared_substance.request_status, DeclaredSubstance.AddableStatus.REQUESTED)
plant = PlantFactory()

response = self.client.post(
reverse("api:declared_element_replace", kwargs={"pk": declared_substance.id, "type": "substance"}),
{
"element": {"id": plant.id, "type": "plant"},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK, response.json())
with self.assertRaises(DeclaredSubstance.DoesNotExist):
DeclaredSubstance.objects.get(id=declared_substance.id)

self.assertEqual(DeclaredPlant.objects.count(), 1)
declared_plant = DeclaredPlant.objects.get(declaration=declaration)

self.assertEqual(declared_plant.plant, plant)
self.assertEqual(declared_plant.request_status, DeclaredPlant.AddableStatus.REPLACED)
self.assertFalse(declared_plant.new)

# est-ce que les anciens champs sont sauvegardés ?
self.assertEqual(declared_plant.new_description, "Test description")
self.assertEqual(declared_plant.unit, unit)

@authenticate
def test_can_add_synonym_on_replace(self):
Expand All @@ -1960,7 +2077,7 @@ def test_can_add_synonym_on_replace(self):
InstructionRoleFactory(user=authenticate.user)

declaration = DeclarationFactory()
declared_plant = DeclaredPlantFactory(declaration=declaration, new=True)
declared_plant = DeclaredPlantFactory(declaration=declaration)
plant = PlantFactory()
synonym = PlantSynonymFactory.create(name="Eucalyptus Plant", standard_name=plant)

Expand All @@ -1982,6 +2099,10 @@ def test_can_add_synonym_on_replace(self):

@authenticate
def test_cannot_provide_synonym_with_no_name(self):
"""
Si on donne un nom vide, ignore-le.
Si on ne donne pas de nom, envoie un 400.
"""
InstructionRoleFactory(user=authenticate.user)

declaration = DeclarationFactory()
Expand Down Expand Up @@ -2039,3 +2160,91 @@ def test_cannot_add_duplicate_synonyms(self):
self.assertEqual(plant.plantsynonym_set.count(), 2)
self.assertIsNotNone(plant.plantsynonym_set.get(name="New synonym"))
self.assertEqual(plant.plantsynonym_set.get(id=synonym.id).name, synonym.name)

@authenticate
def test_elements_unchanged_on_replace_fail(self):
"""
Si on donne des mauvaises données à sauvegarder, annule tout l'action, y compris la MAJ des synonymes
"""
InstructionRoleFactory(user=authenticate.user)

declaration = DeclarationFactory()
declared_microorganism = DeclaredMicroorganismFactory(declaration=declaration)
plant = PlantFactory()

response = self.client.post(
reverse("api:declared_element_replace", kwargs={"pk": declared_microorganism.id, "type": "microorganism"}),
{
"element": {"id": plant.id, "type": "plant"},
"additional_fields": {
"used_part": 99, # fail: id unrecognised
},
"synonyms": [{"name": "New synonym"}],
},
format="json",
)
body = response.json()
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST, body)
self.assertEqual(
body["fieldErrors"]["usedPart"][0], "Clé primaire «\xa099\xa0» non valide - l'objet n'existe pas.", body
)
# assertion implicite - l'objet existe tjs
DeclaredMicroorganism.objects.get(id=declared_microorganism.id)
self.assertFalse(plant.plantsynonym_set.filter(name="New synonym").exists())
self.assertEqual(DeclaredPlant.objects.count(), 0)

@authenticate
def test_elements_unchanged_on_synonym_fail(self):
"""
Si l'ajout de synonyme ne passe pas, on annule le remplacement complètement
"""
InstructionRoleFactory(user=authenticate.user)

declaration = DeclarationFactory()
declared_microorganism = DeclaredMicroorganismFactory(declaration=declaration, quantity=10)
plant = PlantFactory()

response = self.client.post(
reverse("api:declared_element_replace", kwargs={"pk": declared_microorganism.id, "type": "microorganism"}),
{
"element": {"id": plant.id, "type": "plant"},
"additional_fields": {
"quantity": 99,
},
"synonyms": [{}],
},
format="json",
)
body = response.json()
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST, body)
still_existing_declared_microorganism = DeclaredMicroorganism.objects.get(id=declared_microorganism.id)
self.assertEqual(still_existing_declared_microorganism.quantity, 10)
self.assertEqual(DeclaredPlant.objects.count(), 0)

@authenticate
def test_id_ignored_in_additional_fields_replace(self):
"""
Verifier qu'on n'a pas d'erreur de création, ou comportement inattendu, si un identifiant est donné
"""
InstructionRoleFactory(user=authenticate.user)

declaration = DeclarationFactory()
declared_microorganism = DeclaredMicroorganismFactory(declaration=declaration, new_species="test", new=True)
plant = PlantFactory()
unit = SubstanceUnitFactory()

response = self.client.post(
reverse("api:declared_element_replace", kwargs={"pk": declared_microorganism.id, "type": "microorganism"}),
{
"element": {"id": plant.id, "type": "plant"},
"additional_fields": {
"id": 99,
"unit": unit.id,
},
},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_200_OK, response.json())
new_declared_plant = DeclaredPlant.objects.first()
self.assertEqual(new_declared_plant.unit, unit)
self.assertNotEqual(new_declared_plant.id, 99)
Loading
Loading