diff --git a/.liquibase/Data_alias/basic/2021.2.0/MSTeams/changelog.xml b/.liquibase/Data_alias/basic/2021.2.0/MSTeams/changelog.xml
new file mode 100644
index 0000000000000000000000000000000000000000..18db840a17b45feb22de2a4863929d91bf316cb2
--- /dev/null
+++ b/.liquibase/Data_alias/basic/2021.2.0/MSTeams/changelog.xml
@@ -0,0 +1,7 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
+    <include relativeToChangelogFile="true" file="drop_mstTeamLink.xml"/>
+    <include relativeToChangelogFile="true" file="insert_mstTeamAttribute.xml"/>
+    <include relativeToChangelogFile="true" file="insert_KeqwordEntryMSTActivityDirection.xml"/>
+</databaseChangeLog>
diff --git a/.liquibase/Data_alias/basic/2021.2.0/MSTeams/drop_mstTeamLink.xml b/.liquibase/Data_alias/basic/2021.2.0/MSTeams/drop_mstTeamLink.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e0eb40951f8ce85c3c5236241e74ca4a4fa041f8
--- /dev/null
+++ b/.liquibase/Data_alias/basic/2021.2.0/MSTeams/drop_mstTeamLink.xml
@@ -0,0 +1,8 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
+    <changeSet author="" id="79d8f115-929f-4d4e-a961-0aefb807d90f">
+        <dropTable tableName="MST_TEAMLINK"/>
+    </changeSet>
+</databaseChangeLog>
\ No newline at end of file
diff --git a/.liquibase/Data_alias/basic/2021.2.0/MSTeams/insert_KeqwordEntryMSTActivityDirection.xml b/.liquibase/Data_alias/basic/2021.2.0/MSTeams/insert_KeqwordEntryMSTActivityDirection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c296cdc99133b13c2ff97335d66be54a535f2448
--- /dev/null
+++ b/.liquibase/Data_alias/basic/2021.2.0/MSTeams/insert_KeqwordEntryMSTActivityDirection.xml
@@ -0,0 +1,17 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
+    <changeSet author="p.neub" id="29a9e33a-4c3f-4409-af9f-d676ef399103">
+        <insert tableName="AB_KEYWORD_ENTRY">
+            <column name="AB_KEYWORD_ENTRYID" value="7e21e85e-8091-48cb-8bdc-e7bef7fa1f80"/>
+            <column name="AB_KEYWORD_CATEGORY_ID" value="a5206aff-79f8-413e-a76d-4cb9d39694d3"/>
+            <column name="KEYID" value="MST-MESSAGE"/>
+            <column name="TITLE" value="MS-Teams Message"/>
+            <column name="CONTAINER" value="ActivityCategory"/>
+            <column name="SORTING" valueNumeric="8"/>
+            <column name="ISACTIVE" valueNumeric="1"/>
+            <column name="ISESSENTIAL" valueNumeric="0"/>
+        </insert>
+    </changeSet>
+</databaseChangeLog>
\ No newline at end of file
diff --git a/.liquibase/Data_alias/basic/2021.2.0/MSTeams/insert_mstTeamAttribute.xml b/.liquibase/Data_alias/basic/2021.2.0/MSTeams/insert_mstTeamAttribute.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b6cf990d0d9591e74dc31d676a17748cba060eb2
--- /dev/null
+++ b/.liquibase/Data_alias/basic/2021.2.0/MSTeams/insert_mstTeamAttribute.xml
@@ -0,0 +1,20 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
+    <changeSet author="p.neub" id="e14b152c-e772-40d3-b49a-d314cd0f38e4">
+        <insert tableName="AB_ATTRIBUTE">
+            <column name="AB_ATTRIBUTEID" value="9a93f671-fa9e-4783-8ae8-6cef76b3f62f"/>
+            <column name="ATTRIBUTE_ACTIVE" valueNumeric="1"/>
+            <column name="ATTRIBUTE_NAME" value="Team"/>
+            <column name="ATTRIBUTE_TYPE" value="OBJECTSELECTION"/>
+            <column name="DROPDOWNDEFINITION" value="MSTTeam_entity"/>
+        </insert>
+        <insert tableName="AB_ATTRIBUTEUSAGE">
+            <column name="AB_ATTRIBUTEUSAGEID" value="0929cd01-b445-42fc-8878-eebed8dabfd0"/>
+            <column name="AB_ATTRIBUTE_ID" value="9a93f671-fa9e-4783-8ae8-6cef76b3f62f"/>
+            <column name="MAX_COUNT" valueNumeric="1"/>
+            <column name="OBJECT_TYPE" value="Salesproject"/>
+        </insert>
+    </changeSet>
+</databaseChangeLog>
\ No newline at end of file
diff --git a/.liquibase/Data_alias/basic/2021.2.0/changelog.xml b/.liquibase/Data_alias/basic/2021.2.0/changelog.xml
index 822f717495e2e7b04ab38705816c23e05a9f90e9..c1e9160ec73d684baf6f91e098e1afa4ed1d4359 100644
--- a/.liquibase/Data_alias/basic/2021.2.0/changelog.xml
+++ b/.liquibase/Data_alias/basic/2021.2.0/changelog.xml
@@ -8,6 +8,7 @@
     <include relativeToChangelogFile="true" file="Offer/changelog.xml"/>
     <include relativeToChangelogFile="true" file="xRM-Service/changelog.xml"/>
     <include relativeToChangelogFile="true" file="Mosaico/changelog.xml"/>
+    <include relativeToChangelogFile="true" file="MSTeams/changelog.xml"/>
     <include relativeToChangelogFile="true" file="EwsContactSync/changelog.xml"/>
     <include relativeToChangelogFile="true" file="Export/changelog.xml"/>
 </databaseChangeLog>
diff --git a/.liquibase/Data_alias/basic/_demoData/changelog.xml b/.liquibase/Data_alias/basic/_demoData/changelog.xml
index 0f8fd6f15e6939e296eec6fc54d80128f2b42d97..fa3ea507e0c2a01a2ad3e0cc9b80a58a9bb0abc4 100644
--- a/.liquibase/Data_alias/basic/_demoData/changelog.xml
+++ b/.liquibase/Data_alias/basic/_demoData/changelog.xml
@@ -77,7 +77,6 @@
   <include file="generatedData/mail_log.xml" relativeToChangelogFile="true"/>
   <include file="generatedData/mail_run.xml" relativeToChangelogFile="true"/>
   <include file="generatedData/mst_team.xml" relativeToChangelogFile="true"/>
-  <include file="generatedData/mst_teamlink.xml" relativeToChangelogFile="true"/>
   <include file="generatedData/objectmember.xml" relativeToChangelogFile="true"/>
   <include file="generatedData/observation.xml" relativeToChangelogFile="true"/>
   <include file="generatedData/offer.xml" relativeToChangelogFile="true"/>
@@ -113,4 +112,4 @@
   <include file="generatedData/weblink_click.xml" relativeToChangelogFile="true"/>
   <include file="generatedData/workflowsignal.xml" relativeToChangelogFile="true"/>
   <include file="generatedData/workflowstartconfig.xml" relativeToChangelogFile="true"/>
-</databaseChangeLog>
\ No newline at end of file
+</databaseChangeLog>
diff --git a/.liquibase/Data_alias/basic/_demoData/generatedData/communication.xml b/.liquibase/Data_alias/basic/_demoData/generatedData/communication.xml
index 907cc685669e41648203fab04c08b3076e287c7a..794d7a94192b6f7944dc118e6687b22ea8adb9ae 100644
--- a/.liquibase/Data_alias/basic/_demoData/generatedData/communication.xml
+++ b/.liquibase/Data_alias/basic/_demoData/generatedData/communication.xml
@@ -1539,4 +1539,4 @@
             <column name="OBJECT_ROWID" value="59c9d416-0a39-4948-a540-f439178fbafe"/>
         </insert>
     </changeSet>
-</databaseChangeLog>
\ No newline at end of file
+</databaseChangeLog>
diff --git a/.liquibase/Data_alias/basic/_demoData/generatedData/mst_team.xml b/.liquibase/Data_alias/basic/_demoData/generatedData/mst_team.xml
index dfb7506b9d264a176bbfa7cee102fcd78c5db249..23e77fc417a6d82f6b4b2fcca23d9c10d6466b79 100644
--- a/.liquibase/Data_alias/basic/_demoData/generatedData/mst_team.xml
+++ b/.liquibase/Data_alias/basic/_demoData/generatedData/mst_team.xml
@@ -3,4 +3,4 @@
   <changeSet author="autogenerated" id="7ad7747a-cafd-4c32-813c-a23e47450f9e">
     <delete tableName="mst_team"/>
   </changeSet>
-</databaseChangeLog>
\ No newline at end of file
+</databaseChangeLog>
diff --git a/.liquibase/Data_alias/basic/_demoData/generatedData/mst_teamlink.xml b/.liquibase/Data_alias/basic/_demoData/generatedData/mst_teamlink.xml
deleted file mode 100644
index cb4100baa199c70eadfcabe4058aa74dfa30b230..0000000000000000000000000000000000000000
--- a/.liquibase/Data_alias/basic/_demoData/generatedData/mst_teamlink.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.1" encoding="UTF-8" standalone="no"?>
-<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
-  <changeSet author="autogenerated" id="c71eff7a-751b-487f-928d-c888e544df40">
-    <delete tableName="mst_teamlink"/>
-  </changeSet>
-</databaseChangeLog>
\ No newline at end of file
diff --git a/entity/Activity_entity/Activity_entity.aod b/entity/Activity_entity/Activity_entity.aod
index d639aeae48ff82576bdc3a45d10d7b3b1b8a8666..f156d015f0d70c426e669ba082f1ecf7c0121d53 100644
--- a/entity/Activity_entity/Activity_entity.aod
+++ b/entity/Activity_entity/Activity_entity.aod
@@ -576,7 +576,7 @@
       <children>
         <entityActionField>
           <name>importFromTeams</name>
-          <title>Import from Teams</title>
+          <title>Import from MS Teams</title>
           <onActionProcess>%aditoprj%/entity/Activity_entity/entityfields/msteamsactions/children/importfromteams/onActionProcess.js</onActionProcess>
         </entityActionField>
       </children>
diff --git a/entity/Activity_entity/entityfields/msteamsactions/stateProcess.js b/entity/Activity_entity/entityfields/msteamsactions/stateProcess.js
index 2708ddc1ec8a3c626e049d0956bd4b116ed2c10b..7807ac22eb5026361d342c5352ce7dbebffa26d0 100644
--- a/entity/Activity_entity/entityfields/msteamsactions/stateProcess.js
+++ b/entity/Activity_entity/entityfields/msteamsactions/stateProcess.js
@@ -1,7 +1,18 @@
-import("system.vars");
-import("system.neon");
 import("system.result");
+import("system.neon");
+import("system.vars");
+import("Attribute_lib");
+import("AttributeRegistry_basic");
 import("MSTeams_lib");
 
-var isTeamsEnabled = vars.get("$param.ObjectId_param") in MSTeamsUtils.getTeamLinkContexts() && MSTeamsUtils.isTeamsEnabled();
-result.string(isTeamsEnabled ? neon.COMPONENTSTATE_EDITABLE : neon.COMPONENTSTATE_INVISIBLE);
\ No newline at end of file
+var objectId = vars.get("$param.ObjectId_param");
+var rowId = vars.get("$param.RowId_param");
+var editable = MSTeamsUtils.isTeamsEnabled() && objectId && rowId && new AttributeRelationQuery(
+    rowId, $AttributeRegistry.mstTeam(), objectId).getSingleAttributeValue();
+    
+var res = neon.COMPONENTSTATE_INVISIBLE;
+if(editable)
+{
+    res = neon.COMPONENTSTATE_EDITABLE;
+}
+result.string(res);
diff --git a/entity/AttributeRelation_entity/AttributeRelation_entity.aod b/entity/AttributeRelation_entity/AttributeRelation_entity.aod
index 272c64f4f8e08ec92a7f39388059893d33595d5b..a894eef2247fdd0d13224792bd7318f54dbd522d 100644
--- a/entity/AttributeRelation_entity/AttributeRelation_entity.aod
+++ b/entity/AttributeRelation_entity/AttributeRelation_entity.aod
@@ -4,9 +4,13 @@
   <title>Attribute</title>
   <majorModelMode>DISTRIBUTED</majorModelMode>
   <documentation>%aditoprj%/entity/AttributeRelation_entity/documentation.adoc</documentation>
+  <siblings>
+    <element>Salesproject_entity</element>
+  </siblings>
   <grantUpdateProcess>%aditoprj%/entity/AttributeRelation_entity/grantUpdateProcess.js</grantUpdateProcess>
   <grantDeleteProcess>%aditoprj%/entity/AttributeRelation_entity/grantDeleteProcess.js</grantDeleteProcess>
   <contentTitleProcess>%aditoprj%/entity/AttributeRelation_entity/contentTitleProcess.js</contentTitleProcess>
+  <afterSave>%aditoprj%/entity/AttributeRelation_entity/afterSave.js</afterSave>
   <titlePlural>Attributes</titlePlural>
   <recordContainer>jdito</recordContainer>
   <entityFields>
