Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions spp_farmer_registry_demo/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ Dependencies
Changelog
=========

19.0.2.1.3
~~~~~~~~~~

- fix(farmer_demo): type demo farm groups as FARM. Farm groups were
created with no group type, so they did not read as farms in the
registry; a farm stays typed FARM even when it joins a cooperative,
and only the cooperative container is typed COOPERATIVE (#1120)

19.0.2.1.2
~~~~~~~~~~

Expand Down
2 changes: 1 addition & 1 deletion spp_farmer_registry_demo/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "OpenSPP Farmer Registry Demo",
"summary": "Demo generator for Farmer Registry with fixed stories and volume generation",
"category": "OpenSPP",
"version": "19.0.2.1.2",
"version": "19.0.2.1.3",
"sequence": 1,
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
Expand Down
43 changes: 42 additions & 1 deletion spp_farmer_registry_demo/models/farmer_demo_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -808,12 +808,16 @@ def _create_story_farms(self):

story_farms = {}

# OP#1120 review: resolved once for the run rather than per farm.
farm_group_type_id = self._ensure_farm_group_type()

for story_id, story_data in STORY_FARMS.items():
farm_type_id = self._get_vocab_code("urn:openspp:vocab:farm-type", story_data["farm_type"])
tenure_id = self._get_vocab_code("urn:openspp:vocab:land-tenure", story_data["tenure"])
holder_id = self._get_vocab_code("urn:openspp:vocab:holder-type", "individual")

farm = self._create_farm(
farm_group_type_id=farm_group_type_id,
name=story_data["farm_name"],
farmer_name=story_data["farmer_name"],
farm_type_id=farm_type_id,
Expand Down Expand Up @@ -882,8 +886,13 @@ def _create_farm(
phone=None,
bank_name=None,
age=None,
farm_group_type_id=None,
):
"""Create a farm with the given attributes."""
"""Create a farm with the given attributes.

``farm_group_type_id`` is resolved by the caller once per run; left None
(a direct call, as in the tests) it is looked up here.
"""
Partner = self.env["res.partner"].sudo() # nosemgrep

farm_vals = {
Expand All @@ -901,6 +910,13 @@ 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).
if farm_group_type_id is None:
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)
Expand Down Expand Up @@ -1395,6 +1411,31 @@ 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).

Resolved once per run by the caller and passed down, rather than looked
up per farm: recordsets use __slots__, so the run-scoped attribute the
seeded generator caches on (it is a plain utility class, not a model)
is not available here (OP#1120 review).

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)
# ──────────────────────────────────────────────────────────────────────
Expand Down
25 changes: 25 additions & 0 deletions spp_farmer_registry_demo/models/seeded_farm_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions spp_farmer_registry_demo/readme/HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### 19.0.2.1.3

- fix(farmer_demo): type demo farm groups as FARM. Farm groups were created with no group type, so they did not read as farms in the registry; a farm stays typed FARM even when it joins a cooperative, and only the cooperative container is typed COOPERATIVE (#1120)

### 19.0.2.1.2

- fix(demo): put the Input Subsidy Program on **manual** entitlement approval (`auto_approve_entitlements=False`) so a demo user can walk the full cycle → entitlement approval chain, not just cycle approval. The flag is now per-program (every other demo program stays auto-approve), and historically seeded cycles are unaffected because the generator force-approves their pending entitlements (#1122)
Expand Down
15 changes: 12 additions & 3 deletions spp_farmer_registry_demo/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,15 @@ <h2><a class="toc-backref" href="#toc-entry-1">Changelog</a></h2>
</div>
</div>
<div class="section" id="section-1">
<h1>19.0.2.1.3</h1>
<ul class="simple">
<li>fix(farmer_demo): type demo farm groups as FARM. Farm groups were
created with no group type, so they did not read as farms in the
registry; a farm stays typed FARM even when it joins a cooperative,
and only the cooperative container is typed COOPERATIVE (#1120)</li>
</ul>
</div>
<div class="section" id="section-2">
<h1>19.0.2.1.2</h1>
<ul class="simple">
<li>fix(demo): put the Input Subsidy Program on <strong>manual</strong> entitlement
Expand All @@ -498,7 +507,7 @@ <h1>19.0.2.1.2</h1>
the generator force-approves their pending entitlements (#1122)</li>
</ul>
</div>
<div class="section" id="section-2">
<div class="section" id="section-3">
<h1>19.0.2.1.1</h1>
<ul class="simple">
<li>fix(demo): name each farm after its head member and give every member
Expand All @@ -513,7 +522,7 @@ <h1>19.0.2.1.1</h1>
(#1114)</li>
</ul>
</div>
<div class="section" id="section-3">
<div class="section" id="section-4">
<h1>19.0.2.1.0</h1>
<ul class="simple">
<li>feat(demo): add GIS + irrigation scenario (FM4) with reservoir + canal
Expand All @@ -532,7 +541,7 @@ <h1>19.0.2.1.0</h1>
tables and the CR overview</li>
</ul>
</div>
<div class="section" id="section-4">
<div class="section" id="section-5">
<h1>19.0.2.0.0</h1>
<ul class="simple">
<li>Initial migration to OpenSPP2</li>
Expand Down
20 changes: 20 additions & 0 deletions spp_farmer_registry_demo/tests/test_demo_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down
3 changes: 3 additions & 0 deletions spp_farmer_registry_demo/tests/test_seeded_farm_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading