From cf17b9ec50f1a0fdc664f6171c62036170394de2 Mon Sep 17 00:00:00 2001 From: emjay0921 Date: Wed, 22 Jul 2026 10:30:01 +0800 Subject: [PATCH] fix(farmer_demo): assign FARM group type to farm demo data (#1120) --- .../models/farmer_demo_generator.py | 26 +++++++++++++++++++ .../models/seeded_farm_generator.py | 25 ++++++++++++++++++ .../tests/test_demo_generator.py | 20 ++++++++++++++ .../tests/test_seeded_farm_generator.py | 3 +++ 4 files changed, 74 insertions(+) diff --git a/spp_farmer_registry_demo/models/farmer_demo_generator.py b/spp_farmer_registry_demo/models/farmer_demo_generator.py index e5b963ec1..b4f8d74a9 100644 --- a/spp_farmer_registry_demo/models/farmer_demo_generator.py +++ b/spp_farmer_registry_demo/models/farmer_demo_generator.py @@ -901,6 +901,12 @@ def _create_farm( "farm_size_idle": farm_size_idle, "experience_years": experience_years, } + # OP#1120: farm groups default to the FARM group type (a farm stays a + # farm even when it becomes a member of a cooperative — only the + # cooperative container is typed COOPERATIVE, in _create_cooperatives). + farm_group_type_id = self._ensure_farm_group_type() + if farm_group_type_id: + farm_vals["group_type_id"] = farm_group_type_id if phone: farm_vals["phone"] = phone farm = Partner.create(farm_vals) @@ -1395,6 +1401,26 @@ def _ensure_cooperative_group_type(self): _logger.warning("Could not create cooperative group type vocabulary code") return False + def _ensure_farm_group_type(self): + """Ensure the 'farm' group type vocabulary code exists (OP#1120). + + Farm groups default to this type. Mirrors _ensure_cooperative_group_type + (ADR-016: local codes with is_local=True bypass system protection). + + Returns: + int: vocabulary code ID for the farm group type, or False. + """ + try: + code = self.env["spp.vocabulary.code"].get_or_create_local( + "urn:openspp:vocab:group-type", + "farm", + display="Farm", + ) + return code.id + except Exception: + _logger.warning("Could not create farm group type vocabulary code") + return False + # ────────────────────────────────────────────────────────────────────── # GIS Data (Coordinates + Land Records) # ────────────────────────────────────────────────────────────────────── diff --git a/spp_farmer_registry_demo/models/seeded_farm_generator.py b/spp_farmer_registry_demo/models/seeded_farm_generator.py index ffafea0b1..c5c5e4a07 100644 --- a/spp_farmer_registry_demo/models/seeded_farm_generator.py +++ b/spp_farmer_registry_demo/models/seeded_farm_generator.py @@ -369,6 +369,7 @@ def __init__(self, env, locale="fil_PH", seed=42): self._vocab_cache = {} self._species_cache = {} self._head_type_id = None + self._farm_group_type_id = None # OP#1120 # OP#1114: names already handed out this run, so the ~730 farms drawn # from an 86-surname pool don't end up with duplicate farm names (and, @@ -500,6 +501,10 @@ def generate_all_farms(self, blueprints): "farm_size_idle": idle, "experience_years": experience, } + # OP#1120: seeded farm groups default to the FARM group type. + farm_group_type_id = self._ensure_farm_group_type() + if farm_group_type_id: + gvals["group_type_id"] = farm_group_type_id if gps: gvals["coordinates"] = json.dumps({"type": "Point", "coordinates": [gps[0], gps[1]]}) if area_id: @@ -1151,6 +1156,26 @@ def _get_vocab_code(self, namespace_uri, code): self._vocab_cache[cache_key] = vocab.id if vocab else False return self._vocab_cache[cache_key] + def _ensure_farm_group_type(self): + """Ensure and return the 'farm' group type vocabulary code ID (OP#1120). + + Seeded farm groups default to this type. Created via get_or_create_local + so it exists even if the wizard hasn't already made it, and cached for + the run. + """ + if self._farm_group_type_id is None: + try: + code = self.env["spp.vocabulary.code"].get_or_create_local( + "urn:openspp:vocab:group-type", + "farm", + display="Farm", + ) + self._farm_group_type_id = code.id + except Exception: + _logger.warning("Could not create farm group type vocabulary code") + self._farm_group_type_id = False + return self._farm_group_type_id + def _resolve_species(self, species_code): """Map a species code string to a vocabulary code ID.""" if species_code in self._species_cache: diff --git a/spp_farmer_registry_demo/tests/test_demo_generator.py b/spp_farmer_registry_demo/tests/test_demo_generator.py index 7ad5929fb..37044030b 100644 --- a/spp_farmer_registry_demo/tests/test_demo_generator.py +++ b/spp_farmer_registry_demo/tests/test_demo_generator.py @@ -219,6 +219,8 @@ def test_create_story_farms_are_groups(self): for farm in story_farms.values(): self.assertTrue(farm.is_group) self.assertTrue(farm.is_registrant) + # OP#1120: story farm groups are typed FARM. + self.assertEqual(farm.group_type_id.code, "farm") def test_create_story_farms_have_members(self): """Test story farms have farmer members.""" @@ -262,6 +264,24 @@ def test_create_farm_helper(self): self.assertEqual(farm.name, "Test Farm") self.assertTrue(farm.is_group) self.assertEqual(farm.farm_total_size, 3.0) + # OP#1120: farm groups default to the FARM group type. + self.assertEqual(farm.group_type_id.code, "farm") + + def test_1120_cooperative_container_typed_cooperative(self): + """OP#1120: the cooperative container is typed COOPERATIVE, while its + member farms stay FARM (a farm is still a farm inside a cooperative).""" + wizard = self.Generator.create({"name": _unique("Coop Type Test")}) + story_farms = wizard._create_story_farms() + + cooperatives = wizard._create_cooperatives(story_farms) + self.assertTrue(cooperatives, "expected at least one cooperative") + + for coop in cooperatives.values(): + self.assertEqual(coop.group_type_id.code, "cooperative") + members = self.env["spp.group.membership"].search([("group", "=", coop.id)]) + self.assertTrue(members, "cooperative should have member farms") + for membership in members: + self.assertEqual(membership.individual.group_type_id.code, "farm") def test_maria_santos_profile(self): """Test Maria Santos persona - smallholder rice farmer.""" diff --git a/spp_farmer_registry_demo/tests/test_seeded_farm_generator.py b/spp_farmer_registry_demo/tests/test_seeded_farm_generator.py index a965a2b1b..bbdd7dbce 100644 --- a/spp_farmer_registry_demo/tests/test_seeded_farm_generator.py +++ b/spp_farmer_registry_demo/tests/test_seeded_farm_generator.py @@ -964,6 +964,9 @@ def test_generate_all_farms_mixed_farm_type(self): self.assertTrue(farm.is_group) self.assertTrue(farm.is_registrant) self.assertEqual(len(result["members"]), 2) + # OP#1120: seeded farm groups are typed FARM. + self.assertTrue(farm.group_type_id, "farm group must have a group type") + self.assertEqual(farm.group_type_id.code, "farm") # Verify farm details details = farm.farm_details_id