Allow process/resource type assignment via Type-suffix convention

The class-pairing validation added in 10ee5aef4f rejects any type
assignment whose class isn't in the buildingSMART implementer
agreement map. That map only covers physical product occurrence/type
pairs (IfcWallType -> IfcWall, etc); IfcTypeProcess and IfcTypeResource
subtypes such as IfcTaskType, IfcProcedureType and the resource types
have no entry, so previously-valid assignments like
IfcTaskType -> IfcTask were rejected with "allowed occurrence
classes: <none>".

These classes still follow the schema's universal Type-suffix naming
convention, so derive the pairing the same way the existing
ApplicableOccurrence fallback does: strip "Type" from the relating
type's class name and accept it only if the schema actually declares
that entity. This can only add pairings implied by the type's own
class name, so it cannot loosen the existing rejection of genuine
mismatches (e.g. IfcWallType -> IfcWindow).

Generated with the assistance of an AI coding tool.

(cherry picked from commit d188e3beaf)
This commit is contained in:
Stephen Boddy
2026-07-11 15:07:06 +01:00
committed by Dion Moult
parent a341ad29f3
commit 60b4f6191c
@@ -196,13 +196,25 @@ class Usecase:
allowed_occurrences = set(
ifcopenshell.util.type.get_applicable_entities(relating_type.is_a(), schema=self.file.schema)
)
schema = ifcopenshell.schema_by_name(self.file.schema)
# The implementer agreement map has no entry for the abstract
# IfcTypeProduct, which Bonsai uses for annotation types. The schema
# itself defines IfcTypeProduct.ApplicableOccurrence for exactly this
# purpose, so honor it when the leading class token is a valid entity.
if applicable_occurrence := getattr(relating_type, "ApplicableOccurrence", None):
occurrence_class = applicable_occurrence.split("/", 1)[0]
schema = ifcopenshell.schema_by_name(self.file.schema)
try:
schema.declaration_by_name(occurrence_class)
allowed_occurrences.add(occurrence_class)
except RuntimeError:
pass
# The map only covers physical product occurrence/type pairs (e.g.
# IfcWallType -> IfcWall). Process and resource types (IfcTaskType,
# IfcCrewResourceType, ...) aren't in it, but the schema's universal
# Type-suffix naming convention gives the same pairing directly.
if (type_class := relating_type.is_a()).endswith("Type"):
occurrence_class = type_class[: -len("Type")]
try:
schema.declaration_by_name(occurrence_class)
allowed_occurrences.add(occurrence_class)