diff --git a/.liquibase/Data_alias/basic/2020.1.1/changelog.xml b/.liquibase/Data_alias/basic/2020.1.1/changelog.xml index 7070cb88cfdc67dbb126680500fad85499f40887..f5686ce22ccea6e17c577957774efd2009a0dd7f 100644 --- a/.liquibase/Data_alias/basic/2020.1.1/changelog.xml +++ b/.liquibase/Data_alias/basic/2020.1.1/changelog.xml @@ -2,5 +2,4 @@ <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="AlterTablesToDatetime.xml"/> - <include relativeToChangelogFile="true" file="Notification/changelog.xml"/> </databaseChangeLog> diff --git a/.liquibase/Data_alias/basic/2020.1.1/Notification/changelog.xml b/.liquibase/Data_alias/basic/2020.1.2/Notification/changelog.xml similarity index 100% rename from .liquibase/Data_alias/basic/2020.1.1/Notification/changelog.xml rename to .liquibase/Data_alias/basic/2020.1.2/Notification/changelog.xml diff --git a/.liquibase/Data_alias/basic/2020.1.1/Notification/init_NotificationType.xml b/.liquibase/Data_alias/basic/2020.1.2/Notification/init_NotificationType.xml similarity index 100% rename from .liquibase/Data_alias/basic/2020.1.1/Notification/init_NotificationType.xml rename to .liquibase/Data_alias/basic/2020.1.2/Notification/init_NotificationType.xml diff --git a/.liquibase/Data_alias/basic/2020.1.1/Notification/insert_NotificationState.xml b/.liquibase/Data_alias/basic/2020.1.2/Notification/insert_NotificationState.xml similarity index 100% rename from .liquibase/Data_alias/basic/2020.1.1/Notification/insert_NotificationState.xml rename to .liquibase/Data_alias/basic/2020.1.2/Notification/insert_NotificationState.xml diff --git a/.liquibase/Data_alias/basic/2020.1.2/changelog.xml b/.liquibase/Data_alias/basic/2020.1.2/changelog.xml index 7696526b5b01d5485e47a5a69bbade95aa0c6317..1f2c05d8e7b344c7cbe3cd689c4b0c526cd8e56b 100644 --- a/.liquibase/Data_alias/basic/2020.1.2/changelog.xml +++ b/.liquibase/Data_alias/basic/2020.1.2/changelog.xml @@ -2,7 +2,8 @@ <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 file="AlterDatatypeOfKeyColumnsToChar/changelog.xml" relativeToChangelogFile="true"/> - <include file="VisitPlanEntry/rename_VisitPlanEntry_ORGANISATION_ID.xml" relativeToChangelogFile="true"/> - <include file="AddNullableToDateNew.xml" relativeToChangelogFile="true" /> + <include file="AddNullableToDateNew.xml" relativeToChangelogFile="true" /> + <include file="insert_workflowCategory_keyword.xml" relativeToChangelogFile="true"/> + <include file="Notification/changelog.xml" relativeToChangelogFile="true" /> <include file="AlterButtonLabelTitles/AlterButtonLabelTitles.xml" relativeToChangelogFile="true" /> </databaseChangeLog> diff --git a/.liquibase/Data_alias/basic/2020.1.2/insert_workflowCategory_keyword.xml b/.liquibase/Data_alias/basic/2020.1.2/insert_workflowCategory_keyword.xml new file mode 100644 index 0000000000000000000000000000000000000000..a4bbbd1638f60b4023e3ccab49955e358c011ba9 --- /dev/null +++ b/.liquibase/Data_alias/basic/2020.1.2/insert_workflowCategory_keyword.xml @@ -0,0 +1,24 @@ +<?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="s.listl" id="38b13161-5163-48d9-9791-f9c7027cac62"> + <insert tableName="AB_KEYWORD_ENTRY"> + <column name="AB_KEYWORD_ENTRYID" value="f241b36a-c2fe-40be-9e2e-8293c700d096"/> + <column name="KEYID" value="WORKFLOWCATEGORYMARKETING"/> + <column name="TITLE" value="Marketing"/> + <column name="CONTAINER" value="WorkflowCategory"/> + <column name="SORTING" valueNumeric="1"/> + <column name="ISACTIVE" valueNumeric="1"/> + <column name="ISESSENTIAL" valueNumeric="0"/> + </insert> + <insert tableName="AB_KEYWORD_ENTRY"> + <column name="AB_KEYWORD_ENTRYID" value="00a72718-554e-46ab-b83c-fb9b81be4be6"/> + <column name="KEYID" value="WORKFLOWCATEGORYRELEASE"/> + <column name="TITLE" value="Release"/> + <column name="CONTAINER" value="WorkflowCategory"/> + <column name="SORTING" valueNumeric="2"/> + <column name="ISACTIVE" valueNumeric="1"/> + <column name="ISESSENTIAL" valueNumeric="0"/> + </insert> + </changeSet> +</databaseChangeLog> diff --git a/.liquibase/Data_alias/changelog.xml b/.liquibase/Data_alias/changelog.xml index a8cc53b406959a8797b32a02af71dd93e1576384..415dcce4162c36e2f1c7945a6082d40e3968a5b1 100644 --- a/.liquibase/Data_alias/changelog.xml +++ b/.liquibase/Data_alias/changelog.xml @@ -14,7 +14,7 @@ <include relativeToChangelogFile="true" file="basic/2020.1.0/changelog.xml"/> <include relativeToChangelogFile="true" file="basic/2020.1.1/changelog.xml"/> <include relativeToChangelogFile="true" file="basic/2020.1.2/changelog.xml"/> - + <!--enable this only when you definetly want to overwrite the existing data with demo records:--> <!--<include relativeToChangelogFile="true" file="basic/_demoData/changelog.xml" context="example"/>--> </databaseChangeLog> \ No newline at end of file diff --git a/entity/Address_entity/Address_entity.aod b/entity/Address_entity/Address_entity.aod index a8fab2595e8795a739e854e715f1669bbd0477ac..0b6f95c995e62e440e543f9a9839fd760bf4a6c3 100644 --- a/entity/Address_entity/Address_entity.aod +++ b/entity/Address_entity/Address_entity.aod @@ -15,8 +15,6 @@ <mandatoryProcess>%aditoprj%/entity/Address_entity/entityfields/address/mandatoryProcess.js</mandatoryProcess> <textInputAllowed v="true" /> <stateProcess>%aditoprj%/entity/Address_entity/entityfields/address/stateProcess.js</stateProcess> - <valueProcess>%aditoprj%/entity/Address_entity/entityfields/address/valueProcess.js</valueProcess> - <displayValueProcess>%aditoprj%/entity/Address_entity/entityfields/address/displayValueProcess.js</displayValueProcess> <onValueChange>%aditoprj%/entity/Address_entity/entityfields/address/onValueChange.js</onValueChange> <onValueChangeTypes> <element>MASK</element> diff --git a/entity/Address_entity/entityfields/address/displayValueProcess.js b/entity/Address_entity/entityfields/address/displayValueProcess.js deleted file mode 100644 index 18985f7a004b046c160746de13e8970eb6c87ece..0000000000000000000000000000000000000000 --- a/entity/Address_entity/entityfields/address/displayValueProcess.js +++ /dev/null @@ -1,6 +0,0 @@ -import("system.vars"); -import("system.result"); - -// Needed for instant refresh if set by neon.setFieldValue -// use the code if address webservice is active -//result.string(vars.get("$field.ADDRESS")); \ No newline at end of file diff --git a/entity/Address_entity/entityfields/address/valueProcess.js b/entity/Address_entity/entityfields/address/valueProcess.js deleted file mode 100644 index 2c9d496185be419f31c762709ca97d83a1ae4bd4..0000000000000000000000000000000000000000 --- a/entity/Address_entity/entityfields/address/valueProcess.js +++ /dev/null @@ -1,6 +0,0 @@ -import("system.result"); -import("WsValidation_lib"); -import("system.vars"); - -// use the code if address webservice is active -//result.string(WsValidationUtils.valueFromJSON(vars.get("$this.value"))); \ No newline at end of file diff --git a/entity/Address_entity/entityfields/address_ws/valueProcess.js b/entity/Address_entity/entityfields/address_ws/valueProcess.js index 5ccdf5a99f70f8b801158a56c1fa7b8136854130..2c7449d49d0036b67e5de71b0a7d7cf9b75876a9 100644 --- a/entity/Address_entity/entityfields/address_ws/valueProcess.js +++ b/entity/Address_entity/entityfields/address_ws/valueProcess.js @@ -1,3 +1,5 @@ +import("system.vars"); import("WsValidation_lib"); -WsValidationFieldUtils.wsValueProcess(WsValidationType.get().TYPE_STREET_NOMINATIM, "$field.ADDRESS"); \ No newline at end of file +if(!vars.get("$this.value")) + WsValidationFieldUtils.wsValueProcess(WsValidationType.get().TYPE_STREET_NOMINATIM, "$field.ADDRESS"); \ No newline at end of file diff --git a/entity/Email_entity/Email_entity.aod b/entity/Email_entity/Email_entity.aod index f3a15f3c8ad189a57e5dd9c9b3167d59a5043bed..b14e753e4e989ccf0645d68cb7bca2f6cca765c4 100644 --- a/entity/Email_entity/Email_entity.aod +++ b/entity/Email_entity/Email_entity.aod @@ -119,7 +119,7 @@ </entityParameter> <entityActionField> <name>sendMail</name> - <title>send mail</title> + <title>{SEND_MAIL}</title> <onActionProcess>%aditoprj%/entity/Email_entity/entityfields/sendmail/onActionProcess.js</onActionProcess> <iconId>VAADIN:AT</iconId> <stateProcess>%aditoprj%/entity/Email_entity/entityfields/sendmail/stateProcess.js</stateProcess> diff --git a/entity/Employee_entity/entityfields/contact_id/onValueChange.js b/entity/Employee_entity/entityfields/contact_id/onValueChange.js index d469435d5c78d77ed6371942df1cb330d36389d3..4a1dbe67e5fb2353cd24c297f9ca0c1eb08e9304 100644 --- a/entity/Employee_entity/entityfields/contact_id/onValueChange.js +++ b/entity/Employee_entity/entityfields/contact_id/onValueChange.js @@ -22,7 +22,7 @@ if ((vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW || vars.get("$sys.r neon.setFieldValues({ "$field.FIRSTNAME" : name[0] || "", "$field.LASTNAME" : name[1] || "", - "$field.EMAIL_ADDRESS" : name[2] || "", + "$field.EMAIL_ADDRESS" : name[2] || vars.get("$field.EMAIL_ADDRESS") || "", "$field.TITLE" : username }); } \ No newline at end of file diff --git a/entity/Employee_entity/entityfields/email_address/dropDownProcess.js b/entity/Employee_entity/entityfields/email_address/dropDownProcess.js index 99e97c2dddc0cae705cdc328a215b226a386f227..559a598b48b1f347bf21ee32e097eacc741b1083 100644 --- a/entity/Employee_entity/entityfields/email_address/dropDownProcess.js +++ b/entity/Employee_entity/entityfields/email_address/dropDownProcess.js @@ -6,7 +6,7 @@ import("system.result"); import("Sql_lib"); var contactId = vars.get("$field.CONTACT_ID"); -if (contactId && (vars.get("$sys.recordstate") == neon.OPERATINGSTATE_EDIT || vars.get("$sys.recordstate") == neon.OPERATINGSTATE_NEW)) +if (contactId) { var addresses = newSelect("ADDR, ADDR") .from("COMMUNICATION") diff --git a/entity/Notification_entity/Notification_entity.aod b/entity/Notification_entity/Notification_entity.aod index 3f08a4f669f0e68b3182f2d392daab2235930c23..50b11b29d038dfbffc77b87f3ba92e06474cc9d8 100644 --- a/entity/Notification_entity/Notification_entity.aod +++ b/entity/Notification_entity/Notification_entity.aod @@ -34,7 +34,7 @@ </entityField> <entityField> <name>CAPTION</name> - <title>title</title> + <title>Title</title> </entityField> <entityField> <name>CREATEDATE</name> @@ -45,7 +45,7 @@ </entityField> <entityField> <name>DESCRIPTION</name> - <title>description</title> + <title>Description</title> </entityField> <entityField> <name>FORCEDPRIORITY</name> @@ -124,7 +124,7 @@ </entityActionGroup> <entityField> <name>RESOLVEDPRIORITY</name> - <title>priority</title> + <title>Priority</title> <consumer>PrioKeywords</consumer> <textInputAllowed v="false" /> </entityField> diff --git a/entity/Offer_entity/Offer_entity.aod b/entity/Offer_entity/Offer_entity.aod index ef4dc76656ae23282b3d30e6b56db39c4c223e06..8d82f2247f9b980096ecc6f120a15f4711a76e6a 100644 --- a/entity/Offer_entity/Offer_entity.aod +++ b/entity/Offer_entity/Offer_entity.aod @@ -1023,6 +1023,7 @@ <entityAggregateField> <name>NET_aggregate</name> <parentField>NET</parentField> + <title>Sum</title> </entityAggregateField> <entityField> <name>OFFER_OBJECTTYPE</name> @@ -1038,6 +1039,15 @@ <parentField>COUNT</parentField> <title>Count</title> </entityAggregateField> + <entityAggregateField> + <name>PROBABILITY_aggregate</name> + <parentField>PROBABILITY</parentField> + <title>Ø Probability</title> + </entityAggregateField> + <entityProvider> + <name>OfferAggregates</name> + <useAggregates v="true" /> + </entityProvider> </entityFields> <recordContainers> <dbRecordContainer> @@ -1285,6 +1295,10 @@ <recordfield>OFFER.OFFER_ID</recordfield> <aggregateType>COUNT</aggregateType> </aggregateFieldDbMapping> + <aggregateFieldDbMapping> + <name>PROBABILITY_aggregate.value</name> + <expression>%aditoprj%/entity/Offer_entity/recordcontainers/db/recordfieldmappings/probability_aggregate.value/expression.js</expression> + </aggregateFieldDbMapping> </recordFieldMappings> <filterExtensions> <filterExtensionSet> diff --git a/entity/Offer_entity/recordcontainers/db/recordfieldmappings/probability_aggregate.value/expression.js b/entity/Offer_entity/recordcontainers/db/recordfieldmappings/probability_aggregate.value/expression.js new file mode 100644 index 0000000000000000000000000000000000000000..25f76f180180bfc8602cee9566751078ba280678 --- /dev/null +++ b/entity/Offer_entity/recordcontainers/db/recordfieldmappings/probability_aggregate.value/expression.js @@ -0,0 +1,6 @@ +import("system.SQLTYPES"); +import("system.result"); +import("Sql_lib"); + +var helper = new SqlMaskingUtils(); +result.string("AVG("+ helper.cast("OFFER.PROBABILITY", SQLTYPES.INTEGER) + ")"); \ No newline at end of file diff --git a/entity/Salesproject_entity/Salesproject_entity.aod b/entity/Salesproject_entity/Salesproject_entity.aod index 2c5b4e88d58f9275bb82fbb89150e513362331bf..5aee7c68558f7368b7a933c7d6390839203a5716 100644 --- a/entity/Salesproject_entity/Salesproject_entity.aod +++ b/entity/Salesproject_entity/Salesproject_entity.aod @@ -750,6 +750,13 @@ <iconId>VAADIN:PLAY</iconId> <stateProcess>%aditoprj%/entity/Salesproject_entity/entityfields/startworkflow/stateProcess.js</stateProcess> </entityActionField> + <entityField> + <name>PROBABILITY_AI</name> + <title>Probability AI</title> + <state>READONLY</state> + <stateProcess>%aditoprj%/entity/Salesproject_entity/entityfields/probability_ai/stateProcess.js</stateProcess> + <valueProcess>%aditoprj%/entity/Salesproject_entity/entityfields/probability_ai/valueProcess.js</valueProcess> + </entityField> <entityField> <name>SALESPROJECT_OBJECTTYPE</name> <valueProcess>%aditoprj%/entity/Salesproject_entity/entityfields/salesproject_objecttype/valueProcess.js</valueProcess> diff --git a/entity/Salesproject_entity/entityfields/probability_ai/stateProcess.js b/entity/Salesproject_entity/entityfields/probability_ai/stateProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..fd3f09b8d62446c4e3946a0ece4b428e7f8ca48b --- /dev/null +++ b/entity/Salesproject_entity/entityfields/probability_ai/stateProcess.js @@ -0,0 +1,7 @@ +import("system.result"); +import("system.project"); + +if(!JSON.parse(project.getPreferenceValue("custom.ai.salesprojectProbability", "false"))) +{ + result.string("INVISIBLE"); +} \ No newline at end of file diff --git a/entity/Salesproject_entity/entityfields/probability_ai/valueProcess.js b/entity/Salesproject_entity/entityfields/probability_ai/valueProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..844f60472a79f7551370dc0bf2ad0c3607eeed43 --- /dev/null +++ b/entity/Salesproject_entity/entityfields/probability_ai/valueProcess.js @@ -0,0 +1,16 @@ +import("system.vars"); +import("system.logging"); +import("system.result"); +import("AISalesproject_lib"); +import("system.project"); + +if(JSON.parse(project.getPreferenceValue("custom.ai.salesprojectProbability", "false"))) +{ + result.string(AISalesprojectUtil.classify(vars.getString("$field.SALESPROJECTID"), + vars.getString("$field.CONTACT_ID"), + vars.getString("$field.PHASE"), + vars.getString("$field.STATUS"), + vars.getString("$field.VOLUME"), + vars.getString("$field.PROBABILITY"))); + +} \ No newline at end of file diff --git a/entity/WorkflowStartConfig_entity/imageProcess.js b/entity/WorkflowStartConfig_entity/imageProcess.js index 34147b89bd271ec01be2bb3fa1c6eb055186963f..8b918cbc285c80f90e305b0b2aee6655949db65f 100644 --- a/entity/WorkflowStartConfig_entity/imageProcess.js +++ b/entity/WorkflowStartConfig_entity/imageProcess.js @@ -2,5 +2,5 @@ import("system.result"); import("system.vars"); import("system.project"); -var contextModel = project.getDataModel(project.DATAMODEL_KIND_CONTEXT, vars.get("$field.OBJECT_TYPE")); +var contextModel = vars.get("$field.OBJECT_TYPE") && project.getDataModel(project.DATAMODEL_KIND_CONTEXT, vars.get("$field.OBJECT_TYPE")); result.string(contextModel ? contextModel[5] : ""); \ No newline at end of file diff --git a/entity/WorkflowTask_entity/WorkflowTask_entity.aod b/entity/WorkflowTask_entity/WorkflowTask_entity.aod index 3ffbe846c77cc3e4d64c7ed734ff2399dc30450a..938fbcdc991e33b51be6bc7134499438ac1fbabd 100644 --- a/entity/WorkflowTask_entity/WorkflowTask_entity.aod +++ b/entity/WorkflowTask_entity/WorkflowTask_entity.aod @@ -31,6 +31,7 @@ </entityField> <entityField> <name>FORMRESULT</name> + <onValueChange>%aditoprj%/entity/WorkflowTask_entity/entityfields/formresult/onValueChange.js</onValueChange> </entityField> <entityField> <name>NAME</name> diff --git a/entity/WorkflowTask_entity/entityfields/formresult/onValueChange.js b/entity/WorkflowTask_entity/entityfields/formresult/onValueChange.js new file mode 100644 index 0000000000000000000000000000000000000000..249aba04bbb279e4e8ae4dc4f7164fbeee463fe5 --- /dev/null +++ b/entity/WorkflowTask_entity/entityfields/formresult/onValueChange.js @@ -0,0 +1,25 @@ +import("system.result"); +import("system.vars"); +import("system.workflow"); +import("system.neon"); + +var taskId = vars.get("$field.UID"); +var newResult = vars.get("$local.value"); +var oldResult = vars.get("$field.FORMRESULT"); + +if (newResult && newResult !== oldResult) +{ + /* + * fieldListeners = all fields that are used inside a visibility expression + * -> if one of these fields is changed, set the new FORMDEFINITION + */ + var fieldListeners = JSON.parse(workflow.getFormFieldListeners(taskId)); + newResult = newResult ? JSON.parse(newResult) : {}; + oldResult = oldResult ? JSON.parse(oldResult) : {}; + var isRefreshRequired = fieldListeners.some(function (fieldId) + { + return newResult[fieldId] !== oldResult[fieldId]; + }); + if (isRefreshRequired) + neon.setFieldValue("$field.FORMDEFINITION", workflow.getFormProperties(taskId, newResult)); +} diff --git a/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod b/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod index d339195f21f074660303e10b1a80ce278c3bb123..fdd95ea3c05aaba3655b1a0c2acf0b42faf21c30 100644 --- a/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod +++ b/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod @@ -6872,9 +6872,24 @@ <entry> <key>Workflow Model</key> </entry> + <entry> + <key>{SENT_MAIL}</key> + </entry> <entry> <key>The Sales Project can only be filled when a company has been specified</key> </entry> + <entry> + <key>The workflow could not be deployed</key> + </entry> + <entry> + <key>Workflow deploy failed</key> + </entry> + <entry> + <key>Ø Probability</key> + </entry> + <entry> + <key>Probability AI</key> + </entry> </keyValueMap> <font name="Dialog" style="0" size="11" /> <sqlModels> diff --git a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod index a132152c5028d6e84f533b84d53bb64e431762fa..a073f74c706b90066d7b2ba6a1dbe829150d7dbf 100644 --- a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod +++ b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod @@ -6,6 +6,10 @@ <country></country> <variant></variant> <keyValueMap> + <entry> + <key>Probability AI</key> + <value>Wahrscheinlichkeit KI</value> + </entry> <entry> <key>Settings</key> <value>Einstellungen</value> @@ -32,6 +36,7 @@ </entry> <entry> <key>Finished</key> + <value>Abgeschlossen</value> </entry> <entry> <key>Show my organisations</key> @@ -3529,6 +3534,10 @@ <key>Gambia</key> <value>Gambia</value> </entry> + <entry> + <key>Ø Probability</key> + <value>Ø Wahrscheinlichkeit</value> + </entry> <entry> <key>Qatar</key> <value>Katar</value> @@ -8743,15 +8752,12 @@ Bitte Datumseingabe prüfen</value> </entry> <entry> <key>leadimport notification</key> - <value>Leadimport Benachrichtigung</value> </entry> <entry> <key>bulk mail sent</key> - <value>Serienmail versendet</value> </entry> <entry> <key>download ready</key> - <value>Download bereit</value> </entry> <entry> <key>No new recipients found that can be added to the bulk mail.</key> @@ -8761,19 +8767,10 @@ Bitte Datumseingabe prüfen</value> <key>Permission received</key> <value>erhaltene Berechtigung</value> </entry> - <entry> - <key>leadimport notification</key> - </entry> <entry> <key>granted permission</key> <value>vergebene Berechtigung</value> </entry> - <entry> - <key>bulk mail sent</key> - </entry> - <entry> - <key>download ready</key> - </entry> <entry> <key>Receive new Department Permission</key> <value>Neue Abteilungs-Berechtigung erhalten</value> @@ -8789,10 +8786,6 @@ Bitte Datumseingabe prüfen</value> <key>No new recipients found that can be added to the serial letter.</key> <value>Keine neuen Empfänger, die zum Serienbrief werden können, gefunden.</value> </entry> - <entry> - <key>No new recipients found that can be added to the bulk mail.</key> - <value>Keine neuen Empfänger, die zur Serienmail hinzugefügt werden können, gefunden.</value> - </entry> <entry> <key>export using the selected template</key> <value>Mit der ausgewählten Vorlage Exportieren</value> @@ -8807,6 +8800,16 @@ Bitte Datumseingabe prüfen</value> <entry> <key>Workflow Model</key> </entry> + <entry> + <key>{SEND_MAIL}</key> + <value>Email versenden</value> + </entry> + <entry> + <key>The workflow could not be deployed</key> + </entry> + <entry> + <key>Workflow deploy failed</key> + </entry> </keyValueMap> <font name="Dialog" style="0" size="11" /> </language> diff --git a/language/_____LANGUAGE_en/_____LANGUAGE_en.aod b/language/_____LANGUAGE_en/_____LANGUAGE_en.aod index dd32b2a7b3caf16d762271d43e41d228628bf79d..e3bcf535ebd4754b2d7709f21d52dba8b8f7a216 100644 --- a/language/_____LANGUAGE_en/_____LANGUAGE_en.aod +++ b/language/_____LANGUAGE_en/_____LANGUAGE_en.aod @@ -2661,6 +2661,9 @@ <entry> <key>Locked</key> </entry> + <entry> + <key>Ø Probability</key> + </entry> <entry> <key>Timetracking Id</key> </entry> @@ -6941,6 +6944,19 @@ <entry> <key>The Sales Project can only be filled when a company has been specified</key> </entry> + <entry> + <key>{SEND_MAIL}</key> + <value>Send mail</value> + </entry> + <entry> + <key>The workflow could not be deployed</key> + </entry> + <entry> + <key>Workflow deploy failed</key> + </entry> + <entry> + <key>Probability AI</key> + </entry> </keyValueMap> <font name="Dialog" style="0" size="11" /> </language> diff --git a/neonContext/Offer/Offer.aod b/neonContext/Offer/Offer.aod index 2a823d8bf9e71c15d73fdddddfde09bb4b38834d..e8a6d8d34e56933b89b40bd124432ff6e1a89d0c 100644 --- a/neonContext/Offer/Offer.aod +++ b/neonContext/Offer/Offer.aod @@ -8,6 +8,7 @@ <filterView>OfferFilter_view</filterView> <editView>OfferEdit_view</editView> <previewView>OfferPreview_view</previewView> + <previewMultipleView>OfferPreviewMultiple_view</previewMultipleView> <lookupView>OfferFilter_view</lookupView> <entity>Offer_entity</entity> <references> @@ -39,5 +40,9 @@ <name>02938f44-bc24-4542-916b-8db5d1976b40</name> <view>OfferReport_view</view> </neonViewReference> + <neonViewReference> + <name>136dceaa-0eca-452a-9757-132fd54e8c55</name> + <view>OfferPreviewMultiple_view</view> + </neonViewReference> </references> </neonContext> diff --git a/neonView/OfferPreviewMultiple_view/OfferPreviewMultiple_view.aod b/neonView/OfferPreviewMultiple_view/OfferPreviewMultiple_view.aod new file mode 100644 index 0000000000000000000000000000000000000000..d9949333d93de78c8373d7cbe77aa29d49b867d6 --- /dev/null +++ b/neonView/OfferPreviewMultiple_view/OfferPreviewMultiple_view.aod @@ -0,0 +1,55 @@ +<?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.6" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.6"> + <name>OfferPreviewMultiple_view</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <layout> + <headerFooterLayout> + <name>layout</name> + <footer>Scorecard</footer> + </headerFooterLayout> + </layout> + <children> + <dynamicMultiDataChartViewTemplate> + <name>OfferChart</name> + <chartType>BAR</chartType> + <shareParent v="false" /> + <entityField>#ENTITY</entityField> + <columns> + <neonDynamicMultiDataChartColumn> + <name>53b01786-bb5d-41f2-8c30-36bff460b82b</name> + <entityField>NET</entityField> + </neonDynamicMultiDataChartColumn> + </columns> + </dynamicMultiDataChartViewTemplate> + <treeTableViewTemplate> + <name>StatusTreeTable</name> + <defaultGroupFields> + <element>STATUS</element> + </defaultGroupFields> + <hideActions v="true" /> + <entityField>#ENTITY</entityField> + <linkedColumns /> + <columns> + <neonTreeTableColumn> + <name>5cd9839b-0dea-44e8-9e2f-8f75a7e9cb16</name> + <entityField>NET</entityField> + <aggregateEntityField>NET_aggregate</aggregateEntityField> + </neonTreeTableColumn> + </columns> + </treeTableViewTemplate> + <scoreCardViewTemplate> + <name>AggregatedValues</name> + <entityField>OfferAggregates</entityField> + <fields> + <entityFieldLink> + <name>71cd7dd5-c142-4b4c-8a6b-ca5ed8acf92e</name> + <entityField>NET_aggregate</entityField> + </entityFieldLink> + <entityFieldLink> + <name>e76a8a8c-529a-457f-bb23-4dcef79f6f0f</name> + <entityField>PROBABILITY_aggregate</entityField> + </entityFieldLink> + </fields> + </scoreCardViewTemplate> + </children> +</neonView> diff --git a/neonView/OrderDetail_view/OrderDetail_view.aod b/neonView/OrderDetail_view/OrderDetail_view.aod index 9e876a6e5cb22b37ad233e3c14ab8c25073b2169..6e749e3cfbd8e9a0e2e9b348b4c8186d45aae1ec 100644 --- a/neonView/OrderDetail_view/OrderDetail_view.aod +++ b/neonView/OrderDetail_view/OrderDetail_view.aod @@ -1,7 +1,7 @@ <?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.6" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/neonView/1.1.6"> <name>OrderDetail_view</name> - <title>Texts</title> + <title>Cover letter</title> <majorModelMode>DISTRIBUTED</majorModelMode> <layout> <noneLayout> diff --git a/neonView/OrderMain_view/OrderMain_view.aod b/neonView/OrderMain_view/OrderMain_view.aod index b76b54dcaaff68957fa6d0c26f48d5a57a2f9c02..428d0c02e07af523318e7f24d35f87230edab64f 100644 --- a/neonView/OrderMain_view/OrderMain_view.aod +++ b/neonView/OrderMain_view/OrderMain_view.aod @@ -29,16 +29,16 @@ <entityField>#ENTITY</entityField> <view>OrderDetail_view</view> </neonViewReference> - <neonViewReference> - <name>b3014999-da86-45ae-86ae-feb738d62906</name> - <entityField>Documents</entityField> - <view>DocumentFilter_view</view> - </neonViewReference> <neonViewReference> <name>7ec102f5-fb45-44a7-9bbf-0eba7f1536d7</name> <entityField>Activities</entityField> <view>ActivityFilter_view</view> </neonViewReference> + <neonViewReference> + <name>b3014999-da86-45ae-86ae-feb738d62906</name> + <entityField>Documents</entityField> + <view>DocumentFilter_view</view> + </neonViewReference> <neonViewReference> <name>351a9b5d-a050-4fb9-b3e4-402dcd84b331</name> <entityField>Tasks</entityField> diff --git a/neonView/OrderitemFilter_view/OrderitemFilter_view.aod b/neonView/OrderitemFilter_view/OrderitemFilter_view.aod index 5879614ec37bdf2d2ec54e12e0dbf57a17d05042..4c6e8a7eac674bd309fbe4aefe1f9bda7c40bba0 100644 --- a/neonView/OrderitemFilter_view/OrderitemFilter_view.aod +++ b/neonView/OrderitemFilter_view/OrderitemFilter_view.aod @@ -31,6 +31,10 @@ <name>a8dcb2bb-bb09-4a6e-b0f0-1b4d1111eb22</name> <entityField>UNIT</entityField> </neonTableColumn> + <neonTableColumn> + <name>bfd34c94-1a5f-4caf-ae6e-2c09375c023c</name> + <entityField>VAT</entityField> + </neonTableColumn> <neonTableColumn> <name>33ef0703-d4ea-4187-b555-648a1733ee99</name> <entityField>PRICE</entityField> @@ -39,22 +43,18 @@ <name>61a39c6b-f7dd-487c-bc5f-c2e95376ec37</name> <entityField>DISCOUNT</entityField> </neonTableColumn> - <neonTableColumn> - <name>bfd34c94-1a5f-4caf-ae6e-2c09375c023c</name> - <entityField>VAT</entityField> - </neonTableColumn> <neonTableColumn> <name>89fd18d0-f6ee-4323-9277-464dee6da625</name> <entityField>OPTIONAL</entityField> </neonTableColumn> - <neonTableColumn> - <name>03a15cab-67d9-4e9d-b911-0d5599c87671</name> - <entityField>INFO</entityField> - </neonTableColumn> <neonTableColumn> <name>eecc066d-e380-4fe7-9e9b-99d80842981d</name> <entityField>TotalPrice</entityField> </neonTableColumn> + <neonTableColumn> + <name>03a15cab-67d9-4e9d-b911-0d5599c87671</name> + <entityField>INFO</entityField> + </neonTableColumn> </columns> </tableViewTemplate> <treeTableViewTemplate> @@ -77,6 +77,10 @@ <name>4998fc65-67b7-465d-9891-808dcf0fe080</name> <entityField>UNIT</entityField> </neonTreeTableColumn> + <neonTreeTableColumn> + <name>e545cb87-e4a5-4154-89b1-1f88a6d59fde</name> + <entityField>VAT</entityField> + </neonTreeTableColumn> <neonTreeTableColumn> <name>b4470ca6-89e8-421b-bb91-7d32f9c48aa2</name> <entityField>PRICE</entityField> @@ -85,22 +89,18 @@ <name>0c533079-4b39-4412-8de5-086bf7a08706</name> <entityField>DISCOUNT</entityField> </neonTreeTableColumn> - <neonTreeTableColumn> - <name>e545cb87-e4a5-4154-89b1-1f88a6d59fde</name> - <entityField>VAT</entityField> - </neonTreeTableColumn> <neonTreeTableColumn> <name>9ac44050-ea77-43d2-b0fe-f7ca411b91e7</name> <entityField>OPTIONAL</entityField> </neonTreeTableColumn> - <neonTreeTableColumn> - <name>4d4f204a-1c2b-4587-93b9-df03f31da38e</name> - <entityField>INFO</entityField> - </neonTreeTableColumn> <neonTreeTableColumn> <name>c44b5bc2-4283-4dca-bd20-bd048e05fe45</name> <entityField>TotalPrice</entityField> </neonTreeTableColumn> + <neonTreeTableColumn> + <name>4d4f204a-1c2b-4587-93b9-df03f31da38e</name> + <entityField>INFO</entityField> + </neonTreeTableColumn> </columns> </treeTableViewTemplate> </children> diff --git a/neonView/SalesprojectFilter_view/SalesprojectFilter_view.aod b/neonView/SalesprojectFilter_view/SalesprojectFilter_view.aod index 6bfac93548e2b901c3d7952818fa6d21bb0c987a..de645d22f05545f6870869c4347a29066cb23d4c 100644 --- a/neonView/SalesprojectFilter_view/SalesprojectFilter_view.aod +++ b/neonView/SalesprojectFilter_view/SalesprojectFilter_view.aod @@ -77,6 +77,10 @@ <name>9f6b967e-5140-420f-84ca-2273920221bd</name> <entityField>PROJECTTITLE</entityField> </neonTableColumn> + <neonTableColumn> + <name>20f132ef-161e-4b84-b6ae-1f4daf016d16</name> + <entityField>PROBABILITY_AI</entityField> + </neonTableColumn> <neonTableColumn> <name>fec843c3-f7c0-42c7-8295-50386651edb2</name> <entityField>STARTDATE</entityField> diff --git a/neonView/SalesprojectPreview_view/SalesprojectPreview_view.aod b/neonView/SalesprojectPreview_view/SalesprojectPreview_view.aod index da9674dd3bde6002522eb4afdc065c6293b21a02..dbf8adad5727fdd82cff1371702bd42bd82a794e 100644 --- a/neonView/SalesprojectPreview_view/SalesprojectPreview_view.aod +++ b/neonView/SalesprojectPreview_view/SalesprojectPreview_view.aod @@ -65,6 +65,10 @@ <name>0ba7dcb5-9606-4d74-8455-3423a16fd98a</name> <entityField>PROBABILITY</entityField> </entityFieldLink> + <entityFieldLink> + <name>bee2acfb-20ac-485e-be6c-c9c6a25e6013</name> + <entityField>PROBABILITY_AI</entityField> + </entityFieldLink> <entityFieldLink> <name>950d21a3-c0f9-4df5-9810-fa027a6fdb4a</name> <entityField>VOLUME</entityField> diff --git a/neonView/WorkflowStartConfigFilter_view/WorkflowStartConfigFilter_view.aod b/neonView/WorkflowStartConfigFilter_view/WorkflowStartConfigFilter_view.aod index 9c98d1fe51548d2c24a3f1e415cf83a1de05e69d..cdac1faccf7f16acf27044fd57f434dea9502e3c 100644 --- a/neonView/WorkflowStartConfigFilter_view/WorkflowStartConfigFilter_view.aod +++ b/neonView/WorkflowStartConfigFilter_view/WorkflowStartConfigFilter_view.aod @@ -3,9 +3,9 @@ <name>WorkflowStartConfigFilter_view</name> <majorModelMode>DISTRIBUTED</majorModelMode> <layout> - <groupLayout> + <boxLayout> <name>layout</name> - </groupLayout> + </boxLayout> </layout> <children> <tableViewTemplate> @@ -24,19 +24,5 @@ </neonTableColumn> </columns> </tableViewTemplate> - <treeTableViewTemplate> - <name>Treetable</name> - <entityField>#ENTITY</entityField> - <columns> - <neonTreeTableColumn> - <name>4874074f-80af-4e99-8449-2a4dc4428360</name> - <entityField>OBJECT_TYPE</entityField> - </neonTreeTableColumn> - <neonTreeTableColumn> - <name>d479a082-ea95-40c6-81a2-9318fcf9ce98</name> - <entityField>TRIGGER_EVENT</entityField> - </neonTreeTableColumn> - </columns> - </treeTableViewTemplate> </children> </neonView> diff --git a/preferences/_____PREFERENCES_PROJECT/_____PREFERENCES_PROJECT.aod b/preferences/_____PREFERENCES_PROJECT/_____PREFERENCES_PROJECT.aod index 2c486ae8a6b6c5f2b17883cfe535de5f164d468e..5a3ae3b71b41c6205c08d1b791638ac250219ccc 100644 --- a/preferences/_____PREFERENCES_PROJECT/_____PREFERENCES_PROJECT.aod +++ b/preferences/_____PREFERENCES_PROJECT/_____PREFERENCES_PROJECT.aod @@ -102,6 +102,11 @@ <description></description> <property>http://localhost:8080/flowable-modeler/</property> </customStringProperty> + <customBooleanProperty> + <name>ai.salesprojectProbability</name> + <description></description> + <property v="false" /> + </customBooleanProperty> </customProperties> <customConfigProperties> <customBooleanProperty> diff --git a/process/AISalesproject_lib/AISalesproject_lib.aod b/process/AISalesproject_lib/AISalesproject_lib.aod new file mode 100644 index 0000000000000000000000000000000000000000..13b8a275db57d71b190f905695f39072a7756756 --- /dev/null +++ b/process/AISalesproject_lib/AISalesproject_lib.aod @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.1"> + <name>AISalesproject_lib</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/AISalesproject_lib/process.js</process> + <alias>Data_alias</alias> + <variants> + <element>LIBRARY</element> + </variants> +</process> diff --git a/process/AISalesproject_lib/process.js b/process/AISalesproject_lib/process.js new file mode 100644 index 0000000000000000000000000000000000000000..c1a63392f2bfc2786c2e3e79c4f38146e11f990d --- /dev/null +++ b/process/AISalesproject_lib/process.js @@ -0,0 +1,210 @@ +import("AttributeRegistry_basic"); +import("KeywordRegistry_basic"); +import("Sql_lib"); +import("system.logging"); +import("system.db"); +import("system.result"); +import("system.vars"); +import("Keyword_lib"); +import("AI_lib"); +import("AISalesproject_lib"); +import("Attribute_lib"); +import("system.entities"); +import("DataCaching_lib"); + +/** + * Provides static methods for artificial intelligence.<br> + * <b>Do not create an instance of this!</b> + * + * @class + */ +function AISalesprojectUtil(){} + + +AISalesprojectUtil.getTrainedModel = function() +{ + var cache = new CachedData("SalesprojectNBData", false); + return cache.load(function (pTranslationNecessary, pLocale){ + var data = AISalesprojectUtil.train(); + return data; + }); +}; + + +AISalesprojectUtil.train = function () +{ + //Trainingdata + var data = new NBDataSet(); + + var loadConfig = entities.createConfigForLoadingRows() + .entity("Salesproject_entity") + .fields(["#UID", "CONTACT_ID", "PHASE", "STATUS", "VOLUME", "PROBABILITY"]); + + var spRecords = entities.getRows(loadConfig).map(function (spRow) + { + if(spRow["STATUS"] == $KeywordRegistry.salesprojectState$lost() || spRow["STATUS"] == $KeywordRegistry.salesprojectState$order()) + { + var attributes = []; + attributes.push(spRow["PHASE"]) //PHASE + if(spRow["VOLUME"] != null && parseFloat(spRow["VOLUME"]) > 0) + attributes.push(AISalesprojectUtil.getVolumeClassification(parseFloat(spRow["VOLUME"]))); //VOLUME + if(spRow["PROBABILITY"] != null) + attributes.push(AISalesprojectUtil.getProbabilityValue(spRow["PROBABILITY"])); //PROBABILITY + var sectorSP = new AttributeRelationQuery(spRow["CONTACT_ID"], $AttributeRegistry.industry()).getSingleAttributeValue(); + if(sectorSP != null) + attributes.push(sectorSP); //Sector + var doc = new NBDocument(spRow["#UID"], attributes); + + //Classification + + //Department + var department = AISalesprojectUtil.getClassificationValues("SALESPROJ", "Salesproject", spRow["#UID"], "ScoreDepartment"); + if(department) + attributes.push(department); + + //Standard + var standard = AISalesprojectUtil.getClassificationValues("SALESPROJ", "Salesproject", spRow["#UID"], "ScoreStandard"); + if(standard) + attributes.push(standard); + + //Users + var users = AISalesprojectUtil.getClassificationValues("SALESPROJ", "Salesproject", spRow["#UID"], "ScoreUsers"); + if(users) + attributes.push(users); + + + var loadCompConfig = entities.createConfigForLoadingRows() + .entity("Competition_entity") + .provider("Links") + .addParameter("ObjectType_param", "Salesproject") + .addParameter("ObjectRowId_param", spRow["#UID"]) + .fields(["ORGANISATION_NAME"]); + + var compRecords = entities.getRows(loadCompConfig).map(function (compRow) + { + doc.add(compRow["ORGANISATION_NAME"]); //competitors + }); + + data.add(spRow["STATUS"], [doc]); // train model with lost and order salesprojects + //logging.log("data:"); + //logging.log(JSON.stringify(doc)); + } + + }); + + // an optimisation for working with small vocabularies + var options = { + applyInverse: true + }; + + // create a classifier + var classifier = new NBClassifier(options); + + // train the classifier + classifier.train(data); + + //logging.log('Classifier trained.'); + //logging.log(JSON.stringify(classifier)); + return JSON.stringify(classifier); + +}; + +AISalesprojectUtil.classify = function (pSalesprojectId, pContactId, pPhase, pStatus, pVolume, pProb) +{ + + if(pSalesprojectId == null || pContactId == null) + return "--"; + var testAttributes = []; + testAttributes.push(pPhase) //PHASE + if(pVolume != null && parseFloat(pVolume) > 0) + testAttributes.push(AISalesprojectUtil.getVolumeClassification(parseFloat(pVolume))); //VOLUME + if(pProb != null) + testAttributes.push(AISalesprojectUtil.getProbabilityValue(pProb)); //PROBABILITY + if(pContactId != null) + { + var sectorSP = new AttributeRelationQuery(pContactId, $AttributeRegistry.industry()).getSingleAttributeValue(); + if(sectorSP != null) + testAttributes.push(sectorSP); //Sector + } + + //Classification + + //Department + var department = AISalesprojectUtil.getClassificationValues("SALESPROJ", "Salesproject", pSalesprojectId, "ScoreDepartment"); + if(department) + testAttributes.push(department); + + //Standard + var standard = AISalesprojectUtil.getClassificationValues("SALESPROJ", "Salesproject", pSalesprojectId, "ScoreStandard"); + if(standard) + testAttributes.push(standard); + + //Users + var users = AISalesprojectUtil.getClassificationValues("SALESPROJ", "Salesproject", pSalesprojectId, "ScoreUsers"); + if(users) + testAttributes.push(users); + + var loadCompConfig = entities.createConfigForLoadingRows() + .entity("Competition_entity") + .provider("Links") + .addParameter("ObjectType_param", "Salesproject") + .addParameter("ObjectRowId_param", pSalesprojectId) + .fields(["ORGANISATION_NAME"]); + + var compRecords = entities.getRows(loadCompConfig).map(function (compRow) + { + testAttributes.push(compRow["ORGANISATION_NAME"]); //competitors + }); + + //logging.log("testdata"); + //logging.log(JSON.stringify(testAttributes)); + + var model = AISalesprojectUtil.getTrainedModel(); + var classifier = new NBClassifier(JSON.parse(model)); + + // test the classifier on a new test object + var testDoc = new NBDocument(pSalesprojectId, testAttributes); + var classifyResult = classifier.classify(testDoc); + //logging.log("result"); + //logging.log(JSON.stringify(classifyResult)); + if(classifyResult.probability == null || isNaN(classifyResult.probability)) + return "--"; + else + return Math.round(parseFloat((classifyResult.probability) * 100)) + "% / " + KeywordUtils.getViewValue($KeywordRegistry.salesprojectState(), classifyResult.category); +}; + +AISalesprojectUtil.getVolumeClassification = function (pVolume) +{ + if(pVolume < 100000) + return "low"; + else if(pVolume >= 100000 && pVolume < 250000) + return "middle"; + else + return "high"; +}; + +AISalesprojectUtil.getProbabilityValue = function (pProbId) +{ + if(pProbId == "SALPROJPROB0" || pProbId == "SALPROJPROB25") + return "negative"; + else if(pProbId == "SALPROJPROB50") + return "neutral"; + else if(pProbId == "SALPROJPROB75" || pProbId == "SALPROJPROB100") + return "positive"; + return ""; +}; + +AISalesprojectUtil.getClassificationValues = function (pClassificationType, pObjectType, pObjectRowid, pScoreType) +{ + var value = newSelect("TITLE") + .from("CLASSIFICATIONTYPE") + .leftJoin("CLASSIFICATION", newWhere("CLASSIFICATIONTYPEID = CLASSIFICATIONTYPE_ID") + .and("CLASSIFICATION.OBJECT_TYPE", pObjectType) + .and("CLASSIFICATION.OBJECT_ROWID", pObjectRowid)) + .leftJoin("CLASSIFICATIONSCORE", "CLASSIFICATIONSCORE_ID = CLASSIFICATIONSCOREID") + .where("CLASSIFICATIONTYPE.CLASSIFICATIONTYPE", pClassificationType) + .andIfSet("CLASSIFICATIONTYPE.SCORETYPE", pScoreType) + .cell() + + return value; +}; diff --git a/process/AI_lib/AI_lib.aod b/process/AI_lib/AI_lib.aod new file mode 100644 index 0000000000000000000000000000000000000000..153e8fc6b66bfd5d8016c8a0a22521c04ad0e209 --- /dev/null +++ b/process/AI_lib/AI_lib.aod @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.1" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.1"> + <name>AI_lib</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/AI_lib/process.js</process> + <variants> + <element>LIBRARY</element> + </variants> +</process> diff --git a/process/AI_lib/process.js b/process/AI_lib/process.js new file mode 100644 index 0000000000000000000000000000000000000000..b0f8c0f06354aee7dd7718887cafa97f7f92b2d6 --- /dev/null +++ b/process/AI_lib/process.js @@ -0,0 +1,219 @@ +import("system.logging"); +import("system.vars"); +import("AI_lib"); + +/** + * Provides static methods for artificial intelligence.<br> + * <b>Do not create an instance of this!</b> + * + * @class + */ +function AIUtil(){} + + +function NBClassifier(options) { + options = options || {}; + this.applyInverse = options.applyInverse || false; + this.probabilityThreshold = options.probabilityThreshold || 0.5; + this.defaultCategory = options.defaultCategory || null; + this.tokens = options.tokens || {}; + this.categoryCounts = options.categoryCounts || {}; + this.probabilities = options.probabilities || {}; +} + + +NBClassifier.prototype = { + train: function (trainingSet) { + var categories = Object.keys(trainingSet.categorizedItems); + var i = 0, j = 0, k = 0, category = ""; + // Iterate over each category in the training set + for (i = 0; i < categories.length; i++) { + category = categories[i]; + var subSet = trainingSet.categorizedItems[category]; + this.categoryCounts[category] = subSet.length; + // Iterate over each data item in the category + for (j = 0; j < subSet.length; j++) { + var item = subSet[j]; + // for each token in the data item, increment the token:category counter + var tokenlist = item.tokens; + for (k = 0; k < tokenlist.length; k++) { + var token = tokenlist[k]; + if (!this.tokens[token]) { + this.tokens[token] = {}; + } + if (!this.tokens[token][category]) { + this.tokens[token][category] = 1; + } else { + this.tokens[token][category] = 1 + this.tokens[token][category]; + } + } + } + } + //After counting occurences of tokens, calculate probabilities. + for (i = 0; i < categories.length; i++) { + category = categories[i]; + for (k in this.tokens) { + if (this.tokens.hasOwnProperty(k)) { + var count = this.tokens[k][category] || 0; + var total = this.categoryCounts[category]; + var percentage = count / total; + if (!this.probabilities[category]) { + this.probabilities[category] = {}; + } + this.probabilities[category][k] = percentage; + } + } + } + }, + + validate: function (testSet) { + var total = 0; + var correctGuesses = 0; + var wrongGuesses = 0; + var wrongCategories = {}; + var wrongItems = []; + var categories = testSet.categorizedItems; + var category; + for (category in categories) { + validateCategory(category); + } + + function validateCategory(category) { + if (categories.hasOwnProperty(category)) { + var items = categories[category]; + var item; + for (item in items) { + if (items.hasOwnProperty(item)) { + total += 1; + item = items[item]; + var result1 = this.classify(item); + // if certainty is below probabilityThreshold, go with the default + if (result1.probability <= this.probabilityThreshold) { + result1.category = this.defaultCategory || result1.category; + } + if (result1.category === category) { + correctGuesses++; + } else { + wrongCategories[result1.category] = (wrongCategories[result1.category]) ? wrongCategories[result1.category]++ : 1; + wrongItems.push(item.id); + wrongGuesses++; + } + } + } + } + } + + return { + 'total': total, + 'correct': correctGuesses, + 'wrong': wrongGuesses, + 'accuracy': (correctGuesses / (correctGuesses + wrongGuesses)), + 'wrongCategories': wrongCategories, + 'wrongItems': wrongItems + }; + }, + + classify: function (item) { + // for each category + var category; + var learnedProbabilities = this.probabilities; + var itemProbabilities = {}; + var itemTokens = item.tokens; + for (category in learnedProbabilities) { + if (learnedProbabilities.hasOwnProperty(category)) { + itemProbabilities[category] = 1; + var t; + var probs = learnedProbabilities[category]; + for (t in probs) { + // iterate over the tokens + if (probs.hasOwnProperty(t)) { + // and take the product of all probabilities + if (itemTokens.indexOf(t) !== -1) { + itemProbabilities[category] = itemProbabilities[category] * probs[t]; + } else if (this.applyInverse) { + itemProbabilities[category] = itemProbabilities[category] * (1 - probs[t]); + } + } + } + } + } + + // Pick the highest two probabilities + function compareCategories(a, b) { + if (a.probability > b.probability) { + return -1; + } + if (a.probability < b.probability) { + return 1; + } + return 0; + } + + var categoryScores = []; + var sumOfProbabilities = 0; + var k; + for (k in itemProbabilities) { + if (itemProbabilities.hasOwnProperty(k)) { + categoryScores.push({ + category: k, + probability: itemProbabilities[k] + }); + sumOfProbabilities += itemProbabilities[k]; + } + } + categoryScores = categoryScores.sort(compareCategories); + + var firstPlace = categoryScores[0]; + var secondPlace = categoryScores[1]; + var timesMoreLikely = firstPlace.probability / secondPlace.probability; + var probability = firstPlace.probability / sumOfProbabilities; + + return ({ + 'category': firstPlace.category, + 'probability': probability, + 'timesMoreLikely': timesMoreLikely, + 'secondCategory': secondPlace.category, + 'probabilities': categoryScores + }); + } +}; + + +function NBDataSet() { + this.categorizedItems = {}; +} + +NBDataSet.prototype = { + add: function (label, items) { + var originalItems = this.categorizedItems[label] || []; + this.categorizedItems[label] = originalItems.concat(items); + } +}; + + + +function NBDocument(id, tokens) { + if (!id) { + logging.log('Document(id, tokens) requires an id string'); + } + this.id = id; + this.tokens = tokens || []; +} + +NBDocument.prototype = { + add: function (token, factor) { + if(factor == undefined) + factor = 1 + if (Array.isArray(token)) { // array of tokens + for (var i = 0; i < token.length; i++) { + this.add(token[i], factor); + } + return; + } + if (typeof token === 'string') { + for (var j = 0; j < factor; j++) { + this.tokens.push(token); + } + } + } +}; \ No newline at end of file diff --git a/process/AttributeRegistry_basic/process.js b/process/AttributeRegistry_basic/process.js index cacb5a18c5652984a98533ed93ff819bc030ed6e..0d5b68b1c70a1889289312420d1b8902818b0866 100644 --- a/process/AttributeRegistry_basic/process.js +++ b/process/AttributeRegistry_basic/process.js @@ -18,6 +18,7 @@ $AttributeRegistry.targetGroup$competitior = function(){return "1d30d0ab-6103-49 $AttributeRegistry.departments = function(){return "87d4ff5b-0ab6-4534-be26-76c6ef486072";}; $AttributeRegistry.salesprojectType = function(){return "fd3963bc-8e60-411a-9911-b97eb73e5cf7";}; $AttributeRegistry.responsibleADsupervisor = function(){return "c0b26482-c0aa-413d-a9c3-f44c56bd04a9";}; +$AttributeRegistry.industry = function(){return "7e9927a4-41e4-426f-bddd-c3e9ee3b093e";}; $AttributeRegistry.visitPlanFrequency = function(){return "547b8b9d-88ba-4590-9e01-34d2a58116cc";}; $AttributeRegistry.visitPlanFrequency$monthly = function(){return "8c100817-1d2b-4fc7-8fdd-fd0370e19385";}; diff --git a/process/KeywordRegistry_basic/process.js b/process/KeywordRegistry_basic/process.js index b4d74a2a8f0cb51a439f5c8991d3d1e54a77602b..04e469c42da0bb82885071aec0350704dc6d59bc 100644 --- a/process/KeywordRegistry_basic/process.js +++ b/process/KeywordRegistry_basic/process.js @@ -21,6 +21,11 @@ $KeywordRegistry._autoPad = function(pKey){return (pKey + " $KeywordRegistry.attributeType = function(){return "AttributeType";}; $KeywordRegistry.keywordAttributeType = function(){return "KeywordAttributeType";}; +$KeywordRegistry.keywordAttributeType$char = function(){return "CHAR";}; +$KeywordRegistry.keywordAttributeType$number = function(){return "NUMBER";}; +$KeywordRegistry.keywordAttributeType$bool = function(){return "BOOLEAN";}; +$KeywordRegistry.keywordAttributeType$longChar = function(){return "LONGCHAR";}; + $KeywordRegistry.contractPayment = function(){return "ContractPayment";}; $KeywordRegistry.contractStatus = function(){return "ContractStatus";};