@@ -16,14 +16,22 @@ import (
1616
1717// addCreateVariableAction creates a DECLARE statement as a CreateVariableAction.
1818func (fb * flowBuilder ) addCreateVariableAction (s * ast.DeclareStmt ) model.ID {
19+ // Resolve TypeEnumeration → TypeEntity ambiguity using the domain model
20+ declType := s .Type
21+ if declType .Kind == ast .TypeEnumeration && declType .EnumRef != nil && fb .reader != nil {
22+ if fb .isEntity (declType .EnumRef .Module , declType .EnumRef .Name ) {
23+ declType = ast.DataType {Kind : ast .TypeEntity , EntityRef : declType .EnumRef }
24+ }
25+ }
26+
1927 // Register the variable as declared
20- typeName := s . Type .Kind .String ()
28+ typeName := declType .Kind .String ()
2129 fb .declaredVars [s .Variable ] = typeName
2230
2331 action := & microflows.CreateVariableAction {
2432 BaseElement : model.BaseElement {ID : model .ID (mpr .GenerateID ())},
2533 VariableName : s .Variable ,
26- DataType : convertASTToMicroflowDataType (s . Type , nil ), // nil resolver - DECLARE uses primitive types
34+ DataType : convertASTToMicroflowDataType (declType , nil ),
2735 InitialValue : expressionToString (s .InitialValue ),
2836 }
2937
@@ -102,14 +110,7 @@ func (fb *flowBuilder) addCreateObjectAction(s *ast.CreateObjectStmt) model.ID {
102110 Type : microflows .MemberChangeTypeSet ,
103111 Value : expressionToString (change .Value ),
104112 }
105- // Check if this is an association (already qualified name with .) or an attribute
106- if strings .Contains (change .Attribute , "." ) {
107- // Association: Module.AssociationName (already fully qualified)
108- memberChange .AssociationQualifiedName = change .Attribute
109- } else if entityQN != "" {
110- // Attribute: Build qualified name as Module.Entity.Attribute
111- memberChange .AttributeQualifiedName = entityQN + "." + change .Attribute
112- }
113+ fb .resolveMemberChange (memberChange , change .Attribute , entityQN )
113114 action .InitialMembers = append (action .InitialMembers , memberChange )
114115 }
115116
@@ -258,14 +259,7 @@ func (fb *flowBuilder) addChangeObjectAction(s *ast.ChangeObjectStmt) model.ID {
258259 Type : microflows .MemberChangeTypeSet ,
259260 Value : expressionToString (change .Value ),
260261 }
261- // Check if this is an association (already qualified name with .) or an attribute
262- if strings .Contains (change .Attribute , "." ) {
263- // Association: Module.AssociationName (already fully qualified)
264- memberChange .AssociationQualifiedName = change .Attribute
265- } else if entityQN != "" {
266- // Attribute: Build qualified name as Module.Entity.Attribute
267- memberChange .AttributeQualifiedName = entityQN + "." + change .Attribute
268- }
262+ fb .resolveMemberChange (memberChange , change .Attribute , entityQN )
269263 action .Changes = append (action .Changes , memberChange )
270264 }
271265
@@ -726,6 +720,76 @@ func (fb *flowBuilder) addRemoveFromListAction(s *ast.RemoveFromListStmt) model.
726720 return activity .ID
727721}
728722
723+ // isEntity checks whether a qualified name refers to an entity in the domain model.
724+ func (fb * flowBuilder ) isEntity (moduleName , entityName string ) bool {
725+ if fb .reader == nil {
726+ return false
727+ }
728+ mod , err := fb .reader .GetModuleByName (moduleName )
729+ if err != nil || mod == nil {
730+ return false
731+ }
732+ dm , err := fb .reader .GetDomainModel (mod .ID )
733+ if err != nil || dm == nil {
734+ return false
735+ }
736+ for _ , e := range dm .Entities {
737+ if e .Name == entityName {
738+ return true
739+ }
740+ }
741+ return false
742+ }
743+
744+ // resolveMemberChange determines whether a member name is an association or attribute
745+ // and sets the appropriate field on the MemberChange. It queries the domain model
746+ // to check if the name matches an association on the entity; if no reader is available,
747+ // it falls back to the dot-contains heuristic.
748+ func (fb * flowBuilder ) resolveMemberChange (mc * microflows.MemberChange , memberName string , entityQN string ) {
749+ if entityQN == "" {
750+ return
751+ }
752+
753+ // Split entity qualified name into module and entity
754+ parts := strings .SplitN (entityQN , "." , 2 )
755+ if len (parts ) != 2 {
756+ mc .AttributeQualifiedName = entityQN + "." + memberName
757+ return
758+ }
759+ moduleName := parts [0 ]
760+
761+ // Query domain model to check if this member is an association
762+ if fb .reader != nil {
763+ if mod , err := fb .reader .GetModuleByName (moduleName ); err == nil && mod != nil {
764+ if dm , err := fb .reader .GetDomainModel (mod .ID ); err == nil && dm != nil {
765+ for _ , a := range dm .Associations {
766+ if a .Name == memberName {
767+ mc .AssociationQualifiedName = moduleName + "." + memberName
768+ return
769+ }
770+ }
771+ // Also check cross-associations
772+ for _ , a := range dm .CrossAssociations {
773+ if a .Name == memberName {
774+ mc .AssociationQualifiedName = moduleName + "." + memberName
775+ return
776+ }
777+ }
778+ // Not an association — it's an attribute
779+ mc .AttributeQualifiedName = entityQN + "." + memberName
780+ return
781+ }
782+ }
783+ }
784+
785+ // Fallback: if already qualified (contains dot), treat as association
786+ if strings .Contains (memberName , "." ) {
787+ mc .AssociationQualifiedName = memberName
788+ } else {
789+ mc .AttributeQualifiedName = entityQN + "." + memberName
790+ }
791+ }
792+
729793// assocLookupResult holds resolved association metadata.
730794type assocLookupResult struct {
731795 Type domainmodel.AssociationType
0 commit comments