diff --git a/entity/AttributeRelation_entity/afterSave.js b/entity/AttributeRelation_entity/afterSave.js
new file mode 100644
index 0000000000000000000000000000000000000000..221f6eebcd5f71e989c3c76d4aa6d575c6c684d2
--- /dev/null
+++ b/entity/AttributeRelation_entity/afterSave.js
@@ -0,0 +1,18 @@
+import("system.teams");
+import("system.vars");
+import("system.project");
+import("AttributeRegistry_basic");
+
+if(vars.get("$field.AB_ATTRIBUTE_ID") == $AttributeRegistry.mstTeam())
+{
+    var teamId = vars.get("$field.VALUE");
+    var appId = project.getInstanceConfigValue("teamsAppId");
+    try
+    {
+        appId && teams.addApp(teamId, appId);
+    }
+    catch(ex)
+    {
+        // App is allready added
+    }
+}
diff --git a/entity/MSTTeamLink_entity/MSTTeamLink_entity.aod b/entity/MSTTeamLink_entity/MSTTeamLink_entity.aod
deleted file mode 100644
index 6a6afa9c7844c229d0522147b4205702dbc1978d..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/MSTTeamLink_entity.aod
+++ /dev/null
@@ -1,114 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<entity xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.3.22" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/entity/1.3.22">
-  <name>MSTTeamLink_entity</name>
-  <majorModelMode>DISTRIBUTED</majorModelMode>
-  <documentation>%aditoprj%/entity/MSTTeamLink_entity/documentation.adoc</documentation>
-  <siblings>
-    <element>Salesproject_entity</element>
-    <element>Member_entity</element>
-  </siblings>
-  <afterUiInit>%aditoprj%/entity/MSTTeamLink_entity/afterUiInit.js</afterUiInit>
-  <recordContainer>db</recordContainer>
-  <entityFields>
-    <entityProvider>
-      <name>#PROVIDER</name>
-    </entityProvider>
-    <entityProvider>
-      <name>#PROVIDER_AGGREGATES</name>
-      <useAggregates v="true" />
-    </entityProvider>
-    <entityField>
-      <name>MST_TEAMLINKID</name>
-    </entityField>
-    <entityField>
-      <name>MST_TEAM_ID</name>
-      <title>Team</title>
-      <mandatory v="true" />
-      <valueProcess>%aditoprj%/entity/MSTTeamLink_entity/entityfields/mst_team_id/valueProcess.js</valueProcess>
-    </entityField>
-    <entityField>
-      <name>OBJECT_ROWID</name>
-      <valueProcess>%aditoprj%/entity/MSTTeamLink_entity/entityfields/object_rowid/valueProcess.js</valueProcess>
-    </entityField>
-    <entityField>
-      <name>OBJECT_TYPE</name>
-      <valueProcess>%aditoprj%/entity/MSTTeamLink_entity/entityfields/object_type/valueProcess.js</valueProcess>
-    </entityField>
-    <entityParameter>
-      <name>ObjectRowId_param</name>
-      <expose v="true" />
-    </entityParameter>
-    <entityParameter>
-      <name>ObjectType_param</name>
-      <expose v="true" />
-    </entityParameter>
-    <entityConsumer>
-      <name>Teams</name>
-      <dependency>
-        <name>dependency</name>
-        <entityName>MSTTeam_entity</entityName>
-        <fieldName>TeamsByIdAndName</fieldName>
-      </dependency>
-    </entityConsumer>
-    <entityField>
-      <name>TEAMNAME</name>
-      <valueProcess>%aditoprj%/entity/MSTTeamLink_entity/entityfields/teamname/valueProcess.js</valueProcess>
-    </entityField>
-    <entityField>
-      <name>TEAM_ID_AND_NAME</name>
-      <documentation>%aditoprj%/entity/MSTTeamLink_entity/entityfields/team_id_and_name/documentation.adoc</documentation>
-      <title>Team</title>
-      <consumer>Teams</consumer>
-      <valueProcess>%aditoprj%/entity/MSTTeamLink_entity/entityfields/team_id_and_name/valueProcess.js</valueProcess>
-      <displayValueProcess>%aditoprj%/entity/MSTTeamLink_entity/entityfields/team_id_and_name/displayValueProcess.js</displayValueProcess>
-    </entityField>
-  </entityFields>
-  <recordContainers>
-    <dbRecordContainer>
-      <name>db</name>
-      <fromClauseProcess>%aditoprj%/entity/MSTTeamLink_entity/recordcontainers/db/fromClauseProcess.js</fromClauseProcess>
-      <conditionProcess>%aditoprj%/entity/MSTTeamLink_entity/recordcontainers/db/conditionProcess.js</conditionProcess>
-      <onDBInsert>%aditoprj%/entity/MSTTeamLink_entity/recordcontainers/db/onDBInsert.js</onDBInsert>
-      <onDBUpdate>%aditoprj%/entity/MSTTeamLink_entity/recordcontainers/db/onDBUpdate.js</onDBUpdate>
-      <alias>Data_alias</alias>
-      <recordFieldMappings>
-        <dbRecordFieldMapping>
-          <name>MST_TEAMLINKID.value</name>
-          <recordfield>MST_TEAMLINK.MST_TEAMLINKID</recordfield>
-        </dbRecordFieldMapping>
-        <dbRecordFieldMapping>
-          <name>MST_TEAM_ID.value</name>
-          <recordfield>MST_TEAMLINK.MST_TEAM_ID</recordfield>
-        </dbRecordFieldMapping>
-        <dbRecordFieldMapping>
-          <name>OBJECT_ROWID.value</name>
-          <recordfield>MST_TEAMLINK.OBJECT_ROWID</recordfield>
-        </dbRecordFieldMapping>
-        <dbRecordFieldMapping>
-          <name>OBJECT_TYPE.value</name>
-          <recordfield>MST_TEAMLINK.OBJECT_TYPE</recordfield>
-        </dbRecordFieldMapping>
-        <dbRecordFieldMapping>
-          <name>TEAMNAME.value</name>
-          <recordfield>MST_TEAM.TEAMNAME</recordfield>
-        </dbRecordFieldMapping>
-      </recordFieldMappings>
-      <linkInformation>
-        <linkInformation>
-          <name>178230f3-4ee4-4ec0-a7f6-4f4ddddbbb22</name>
-          <tableName>MST_TEAMLINK</tableName>
-          <primaryKey>MST_TEAMLINKID</primaryKey>
-          <isUIDTable v="true" />
-          <readonly v="false" />
-        </linkInformation>
-        <linkInformation>
-          <name>53378325-f195-4eb3-9e22-68fd769eef03</name>
-          <tableName>MST_TEAM</tableName>
-          <primaryKey>MST_TEAMID</primaryKey>
-          <isUIDTable v="false" />
-          <readonly v="true" />
-        </linkInformation>
-      </linkInformation>
-    </dbRecordContainer>
-  </recordContainers>
-</entity>
diff --git a/entity/MSTTeamLink_entity/afterUiInit.js b/entity/MSTTeamLink_entity/afterUiInit.js
deleted file mode 100644
index bac4e960d87d6ae60ae5e5d5f2fc506fdd3d191b..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/afterUiInit.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import("system.vars");
-import("system.neon");
-import("system.entities");
-
-//reload the teams once, because the cache might not be up-to-date
-if (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW || vars.get("$sys.recordstate") == neon.OPERATINGSTATE_EDIT)
-{
-    entities.invalidateCache("MSTTeam_entity", "jdito");
-    entities.getRows(entities.createConfigForLoadingConsumerRows()
-        .consumer("Teams")
-        .fields(["UID", "TEAMNAME", "TEAMID_AND_NAME", "ISARCHIVED", "DESCRIPTION"])
-    );
-}
\ No newline at end of file
diff --git a/entity/MSTTeamLink_entity/documentation.adoc b/entity/MSTTeamLink_entity/documentation.adoc
deleted file mode 100644
index 55fcd72aba98ec0011c6562a4a81014327899e72..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/documentation.adoc
+++ /dev/null
@@ -1,3 +0,0 @@
-= MSTTeamLink_entity
-
-This entity represents a relation between a MST team and an object (e. g. a Sales Project).
\ No newline at end of file
diff --git a/entity/MSTTeamLink_entity/entityfields/mst_team_id/valueProcess.js b/entity/MSTTeamLink_entity/entityfields/mst_team_id/valueProcess.js
deleted file mode 100644
index 8b66b4de6f181006ed9464bc5c730db279e5af42..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/entityfields/mst_team_id/valueProcess.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import("system.result");
-import("system.neon");
-import("system.vars");
-import("Util_lib");
-
-var teamIdAndName = Utils.parseJSON(vars.get("$field.TEAM_ID_AND_NAME"));
-if (teamIdAndName && (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW || vars.get("$sys.recordstate") == neon.OPERATINGSTATE_EDIT))
-    result.string(teamIdAndName[0]);
\ No newline at end of file
diff --git a/entity/MSTTeamLink_entity/entityfields/object_rowid/valueProcess.js b/entity/MSTTeamLink_entity/entityfields/object_rowid/valueProcess.js
deleted file mode 100644
index 53123b405da6e47e653fc29b6ea5b1d7ab0b7aba..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/entityfields/object_rowid/valueProcess.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import("system.neon");
-import("system.vars");
-import("system.result");
-
-if (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
-    result.string(vars.get("$param.ObjectRowId_param"));
\ No newline at end of file
diff --git a/entity/MSTTeamLink_entity/entityfields/object_type/valueProcess.js b/entity/MSTTeamLink_entity/entityfields/object_type/valueProcess.js
deleted file mode 100644
index 7f34d7fdb9049c4c3427fb40b45a320b1585d5b0..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/entityfields/object_type/valueProcess.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import("system.neon");
-import("system.vars");
-import("system.result");
-
-if (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
-    result.string(vars.get("$param.ObjectType_param"));
\ No newline at end of file
diff --git a/entity/MSTTeamLink_entity/entityfields/team_id_and_name/displayValueProcess.js b/entity/MSTTeamLink_entity/entityfields/team_id_and_name/displayValueProcess.js
deleted file mode 100644
index ada1bccad1d8dba906a5f5db4d9289e0abf3f44f..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/entityfields/team_id_and_name/displayValueProcess.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import("system.vars");
-import("system.result");
-
-result.string(vars.get("$field.TEAMNAME"));
\ No newline at end of file
diff --git a/entity/MSTTeamLink_entity/entityfields/team_id_and_name/documentation.adoc b/entity/MSTTeamLink_entity/entityfields/team_id_and_name/documentation.adoc
deleted file mode 100644
index 7ef07b42d0e7f4ddef718cf1cf07362e36866b23..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/entityfields/team_id_and_name/documentation.adoc
+++ /dev/null
@@ -1,7 +0,0 @@
-= TEAM_ID_AND_NAME
-
-This field is used for selecting a MST team for the teamLink. Because we need both the id and the name from the team, it can't be selected
-directly in the MST_TEAM_ID field, which contains only the id (and fetching the name afterwards using the id would be rather slow).
-
-In new- or edit-mode, the two fields MST_TEAM_ID and TEAMNAME pull their values from this field. In view-mode, this field gets the value
-from MST_TEAM_ID and TEAMNAME, because only these two fields are connected to the recordContainer.
\ No newline at end of file
diff --git a/entity/MSTTeamLink_entity/entityfields/team_id_and_name/valueProcess.js b/entity/MSTTeamLink_entity/entityfields/team_id_and_name/valueProcess.js
deleted file mode 100644
index 94638c9fe9031b798104127c1426fc2d471dca36..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/entityfields/team_id_and_name/valueProcess.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import("system.result");
-import("system.neon");
-import("system.vars");
-
-//in view-mode, load the values from the fields that are connected to the recordcontainer
-if (vars.get("$sys.recordstate") != neon.OPERATINGSTATE_NEW && vars.get("$sys.recordstate") != neon.OPERATINGSTATE_EDIT)
-    result.string(JSON.stringify([vars.get("$field.MST_TEAM_ID"), vars.get("$field.TEAMNAME")]));
\ No newline at end of file
diff --git a/entity/MSTTeamLink_entity/entityfields/teamname/valueProcess.js b/entity/MSTTeamLink_entity/entityfields/teamname/valueProcess.js
deleted file mode 100644
index d3f581dba2d01626cb9050634dac3abba981f6ab..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/entityfields/teamname/valueProcess.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import("system.result");
-import("system.neon");
-import("system.vars");
-import("Util_lib");
-
-var teamIdAndName = Utils.parseJSON(vars.get("$field.TEAM_ID_AND_NAME"));
-if (teamIdAndName && (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW || vars.get("$sys.recordstate") == neon.OPERATINGSTATE_EDIT))
-    result.string(teamIdAndName[1]);
\ No newline at end of file
diff --git a/entity/MSTTeamLink_entity/recordcontainers/db/conditionProcess.js b/entity/MSTTeamLink_entity/recordcontainers/db/conditionProcess.js
deleted file mode 100644
index ad0a18104e1291790bd33463603537bce7c5914b..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/recordcontainers/db/conditionProcess.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import("system.result");
-import("Sql_lib");
-
-var condition = newWhereIfSet("MST_TEAMLINK.OBJECT_ROWID", "$param.ObjectRowId_param")
-    .andIfSet("MST_TEAMLINK.OBJECT_TYPE", "$param.ObjectType_param");
-
-result.string(condition.toString(SqlBuilder.NORESULT_CONDITION()));
\ No newline at end of file
diff --git a/entity/MSTTeamLink_entity/recordcontainers/db/fromClauseProcess.js b/entity/MSTTeamLink_entity/recordcontainers/db/fromClauseProcess.js
deleted file mode 100644
index 4457ba9b19fa0da6c133ce39565cb83164888cfb..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/recordcontainers/db/fromClauseProcess.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import("system.result");
-
-result.string("MST_TEAMLINK left join MST_TEAM on MST_TEAMLINK.MST_TEAM_ID = MST_TEAM.MST_TEAMID");
\ No newline at end of file
diff --git a/entity/MSTTeamLink_entity/recordcontainers/db/onDBInsert.js b/entity/MSTTeamLink_entity/recordcontainers/db/onDBInsert.js
deleted file mode 100644
index bf45fb336010b2db0a4ddc1e03a35ce009fb2c15..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/recordcontainers/db/onDBInsert.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import("system.vars");
-import("MSTeams_lib");
-
-var teamLinkId = vars.get("$local.uid");
-var rowData = vars.get("$local.rowdata");
-//if the linked context is configured to have just one teamLink but the object already has a teamLink, the old teamLink will be deleted
-MSTeamsUtils.purgeCorruptTeamLinks(rowData["MST_TEAMLINK.OBJECT_ROWID"], rowData["MST_TEAMLINK.OBJECT_TYPE"], teamLinkId);
-
-MSTeamsUtils.insertTeamIfMissing({
-    teamId: rowData["MST_TEAMLINK.MST_TEAM_ID"],
-    teamName: rowData["MST_TEAM.TEAMNAME"]
-});
\ No newline at end of file
diff --git a/entity/MSTTeamLink_entity/recordcontainers/db/onDBUpdate.js b/entity/MSTTeamLink_entity/recordcontainers/db/onDBUpdate.js
deleted file mode 100644
index 61769345ec3f8bb71aad2e776267b4f5f009f185..0000000000000000000000000000000000000000
--- a/entity/MSTTeamLink_entity/recordcontainers/db/onDBUpdate.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import("system.vars");
-import("MSTeams_lib");
-
-var rowData = vars.get("$local.rowdata");
-
-MSTeamsUtils.insertTeamIfMissing({
-    teamId: rowData["MST_TEAMLINK.MST_TEAM_ID"],
-    teamName: rowData["MST_TEAM.TEAMNAME"]
-});
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/MSTTeamMember_entity.aod b/entity/MSTTeamMember_entity/MSTTeamMember_entity.aod
index 67663ffde3a52b12cb6949df2a2f618cebd4120e..d173fc4f9f989d1d9de5f9046dca945cabe1794c 100644
--- a/entity/MSTTeamMember_entity/MSTTeamMember_entity.aod
+++ b/entity/MSTTeamMember_entity/MSTTeamMember_entity.aod
@@ -12,47 +12,16 @@
     </entityProvider>
     <entityField>
       <name>CONTACT_ID</name>
-      <title>Members</title>
-      <consumer>ProjectMembers</consumer>
+      <title>Contact</title>
+      <consumer>Persons</consumer>
       <linkedContextProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/contact_id/linkedContextProcess.js</linkedContextProcess>
-      <mandatory v="true" />
+      <mandatoryProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/contact_id/mandatoryProcess.js</mandatoryProcess>
       <stateProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/contact_id/stateProcess.js</stateProcess>
       <displayValueProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/contact_id/displayValueProcess.js</displayValueProcess>
     </entityField>
     <entityField>
       <name>UID</name>
     </entityField>
-    <entityParameter>
-      <name>UpnsOfMembers_param</name>
-      <expose v="true" />
-    </entityParameter>
-    <entityParameter>
-      <name>MailsOfExtern_param</name>
-      <expose v="true" />
-    </entityParameter>
-    <entityConsumer>
-      <name>ProjectMembers</name>
-      <dependency>
-        <name>dependency</name>
-        <entityName>Member_entity</entityName>
-        <fieldName>TeamMemberProvider</fieldName>
-      </dependency>
-      <children>
-        <entityParameter>
-          <name>ObjectRowId_param</name>
-          <valueProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/projectmembers/children/objectrowid_param/valueProcess.js</valueProcess>
-        </entityParameter>
-        <entityParameter>
-          <name>ObjectType_param</name>
-          <valueProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/projectmembers/children/objecttype_param/valueProcess.js</valueProcess>
-        </entityParameter>
-      </children>
-    </entityConsumer>
-    <entityField>
-      <name>ISEXTERN</name>
-      <state>INVISIBLE</state>
-      <valueProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/isextern/valueProcess.js</valueProcess>
-    </entityField>
     <entityField>
       <name>MEMBERNAME</name>
     </entityField>
@@ -76,37 +45,31 @@
       <name>ROLE</name>
       <title>Role</title>
       <mandatory v="true" />
-      <mandatoryProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/role/mandatoryProcess.js</mandatoryProcess>
       <dropDownProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/role/dropDownProcess.js</dropDownProcess>
-      <stateProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/role/stateProcess.js</stateProcess>
       <valueProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/role/valueProcess.js</valueProcess>
     </entityField>
-    <entityField>
-      <name>INVITE</name>
-      <title>Invitation</title>
-      <mandatoryProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/invite/mandatoryProcess.js</mandatoryProcess>
-      <dropDownProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/invite/dropDownProcess.js</dropDownProcess>
-      <stateProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/invite/stateProcess.js</stateProcess>
-      <valueProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/invite/valueProcess.js</valueProcess>
-    </entityField>
     <entityProvider>
       <name>#PROVIDER_AGGREGATES</name>
       <useAggregates v="true" />
     </entityProvider>
-    <entityField>
-      <name>AZUREID</name>
-      <state>INVISIBLE</state>
-      <valueProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/azureid/valueProcess.js</valueProcess>
-    </entityField>
-    <entityField>
-      <name>AZUREUPN</name>
-      <state>INVISIBLE</state>
-      <valueProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/azureupn/valueProcess.js</valueProcess>
-    </entityField>
-    <entityField>
-      <name>AZURE_DATA</name>
-      <valueProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/azure_data/valueProcess.js</valueProcess>
-    </entityField>
+    <entityParameter>
+      <name>ContactIds_param</name>
+      <expose v="true" />
+    </entityParameter>
+    <entityConsumer>
+      <name>Persons</name>
+      <dependency>
+        <name>dependency</name>
+        <entityName>Person_entity</entityName>
+        <fieldName>Contacts</fieldName>
+      </dependency>
+      <children>
+        <entityParameter>
+          <name>OnlyShowContactIds_param</name>
+          <valueProcess>%aditoprj%/entity/MSTTeamMember_entity/entityfields/persons/children/onlyshowcontactids_param/valueProcess.js</valueProcess>
+        </entityParameter>
+      </children>
+    </entityConsumer>
   </entityFields>
   <recordContainers>
     <jDitoRecordContainer>
@@ -127,18 +90,9 @@
         <jDitoRecordFieldMapping>
           <name>ROLE.value</name>
         </jDitoRecordFieldMapping>
-        <jDitoRecordFieldMapping>
-          <name>ROLE.displayValue</name>
-        </jDitoRecordFieldMapping>
         <jDitoRecordFieldMapping>
           <name>CONTACT_ID.value</name>
         </jDitoRecordFieldMapping>
-        <jDitoRecordFieldMapping>
-          <name>CONTACT_ID.displayValue</name>
-        </jDitoRecordFieldMapping>
-        <jDitoRecordFieldMapping>
-          <name>ISEXTERN.value</name>
-        </jDitoRecordFieldMapping>
       </recordFieldMappings>
     </jDitoRecordContainer>
   </recordContainers>
diff --git a/entity/MSTTeamMember_entity/entityfields/azure_data/valueProcess.js b/entity/MSTTeamMember_entity/entityfields/azure_data/valueProcess.js
deleted file mode 100644
index 2df756b62a28fb058c3cec8a2df5de3090bf9538..0000000000000000000000000000000000000000
--- a/entity/MSTTeamMember_entity/entityfields/azure_data/valueProcess.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import("system.result");
-import("system.neon");
-import("system.tools");
-import("system.vars");
-import("Employee_lib");
-
-var contactId = vars.get("$field.CONTACT_ID");
-var user = EmployeeUtils.getUserByContactId(contactId);
-var azureId = user ? user[tools.PARAMS][tools.TEAMS_AZUREID] : "";
-var azureUpn = user ? user[tools.PARAMS][tools.TEAMS_AZUREUPN] : "";
-
-var azureData = {
-    azureId: azureId,
-    azureUpn: azureUpn
-};
-result.string(JSON.stringify(azureData));
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/entityfields/azureid/valueProcess.js b/entity/MSTTeamMember_entity/entityfields/azureid/valueProcess.js
deleted file mode 100644
index 7744184c730b891e88e88ada811a017db109b03d..0000000000000000000000000000000000000000
--- a/entity/MSTTeamMember_entity/entityfields/azureid/valueProcess.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import("system.result");
-import("Util_lib");
-import("system.vars");
-
-var azureData = Utils.parseJSON(vars.get("$field.AZURE_DATA"));
-result.string(azureData ? azureData.azureId : "");
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/entityfields/azureupn/valueProcess.js b/entity/MSTTeamMember_entity/entityfields/azureupn/valueProcess.js
deleted file mode 100644
index 554d4e727325955acb211567d4ea1878a534128c..0000000000000000000000000000000000000000
--- a/entity/MSTTeamMember_entity/entityfields/azureupn/valueProcess.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import("system.result");
-import("Util_lib");
-import("system.vars");
-
-var azureData = Utils.parseJSON(vars.get("$field.AZURE_DATA"));
-result.string(azureData ? azureData.azureUpn : "");
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/entityfields/contact_id/mandatoryProcess.js b/entity/MSTTeamMember_entity/entityfields/contact_id/mandatoryProcess.js
new file mode 100644
index 0000000000000000000000000000000000000000..a8455294663ed399493e79ad2f1788b0e71ff96d
--- /dev/null
+++ b/entity/MSTTeamMember_entity/entityfields/contact_id/mandatoryProcess.js
@@ -0,0 +1,6 @@
+import("system.result");
+import("system.vars");
+
+// if membername is present the contact_id is invisible (and thus not mandatory)
+// because we cannot resolve an ms-teams user into an adito contact
+result.string(!vars.get("$field.MEMBERNAME"));
diff --git a/entity/MSTTeamMember_entity/entityfields/contact_id/stateProcess.js b/entity/MSTTeamMember_entity/entityfields/contact_id/stateProcess.js
index 53c48b1aa60c14977805717d383efb2668f5bd64..43cc1f51d3ba4e9b4b0becddb76cbfec0e93593a 100644
--- a/entity/MSTTeamMember_entity/entityfields/contact_id/stateProcess.js
+++ b/entity/MSTTeamMember_entity/entityfields/contact_id/stateProcess.js
@@ -2,4 +2,10 @@ import("system.result");
 import("system.neon");
 import("system.vars");
 
-result.string(vars.get("$field.MEMBERNAME") ? neon.COMPONENTSTATE_READONLY : neon.COMPONENTSTATE_EDITABLE);
\ No newline at end of file
+// if a mebername exists the user cant be edited because its allready synced with teams´
+var res = neon.COMPONENTSTATE_EDITABLE;
+if(vars.get("$field.MEMBERNAME"))
+{
+    res = neon.COMPONENTSTATE_READONLY;
+}
+result.string(res);
diff --git a/entity/MSTTeamMember_entity/entityfields/invite/dropDownProcess.js b/entity/MSTTeamMember_entity/entityfields/invite/dropDownProcess.js
deleted file mode 100644
index 0a4ec38e3c77cac49e9a94b463d4c483a63715a7..0000000000000000000000000000000000000000
--- a/entity/MSTTeamMember_entity/entityfields/invite/dropDownProcess.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import("system.result");
-import("system.translate");
-
-result.object([
-    ["true", translate.text("Invite")],
-    ["false", translate.text("Don't invite")]
-]);
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/entityfields/invite/mandatoryProcess.js b/entity/MSTTeamMember_entity/entityfields/invite/mandatoryProcess.js
deleted file mode 100644
index 488ee5dc7e0ad5e6122271731fa275245d7057e6..0000000000000000000000000000000000000000
--- a/entity/MSTTeamMember_entity/entityfields/invite/mandatoryProcess.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import("Util_lib");
-import("system.vars");
-import("system.result");
-
-result.string(Utils.toBoolean(vars.get("$field.ISEXTERN")));
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/entityfields/invite/stateProcess.js b/entity/MSTTeamMember_entity/entityfields/invite/stateProcess.js
deleted file mode 100644
index 37feecaa37dcd3ffed35221ff2e9157477fa6f4e..0000000000000000000000000000000000000000
--- a/entity/MSTTeamMember_entity/entityfields/invite/stateProcess.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import("system.result");
-import("system.vars");
-import("system.neon");
-
-result.string(vars.get("$field.MEMBERNAME") || vars.get("$field.ISEXTERN") != "true" ? neon.COMPONENTSTATE_INVISIBLE : neon.COMPONENTSTATE_EDITABLE);
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/entityfields/invite/valueProcess.js b/entity/MSTTeamMember_entity/entityfields/invite/valueProcess.js
deleted file mode 100644
index 516e09d60f21ed6d91e8f27b41593afec8f686ff..0000000000000000000000000000000000000000
--- a/entity/MSTTeamMember_entity/entityfields/invite/valueProcess.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import("system.neon");
-import("system.vars");
-import("system.result");
-
-if (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW && vars.get("$this.value") == null)
-    result.string(false);
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/entityfields/isextern/valueProcess.js b/entity/MSTTeamMember_entity/entityfields/isextern/valueProcess.js
deleted file mode 100644
index c2bd7034a52ed887060a47de48959ab0db7792f5..0000000000000000000000000000000000000000
--- a/entity/MSTTeamMember_entity/entityfields/isextern/valueProcess.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import("system.result");
-import("Util_lib");
-import("system.vars");
-
-var azureData = Utils.parseJSON(vars.get("$field.AZURE_DATA"));
-result.string(!azureData || !(azureData.azureId || azureData.azureUpn));
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/entityfields/persons/children/onlyshowcontactids_param/valueProcess.js b/entity/MSTTeamMember_entity/entityfields/persons/children/onlyshowcontactids_param/valueProcess.js
new file mode 100644
index 0000000000000000000000000000000000000000..b806067ae4c101ba28ef6c35e2196567310bbc89
--- /dev/null
+++ b/entity/MSTTeamMember_entity/entityfields/persons/children/onlyshowcontactids_param/valueProcess.js
@@ -0,0 +1,4 @@
+import("system.result");
+import("system.vars");
+
+result.string(vars.get("$param.ContactIds_param"));
diff --git a/entity/MSTTeamMember_entity/entityfields/projectmembers/children/objectrowid_param/valueProcess.js b/entity/MSTTeamMember_entity/entityfields/projectmembers/children/objectrowid_param/valueProcess.js
deleted file mode 100644
index f21aa5c2a735e552ff099c71fed0cd5fd88cbca1..0000000000000000000000000000000000000000
--- a/entity/MSTTeamMember_entity/entityfields/projectmembers/children/objectrowid_param/valueProcess.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import("system.vars");
-import("system.result");
-import("Sql_lib");
-
-var rowId = vars.get("$param.ObjectRowId_param");
-if (!rowId)
-{
-    rowId = newSelect("OBJECT_ROWID")
-        .from("MST_TEAMLINK")
-        .where("MST_TEAMLINK.MST_TEAM_ID", "$param.MSTTeamId_param")
-        .cell();
-}
-result.string(rowId);
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/entityfields/projectmembers/children/objecttype_param/valueProcess.js b/entity/MSTTeamMember_entity/entityfields/projectmembers/children/objecttype_param/valueProcess.js
deleted file mode 100644
index 95c8514f3bbc2804547c47b50ec222c09aec3f59..0000000000000000000000000000000000000000
--- a/entity/MSTTeamMember_entity/entityfields/projectmembers/children/objecttype_param/valueProcess.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import("system.vars");
-import("system.result");
-
-result.string(vars.get("$param.ObjectType_param"));
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/entityfields/role/dropDownProcess.js b/entity/MSTTeamMember_entity/entityfields/role/dropDownProcess.js
index 97558334115b6199684018810a38d54788a0e56b..6287caf1c8e3d9fd3cba24a9ec3edd641508a3a6 100644
--- a/entity/MSTTeamMember_entity/entityfields/role/dropDownProcess.js
+++ b/entity/MSTTeamMember_entity/entityfields/role/dropDownProcess.js
@@ -1,7 +1,18 @@
 import("system.result");
+import("system.vars");
 import("system.translate");
+import("MSTeams_lib");
 
-result.object([
-    ["owner", translate.text("Owner")],
-    ["member", translate.text("Member")]
-]);
\ No newline at end of file
+var roles = [];
+var contactId = vars.get("$field.CONTACT_ID");
+var azureId = contactId && MSTeamsUtils.getAzureIdByContactId(contactId);
+if(azureId)
+{
+    roles.push([$MSTeamsRoles.Owner(), translate.text("Owner")]);
+    roles.push([$MSTeamsRoles.Member(), translate.text("Member")]);
+}
+else if(contactId || vars.get("$field.MEMBERNAME"))
+{
+    roles.push([$MSTeamsRoles.Guest(), translate.text("Guest")]);
+}
+result.object(roles);
diff --git a/entity/MSTTeamMember_entity/entityfields/role/mandatoryProcess.js b/entity/MSTTeamMember_entity/entityfields/role/mandatoryProcess.js
deleted file mode 100644
index 79bef7b2ca07a9f35c95021eedcee8f89d68f873..0000000000000000000000000000000000000000
--- a/entity/MSTTeamMember_entity/entityfields/role/mandatoryProcess.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import("Util_lib");
-import("system.vars");
-import("system.result");
-
-result.string(!Utils.toBoolean(vars.get("$field.ISEXTERN")));
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/entityfields/role/stateProcess.js b/entity/MSTTeamMember_entity/entityfields/role/stateProcess.js
deleted file mode 100644
index f76be28d47e7adee58f41d88a7a7993bda575de0..0000000000000000000000000000000000000000
--- a/entity/MSTTeamMember_entity/entityfields/role/stateProcess.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import("system.result");
-import("system.vars");
-import("system.neon");
-import("system.logging")
-
-var state = neon.COMPONENTSTATE_EDITABLE;
-if (vars.get("$field.ISEXTERN") == "true")
-{
-    if (!vars.get("$field.MEMBERNAME"))
-        state = neon.COMPONENTSTATE_INVISIBLE;
-    else
-        state = neon.COMPONENTSTATE_READONLY;
-}
-
-result.string(state);
\ No newline at end of file
diff --git a/entity/MSTTeamMember_entity/entityfields/role/valueProcess.js b/entity/MSTTeamMember_entity/entityfields/role/valueProcess.js
index d79978a6cbc07884ec953adb3be3de3d78997b78..40b6defffba382a293ac62afd7e17185a94836a3 100644
--- a/entity/MSTTeamMember_entity/entityfields/role/valueProcess.js
+++ b/entity/MSTTeamMember_entity/entityfields/role/valueProcess.js
@@ -1,6 +1,31 @@
+import("system.result");
 import("system.neon");
 import("system.vars");
-import("system.result");
+import("Employee_lib");
+import("MSTeams_lib");
+
+// if this is the current logged in user
+// the role will preset to owner
+// otherwise the first option from the dropdown is selected
+
+var value = vars.get("$this.value");
+var dropDown = vars.get("$property.ROLE.dropDown");
 
-if (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW && vars.get("$this.value") == null)
-    result.string("member");
\ No newline at end of file
+if(!(value in dropDown))
+{
+    var emplContactId = EmployeeUtils.getCurrentContactId();
+    if(vars.get("$field.CONTACT_ID") == emplContactId && $MSTeamsRoles.Owner() in dropDown)
+    {
+        result.string($MSTeamsRoles.Owner());
+    }
+    else
+    {
+        var values = Object.keys(dropDown);
+        var res = null;
+        if(values.length)
+        {
+            res = values[values.length - 1];
+        }
+        result.string(res);
+    }
+}
diff --git a/entity/MSTTeamMember_entity/recordcontainers/jdito/contentProcess.js b/entity/MSTTeamMember_entity/recordcontainers/jdito/contentProcess.js
index 4f615a675a9df54c018305fd39293403cd7f852f..19693ce67cef45da1d518ef8a29df9ee9c010a00 100644
--- a/entity/MSTTeamMember_entity/recordcontainers/jdito/contentProcess.js
+++ b/entity/MSTTeamMember_entity/recordcontainers/jdito/contentProcess.js
@@ -1,44 +1,29 @@
-import("system.translate");
-import("system.teams");
 import("system.result");
+import("system.neon");
 import("system.vars");
+import("system.teams");
+import("system.translate");
+import("Sql_lib");
 import("MSTeams_lib");
 
+// get the team members using the mst api
 var teamId = vars.get("$param.MSTTeamId_param");
 var allMembers = teamId ? teams.getAllMembers(teamId) : {};
-var impersoAzureId = MSTeamsUtils.getImpersoAzureId();
-if (impersoAzureId in allMembers) //don't display the imperso user
-    delete allMembers[impersoAzureId];
-    
-var memberIds = vars.get("$local.idvalues") || Object.keys(allMembers);
-var owners = teamId ? teams.getAllOwners(teamId) : {};
-var memberContactIds = MSTeamsUtils.getContactIdsByAzureIds(memberIds);
-var memberArray = [];
+var allOwners = teamId ? teams.getAllOwners(teamId) : {};
+Object.assign(allMembers, allOwners);
+
+// don't display the impersonation user
+var impersoId = MSTeamsUtils.getImpersoAzureId();
+delete allMembers[impersoId];
+delete allOwners[impersoId];
 
-memberArray = memberIds.map(function (memberId) 
-{
-    var role = "guest";
-    var roleName = translate.text("Guest");
-    if (memberId in owners)
-    {
-        role = "owner";
-        roleName = translate.text("Owner");
-    }
-    else if (memberId in memberContactIds)
-    {
-        role = "member";
-        roleName = translate.text("Member");
-    }
-    
-    return [
-        memberId, 
-        allMembers[memberId] || "", 
-        role,
-        roleName,
-        memberContactIds[memberId] || "",
-        allMembers[memberId] || "",
-        !(memberId in memberContactIds)
-    ];
-});   
+// return the requested members
+var memberIds = vars.get("$local.idvalues") || Object.keys(allMembers);
+var azureContactMap = MSTeamsUtils.getContactIdsByAzureIds(memberIds);
 
-result.object(memberArray);
\ No newline at end of file
+var data = memberIds.map(function(memberId) {
+    var role = memberId in allOwners ? $MSTeamsRoles.Owner() :
+        (memberId in azureContactMap ? $MSTeamsRoles.Member() : $MSTeamsRoles.Guest());
+    return [memberId, allMembers[memberId], role, azureContactMap[memberId]];
+});
+result.object(data);
diff --git a/entity/MSTTeam_entity/MSTTeam_entity.aod b/entity/MSTTeam_entity/MSTTeam_entity.aod
index 34e9af2d1ae02853be662ff4761e782cd85cc2e2..84d08f4679b9a707f0891fe50051009df1e2ed1a 100644
--- a/entity/MSTTeam_entity/MSTTeam_entity.aod
+++ b/entity/MSTTeam_entity/MSTTeam_entity.aod
@@ -3,10 +3,12 @@
   <name>MSTTeam_entity</name>
   <majorModelMode>DISTRIBUTED</majorModelMode>
   <documentation>%aditoprj%/entity/MSTTeam_entity/documentation.adoc</documentation>
-  <grantUpdateProcess>%aditoprj%/entity/MSTTeam_entity/grantUpdateProcess.js</grantUpdateProcess>
-  <grantDeleteProcess>%aditoprj%/entity/MSTTeam_entity/grantDeleteProcess.js</grantDeleteProcess>
+  <siblings>
+    <element>AttributeRelation_entity</element>
+  </siblings>
   <contentTitleProcess>%aditoprj%/entity/MSTTeam_entity/contentTitleProcess.js</contentTitleProcess>
   <afterUiInit>%aditoprj%/entity/MSTTeam_entity/afterUiInit.js</afterUiInit>
+  <onValidation>%aditoprj%/entity/MSTTeam_entity/onValidation.js</onValidation>
   <recordContainer>jdito</recordContainer>
   <entityFields>
     <entityProvider>
@@ -40,7 +42,6 @@
     <entityConsumer>
       <name>TeamMembers</name>
       <stateProcess>%aditoprj%/entity/MSTTeam_entity/entityfields/teammembers/stateProcess.js</stateProcess>
-      <onValidation>%aditoprj%/entity/MSTTeam_entity/entityfields/teammembers/onValidation.js</onValidation>
       <dependency>
         <name>dependency</name>
         <entityName>MSTTeamMember_entity</entityName>
@@ -59,15 +60,16 @@
           <name>ObjectType_param</name>
           <valueProcess>%aditoprj%/entity/MSTTeam_entity/entityfields/teammembers/children/objecttype_param/valueProcess.js</valueProcess>
         </entityParameter>
+        <entityParameter>
+          <name>ContactIds_param</name>
+          <valueProcess>%aditoprj%/entity/MSTTeam_entity/entityfields/teammembers/children/contactids_param/valueProcess.js</valueProcess>
+        </entityParameter>
       </children>
     </entityConsumer>
-    <entityField>
-      <name>TEAMID_AND_NAME</name>
-    </entityField>
     <entityField>
       <name>ISARCHIVED</name>
       <title>Archived</title>
-      <dropDownProcess>%aditoprj%/entity/MSTTeam_entity/entityfields/isarchived/dropDownProcess.js</dropDownProcess>
+      <contentType>BOOLEAN</contentType>
     </entityField>
     <entityParameter>
       <name>ObjectRowId_param</name>
@@ -96,24 +98,11 @@
       <useAggregates v="true" />
     </entityProvider>
     <entityParameter>
-      <name>SalesprojectCode_param</name>
+      <name>IncludeArchived_param</name>
       <expose v="true" />
-      <description>PARAMETER</description>
     </entityParameter>
-    <entityProvider>
-      <name>TeamsByIdAndName</name>
-      <lookupIdfield>TEAMID_AND_NAME</lookupIdfield>
-      <documentation>%aditoprj%/entity/MSTTeam_entity/entityfields/teamsbyidandname/documentation.adoc</documentation>
-      <children>
-        <entityParameter>
-          <name>UseCache_param</name>
-          <valueProcess>%aditoprj%/entity/MSTTeam_entity/entityfields/teamsbyidandname/children/usecache_param/valueProcess.js</valueProcess>
-          <expose v="false" />
-        </entityParameter>
-      </children>
-    </entityProvider>
     <entityParameter>
-      <name>UseCache_param</name>
+      <name>ContactIds_param</name>
       <expose v="true" />
     </entityParameter>
   </entityFields>
@@ -121,14 +110,10 @@
     <jDitoRecordContainer>
       <name>jdito</name>
       <jDitoRecordAlias>Data_alias</jDitoRecordAlias>
-      <isFilterable v="true" />
-      <isRequireContainerFiltering v="true" />
       <contentProcess>%aditoprj%/entity/MSTTeam_entity/recordcontainers/jdito/contentProcess.js</contentProcess>
       <onInsert>%aditoprj%/entity/MSTTeam_entity/recordcontainers/jdito/onInsert.js</onInsert>
       <onUpdate>%aditoprj%/entity/MSTTeam_entity/recordcontainers/jdito/onUpdate.js</onUpdate>
       <onDelete>%aditoprj%/entity/MSTTeam_entity/recordcontainers/jdito/onDelete.js</onDelete>
-      <cacheType>SESSION</cacheType>
-      <cacheKeyProcess>%aditoprj%/entity/MSTTeam_entity/recordcontainers/jdito/cacheKeyProcess.js</cacheKeyProcess>
       <recordFieldMappings>
         <jDitoRecordFieldMapping>
           <name>UID.value</name>
@@ -136,12 +121,6 @@
         <jDitoRecordFieldMapping>
           <name>TEAMNAME.value</name>
         </jDitoRecordFieldMapping>
-        <jDitoRecordFieldMapping>
-          <name>TEAMID_AND_NAME.value</name>
-        </jDitoRecordFieldMapping>
-        <jDitoRecordFieldMapping>
-          <name>DESCRIPTION.value</name>
-        </jDitoRecordFieldMapping>
         <jDitoRecordFieldMapping>
           <name>ISARCHIVED.value</name>
         </jDitoRecordFieldMapping>
diff --git a/entity/MSTTeam_entity/entityfields/description/stateProcess.js b/entity/MSTTeam_entity/entityfields/description/stateProcess.js
index 18f779e6910541c7a8e75facfe14a54cfc74458b..ae683c19cc3f28bd0a0bc9c5a43171e1fa2b8b53 100644
--- a/entity/MSTTeam_entity/entityfields/description/stateProcess.js
+++ b/entity/MSTTeam_entity/entityfields/description/stateProcess.js
@@ -2,4 +2,9 @@ import("system.result");
 import("system.vars");
 import("system.neon");
 
-result.string(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW ? neon.COMPONENTSTATE_EDITABLE : neon.COMPONENTSTATE_READONLY);
\ No newline at end of file
+var res = neon.COMPONENTSTATE_INVISIBLE;
+if(vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)
+{
+    res = neon.COMPONENTSTATE_EDITABLE;
+}
+result.string(res);
diff --git a/entity/MSTTeam_entity/entityfields/isarchived/dropDownProcess.js b/entity/MSTTeam_entity/entityfields/isarchived/dropDownProcess.js
deleted file mode 100644
index f77d640ef1d32a67d4fd3e8da7bcff5531277f05..0000000000000000000000000000000000000000
--- a/entity/MSTTeam_entity/entityfields/isarchived/dropDownProcess.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import("system.result");
-import("system.translate");
-
-result.object([
-    ["true", translate.text("Yes")],
-    ["false", translate.text("No")]
-]);
\ No newline at end of file
diff --git a/entity/MSTTeam_entity/entityfields/teammembers/children/contactids_param/valueProcess.js b/entity/MSTTeam_entity/entityfields/teammembers/children/contactids_param/valueProcess.js
new file mode 100644
index 0000000000000000000000000000000000000000..b806067ae4c101ba28ef6c35e2196567310bbc89
--- /dev/null
+++ b/entity/MSTTeam_entity/entityfields/teammembers/children/contactids_param/valueProcess.js
@@ -0,0 +1,4 @@
+import("system.result");
+import("system.vars");
+
+result.string(vars.get("$param.ContactIds_param"));
diff --git a/entity/MSTTeam_entity/entityfields/teammembers/children/mstteamid_param/valueProcess.js b/entity/MSTTeam_entity/entityfields/teammembers/children/mstteamid_param/valueProcess.js
index 7b7daa293fe35f6acc203b39e6cb67bb7a987151..93a89968b87df6847b87e54835c66b3fa7a2c554 100644
--- a/entity/MSTTeam_entity/entityfields/teammembers/children/mstteamid_param/valueProcess.js
+++ b/entity/MSTTeam_entity/entityfields/teammembers/children/mstteamid_param/valueProcess.js
@@ -1,6 +1,11 @@
-import("system.vars");
 import("system.result");
 import("system.neon");
+import("system.vars");
 
-if (vars.get("$sys.recordstate") != neon.OPERATINGSTATE_NEW && vars.get("$field.TEAMID_AND_NAME"))
-    result.string(vars.get("$field.UID"));
\ No newline at end of file
+// this process ensures that we dont get the team members
+// of a team with an autogenerated uid (if recordState is NEW)
+var recordState = vars.get("$sys.recordstate");
+if(recordState && recordState != neon.OPERATINGSTATE_NEW)
+{
+    result.string(vars.get("$field.UID"));
+}
diff --git a/entity/MSTTeam_entity/entityfields/teammembers/onValidation.js b/entity/MSTTeam_entity/entityfields/teammembers/onValidation.js
deleted file mode 100644
index ab18aabd0ba1fb0f4c2d93a91aa191b29ba2c352..0000000000000000000000000000000000000000
--- a/entity/MSTTeam_entity/entityfields/teammembers/onValidation.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import("system.vars");
-import("system.entities");
-import("Entity_lib");
-import("system.result");
-import("system.translate");
-
-//this is a workaround, EntityConsumerRowsHelper.getCurrentConsumerRows should be used here
-var loadRowsConfig = entities.createConfigForLoadingRows()
-    .entity("MSTTeamMember_entity")
-    .provider("MembersOfTeam")
-    .fields(["#UID", "ROLE"])
-    .addParameter("MSTTeamId_param", vars.get("$field.UID"));
-var savedRows = entities.getRows(loadRowsConfig);
-var members = new EntityConsumerRowsHelper(savedRows)
-    .consumer("TeamMembers")
-    .applyConsumerRowChanges()
-    .getRows();
-var hasOwner = members.some(function (member)
-{
-    return member["ROLE"] == "owner";
-});
-if (!hasOwner)
-    result.string(translate.text("The team must have at least one owner"));
\ No newline at end of file
diff --git a/entity/MSTTeam_entity/entityfields/teammembers/stateProcess.js b/entity/MSTTeam_entity/entityfields/teammembers/stateProcess.js
index 1c53ca6a0df2f0f9fe9edc13efa4226f1b936c5f..9ff54e6419f4cbb51db0d5ce360f5ee2f44fd3b6 100644
--- a/entity/MSTTeam_entity/entityfields/teammembers/stateProcess.js
+++ b/entity/MSTTeam_entity/entityfields/teammembers/stateProcess.js
@@ -1,6 +1,11 @@
-import("Util_lib");
 import("system.result");
 import("system.vars");
 import("system.neon");
+import("Util_lib");
 
-result.string(Utils.toBoolean(vars.get("$field.ISARCHIVED")) ? neon.COMPONENTSTATE_INVISIBLE : neon.COMPONENTSTATE_EDITABLE);
\ No newline at end of file
+var res = neon.COMPONENTSTATE_EDITABLE;
+if(Utils.toBoolean(vars.get("$field.ISARCHIVED")))
+{
+    res = neon.COMPONENTSTATE_READONLY;
+}
+result.string(res);
diff --git a/entity/MSTTeam_entity/entityfields/teamsbyidandname/children/usecache_param/valueProcess.js b/entity/MSTTeam_entity/entityfields/teamsbyidandname/children/usecache_param/valueProcess.js
deleted file mode 100644
index 40effa0178464da0c7850912345f19c7fa95975a..0000000000000000000000000000000000000000
--- a/entity/MSTTeam_entity/entityfields/teamsbyidandname/children/usecache_param/valueProcess.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import("system.result");
-
-result.string(true);
\ No newline at end of file
diff --git a/entity/MSTTeam_entity/entityfields/teamsbyidandname/documentation.adoc b/entity/MSTTeam_entity/entityfields/teamsbyidandname/documentation.adoc
deleted file mode 100644
index 84522789b1b5dd1e014bd0db3e6c4c7b740f7b3a..0000000000000000000000000000000000000000
--- a/entity/MSTTeam_entity/entityfields/teamsbyidandname/documentation.adoc
+++ /dev/null
@@ -1,4 +0,0 @@
-= TeamsByIdAndName
-
-The provider uses a JSON containing [teamId, teamName] as the lookupIdField for cases when both values are needed.
-Because the request that loads the MST teams can be slow, the records will be cached if this provider is used.
\ No newline at end of file
diff --git a/entity/MSTTeam_entity/grantDeleteProcess.js b/entity/MSTTeam_entity/grantDeleteProcess.js
deleted file mode 100644
index 3fad7d06d00a3a204f60e7ef811caa4e1fd000c5..0000000000000000000000000000000000000000
--- a/entity/MSTTeam_entity/grantDeleteProcess.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import("system.result");
-import("system.vars");
-import("MSTeams_lib");
-
-result.string(vars.get("$field.TEAMID_AND_NAME") && vars.get("$field.ISARCHIVED") != "true" && MSTeamsUtils.isUserTeamOwner(vars.get("$field.UID")));
\ No newline at end of file
diff --git a/entity/MSTTeam_entity/grantUpdateProcess.js b/entity/MSTTeam_entity/grantUpdateProcess.js
deleted file mode 100644
index 3fad7d06d00a3a204f60e7ef811caa4e1fd000c5..0000000000000000000000000000000000000000
--- a/entity/MSTTeam_entity/grantUpdateProcess.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import("system.result");
-import("system.vars");
-import("MSTeams_lib");
-
-result.string(vars.get("$field.TEAMID_AND_NAME") && vars.get("$field.ISARCHIVED") != "true" && MSTeamsUtils.isUserTeamOwner(vars.get("$field.UID")));
\ No newline at end of file
diff --git a/entity/MSTTeam_entity/onValidation.js b/entity/MSTTeam_entity/onValidation.js
new file mode 100644
index 0000000000000000000000000000000000000000..c5160ffaeda2a0aec8ef57d368a8d817ee2dd891
--- /dev/null
+++ b/entity/MSTTeam_entity/onValidation.js
@@ -0,0 +1,33 @@
+import("system.result");
+import("system.vars");
+import("system.translate");
+import("Entity_lib");
+import("MSTeams_lib");
+
+var errors = [];
+var members = EntityConsumerRowsHelper.getCurrentConsumerRows("TeamMembers", ["CONTACT_ID", "ROLE"]);
+
+!members.some(function(member) {
+    return member["ROLE"] == $MSTeamsRoles.Owner();
+}) && errors.push(translate.text("The team must have at least one owner"));
+
+var duplicates = new Set();
+for(var i = 0; i < members.length; i++)
+{
+    var contactId = members[i]["CONTACT_ID"];
+    if(!contactId)
+    {
+        continue;
+    }
+    if(duplicates.has(contactId))
+    {
+        errors.push(translate.text("Found a duplicate member"));
+        break;
+    }
+    duplicates.add(contactId)
+}
+
+if(errors.length)
+{
+    result.string(errors.join("\n"));
+}
diff --git a/entity/MSTTeam_entity/recordcontainers/jdito/cacheKeyProcess.js b/entity/MSTTeam_entity/recordcontainers/jdito/cacheKeyProcess.js
deleted file mode 100644
index f9c2f6bbd9ddf14c6cd10228138b52546d0184b7..0000000000000000000000000000000000000000
--- a/entity/MSTTeam_entity/recordcontainers/jdito/cacheKeyProcess.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import("system.result");
-import("system.vars");
-import("Util_lib");
-import("CachedRecordContainer_lib");
-
-if (Utils.toBoolean(vars.get("$param.UseCache_param")))
-    result.string(CachedRecordContainerUtils.getCommonKey("$param.UseCache_param"));
\ No newline at end of file
diff --git a/entity/MSTTeam_entity/recordcontainers/jdito/contentProcess.js b/entity/MSTTeam_entity/recordcontainers/jdito/contentProcess.js
index dccb70e0330c7d584be6a0786ea150e8eb508396..8bb344f8b871067a869b0c159f41b52cb696b567 100644
--- a/entity/MSTTeam_entity/recordcontainers/jdito/contentProcess.js
+++ b/entity/MSTTeam_entity/recordcontainers/jdito/contentProcess.js
@@ -1,65 +1,103 @@
-import("system.teams");
 import("system.result");
 import("system.vars");
-import("JditoFilter_lib");
+import("system.teams");
+import("system.entities");
+import("system.db");
+import("Sql_lib");
+import("Util_lib");
 
-var lookupFilter = new FilterConditionGroup(vars.get("$local.filter")).getRow("$$$LOOKUPFIELD$$$");
-var searchValue = lookupFilter ? lookupFilter.key : null;
+var data = newSelect([
+    "MST_TEAM.MST_TEAMID",
+    "MST_TEAM.TEAMNAME",
+    "MST_TEAM.IS_ARCHIVED"
+]).from("MST_TEAM").table();
 
-var allTeams = teams.getAllTeams(searchValue || null);
-var teamsArray = [];
+var idvalues = vars.get("$local.idvalues");
+if(idvalues)
+{
+    var iddata = data.filter(function(entry){return idvalues.includes(entry[0])});
+    if(iddata.length < idvalues.length)
+    {
+        iddata = _apiData().filter(function(entry){return idvalues.includes(entry[0])});
+    }
+    result.object(iddata);
+}
+else
+{
+    var apiData = _apiData();
+    if(!vars.get("$param.IncludeArchived_param"))
+    {
+        apiData = apiData.filter(function(entry){return !Utils.toBoolean(entry[2])});
+    }
+    result.object(apiData);
+}
 
-if (vars.get("$local.idvalues"))
+/**
+ * Fetches all teams from MS-Teams and returns them
+ * in the correct format for the jDito record container.
+ * 
+ * It also caches the teams in the database
+ * to archive fast name resolution.
+ */
+function _apiData()
 {
-    var teamInfoLoader = {
-        normalTeams : allTeams,
-        archivedTeams : null,
-        getTeamInfo : function (pTeamId)
+    // fetch all teams since getAllTeams doesn't include the archived teams
+    // we will also add the archived teams to the result of getAllTeams
+    var allTeams = teams.getAllTeams(null);
+    var archivedTeams = teams.getAllArchivedTeams(null);
+    Object.assign(allTeams, archivedTeams);
+    // set of teams that are already in the db
+    var dbTeams = new Set();
+    
+    // loop through all data in the database fetched previosly
+    // and insert the changes that have to be made to get
+    // the remote data in sync with the local cached teams
+    var mstTeamSyncStmts = [];
+    data.forEach(function(entry) {
+        if(!(entry[0] in allTeams))
+        {
+            // team doesnt exist anymore delete the cached entry
+            mstTeamSyncStmts.push(["MST_TEAM", newWhere("MST_TEAM.MST_TEAMID", entry[0]).toString()]);
+        }
+        else
         {
-            if (pTeamId in this.normalTeams)
+            // team exists, check if any changes where made
+            // and apply those to the cached entries
+            var teamname = allTeams[entry[0]];
+            var isarchived = entry[0] in archivedTeams ? "1" : "0";
+            if(entry[1] != teamname || entry[2] != isarchived)
             {
-                return {
-                    name : this.normalTeams[pTeamId],
-                    isArchived : false
-                };
+                mstTeamSyncStmts.push(["MST_TEAM", [
+                    "TEAMNAME", "IS_ARCHIVED"
+                ], null, [
+                    teamname, isarchived
+                ], newWhere("MST_TEAM.MST_TEAMID", entry[0]).toString()]);
             }
-            
-            if (this.archivedTeams == null)
-                this.archivedTeams = teams.getAllArchivedTeams(null);
-            
-            return {
-                name : this.archivedTeams[pTeamId] || "",
-                isArchived : pTeamId in this.archivedTeams
-            };
+            dbTeams.add(entry[0]);
         }
-    };
+    });
     
-    teamsArray = vars.get("$local.idvalues").map(function (teamId) 
+    // loop trough the remote data that wasnt yet cached in the db
+    // and insert the teams. It also fills the liveData array
+    // will all teams from the api.
+    var liveData = [];
+    Utils.objectEntries(allTeams).forEach(function(team)
     {
-        var teamInfo = teamInfoLoader.getTeamInfo(teamId);
-        var idAndName = JSON.stringify([teamId, teamInfo.name]);
-        return [
-            teamId, 
-            teamInfo.name, 
-            idAndName, 
-            "", 
-            teamInfo.isArchived
-        ];
-    });   
-}
-else
-{
-    for (let teamId in allTeams)
+        var dataEntry = [team[0], team[1], team[0] in archivedTeams ? "1" : "0"];
+        liveData.push(dataEntry);
+        if(dbTeams.has(team[0]))
+        {
+            return;
+        }
+        mstTeamSyncStmts.push(["MST_TEAM", ["MST_TEAMID", "TEAMNAME", "IS_ARCHIVED"], null, dataEntry]);
+    });
+    
+    // if updates to the local cache are needed execute those updates
+    if(mstTeamSyncStmts.length)
     {
-        var idAndName = JSON.stringify([teamId, allTeams[teamId]]);
-        teamsArray.push([
-            teamId, 
-            allTeams[teamId], 
-            idAndName, 
-            "", 
-            false
-        ]);
+        db.execute(mstTeamSyncStmts);
     }
+    
+    // return the up-to-date teams data in jDito recordcontainer format
+    return liveData;
 }
-
-result.object(teamsArray);
\ No newline at end of file
diff --git a/entity/MSTTeam_entity/recordcontainers/jdito/onInsert.js b/entity/MSTTeam_entity/recordcontainers/jdito/onInsert.js
index 8a1c6e0c83d6d16ef62ed23cb2594be121eb5a0f..7bc41a1a5b9732718aecd06bec2b5f8c4f2ffabf 100644
--- a/entity/MSTTeam_entity/recordcontainers/jdito/onInsert.js
+++ b/entity/MSTTeam_entity/recordcontainers/jdito/onInsert.js
@@ -1,66 +1,68 @@
-import("Util_lib");
-import("Communication_lib");
-import("Sql_lib");
+import("system.neon");
 import("system.vars");
+import("system.project");
+import("system.entities");
 import("system.teams");
-import("system.tools");
-import("Employee_lib");
+import("Util_lib");
+import("Attribute_lib");
+import("Sql_lib");
+import("AttributeRegistry_basic");
+import("Communication_lib");
 import("MSTeams_lib");
 
-var rowData = vars.get("$local.rowdata");
-var teamName = rowData["TEAMNAME.value"];
-var description = rowData["DESCRIPTION.value"];
-var teamMembers = vars.get("$field.TeamMembers.insertedRows");
-
+// get members
 var ownerIds = [];
 var internalMembers = [];
 var externalMembers = [];
-teamMembers.forEach(function (teamMember)
-{
-    var contactId = teamMember["CONTACT_ID"];
-    var isExtern = Utils.toBoolean(teamMember["ISEXTERN"]);
-    var isInvite = Utils.toBoolean(teamMember["INVITE"]);
-    if (isExtern)
+vars.get("$field.TeamMembers.insertedRows").forEach(function(member) {
+    if(member["ROLE"] == $MSTeamsRoles.Guest())
     {
-        var memberEmail = CommUtil.getStandardMail(contactId);
-        if (memberEmail)
+        var externalMail = CommUtil.getStandardMail(member["CONTACT_ID"]);
+        if(externalMail)
         {
-            externalMembers.push(
-                teams.createExternalUserConfig(memberEmail)
-                    .sendInvitation(isInvite)
-            );
+            var extUser = teams.createExternalUserConfig(externalMail);
+            externalMembers.push(extUser.sendInvitation(true));
         }
-        return;
     }
-    var memberAzureId = teamMember["AZUREID"];
-    var memberUpn = teamMember["AZUREUPN"];
-    var isOwner = teamMember["ROLE"] == "owner";
-    if (!memberAzureId && memberUpn)
-        memberAzureId = teams.getInternalAzureId(memberUpn);
-    if (memberAzureId)
+    else
     {
-        if (isOwner)
-            ownerIds.push(memberAzureId);
+        var azureId = MSTeamsUtils.getAzureIdByContactId(member["CONTACT_ID"]);
+        if(member["ROLE"] == $MSTeamsRoles.Owner())
+        {
+            ownerIds.push(azureId);
+        }
         else
-            internalMembers.push(teams.createInternalUserConfig(memberAzureId));
+        {
+            var user = teams.createInternalUserConfig(azureId);
+            internalMembers.push(user);
+        }
     }
 });
 
-var mailNickName = StringUtils.toLowerCamelCase(teamName);  //mailNickName = teamName in camel case
-mailNickName = mailNickName + vars.get("$param.SalesprojectCode_param");
-
+// create team
+var teamName = vars.get("$field.TEAMNAME");
+var mailNickName = StringUtils.toLowerCamelCase(teamName).replace(/[^\w]/g, "");
 var groupConfig = teams.createGroupConfig(teamName, mailNickName, ownerIds);
-if (description)
-    groupConfig.setDescription(description);
+var description = vars.get("$field.DESCRIPTION");
+description && groupConfig.setDescription(description);
 var teamConfig = teams.createTeamConfig();
-
 var mstTeam = teams.createTeam(groupConfig, teamConfig);
 
-if (internalMembers.length !== 0)
-    teams.addInternalMembers(mstTeam.teamId, internalMembers);
+// add members
+internalMembers.length && teams.addInternalMembers(mstTeam.teamId, internalMembers);
+externalMembers.length && teams.addExternalMembers(mstTeam.teamId, externalMembers);
 
-if (externalMembers.length !== 0)
-    teams.addExternalMembers(mstTeam.teamId, externalMembers);
+// add app
+var appId = project.getInstanceConfigValue("teamsAppId");
+appId && teams.addApp(mstTeam.teamId, appId);
 
-MSTeamsUtils.insertTeamIfMissing(mstTeam);
-MSTeamsUtils.createTeamLink(mstTeam.teamId, vars.get("$param.ObjectRowId_param"), vars.get("$param.ObjectType_param"));
\ No newline at end of file
+// database
+new SqlBuilder().insertFields({
+    "MST_TEAMID": mstTeam.teamId,
+    "TEAMNAME": teamName,
+    "IS_ARCHIVED": "0",
+    "GENERAL_CHANNELID": mstTeam.generalChannelId,
+    "WEB_URL": mstTeam.webUrl
+}, "MST_TEAM");
+new AttributeRelationQuery(vars.get("$param.ObjectRowId_param"), $AttributeRegistry.mstTeam(),
+    vars.get("$param.ObjectType_param")).insertAttribute(mstTeam.teamId, true);
diff --git a/entity/MSTTeam_entity/recordcontainers/jdito/onUpdate.js b/entity/MSTTeam_entity/recordcontainers/jdito/onUpdate.js
index 54e7ab9ab6b4e76d9a767db9d8314b6cbb3d2dc5..8407325255bd6d27a74d45019763120ffaf1feb3 100644
--- a/entity/MSTTeam_entity/recordcontainers/jdito/onUpdate.js
+++ b/entity/MSTTeam_entity/recordcontainers/jdito/onUpdate.js
@@ -1,77 +1,73 @@
-import("Communication_lib");
-import("Sql_lib");
+import("system.neon");
 import("system.vars");
 import("system.teams");
-import("system.tools");
-import("Employee_lib");
+import("Util_lib");
+import("Communication_lib");
+import("MSTeams_lib");
+
+var teamId = vars.get("$field.UID");
 
-var rowData = vars.get("$local.rowdata");
-var teamId = rowData["UID.value"];
-var teamName = rowData["TEAMNAME.value"];
-var description = rowData["DESCRIPTION.value"];
-var insertedTeamMembers = vars.get("$field.TeamMembers.insertedRows");
-var changedTeamMembers = vars.get("$field.TeamMembers.changedRows");
-var deletedTeamMembers = vars.get("$field.TeamMembers.deletedRows");
+var addInternalMembers = [];
+var addExternalMembers = [];
+var removeInternalMembers = [];
+var removeExternalMembers = [];
 
-var internalMembers = [];
-var externalMembers = [];
-insertedTeamMembers.forEach(function (teamMember)
+var insertedMembers = vars.get("$field.TeamMembers.insertedRows");
+var changedMembers = vars.get("$field.TeamMembers.changedRows");
+var deletedMembers = vars.get("$field.TeamMembers.deletedRows");
+
+var insertedContactIds = insertedMembers.map(function(member){return member["CONTACT_ID"]});
+deletedMembers.forEach(function(member, idx)
 {
-    var memberUser = EmployeeUtils.getUserByContactId(teamMember["CONTACT_ID"]);
-    if (!memberUser)
+    var insertedContactIdsIndex = insertedContactIds.indexOf(member["CONTACT_ID"])
+    if(insertedContactIdsIndex >= 0)
     {
-        var memberEmail = CommUtil.getStandardMail(teamMember["CONTACT_ID"]);
-        if (memberEmail)
-        {
-            externalMembers.push(
-                teams.createExternalUserConfig(memberEmail)
-                    .sendInvitation(teamMember["INVITE"] == "true")
-            );
-        }
-        return;
+        insertedMembers[insertedContactIdsIndex]["ROLE"] != deletedMembers[idx]["ROLE"]
+            && changedMembers.push(insertedMembers[insertedContactIdsIndex]);
+        insertedMembers.splice(insertedContactIdsIndex, 1);
+        deletedMembers.splice(idx, 1);
     }
-    var internalUserConfigToAdd = _getTeamsInternalUserConfig(memberUser, teamMember["ROLE"] == "owner");
-    if (internalUserConfigToAdd)
-        internalMembers.push(internalUserConfigToAdd);
 });
 
-if (internalMembers.length !== 0)
-    teams.addInternalMembers(teamId, internalMembers);
-
-if (externalMembers.length !== 0)
-    teams.addExternalMembers(teamId, externalMembers);
-
-changedTeamMembers.forEach(function (teamMember)
-{
-    var memberUser = EmployeeUtils.getUserByContactId(teamMember["CONTACT_ID"]);
-    if (!memberUser)
-        return;
-    
-    var internalUserConfigToChange = _getTeamsInternalUserConfig(memberUser, teamMember["ROLE"] == "owner");
-    if (internalUserConfigToChange)
-        teams.changeRole(teamId, internalUserConfigToChange);
+insertedMembers.forEach(function(member) {
+    if(member["ROLE"] == $MSTeamsRoles.Guest())
+    {
+        var extMail = CommUtil.getStandardMail(member["CONTACT_ID"]);
+        if(extMail)
+        {
+            var extUser = teams.createExternalUserConfig(extMail);
+            addExternalMembers.push(extUser.sendInvitation(false));
+        }
+    }
+    else
+    {
+        var azureId = MSTeamsUtils.getAzureIdByContactId(member["CONTACT_ID"]);
+        var user = teams.createInternalUserConfig(azureId);
+        addInternalMembers.push(user.setOwner(member["ROLE"] == $MSTeamsRoles.Owner()));
+    }
 });
 
-
-var owners = teams.getAllOwners(teamId);
-var removeMembers = deletedTeamMembers.map(function (member)
+changedMembers.forEach(function(member)
 {
-    var isOwner = member["#UID"] in owners;
-    return teams.createInternalUserConfig(member["#UID"]).setOwner(isOwner);
+    var user = teams.createInternalUserConfig(member["#UID"]);
+    teams.changeRole(teamId, user.setOwner(member["ROLE"] == $MSTeamsRoles.Owner()));
 });
 
-if (removeMembers.length !== 0)
-    teams.removeInternalMembers(teamId, removeMembers);
-
-function _getTeamsInternalUserConfig(pUserObject, pAsOwner)
+deletedMembers.forEach(function(member)
 {
-    var memberAzureId = pUserObject[tools.PARAMS][tools.TEAMS_AZUREID];
-    var memberUpn = pUserObject[tools.PARAMS][tools.TEAMS_AZUREUPN];
-    if (!memberAzureId && memberUpn)
-        memberAzureId = teams.getInternalAzureId(memberUpn);
-    if (memberAzureId)
+    if(member["ROLE"] == $MSTeamsRoles.Guest())
+    {
+        var extUser = teams.createExternalUserConfig(member["MEMBERNAME"]);
+        removeExternalMembers.push(extUser);
+    }
+    else
     {
-        return teams.createInternalUserConfig(memberAzureId).setOwner(pAsOwner);
+        var user = teams.createInternalUserConfig(member["#UID"]);
+        removeInternalMembers.push(user.setOwner(member["ROLE"] == $MSTeamsRoles.Owner()));
     }
-    return null;
-}
\ No newline at end of file
+});
+
+addInternalMembers.length && teams.addInternalMembers(teamId, addInternalMembers);
+addExternalMembers.length && teams.addExternalMembers(teamId, addExternalMembers);
+removeInternalMembers.length && teams.removeInternalMembers(teamId, removeInternalMembers);
+removeExternalMembers.length && teams.removeExternalMembers(teamId, removeExternalMembers);
diff --git a/entity/MSTeamsActivityImport_entity/MSTeamsActivityImport_entity.aod b/entity/MSTeamsActivityImport_entity/MSTeamsActivityImport_entity.aod
index 9ef9e4fcc26e268958b733c40535540dedea7470..1bf181c3d715b5a4b00c168ed9a0c1f0a035dd59 100644
--- a/entity/MSTeamsActivityImport_entity/MSTeamsActivityImport_entity.aod
+++ b/entity/MSTeamsActivityImport_entity/MSTeamsActivityImport_entity.aod
@@ -37,6 +37,8 @@
       <title>Channel</title>
       <consumer>Channels</consumer>
       <state>EDITABLE</state>
+      <valueProcess>%aditoprj%/entity/MSTeamsActivityImport_entity/entityfields/channel_id/valueProcess.js</valueProcess>
+      <displayValueProcess>%aditoprj%/entity/MSTeamsActivityImport_entity/entityfields/channel_id/displayValueProcess.js</displayValueProcess>
     </entityField>
     <entityParameter>
       <name>ObjectRowId_param</name>
diff --git a/entity/MSTeamsActivityImport_entity/entityfields/channel_id/displayValueProcess.js b/entity/MSTeamsActivityImport_entity/entityfields/channel_id/displayValueProcess.js
new file mode 100644
index 0000000000000000000000000000000000000000..6c55fb855e76fcd3c47208f19d2348da9d245b27
--- /dev/null
+++ b/entity/MSTeamsActivityImport_entity/entityfields/channel_id/displayValueProcess.js
@@ -0,0 +1,8 @@
+import("system.result");
+import("system.vars");
+import("system.entities");
+
+var uid = vars.get("$this.value");
+var config = entities.createConfigForLoadingRows().entity("MSTeamsChannel_entity")
+    .uid(uid).addParameter("TeamId_param", vars.get("$field.TEAM_ID")).fields(["CHANNELNAME"]);
+uid && result.string(entities.getRow(config).CHANNELNAME);
diff --git a/entity/MSTeamsActivityImport_entity/entityfields/channel_id/valueProcess.js b/entity/MSTeamsActivityImport_entity/entityfields/channel_id/valueProcess.js
new file mode 100644
index 0000000000000000000000000000000000000000..f2e81cd3dffc67136e990f5be03361d72deac65e
--- /dev/null
+++ b/entity/MSTeamsActivityImport_entity/entityfields/channel_id/valueProcess.js
@@ -0,0 +1,10 @@
+import("system.result");
+import("system.vars");
+import("system.teams");
+
+var teamId = vars.get("$field.TEAM_ID");
+if(teamId && vars.get("$this.value") == null)
+{
+    var channelId = teams.getGeneralChannelId(teamId);
+    result.string(channelId);
+}
diff --git a/entity/MSTeamsActivityImport_entity/entityfields/team_id/valueProcess.js b/entity/MSTeamsActivityImport_entity/entityfields/team_id/valueProcess.js
index c431bd3ac3e9bc6fa97d0b85729ed1c4f9b203b0..3205eef8b0cd4c870ccaea928309e76a5354240f 100644
--- a/entity/MSTeamsActivityImport_entity/entityfields/team_id/valueProcess.js
+++ b/entity/MSTeamsActivityImport_entity/entityfields/team_id/valueProcess.js
@@ -1,6 +1,7 @@
-import("system.vars");
-import("MSTeams_lib");
 import("system.result");
-import("Sql_lib");
+import("system.vars");
+import("Attribute_lib");
+import("AttributeRegistry_basic");
 
-result.string(MSTeamsUtils.getLinkedTeam(vars.get("$param.ObjectRowId_param"), vars.get("$param.ObjectType_param")) || "");
\ No newline at end of file
+result.string(new AttributeRelationQuery(vars.get("$param.ObjectRowId_param"),
+    $AttributeRegistry.mstTeam(), vars.get("$param.ObjectType_param")).getSingleAttributeValue());
diff --git a/entity/MSTeamsMessage_entity/entityfields/importmessages/onActionProcess.js b/entity/MSTeamsMessage_entity/entityfields/importmessages/onActionProcess.js
index 8b5a94d660b9cbd5ef4a49457965ad8bd4767f0e..a5650f2a632efa216c3877c4e2ce4a5544a1c95c 100644
--- a/entity/MSTeamsMessage_entity/entityfields/importmessages/onActionProcess.js
+++ b/entity/MSTeamsMessage_entity/entityfields/importmessages/onActionProcess.js
@@ -1,23 +1,34 @@
+import("system.text");
+import("system.translate");
 import("Sql_lib");
 import("system.datetime");
 import("system.neon");
 import("Util_lib");
 import("system.vars");
+import("AttributeRegistry_basic");
+import("KeywordRegistry_basic");
+import("Context_lib");
 
 var messages = vars.get("$sys.selection");
 var description = messages.map(function (id)
 {
-    var [user, date, message] = JSON.parse(id);
-    date = Date.parse(date).valueOf();
+    var [user, date, message] = text.decodeMS(id);
     return "[" + datetime.toDate(date, "dd.MM.yyyy HH:mm") + "] " + user + "<br>" + message + "<br>";
 }).join("<br>");
 
 var links = newSelect(["OBJECT_TYPE", "OBJECT_ROWID"])
-    .from("MST_TEAMLINK")
-    .where("MST_TEAMLINK.MST_TEAM_ID", vars.get("$param.TeamId_param"))
+    .from("AB_ATTRIBUTERELATION")
+    .where("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID", $AttributeRegistry.mstTeam())
+    .and("AB_ATTRIBUTERELATION.ID_VALUE", vars.get("$param.TeamId_param"))
     .table();
 
+var teamName = ContextUtils.loadContentTitle("MSTTeam_entity", vars.get("$param.TeamId_param"));
+var channelName = ContextUtils.loadContentTitle("MSTeamsChannel_entity", vars.get("$param.Channel_param"),
+    undefined, undefined, {TeamId_param: vars.get("$param.TeamId_param")});
 neon.openContext("Activity", "ActivityEdit_view", null, neon.OPERATINGSTATE_NEW, {
-    "Info_param": description,
-    "PresetLinks_param": JSON.stringify(links)
-});
\ No newline at end of file
+    Info_param: description,
+    PresetLinks_param: JSON.stringify(links),
+    Direction_param: $KeywordRegistry.activityDirection$internal(),
+    Subject_param: translate.text("MS-Teams Message") + " - " + teamName + " - " + channelName,
+    Category_param: $KeywordRegistry.activityCategory$mstMessage()
+});
diff --git a/entity/MSTeamsMessage_entity/recordcontainers/jdito/contentProcess.js b/entity/MSTeamsMessage_entity/recordcontainers/jdito/contentProcess.js
index 0007cf9a2d5994cbbf002e496d12620d49ee358c..d9f5ab8061b96013f6706b114a9def2b0def4d55 100644
--- a/entity/MSTeamsMessage_entity/recordcontainers/jdito/contentProcess.js
+++ b/entity/MSTeamsMessage_entity/recordcontainers/jdito/contentProcess.js
@@ -1,3 +1,4 @@
+import("system.text");
 import("Sql_lib");
 import("system.teams");
 import("system.result");
@@ -30,12 +31,12 @@ else if (vars.get("$param.TeamId_param") && vars.get("$param.Channel_param"))
 
 messages = messages.map(function (message)
 {
+    // replace workaround needed, since Rhino's Date.parse struggles with the date format returned by teams
+    var timestamp = Date.parse(message.createdDateTime.replace(/\.\d+Z/, "")).toString()
     return [
-        JSON.stringify([message.author.userName, message.createdDateTime, message.message.message]), 
-        message.author.userName, 
-        message.message.message, 
-        Date.parse(message.createdDateTime).toString()
+        text.encodeMS([message.author.userName, timestamp, message.message.message]), 
+        message.author.userName, message.message.message, timestamp
     ];
 });
-
+messages = messages.sort(function(a, b){return a[3] - b[3]});
 result.object(messages);
diff --git a/entity/Member_entity/Member_entity.aod b/entity/Member_entity/Member_entity.aod
index 592118582b0fd365988013d8d85f9a6450832ed7..f643a48c17d2e1816a4d216ddb1bd29b0914af43 100644
--- a/entity/Member_entity/Member_entity.aod
+++ b/entity/Member_entity/Member_entity.aod
@@ -4,10 +4,6 @@
   <title>${SALESPROJECT_MEMBER}</title>
   <majorModelMode>DISTRIBUTED</majorModelMode>
   <documentation>%aditoprj%/entity/Member_entity/documentation.adoc</documentation>
-  <siblings>
-    <element>MSTTeam_entity</element>
-    <element>MSTTeamLink_entity</element>
-  </siblings>
   <grantUpdateProcess>%aditoprj%/entity/Member_entity/grantUpdateProcess.js</grantUpdateProcess>
   <grantDeleteProcess>%aditoprj%/entity/Member_entity/grantDeleteProcess.js</grantDeleteProcess>
   <contentTitleProcess>%aditoprj%/entity/Member_entity/contentTitleProcess.js</contentTitleProcess>
@@ -234,13 +230,6 @@
           <iconId>NEON:PENCIL</iconId>
           <stateProcess>%aditoprj%/entity/Member_entity/entityfields/msteamsactiongroup/children/editteam/stateProcess.js</stateProcess>
         </entityActionField>
-        <entityActionField>
-          <name>addApp</name>
-          <title>Add app</title>
-          <onActionProcess>%aditoprj%/entity/Member_entity/entityfields/msteamsactiongroup/children/addapp/onActionProcess.js</onActionProcess>
-          <iconId>VAADIN:MOBILE</iconId>
-          <stateProcess>%aditoprj%/entity/Member_entity/entityfields/msteamsactiongroup/children/addapp/stateProcess.js</stateProcess>
-        </entityActionField>
         <entityActionField>
           <name>chooseTeam</name>
           <title>Choose Team</title>
@@ -257,7 +246,7 @@
         </entityActionField>
         <entityActionField>
           <name>openMSTeams</name>
-          <title>Open Teams</title>
+          <title>Open MS Teams</title>
           <onActionProcess>%aditoprj%/entity/Member_entity/entityfields/msteamsactiongroup/children/openmsteams/onActionProcess.js</onActionProcess>
           <iconId>VAADIN:EXTERNAL_LINK</iconId>
           <stateProcess>%aditoprj%/entity/Member_entity/entityfields/msteamsactiongroup/children/openmsteams/stateProcess.js</stateProcess>
@@ -272,8 +261,10 @@
       </children>
     </entityActionGroup>
     <entityProvider>
-      <name>TeamMemberProvider</name>
+      <name>MemberContacts</name>
       <lookupIdfield>CONTACT_ID</lookupIdfield>
+      <targetContextField>TARGETCONTEXT</targetContextField>
+      <targetIdField>CONTACT_ID</targetIdField>
     </entityProvider>
     <entityParameter>
       <name>MSTTeamId_param</name>
@@ -293,11 +284,6 @@
       <name>recordType</name>
       <documentation>%aditoprj%/entity/Member_entity/entityfields/recordtype/documentation.adoc</documentation>
     </entityField>
-    <entityParameter>
-      <name>SalesprojectCode_param</name>
-      <expose v="true" />
-      <description>PARAMETER</description>
-    </entityParameter>
     <entityField>
       <name>ONSITE</name>
       <title>On Site</title>
@@ -315,6 +301,10 @@
       <groupable v="true" />
       <displayValueProcess>%aditoprj%/entity/Member_entity/entityfields/person_attr_loyalty/displayValueProcess.js</displayValueProcess>
     </entityField>
+    <entityParameter>
+      <name>ContactIds_param</name>
+      <valueProcess>%aditoprj%/entity/Member_entity/entityfields/contactids_param/valueProcess.js</valueProcess>
+    </entityParameter>
     <entityField>
       <name>RESPONSIBLE</name>
       <title>Responsible</title>
diff --git a/entity/Member_entity/entityfields/contactids_param/valueProcess.js b/entity/Member_entity/entityfields/contactids_param/valueProcess.js
new file mode 100644
index 0000000000000000000000000000000000000000..69313097ded5f8871c2d98dae06884a73ea6daac
--- /dev/null
+++ b/entity/Member_entity/entityfields/contactids_param/valueProcess.js
@@ -0,0 +1,8 @@
+import("system.result");
+import("system.vars");
+import("Sql_lib");
+
+var contactIds = newSelect("OBJECTMEMBER.CONTACT_ID")
+    .from("OBJECTMEMBER").whereIfSet("OBJECTMEMBER.OBJECT_TYPE", vars.get("$param.ObjectType_param"))
+    .andIfSet("OBJECTMEMBER.OBJECT_ROWID", vars.get("$param.ObjectRowId_param")).arrayColumn(true);
+result.string(JSON.stringify(contactIds));
diff --git a/entity/Member_entity/entityfields/msteamsactiongroup/children/addapp/onActionProcess.js b/entity/Member_entity/entityfields/msteamsactiongroup/children/addapp/onActionProcess.js
deleted file mode 100644
index f53cd7c87d173497b7b8fd5bcbb4ba4212bac637..0000000000000000000000000000000000000000
--- a/entity/Member_entity/entityfields/msteamsactiongroup/children/addapp/onActionProcess.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import("system.teams");
-import("system.project");
-import("system.vars");
-
-var appId = project.getInstanceConfigValue("teamsAppId", null);
-if (appId)
-    teams.addApp(vars.get("$param.MSTTeamId_param"), appId);
\ No newline at end of file
diff --git a/entity/Member_entity/entityfields/msteamsactiongroup/children/addapp/stateProcess.js b/entity/Member_entity/entityfields/msteamsactiongroup/children/addapp/stateProcess.js
deleted file mode 100644
index f1febe96416f12e029c6de6537cf96e5c7f4d5db..0000000000000000000000000000000000000000
--- a/entity/Member_entity/entityfields/msteamsactiongroup/children/addapp/stateProcess.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import("MSTeams_lib");
-import("system.vars");
-import("system.neon");
-import("system.result");
-import("Sql_lib");
-
-var res = neon.COMPONENTSTATE_DISABLED;
-var teamId = vars.get("$param.MSTTeamId_param");
-if (teamId && MSTeamsUtils.isTeamsEnabled())
-{    
-    var teamInfo = MSTeamsUtils.getTeamInfo(teamId);
-    if (teamInfo && !teamInfo.isArchived && !teamInfo.serviceUrl)
-        res = neon.COMPONENTSTATE_EDITABLE;
-}
-
-result.string(res);
\ No newline at end of file
diff --git a/entity/Member_entity/entityfields/msteamsactiongroup/children/chooseteam/onActionProcess.js b/entity/Member_entity/entityfields/msteamsactiongroup/children/chooseteam/onActionProcess.js
index f5761f78639c0a8b2fc209d0fe50d47ccbb390e2..b6ffb62af2e93df3b267c9b5f695aeb6282624db 100644
--- a/entity/Member_entity/entityfields/msteamsactiongroup/children/chooseteam/onActionProcess.js
+++ b/entity/Member_entity/entityfields/msteamsactiongroup/children/chooseteam/onActionProcess.js
@@ -1,4 +1,9 @@
-import("MSTeams_lib");
+import("system.neon");
 import("system.vars");
+import("AttributeRegistry_basic");
 
-MSTeamsUtils.changeTeamsLink(vars.get("$field.OBJECT_ROWID"), vars.get("$field.OBJECT_TYPE"));
\ No newline at end of file
+neon.openContext("AttributeRelation", null, null, neon.OPERATINGSTATE_NEW, {
+    AttributeId_param: $AttributeRegistry.mstTeam(),
+    ObjectType_param: vars.get("$param.ObjectType_param"),
+    ObjectRowId_param: vars.get("$param.ObjectRowId_param")
+});
diff --git a/entity/Member_entity/entityfields/msteamsactiongroup/children/chooseteam/stateProcess.js b/entity/Member_entity/entityfields/msteamsactiongroup/children/chooseteam/stateProcess.js
index 82ce3fae3fb447579e492f43ad7158f1d8bd0182..842a7a8b1f45f7d438f605824280bd6e42e2373c 100644
--- a/entity/Member_entity/entityfields/msteamsactiongroup/children/chooseteam/stateProcess.js
+++ b/entity/Member_entity/entityfields/msteamsactiongroup/children/chooseteam/stateProcess.js
@@ -1,8 +1,13 @@
+import("system.result");
 import("system.vars");
 import("system.neon");
-import("system.result");
+import("Util_lib");
 
-result.string(vars.exists("$param.MSTTeamId_param") && vars.get("$param.MSTTeamId_param") 
-    ? neon.COMPONENTSTATE_DISABLED 
-    : neon.COMPONENTSTATE_EDITABLE
-);
\ No newline at end of file
+var hasMembers = (Utils.parseJSON(vars.get("$param.ContactIds_param")) || []).length;
+var hasTeamId = vars.exists("$param.MSTTeamId_param") && vars.get("$param.MSTTeamId_param");
+var res = neon.COMPONENTSTATE_EDITABLE;
+if(!hasMembers || hasTeamId)
+{
+    res = neon.COMPONENTSTATE_DISABLED ;
+}
+result.string(res);
diff --git a/entity/Member_entity/entityfields/msteamsactiongroup/children/createteam/onActionProcess.js b/entity/Member_entity/entityfields/msteamsactiongroup/children/createteam/onActionProcess.js
index 611e6d3926780aa4e436f626508e3b1f7a8a4cbc..ed5f6c0ceae9984aca8f2eaa9daa7797247da83c 100644
--- a/entity/Member_entity/entityfields/msteamsactiongroup/children/createteam/onActionProcess.js
+++ b/entity/Member_entity/entityfields/msteamsactiongroup/children/createteam/onActionProcess.js
@@ -5,9 +5,11 @@ import("Sql_lib");
 
 var rowId = vars.get("$param.ObjectRowId_param");
 var objectType = vars.get("$param.ObjectType_param");
+var contactIds = vars.get("$param.ContactIds_param");
 var params = {
     "ObjectRowId_param": rowId,
-    "ObjectType_param": objectType
+    "ObjectType_param": objectType,
+    "ContactIds_param": contactIds
 };
 
 if (objectType === "Salesproject")
@@ -18,7 +20,6 @@ if (objectType === "Salesproject")
         .arrayRow();
     params["TeamName_param"] = teamName;
     params["Description_param"] = description;
-    params["SalesprojectCode_param"] = vars.get("$param.SalesprojectCode_param");
 }
 
-neon.openContext("MSTTeam", null, null, neon.OPERATINGSTATE_NEW, params);
\ No newline at end of file
+neon.openContext("MSTTeam", null, null, neon.OPERATINGSTATE_NEW, params);
diff --git a/entity/Member_entity/entityfields/msteamsactiongroup/children/createteam/stateProcess.js b/entity/Member_entity/entityfields/msteamsactiongroup/children/createteam/stateProcess.js
index 82ce3fae3fb447579e492f43ad7158f1d8bd0182..ec1ae4dba8900c359402a9ba7e77a6155db28fdd 100644
--- a/entity/Member_entity/entityfields/msteamsactiongroup/children/createteam/stateProcess.js
+++ b/entity/Member_entity/entityfields/msteamsactiongroup/children/createteam/stateProcess.js
@@ -1,8 +1,10 @@
+import("system.result");
 import("system.vars");
 import("system.neon");
-import("system.result");
+import("Util_lib");
 
-result.string(vars.exists("$param.MSTTeamId_param") && vars.get("$param.MSTTeamId_param") 
+var hasMembers = (Utils.parseJSON(vars.get("$param.ContactIds_param")) || []).length;
+result.string(!hasMembers || (vars.exists("$param.MSTTeamId_param") && vars.get("$param.MSTTeamId_param"))
     ? neon.COMPONENTSTATE_DISABLED 
     : neon.COMPONENTSTATE_EDITABLE
-);
\ No newline at end of file
+);
diff --git a/entity/Member_entity/entityfields/msteamsactiongroup/children/editteam/onActionProcess.js b/entity/Member_entity/entityfields/msteamsactiongroup/children/editteam/onActionProcess.js
index 8f072bc03d016ca70df4643d9f32d8c29c8523e5..746823a3b6e4b00635d7b22610a74dae24415986 100644
--- a/entity/Member_entity/entityfields/msteamsactiongroup/children/editteam/onActionProcess.js
+++ b/entity/Member_entity/entityfields/msteamsactiongroup/children/editteam/onActionProcess.js
@@ -1,7 +1,8 @@
 import("system.vars");
 import("system.neon");
 
-neon.openContext("MSTTeam", "MSTTeamEditMembers_view", [vars.get("$param.MSTTeamId_param")], neon.OPERATINGSTATE_EDIT, {
+neon.openContext("MSTTeam", null, [vars.get("$param.MSTTeamId_param")], neon.OPERATINGSTATE_EDIT, {
     "ObjectRowId_param": vars.get("$param.ObjectRowId_param"),
-    "ObjectType_param": vars.get("$param.ObjectType_param")
-});
\ No newline at end of file
+    "ObjectType_param": vars.get("$param.ObjectType_param"),
+    "ContactIds_param": vars.get("$param.ContactIds_param")
+});
diff --git a/entity/Member_entity/entityfields/mstteamid_param/valueProcess.js b/entity/Member_entity/entityfields/mstteamid_param/valueProcess.js
index 566da0b330d91a6b956f6a40c0517ce03f795a91..b09bae08d23b772e21e26b7e4edc54d5bbb9cd23 100644
--- a/entity/Member_entity/entityfields/mstteamid_param/valueProcess.js
+++ b/entity/Member_entity/entityfields/mstteamid_param/valueProcess.js
@@ -1,8 +1,7 @@
-import("system.vars");
 import("system.result");
-import("Sql_lib");
-import("MSTeams_lib");
+import("system.vars");
+import("Attribute_lib");
+import("AttributeRegistry_basic");
 
-var objectType = vars.get("$param.ObjectType_param");
-if (objectType in MSTeamsUtils.getTeamLinkContexts())
-    result.string(MSTeamsUtils.getLinkedTeam(vars.get("$param.ObjectRowId_param"), objectType) || "");
\ No newline at end of file
+result.string(new AttributeRelationQuery(vars.get("$param.ObjectRowId_param"), 
+    $AttributeRegistry.mstTeam(), vars.get("$param.ObjectType_param")).getSingleAttributeValue());
diff --git a/entity/Member_entity/entityfields/object_rowid/displayValueProcess.js b/entity/Member_entity/entityfields/object_rowid/displayValueProcess.js
index e6e750d04788d9ab345eb50e83c174f53b70f6fa..43825745b43d95f3485fa53e23086d48c233b51d 100644
--- a/entity/Member_entity/entityfields/object_rowid/displayValueProcess.js
+++ b/entity/Member_entity/entityfields/object_rowid/displayValueProcess.js
@@ -12,7 +12,7 @@ else if(vars.exists("$field.OBJECT_TYPE") && vars.get("$field.OBJECT_TYPE"))
 {
     if(vars.get("$field.OBJECT_TYPE") == "Salesproject")
     {
-        result.string(ContextUtils.getTitleByContext(vars.get("$field.OBJECT_TYPE"), vars.get("$field.OBJECT_ROWID"), "IgnoreChecklists_param"));
+        result.string(ContextUtils.getTitleByContext(vars.get("$field.OBJECT_TYPE"), vars.get("$field.OBJECT_ROWID"), null, {"IgnoreChecklists_param": true}));
     }
     else
     {
diff --git a/entity/Salesproject_entity/Salesproject_entity.aod b/entity/Salesproject_entity/Salesproject_entity.aod
index a869641a5b7dce9cb61e167c092bf8a0e6e88c7a..cf3ecbb9f02d5a3993b9fac752fe006df9fb8708 100644
--- a/entity/Salesproject_entity/Salesproject_entity.aod
+++ b/entity/Salesproject_entity/Salesproject_entity.aod
@@ -8,7 +8,7 @@
     <element>SalesprojectMilestone_entity</element>
     <element>Member_entity</element>
     <element>MSTTeam_entity</element>
-    <element>MSTTeamLink_entity</element>
+    <element>AttributeRelation_entity</element>
   </siblings>
   <grantDeleteProcess>%aditoprj%/entity/Salesproject_entity/grantDeleteProcess.js</grantDeleteProcess>
   <contentTitleProcess>%aditoprj%/entity/Salesproject_entity/contentTitleProcess.js</contentTitleProcess>
@@ -274,10 +274,6 @@
           <name>EnableMSTeams_param</name>
           <valueProcess>%aditoprj%/entity/Salesproject_entity/entityfields/members/children/enablemsteams_param/valueProcess.js</valueProcess>
         </entityParameter>
-        <entityParameter>
-          <name>SalesprojectCode_param</name>
-          <valueProcess>%aditoprj%/entity/Salesproject_entity/entityfields/members/children/salesprojectcode_param/valueProcess.js</valueProcess>
-        </entityParameter>
       </children>
     </entityConsumer>
     <entityConsumer>
@@ -672,10 +668,7 @@
     <entityField>
       <name>MST_TEAM_ID</name>
       <title>Team</title>
-      <linkedContext>MSTTeam</linkedContext>
-      <state>INVISIBLE</state>
-      <stateProcess>%aditoprj%/entity/Salesproject_entity/entityfields/mst_team_id/stateProcess.js</stateProcess>
-      <displayValueProcess>%aditoprj%/entity/Salesproject_entity/entityfields/mst_team_id/displayValueProcess.js</displayValueProcess>
+      <state>READONLY</state>
     </entityField>
     <entityField>
       <name>COUNT</name>
@@ -1020,7 +1013,7 @@
         </dbRecordFieldMapping>
         <dbRecordFieldMapping>
           <name>MST_TEAM_ID.value</name>
-          <recordfield>MST_TEAM.MST_TEAMID</recordfield>
+          <expression>%aditoprj%/entity/Salesproject_entity/recordcontainers/db/recordfieldmappings/mst_team_id.value/expression.js</expression>
         </dbRecordFieldMapping>
         <aggregateFieldDbMapping>
           <name>COUNT_aggregate.value</name>
@@ -1032,10 +1025,6 @@
           <recordfield>CLASSIFICATIONSTORAGE.CLASSIFICATIONVALUE</recordfield>
           <isFilterable v="true" />
         </dbRecordFieldMapping>
-        <dbRecordFieldMapping>
-          <name>MST_TEAM_ID.displayValue</name>
-          <recordfield>MST_TEAM.TEAMNAME</recordfield>
-        </dbRecordFieldMapping>
         <dbRecordFieldMapping>
           <name>DATE_NEW.value</name>
           <recordfield>SALESPROJECT.DATE_NEW</recordfield>
@@ -1070,6 +1059,10 @@
           <expression>%aditoprj%/entity/Salesproject_entity/recordcontainers/db/recordfieldmappings/volumeweighted_aggregate.value/expression.js</expression>
           <aggregateType>AVG</aggregateType>
         </aggregateFieldDbMapping>
+        <dbRecordFieldMapping>
+          <name>MST_TEAM_ID.displayValue</name>
+          <expression>%aditoprj%/entity/Salesproject_entity/recordcontainers/db/recordfieldmappings/mst_team_id.displayvalue/expression.js</expression>
+        </dbRecordFieldMapping>
         <dbRecordFieldMapping>
           <name>DATE_EDIT.value</name>
           <recordfield>SALESPROJECT.DATE_EDIT</recordfield>
diff --git a/entity/Salesproject_entity/entityfields/checklistentryvalues/children/checklistids_param/valueProcess.js b/entity/Salesproject_entity/entityfields/checklistentryvalues/children/checklistids_param/valueProcess.js
index 19ac32389a050be943aa0c0c272713fbdbd5f687..b90d52e23c9b51cec739baa5536923931cd08e53 100644
--- a/entity/Salesproject_entity/entityfields/checklistentryvalues/children/checklistids_param/valueProcess.js
+++ b/entity/Salesproject_entity/entityfields/checklistentryvalues/children/checklistids_param/valueProcess.js
@@ -14,8 +14,6 @@ if(select){
                         .leftJoin("CONTACT", "SALESPROJECT.CONTACT_ID = CONTACT.CONTACTID")
                         .leftJoin("ORGANISATION", "CONTACT.ORGANISATION_ID = ORGANISATION.ORGANISATIONID")
                         .leftJoin("CLASSIFICATIONSTORAGE", "CLASSIFICATIONSTORAGE.OBJECT_ROWID = SALESPROJECT.SALESPROJECTID")
-                        .leftJoin("MST_TEAMLINK", "SALESPROJECT.SALESPROJECTID = MST_TEAMLINK.OBJECT_ROWID and MST_TEAMLINK.OBJECT_TYPE = '" + context + "'")
-                        .leftJoin("MST_TEAM", "MST_TEAM.MST_TEAMID = MST_TEAMLINK.MST_TEAM_ID")
                         .where("SALESPROJECT.SALESPROJECTID", vars.get("$field.SALESPROJECTID"))
                         .arrayRow();
                         
diff --git a/entity/Salesproject_entity/entityfields/members/children/salesprojectcode_param/valueProcess.js b/entity/Salesproject_entity/entityfields/members/children/salesprojectcode_param/valueProcess.js
deleted file mode 100644
index 598d579e335bcff49a488b488728055a608e6da4..0000000000000000000000000000000000000000
--- a/entity/Salesproject_entity/entityfields/members/children/salesprojectcode_param/valueProcess.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import("system.result");
-import("system.vars");
-
-result.string(vars.get("$field.PROJECTCODE"));
\ No newline at end of file
diff --git a/entity/Salesproject_entity/entityfields/mst_team_id/displayValueProcess.js b/entity/Salesproject_entity/entityfields/mst_team_id/displayValueProcess.js
deleted file mode 100644
index 14271e0911152fe467c4283f2b8a6245288c0bc7..0000000000000000000000000000000000000000
--- a/entity/Salesproject_entity/entityfields/mst_team_id/displayValueProcess.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import("MSTeams_lib");
-import("system.result");
-import("Sql_lib");
-
-var res;
-
-if (MSTeamsUtils.isTeamsEnabled())
-    res = newSelect("TEAMNAME")
-        .from("MST_TEAM")
-        .whereIfSet("MST_TEAM.MST_TEAMID", "$field.MST_TEAM_ID")
-        .cell(true);
-else
-    res = "";
-
-result.string(res);
\ No newline at end of file
diff --git a/entity/Salesproject_entity/entityfields/mst_team_id/stateProcess.js b/entity/Salesproject_entity/entityfields/mst_team_id/stateProcess.js
deleted file mode 100644
index 0a84b37c599e80e8b1295c2cd50f2aa0c548041b..0000000000000000000000000000000000000000
--- a/entity/Salesproject_entity/entityfields/mst_team_id/stateProcess.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import("MSTeams_lib");
-import("system.result");
-import("system.vars");
-import("system.neon");
-
-var res = neon.COMPONENTSTATE_INVISIBLE;
-var recordState = vars.get("$sys.recordstate");
-if (MSTeamsUtils.isTeamsEnabled() && recordState != neon.OPERATINGSTATE_NEW && recordState != neon.OPERATINGSTATE_EDIT)
-    res = neon.COMPONENTSTATE_READONLY;
-    
-result.string(res);
\ No newline at end of file
diff --git a/entity/Salesproject_entity/recordcontainers/db/filterextensions/attribute_filter/groupQueryProcess.js b/entity/Salesproject_entity/recordcontainers/db/filterextensions/attribute_filter/groupQueryProcess.js
index 2db6219c71582c8d5d3fd91e4a8c1196fe87470b..b5c569dfda29bdfcee8129b335c981e64cf3272a 100644
--- a/entity/Salesproject_entity/recordcontainers/db/filterextensions/attribute_filter/groupQueryProcess.js
+++ b/entity/Salesproject_entity/recordcontainers/db/filterextensions/attribute_filter/groupQueryProcess.js
@@ -4,7 +4,5 @@ import("AttributeFilter_lib");
 var sqlCond = AttributeFilterExtensionMaker.makeFilterGroupQuery("SALESPROJECT \n\
     left join CONTACT on (SALESPROJECT.CONTACT_ID = CONTACT.CONTACTID) \n\
     left join ORGANISATION on (CONTACT.ORGANISATION_ID = ORGANISATION.ORGANISATIONID)\n\
-    left join CLASSIFICATIONSTORAGE on (CLASSIFICATIONSTORAGE.OBJECT_ROWID = SALESPROJECT.SALESPROJECTID)\n\
-    left join MST_TEAMLINK on (SALESPROJECT.SALESPROJECTID = MST_TEAMLINK.OBJECT_ROWID and MST_TEAMLINK.OBJECT_TYPE = 'Salesproject')\n\
-    left join MST_TEAM on (MST_TEAM.MST_TEAMID = MST_TEAMLINK.MST_TEAM_ID)");
+    left join CLASSIFICATIONSTORAGE on (CLASSIFICATIONSTORAGE.OBJECT_ROWID = SALESPROJECT.SALESPROJECTID)");
 result.string(sqlCond);
\ No newline at end of file
diff --git a/entity/Salesproject_entity/recordcontainers/db/fromClauseProcess.js b/entity/Salesproject_entity/recordcontainers/db/fromClauseProcess.js
index 5532af0f5da27e0cb1351373a063ca94cf02f786..b9e273ff9f34870de6b80808285eda2d0205fbc9 100644
--- a/entity/Salesproject_entity/recordcontainers/db/fromClauseProcess.js
+++ b/entity/Salesproject_entity/recordcontainers/db/fromClauseProcess.js
@@ -6,6 +6,4 @@ import("KeywordRegistry_basic");
 result.string("SALESPROJECT \n\
     left join CONTACT on (SALESPROJECT.CONTACT_ID = CONTACT.CONTACTID) \n\
     left join ORGANISATION on (CONTACT.ORGANISATION_ID = ORGANISATION.ORGANISATIONID)\n\
-    left join CLASSIFICATIONSTORAGE on (CLASSIFICATIONSTORAGE.OBJECT_ROWID = SALESPROJECT.SALESPROJECTID)\n\
-    left join MST_TEAMLINK on (SALESPROJECT.SALESPROJECTID = MST_TEAMLINK.OBJECT_ROWID and MST_TEAMLINK.OBJECT_TYPE = 'Salesproject')\n\
-    left join MST_TEAM on (MST_TEAM.MST_TEAMID = MST_TEAMLINK.MST_TEAM_ID)");
\ No newline at end of file
+    left join CLASSIFICATIONSTORAGE on (CLASSIFICATIONSTORAGE.OBJECT_ROWID = SALESPROJECT.SALESPROJECTID)");
diff --git a/entity/Salesproject_entity/recordcontainers/db/onDBUpdate.js b/entity/Salesproject_entity/recordcontainers/db/onDBUpdate.js
index 61aa43cba4691fadddc0a7dd5a6f6789b966cefd..35e786cfb3008b4557e02081b0dd6a55622745ee 100644
--- a/entity/Salesproject_entity/recordcontainers/db/onDBUpdate.js
+++ b/entity/Salesproject_entity/recordcontainers/db/onDBUpdate.js
@@ -1,3 +1,4 @@
+import("MSTeams_lib");
 import("ActivityTask_lib");
 import("system.tools");
 import("system.datetime");
@@ -19,6 +20,7 @@ import("system.teams");
 var needToUpdateForecast = null;
 var rowdata = vars.get("$local.rowdata");
 
+var mstEventMessages = [];
 // create Milestones if changed
 vars.get("$local.changed").forEach(function(fieldName) {
     var typeValue;
@@ -28,6 +30,12 @@ vars.get("$local.changed").forEach(function(fieldName) {
     switch (fieldName) {
         case "SALESPROJECT.PHASE":
             typeValue = "SalesprojectPhase";
+            mstEventMessages.push(translate.withArguments(
+                "${SALESPROJECT_MST_PHASE_CHANGE}", [
+                    vars.get("$field.PROJECTTITLE"),
+                    vars.get("$field.PHASE.displayValue")
+                ]
+            ));
             break;
         case "SALESPROJECT.STATUS":
             // Milestone
@@ -117,6 +125,12 @@ vars.get("$local.changed").forEach(function(fieldName) {
             {
                 clearReason = true;
             }
+            mstEventMessages.push(translate.withArguments(
+                "${SALESPROJECT_MST_STATUS_CHANGE}", [
+                    vars.get("$field.PROJECTTITLE"),
+                    vars.get("$field.STATUS.displayValue")
+                ]
+            ));
             break;
     }
     
@@ -136,6 +150,16 @@ vars.get("$local.changed").forEach(function(fieldName) {
     }
 });
 
+var teamId = vars.get("$field.MST_TEAM_ID");
+if(mstEventMessages.length && teamId)
+{
+    var serviceUrl = MSTeamsUtils.getTeamInfo(teamId).serviceUrl;
+    serviceUrl && teams.sendNotification(serviceUrl,
+        teams.getGeneralChannelId(teamId),
+        mstEventMessages.join("<br><br>")
+    );
+}
+
 if(rowdata["SALESPROJECT.STATUS"] == $KeywordRegistry.salesprojectState$aborted()
     || rowdata["SALESPROJECT.STATUS"] == $KeywordRegistry.salesprojectState$partialOrder()
     || rowdata["SALESPROJECT.STATUS"] == $KeywordRegistry.salesprojectState$order()
@@ -146,14 +170,6 @@ if(rowdata["SALESPROJECT.STATUS"] == $KeywordRegistry.salesprojectState$aborted(
     .and("SALESPROJECT_MILESTONE.KIND", "SalesprojectPhase").updateData(true, "SALESPROJECT_MILESTONE", ["DATE_END"], null, [datetime.date()]);
 }
 
-var [serviceUrl, channelId] = newSelect(["SERVICE_URL", "GENERAL_CHANNELID"])
-    .from("MST_TEAM")
-    .where("MST_TEAM.MST_TEAMID", "$field.MST_TEAM_ID")
-    .arrayRow();
-
-if (serviceUrl)
-    teams.sendNotification(serviceUrl, channelId, "Etwas hat sich im Projekt "+ vars.get("$field.PROJECTTITLE") +" geaendert");
-
 if (needToUpdateForecast)
     Salesproject.notifyToUpdateForecast();
 
diff --git a/entity/Salesproject_entity/recordcontainers/db/recordfieldmappings/mst_team_id.displayvalue/expression.js b/entity/Salesproject_entity/recordcontainers/db/recordfieldmappings/mst_team_id.displayvalue/expression.js
new file mode 100644
index 0000000000000000000000000000000000000000..b3e59d0b10034cb5f383b3358b674ebbddc9dec6
--- /dev/null
+++ b/entity/Salesproject_entity/recordcontainers/db/recordfieldmappings/mst_team_id.displayvalue/expression.js
@@ -0,0 +1,10 @@
+import("system.result");
+import("AttributeRegistry_basic");
+import("Sql_lib");
+
+var valueSql = newSelect("AB_ATTRIBUTERELATION.ID_VALUE").from("AB_ATTRIBUTERELATION")
+    .where("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID", $AttributeRegistry.mstTeam())
+    .and("AB_ATTRIBUTERELATION.OBJECT_ROWID = SALESPROJECT.SALESPROJECTID");
+var sql = newSelect("TEAMNAME").from("MST_TEAM")
+    .where("MST_TEAM.MST_TEAMID", valueSql).toString();
+result.string("(" + sql + ")");
diff --git a/entity/Salesproject_entity/recordcontainers/db/recordfieldmappings/mst_team_id.value/expression.js b/entity/Salesproject_entity/recordcontainers/db/recordfieldmappings/mst_team_id.value/expression.js
new file mode 100644
index 0000000000000000000000000000000000000000..75ea5930056ae54635651aad7d220e90581eb2f3
--- /dev/null
+++ b/entity/Salesproject_entity/recordcontainers/db/recordfieldmappings/mst_team_id.value/expression.js
@@ -0,0 +1,8 @@
+import("system.result");
+import("AttributeRegistry_basic");
+import("Sql_lib");
+
+var sql = newSelect("AB_ATTRIBUTERELATION.ID_VALUE").from("AB_ATTRIBUTERELATION")
+    .where("AB_ATTRIBUTERELATION.AB_ATTRIBUTE_ID", $AttributeRegistry.mstTeam())
+    .and("AB_ATTRIBUTERELATION.OBJECT_ROWID = SALESPROJECT.SALESPROJECTID").toString();
+result.string("(" + sql + ")");
diff --git a/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod b/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod
index c0dec5cc101010be254693ba27a95db96b9ec379..fd79e91c3ae7e5d3418341720b12e613d85a8043 100644
--- a/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod
+++ b/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod
@@ -7690,7 +7690,7 @@
       <key>Add app</key>
     </entry>
     <entry>
-      <key>Open Teams</key>
+      <key>Open MS Teams</key>
     </entry>
     <entry>
       <key>Choose Team</key>
diff --git a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
index e87c99efc3226b85b833b9cc34a2eb00945b80af..cb59770d9936b7157c6b4e60aff8192d9526c754 100644
--- a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
+++ b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
@@ -607,8 +607,8 @@
       <value>Team löschen</value>
     </entry>
     <entry>
-      <key>Open Teams</key>
-      <value>Teams öffnen</value>
+      <key>Open MS Teams</key>
+      <value>MS Teams öffnen</value>
     </entry>
     <entry>
       <key>Restore Team</key>
@@ -13136,6 +13136,19 @@ Bitte Datumseingabe prüfen</value>
       <key>Zip Archive</key>
       <value>Zip Archiv</value>
     </entry>
+    <entry>
+      <key>${MST_APP_ALREADY_ADDED}</key>
+      <value>Die ADITO-App wurde bereits hinzugefügt.</value>
+    </entry>
+    <entry>
+      <key>set attribute (use filter result)</key>
+    </entry>
+    <entry>
+      <key>Copy Recipients</key>
+    </entry>
+    <entry>
+      <key>copy</key>
+    </entry>
     <entry>
       <key>Attendees cannot be assigned repeatedly!</key>
       <value>Teilnehmer können nicht mehrmals zugewiesen werden!</value>
@@ -14338,6 +14351,30 @@ Bitte Datumseingabe prüfen</value>
     <entry>
       <key>We save your data until</key>
     </entry>
+    <entry>
+      <key>Guest</key>
+      <value>Gast</value>
+    </entry>
+    <entry>
+      <key>Import from MS Teams</key>
+      <value>Von MS Teams importieren</value>
+    </entry>
+    <entry>
+      <key>Found a duplicate member</key>
+      <value>Mitglied ist doppelt vorhanden</value>
+    </entry>
+    <entry>
+      <key>${SALESPROJECT_MST_STATUS_CHANGE}</key>
+      <value>Der Status des Vertriebsprojekts '%0' wurde auf '%1' geändert.</value>
+    </entry>
+    <entry>
+      <key>${SALESPROJECT_MST_PHASE_CHANGE}</key>
+      <value>Die Phase des Vertriebsprojekts '%0' wurde auf '%1' geändert.</value>
+    </entry>
+    <entry>
+      <key>MS-Teams Message</key>
+      <value>MS-Teams Nachricht</value>
+    </entry>
     <entry>
       <key>Dependencies</key>
       <value>Abhängigkeiten</value>
diff --git a/language/_____LANGUAGE_en/_____LANGUAGE_en.aod b/language/_____LANGUAGE_en/_____LANGUAGE_en.aod
index ec3bdcc2ffb7a9d70102288e28736633c99ac921..e5be127722789567170803c196915064ce442b1c 100644
--- a/language/_____LANGUAGE_en/_____LANGUAGE_en.aod
+++ b/language/_____LANGUAGE_en/_____LANGUAGE_en.aod
@@ -7503,7 +7503,7 @@
       <key>Add app</key>
     </entry>
     <entry>
-      <key>Open Teams</key>
+      <key>Open MS Teams</key>
     </entry>
     <entry>
       <key>Choose Team</key>
@@ -9631,6 +9631,10 @@
     <entry>
       <key>TrueType Font</key>
     </entry>
+    <entry>
+      <key>${MST_APP_ALREADY_ADDED}</key>
+      <value>The ADITO app has already been added.</value>
+    </entry>
     <entry>
       <key>No recipient is marked for test run replacement.</key>
     </entry>
@@ -9868,6 +9872,14 @@
     <entry>
       <key>Paste as Top Topic</key>
     </entry>
+    <entry>
+      <key>${SALESPROJECT_MST_STATUS_CHANGE}</key>
+      <value>The status of the salesproject '%0' has been changed to '%1'.</value>
+    </entry>
+    <entry>
+      <key>${SALESPROJECT_MST_PHASE_CHANGE}</key>
+      <value>The phase of the salesproject '%0' has been changed to '%1'.</value>
+    </entry>
     <entry>
       <key>Search term</key>
     </entry>
diff --git a/neonContext/MSTTeam/MSTTeam.aod b/neonContext/MSTTeam/MSTTeam.aod
index 7f715ccd447d45dbce5deed363aebf485646fab9..3f0ea6b92907c6b28ee8c669151af3cb44df8b35 100644
--- a/neonContext/MSTTeam/MSTTeam.aod
+++ b/neonContext/MSTTeam/MSTTeam.aod
@@ -16,7 +16,6 @@
     </neonViewReference>
     <neonViewReference>
       <name>b1e14c73-f48f-43a7-a459-2aa0172d015b</name>
-      <view>MSTTeamEditMembers_view</view>
     </neonViewReference>
   </references>
 </neonContext>
diff --git a/neonContext/MSTTeamLink/MSTTeamLink.aod b/neonContext/MSTTeamLink/MSTTeamLink.aod
deleted file mode 100644
index d0d6d646fe31690e5c9d26697ec9adf9f471f40c..0000000000000000000000000000000000000000
--- a/neonContext/MSTTeamLink/MSTTeamLink.aod
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<neonContext xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonContext/1.1.1">
-  <name>MSTTeamLink</name>
-  <majorModelMode>DISTRIBUTED</majorModelMode>
-  <editView>MSTTeamLinkEdit_view</editView>
-  <entity>MSTTeamLink_entity</entity>
-  <references>
-    <neonViewReference>
-      <name>a165469f-eea5-4427-b5ba-4477fe7e04e6</name>
-      <view>MSTTeamLinkEdit_view</view>
-    </neonViewReference>
-  </references>
-</neonContext>
diff --git a/neonView/MSTTeamEditMembers_view/MSTTeamEditMembers_view.aod b/neonView/MSTTeamEditMembers_view/MSTTeamEditMembers_view.aod
deleted file mode 100644
index 49a58b14aac140e3dd338dc2a934d39ff2e25a15..0000000000000000000000000000000000000000
--- a/neonView/MSTTeamEditMembers_view/MSTTeamEditMembers_view.aod
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<neonView xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8">
-  <name>MSTTeamEditMembers_view</name>
-  <majorModelMode>DISTRIBUTED</majorModelMode>
-  <size>SMALL</size>
-  <layout>
-    <boxLayout>
-      <name>layout</name>
-    </boxLayout>
-  </layout>
-  <children>
-    <genericViewTemplate>
-      <name>Teamname</name>
-      <entityField>#ENTITY</entityField>
-      <fields>
-        <entityFieldLink>
-          <name>b17446e0-ad87-4098-8bd2-de716a8e12ae</name>
-          <entityField>TEAMNAME</entityField>
-        </entityFieldLink>
-      </fields>
-    </genericViewTemplate>
-    <neonViewReference>
-      <name>189bbef9-e646-416a-835f-6348199dbd24</name>
-      <entityField>TeamMembers</entityField>
-      <view>MSTTeamMemberMultiEdit_view</view>
-    </neonViewReference>
-  </children>
-</neonView>
diff --git a/neonView/MSTTeamLinkEdit_view/MSTTeamLinkEdit_view.aod b/neonView/MSTTeamLinkEdit_view/MSTTeamLinkEdit_view.aod
deleted file mode 100644
index b3ce9642c95bd0bd6f3d72802cae3a46da66d829..0000000000000000000000000000000000000000
--- a/neonView/MSTTeamLinkEdit_view/MSTTeamLinkEdit_view.aod
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<neonView xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.1.8" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.8">
-  <name>MSTTeamLinkEdit_view</name>
-  <majorModelMode>DISTRIBUTED</majorModelMode>
-  <size>SMALL</size>
-  <layout>
-    <boxLayout>
-      <name>layout</name>
-    </boxLayout>
-  </layout>
-  <children>
-    <genericViewTemplate>
-      <name>TeamSelection</name>
-      <editMode v="true" />
-      <fields>
-        <entityFieldLink>
-          <name>1b10e15e-0774-430d-8b76-36307abdff33</name>
-          <entityField>TEAM_ID_AND_NAME</entityField>
-        </entityFieldLink>
-      </fields>
-    </genericViewTemplate>
-  </children>
-</neonView>
diff --git a/neonView/MSTTeamMemberMultiEdit_view/MSTTeamMemberMultiEdit_view.aod b/neonView/MSTTeamMemberMultiEdit_view/MSTTeamMemberMultiEdit_view.aod
index d0b7385720860123a095e52453864b2d0b8fbc13..cb23e65ab0056e6fc01031d5e8b1c9b0292aea42 100644
--- a/neonView/MSTTeamMemberMultiEdit_view/MSTTeamMemberMultiEdit_view.aod
+++ b/neonView/MSTTeamMemberMultiEdit_view/MSTTeamMemberMultiEdit_view.aod
@@ -20,22 +20,6 @@
           <name>467ab169-f46a-4036-9e5e-5c480bd947a6</name>
           <entityField>ROLE</entityField>
         </neonGenericMultipleTableColumn>
-        <neonGenericMultipleTableColumn>
-          <name>b85c25c8-09dd-4f9a-b0b4-31c0b8a8f12c</name>
-          <entityField>INVITE</entityField>
-        </neonGenericMultipleTableColumn>
-        <neonGenericMultipleTableColumn>
-          <name>82cdb323-6c1a-428f-a01e-e29da9f338ec</name>
-          <entityField>AZUREID</entityField>
-        </neonGenericMultipleTableColumn>
-        <neonGenericMultipleTableColumn>
-          <name>3f766fe4-119d-40df-b071-b7a578af2a71</name>
-          <entityField>AZUREUPN</entityField>
-        </neonGenericMultipleTableColumn>
-        <neonGenericMultipleTableColumn>
-          <name>0b3ac2a1-91ad-4761-9a1d-7231d5b55169</name>
-          <entityField>ISEXTERN</entityField>
-        </neonGenericMultipleTableColumn>
       </columns>
     </genericMultipleViewTemplate>
   </children>
diff --git a/process/AttributeRegistry_basic/process.js b/process/AttributeRegistry_basic/process.js
index 030f4c7baaf7e322a0cfd21f278075aa736f6fd0..783d995032790ef2c8e196898ad03df977b1294b 100644
--- a/process/AttributeRegistry_basic/process.js
+++ b/process/AttributeRegistry_basic/process.js
@@ -28,4 +28,5 @@ $AttributeRegistry.visitPlanFrequency$quarterly = function(){return "22722783-aa
 $AttributeRegistry.visitPlanFrequency$yearly = function(){return "a9575350-c1e7-4f96-b0ed-a8f52cc8b123";};
 $AttributeRegistry.visitPlanPointOfContact = function(){return "7d78320a-31c6-4c24-992a-a583f47caeb5";};
 
-$AttributeRegistry.digitalAdvertisingMaterial = function(){return "5a596342-679a-4c55-b368-fad0bd817aa0";};
\ No newline at end of file
+$AttributeRegistry.digitalAdvertisingMaterial = function(){return "5a596342-679a-4c55-b368-fad0bd817aa0";};
+$AttributeRegistry.mstTeam = function(){return "9a93f671-fa9e-4783-8ae8-6cef76b3f62f";};
diff --git a/process/Context_lib/process.js b/process/Context_lib/process.js
index 40cbdbeaaeb4c3de77866c99dd5d03fed9bf4472..e7d67c7ed86b1e65d179e54ba6dc82ea7a7d741b 100644
--- a/process/Context_lib/process.js
+++ b/process/Context_lib/process.js
@@ -186,11 +186,11 @@ ContextUtils.getContextConsumer = function(pContextId)
  * @param {String} pEntity The entity name you want to load the title for
  * @param {String} pUid the uid for which to load the title
  * @param {String} [pProvider] providername when instead of the default provider another provider shall be used
- * @param {String} [pParameter] parameter you want to set (to avoid loading specific things when getting the contentTitle)
- * 
+ * @param {String} [pParams] parameter object
+
  * @return the #CONTENTTITLE or ""
  */
-ContextUtils.loadContentTitle = function(pEntity, pUid, pProvider, pParameter)
+ContextUtils.loadContentTitle = function(pEntity, pUid, pProvider, pParams)
 {    
     if (!pUid)
         return "";
@@ -204,9 +204,12 @@ ContextUtils.loadContentTitle = function(pEntity, pUid, pProvider, pParameter)
     {
         conf.provider(pProvider);
     }
-    if (pParameter && pEntity == "Salesproject_entity")
+    if(pParams)
     {
-        conf.addParameter(pParameter, true);
+        for(var p in pParams)
+        {
+            conf.addParameter(p, pParams[p]);
+        }
     }
     
     var rows = entities.getRows(conf);
@@ -377,13 +380,14 @@ ContextUtils._contextDataMapping = function(pContext)
  * 
  * @param {String} pContextId the name of the context
  * @param {String} pRowId Uid of certain Context
- * @param {String} [pParameter] parameter you want to set (to avoid loading specific things when getting the contentTitle)
+ * @param {String} pProvider providername when instead of the default provider another provider shall be used
+ * @param {String} pParams parameter object
  * 
  * @return {String} The #CONTENTTITLE
  */
-ContextUtils.getTitleByContext = function(pContextId, pRowId, pParameter)
+ContextUtils.getTitleByContext = function(pContextId, pRowId, pProvider, pParams)
 {
-    return ContextUtils.loadContentTitle(ContextUtils.getEntity(pContextId), pRowId, null, pParameter);
+    return ContextUtils.loadContentTitle(ContextUtils.getEntity(pContextId), pRowId, pProvider, pParams);
 }
 
 
diff --git a/process/KeywordRegistry_basic/process.js b/process/KeywordRegistry_basic/process.js
index cc85d170a716afb3a5e352b754534ce5b5b93330..e89f84085226a7cef14fb0d5796dd4a978304536 100644
--- a/process/KeywordRegistry_basic/process.js
+++ b/process/KeywordRegistry_basic/process.js
@@ -145,6 +145,7 @@ $KeywordRegistry.activityCategory$onlineMeeting = function(){return "ONLINE-MEET
 $KeywordRegistry.activityCategory$approval = function(){return "APPROVAL";}
 $KeywordRegistry.activityCategory$sys = function(){return "SYS";};
 
+$KeywordRegistry.activityCategory$mstMessage = function(){return "MST-MESSAGE";};
 
 $KeywordRegistry.addressType = function(){return "AddressType";};
 $KeywordRegistry.addressType$private = function(){return "HOMEADDR";};
diff --git a/process/MSTeams_lib/process.js b/process/MSTeams_lib/process.js
index 892ed53b3e815ba9bc00dd562b43f1c06fe7781b..978f63390349075c5aa8c372fa5e1428070ce0d7 100644
--- a/process/MSTeams_lib/process.js
+++ b/process/MSTeams_lib/process.js
@@ -1,3 +1,4 @@
+import("Employee_lib");
 import("system.db");
 import("system.logging");
 import("system.neon");
@@ -24,187 +25,44 @@ MSTeamsUtils.isTeamsEnabled = function ()
     return Utils.toBoolean(project.getInstanceConfigValue("teamsEnabled", "false"));
 }
 
-MSTeamsUtils.getImpersoAzureId = function ()
-{
-    return project.getInstanceConfigValue("teamsImpersoAzureId", "");
-}
-
-/**
- * Contains the configuration for the possible contexts that can be used for teamLinks.
- * 
- * @return {Object} object with the context-names as keys and configuration-objects as values
- */
-MSTeamsUtils.getTeamLinkContexts = function ()
-{
-    return {
-        "Salesproject": {singleLinkPerObject: true}
-    };
-}
-
-/**
- * Opens a context for changing the linked team of the given object. This function also checks the configuration from
- * MSTeamsUtils.getTeamLinkContexts to make sure the created teamLink is allowed
+/*
+ * Returns the azure object id of the impersonation user
+ * If no impersonation user is configured this function returns null
  * 
- * @param {String} pRowId       uid of the linked object
- * @param {String} pObjectType  context of the linked object
+ * @returns {String} the azure object id of the impersonation user
  */
-MSTeamsUtils.changeTeamsLink = function (pRowId, pObjectType)
+MSTeamsUtils.getImpersoAzureId = function()
 {
-    var linkConfig = MSTeamsUtils.getTeamLinkContexts()[pObjectType];
-    if (!linkConfig)
-        return;
-    
-    var existingTeamLinks = newSelect("MST_TEAMLINKID")
-        .from("MST_TEAMLINK")
-        .where("MST_TEAMLINK.OBJECT_ROWID", pRowId)
-        .and("MST_TEAMLINK.OBJECT_TYPE", pObjectType)
-        .arrayColumn();
-    var params = {
-        "ObjectRowId_param": pRowId,
-        "ObjectType_param": pObjectType
-    };
-    if (existingTeamLinks.length === 0 || !linkConfig.singleLinkPerObject)
-        neon.openContext("MSTTeamLink", null, null, neon.OPERATINGSTATE_NEW, params);
-    else
-        neon.openContext("MSTTeamLink", null, [existingTeamLinks[0]], neon.OPERATINGSTATE_EDIT, params);
+    return project.getInstanceConfigValue("teamsImpersoAzureId", null);
 }
 
-/**
- * Removes all teamLinks of the given object that are invalid.
+/*
+ * Returns the azure object id of the user with the specified contactid
+ * If no azure id is configured the function first ties to get the azure id via the upn
+ * if that also fails the function will return null
  * 
- * @param {String} pRowId               uid of the linked object
- * @param {String} pObjectType          context of the linked object
- * @param {String} [pTeamLinkIdToKeep]  Only applies to contexts with 'singleLinkPerObject: true', if there are more than one teamLink, every
- *                                      teamLink except one will be deleted. This param controls which one shouldn't be deleted.
+ * @returns {string} the azure object id or null (on failure)
  */
-MSTeamsUtils.purgeCorruptTeamLinks = function (pRowId, pObjectType, pTeamLinkIdToKeep)
+MSTeamsUtils.getAzureIdByContactId = function(pContactId)
 {
-    var teamLinks = MSTeamsUtils.getLinkedTeams(pRowId, pObjectType).map(function (row)
-    {
-        return row.teamLinkId;
-    });
-    var linkConfig = MSTeamsUtils.getTeamLinkContexts()[pObjectType];
-    var removeLinks = [];
-    if (!linkConfig)
-        removeLinks = teamLinks;
-    else if (linkConfig.singleLinkPerObject && pTeamLinkIdToKeep)
-        removeLinks = teamLinks.filter(function (id) {return id != pTeamLinkIdToKeep;});
-    else if (linkConfig.singleLinkPerObject)
-        removeLinks = teamLinks.slice(1);
-    
-    if (removeLinks.length === 0)
-        return 0;
-    return newWhere("MST_TEAMLINK.MST_TEAMLINKID", removeLinks, SqlBuilder.IN()).deleteData();
-}
-
-/**
- * Saves the given team in the database if it is not already present.
- * 
- * @param {Object} pTeam    An object containing the data of the team. Required properties:
- *                          <ul>
- *                              <li>teamId</li>
- *                              <li>teamName</li>
- *                          </ul>
- *                          Optional properties (will be loaded automatically if not given):
- *                          <ul>
- *                              <li>generalChannelId</li>
- *                              <li>webUrl</li>
- *                          </ul>
- * @return {Boolean} true if the team was inserted, false if it already exists
- */
-MSTeamsUtils.insertTeamIfMissing = function (pTeam)
-{
-    var existsTeam = newSelect("count(*)")
-        .from("MST_TEAM")
-        .where("MST_TEAM.MST_TEAMID", pTeam.teamId)
-        .cell() > 0;
-    if (existsTeam)
-        return false;
-    
-    new SqlBuilder().insertFields({
-        "MST_TEAMID": pTeam.teamId,
-        "TEAMNAME": pTeam.teamName,
-        "GENERAL_CHANNELID": pTeam.generalChannelId || teams.getGeneralChannelId(pTeam.teamId),
-        "IS_ARCHIVED": 0,
-        "WEB_URL": pTeam.webUrl || teams.getWebUrl(pTeam.teamId)
-    }, "MST_TEAM");
-    
-    return true;
-}
-
-/**
- * Creates a new teamLink.
- * 
- * @param {String} pTeamId      id of the team
- * @param {String} pRowId       uid of the linked object
- * @param {String} pObjectType  context of the linked object
- */
-MSTeamsUtils.createTeamLink = function (pTeamId, pRowId, pObjectType)
-{
-    var currentTeamLink;
-    var contextConfig = MSTeamsUtils.getTeamLinkContexts()[pObjectType];
-    if (!contextConfig)
-        return;
-    
-    //if singleLinkPerObject is true, only one team link can be created for the given objectRowId
-    if (contextConfig.singleLinkPerObject)
+    var userObj = EmployeeUtils.getUserByContactId(pContactId);
+    if(!userObj)
     {
-        currentTeamLink = MSTeamsUtils.getLinkedTeam(pRowId, pObjectType);
+        return null;
     }
-    if (currentTeamLink)
+    
+    var azureId = userObj[tools.PARAMS][tools.TEAMS_AZUREID];
+    if(azureId)
     {
-        newWhere("MST_TEAMLINK.MST_TEAMLINKID", currentTeamLink.teamLinkId).updateFields({
-            "MST_TEAM_ID": pTeamId
-        });
+        return azureId;
     }
-    else
+    
+    var azureUpn = userObj[tools.PARAMS][tools.TEAMS_AZUREUPN];
+    if(azureUpn)
     {
-        new SqlBuilder().insertFields({
-            "MST_TEAM_ID": pTeamId,
-            "OBJECT_ROWID": pRowId,
-            "OBJECT_TYPE": pObjectType
-        }, "MST_TEAMLINK", "MST_TEAMLINKID");
+        return teams.getInternalAzureId(azureUpn);
     }
-}
-
-/**
- * Gets all linked teams of an object
- * 
- * @param {String} pRowId       uid of the linked object
- * @param {String} pObjectType  context of the linked object
- * @return {Object[]} array of {teamLinkId, teamId}
- */
-MSTeamsUtils.getLinkedTeams = function (pRowId, pObjectType)
-{
-    return newSelect(["MST_TEAMLINKID", "MST_TEAM_ID"])
-        .from("MST_TEAMLINK")
-        .where("MST_TEAMLINK.OBJECT_ROWID", pRowId)
-        .andIfSet("MST_TEAMLINK.OBJECT_TYPE", pObjectType)
-        .orderBy("MST_TEAMLINKID")
-        .table()
-        .map(function ([linkId, teamId])
-        {
-            return {
-                teamLinkId: linkId,
-                teamId: teamId
-            };
-        });
-    
-}
-
-/**
- * Gets the linked team of an object
- * 
- * @param {String} pRowId       uid of the linked object
- * @param {String} pObjectType  context of the linked object
- * @return {String} id of the linked team, or null if ther is none
- */
-MSTeamsUtils.getLinkedTeam = function (pRowId, pObjectType)
-{
-    var linkedTeams = MSTeamsUtils.getLinkedTeams(pRowId, pObjectType);
-    if (linkedTeams.length === 0)
-        return null;
-    return linkedTeams[0].teamId;
+    return null;
 }
 
 /**
@@ -216,7 +74,9 @@ MSTeamsUtils.getLinkedTeam = function (pRowId, pObjectType)
 MSTeamsUtils.getContactIdsByAzureIds = function (pAzureIds)
 {
     if (pAzureIds.length === 0)
+    {
         return {};
+    }
     
     var teamsUsers = tools.getUsersByAttribute(tools.TEAMS_AZUREID, pAzureIds);
     var contactIdMap = {};
@@ -238,9 +98,12 @@ MSTeamsUtils.getContactIdsByAzureIds = function (pAzureIds)
 MSTeamsUtils.getTeamRoleSubSql = function (pTeamId, pContactIdField)
 {
     if (!pTeamId)
+    {
         return "''";
+    }
     
-    try {
+    try
+    {
         var ownerIds = Object.keys(teams.getAllOwners(pTeamId));
         var ownerContactIds = MSTeamsUtils.getContactIdsByAzureIds(ownerIds);
         ownerContactIds = Utils.objectValues(ownerContactIds);
@@ -257,13 +120,19 @@ MSTeamsUtils.getTeamRoleSubSql = function (pTeamId, pContactIdField)
     }
     
     if (ownerContactIds.length === 0 && memberContactIds.length === 0)
+    {
         return "''";
+    }
     
     var caseWhen = SqlBuilder.caseStatement();
     if (ownerContactIds.length !== 0)
+    {
         caseWhen.when(pContactIdField, ownerContactIds, SqlBuilder.IN()).thenString(translate.text("Owner"));
+    }
     if (memberContactIds.length !== 0)
+    {
         caseWhen.when(pContactIdField, memberContactIds, SqlBuilder.IN()).thenString(translate.text("Member"));
+    }
     
     //the SqlBuilder.IN() can't be used here because of the sub-sql string for the email
     caseWhen.when("(" + CommUtil.getStandardSubSqlMail() + ") in ('" + Utils.objectValues(members).join("', '") + "')")
@@ -281,16 +150,13 @@ MSTeamsUtils.getTeamRoleSubSql = function (pTeamId, pContactIdField)
  */
 MSTeamsUtils.isUserTeamOwner = function (pTeamId, pUserId)
 {
-    var user;
-    if (!pUserId)
-        user = tools.getCurrentUser();
-    else
-        user = tools.getUserByAttribute(tools.NAME, pUserId);
-    
-    var azureId = user ? user[tools.PARAMS][tools.TEAMS_AZUREID] : null;
-    if (!azureId)
+    if(!pTeamId)
+    {
         return false;
-    return azureId in teams.getAllOwners(pTeamId);
+    }
+    var user = pUserId ? tools.getUserByAttribute(tools.NAME, pUserId) : tools.getCurrentUser();
+    var azureId = user ? user[tools.PARAMS][tools.TEAMS_AZUREID] : null;
+    return azureId && azureId in teams.getAllOwners(pTeamId);
 }
 
 /**
@@ -309,7 +175,7 @@ MSTeamsUtils.isUserTeamOwner = function (pTeamId, pUserId)
  */
 MSTeamsUtils.getTeamInfo = function (pTeamId)
 {
-    var teamInfo = new SqlBuilder(db.getCurrentAlias())
+    var teamInfo = new SqlBuilder(SqlUtils.getDataAlias())
         .select(["TEAMNAME", "WEB_URL", "SERVICE_URL", "IS_ARCHIVED", "GENERAL_CHANNELID"])
         .from("MST_TEAM")
         .whereIfSet("MST_TEAM.MST_TEAMID", pTeamId)
@@ -337,4 +203,9 @@ MSTeamsUtils.isTeamActive = function (pTeamId)
 {
     var teamInfo = MSTeamsUtils.getTeamInfo(pTeamId);
     return teamInfo != null && !teamInfo.isArchived;
-}
\ No newline at end of file
+}
+
+function $MSTeamsRoles(){}
+$MSTeamsRoles.Owner = function(){return "owner"};
+$MSTeamsRoles.Member = function(){return "member"};
+$MSTeamsRoles.Guest = function(){return "guest"};
diff --git a/process/Sql_lib/process.js b/process/Sql_lib/process.js
index 6926f5ec35b7aea6d9d61c3e69b829531885a786..4ec5fbabe25fda4e48a39fc5c4d59e55d50fd919 100644
--- a/process/Sql_lib/process.js
+++ b/process/Sql_lib/process.js
@@ -4561,6 +4561,14 @@ SqlUtils.getSystemAlias = function()
     return "_____SYSTEMALIAS";
 }
 
+/**
+ *  @return the dataalias
+ */
+SqlUtils.getDataAlias = function()
+{
+    return "Data_alias";
+}
+
 /**
      * Builds a SQL IN condition, while accounting for the 1000 elements maximum
      * Single conditions are concatenated with OR, which can be devastating for performance!