diff --git a/.liquibase/Data_alias/basic/2020.1.0/make_CommunicationMediumKeyword_isEssential.xml b/.liquibase/Data_alias/basic/2020.1.0/make_CommunicationMediumKeyword_isEssential.xml new file mode 100644 index 0000000000000000000000000000000000000000..116ac52021bc0de7559c63f7671fb9fd17641afe --- /dev/null +++ b/.liquibase/Data_alias/basic/2020.1.0/make_CommunicationMediumKeyword_isEssential.xml @@ -0,0 +1,10 @@ +<?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="d.tran" id="259351e7-a888-4744-a844-2be0a16693c8"> + <update tableName="AB_KEYWORD_ENTRY"> + <column name="ISESSENTIAL" valueNumeric="1"/> + <where>CONTAINER = 'CommunicationMedium'</where> + </update> + </changeSet> +</databaseChangeLog> diff --git a/entity/QuickEntry_entity/QuickEntry_entity.aod b/entity/QuickEntry_entity/QuickEntry_entity.aod index 5a068896ab8baf86702e15f8537324b65f2352b7..9a64999796e7dcca66a82c70a6cc9a5560b6ce9f 100644 --- a/entity/QuickEntry_entity/QuickEntry_entity.aod +++ b/entity/QuickEntry_entity/QuickEntry_entity.aod @@ -211,6 +211,13 @@ </entityParameter> </children> </entityConsumer> + <entityField> + <name>leadQuickAcquisition</name> + <title>{$QUICK_ENTRY_LEAD_QUICK_ACQUISITION}</title> + <contentType>FILE</contentType> + <stateProcess>%aditoprj%/entity/QuickEntry_entity/entityfields/leadquickacquisition/stateProcess.js</stateProcess> + <onValueChange>%aditoprj%/entity/QuickEntry_entity/entityfields/leadquickacquisition/onValueChange.js</onValueChange> + </entityField> </entityFields> <recordContainers> <jDitoRecordContainer> diff --git a/entity/QuickEntry_entity/entityfields/leadquickacquisition/onValueChange.js b/entity/QuickEntry_entity/entityfields/leadquickacquisition/onValueChange.js new file mode 100644 index 0000000000000000000000000000000000000000..c6d127a51319f7caa58cf93e631722e2deeed63d --- /dev/null +++ b/entity/QuickEntry_entity/entityfields/leadquickacquisition/onValueChange.js @@ -0,0 +1,94 @@ +import("system.neon"); +import("system.vars"); +import("Entity_lib"); +import("KeywordRegistry_basic"); +import("LeadQuickAcquisition_lib"); + + +if(vars.get("$local.value")) +{ + //Get file, call web service and get response. + var upload = new FileUpload(vars.get("$local.value")); + var response = new WebserviceResponse(); + response = LeadQuickAcquisition.callWebService(upload); + + if (response != null) + { + EntityConsumerUtils.rmInsertedConsumerRows("OrgAddresses"); + EntityConsumerUtils.rmInsertedConsumerRows("Communications"); + + //Filling necessary fields for company address + if (response.street && response.buildingNo && response.postalCode && response.city) + { + var addressFields = { + "ADDRESS" : response.street, + "BUILDINGNO" : response.buildingNo, + "ZIP" : response.zip, + "CITY" : response.city + }; + + if (response.country && response.state) + { + addressFields.COUNTRY = response.country; + addressFields.STATE = response.state; + } + + neon.addRecord("OrgAdresses", addressFields) + } + else if(response.address) + { + neon.addRecord("OrgAddresses", { + "AddressSearch" : response.address + }); + } + + if(response.companyName) neon.setFieldValue("$field.ORGANISATION_NAME", response.companyName); + + //Filling optional fields for communication e.g. email-address, phonenumber.. + if(response.emailAddress) + { + neon.addRecord("Communications", { + "MEDIUM_ID" : $KeywordRegistry.communicationMedium$mail(), + "ADDR" : response.emailAddress + }); + } + + if(response.phoneNumber) + { + neon.addRecord("Communications", { + "MEDIUM_ID" : $KeywordRegistry.communicationMedium$phone(), + "ADDR" : response.phoneNumber + }); + } + + if(response.mobilePhone) + { + neon.addRecord("Communications", { + "MEDIUM_ID" : $KeywordRegistry.communicationMedium$mobil(), + "ADDR" : response.mobilePhone + }); + } + + if(response.linkedInUrl) + { + neon.addRecord("Communications", { + "MEDIUM_ID" : $KeywordRegistry.communicationMedium$linkedin(), + "ADDR" : response.linkedInUrl + }); + } + + if(response.website) + { + neon.addRecord("Communications", { + "MEDIUM_ID" : $KeywordRegistry.communicationMedium$internet(), + "ADDR" : response.website + }); + } + + //Filling optional fields for personal details e.g. firstname, lastname.. + if(response.title) neon.setFieldValue("$field.PERSON_TITLE", response.title); + if(response.firstName) neon.setFieldValue("$field.FIRSTNAME", response.firstName); + if(response.lastName) neon.setFieldValue("$field.LASTNAME", response.lastName); + } + +} \ No newline at end of file diff --git a/entity/QuickEntry_entity/entityfields/leadquickacquisition/stateProcess.js b/entity/QuickEntry_entity/entityfields/leadquickacquisition/stateProcess.js new file mode 100644 index 0000000000000000000000000000000000000000..7376f4d25ace8fcbbe6139214369ecb85f05df79 --- /dev/null +++ b/entity/QuickEntry_entity/entityfields/leadquickacquisition/stateProcess.js @@ -0,0 +1,12 @@ +import("system.result"); +import("system.neon"); +import("LeadQuickAcquisition_lib"); + +var isMailSigWebserviceEnabled = LeadQuickAcquisitionBusinessCardUtils.isEnabled(); +var isBusinessCardWebserviceEnabled = LeadQuickAcquisitionMailSigUtils.isEnabled(); +var state = null; + +if (isBusinessCardWebserviceEnabled || isMailSigWebserviceEnabled) state = neon.COMPONENTSTATE_AUTO; +else state = neon.COMPONENTSTATE_INVISIBLE; + +result.string(state); \ No newline at end of file diff --git a/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod b/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod index 08b47fbbce4535bc433a62f1a448a025b404a353..ece2339fbbf4d337c9162ee10fcca099beda86ba 100644 --- a/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod +++ b/language/_____LANGUAGE_EXTRA/_____LANGUAGE_EXTRA.aod @@ -6315,19 +6315,7 @@ <key>Finished tasks</key> </entry> <entry> - <key>File from this template could not be found anymore. </key> - </entry> - <entry> - <key>delete linked permissions and hierarchies</key> - </entry> - <entry> - <key>Child Roles</key> - </entry> - <entry> - <key>Parent Roles</key> - </entry> - <entry> - <key>Export columns using a exporttemplate</key> + <key>{$QUICK_ENTRY_LEAD_QUICK_ACQUISITION}</key> </entry> <entry> <key>Export workflow</key> diff --git a/language/_____LANGUAGE_en/_____LANGUAGE_en.aod b/language/_____LANGUAGE_en/_____LANGUAGE_en.aod index 25185da3137ef1e2f4867209655bd2d2970bde9d..e77f97b2251f9b7e62d024c1973467a5c6700343 100644 --- a/language/_____LANGUAGE_en/_____LANGUAGE_en.aod +++ b/language/_____LANGUAGE_en/_____LANGUAGE_en.aod @@ -6376,6 +6376,10 @@ <entry> <key>Finished tasks</key> </entry> + <entry> + <key>{$QUICK_ENTRY_LEAD_QUICK_ACQUISITION}</key> + <value>Lead quick acquisition</value> + </entry> <entry> <key>File from this template could not be found anymore. </key> </entry> diff --git a/neonView/QuickEntryEdit_view/QuickEntryEdit_view.aod b/neonView/QuickEntryEdit_view/QuickEntryEdit_view.aod index 02282a4ff361d1aaaac4e669273ab6f276c3e297..d5db6f9b1c48f0d8c5e417d1401525a507bc088a 100644 --- a/neonView/QuickEntryEdit_view/QuickEntryEdit_view.aod +++ b/neonView/QuickEntryEdit_view/QuickEntryEdit_view.aod @@ -16,6 +16,10 @@ <editMode v="true" /> <entityField>#ENTITY</entityField> <fields> + <entityFieldLink> + <name>8ed8f12d-2a91-4009-8868-55a0c11880ff</name> + <entityField>leadQuickAcquisition</entityField> + </entityFieldLink> <entityFieldLink> <name>5d319cad-a0e5-49c3-867a-ccbdd217d5e0</name> <entityField>ORGANISATION_NAME</entityField> diff --git a/preferences/_____PREFERENCES_PROJECT/_____PREFERENCES_PROJECT.aod b/preferences/_____PREFERENCES_PROJECT/_____PREFERENCES_PROJECT.aod index 32b54a5e57c71741a8ae56cf8d39fc5bc87e22cf..38c233a929edb1df5a9fe8a8a3d1544186271970 100644 --- a/preferences/_____PREFERENCES_PROJECT/_____PREFERENCES_PROJECT.aod +++ b/preferences/_____PREFERENCES_PROJECT/_____PREFERENCES_PROJECT.aod @@ -93,4 +93,30 @@ <property v="5000" /> </customIntegerProperty> </customProperties> + <customConfigProperties> + <customBooleanProperty> + <name>sigparser.isEnabled</name> + <description>Whether the webservice sigparser, for mail signature parsing is useable or not.</description> + </customBooleanProperty> + <customBooleanProperty> + <name>abbyy.ocr.isEnabled</name> + <description>Whether the webservice abbyy ocr, is useable or not.</description> + </customBooleanProperty> + <customStringProperty> + <name>abbyy.ocr.appId</name> + <description>The application id for the webservice abbyy ocr.</description> + </customStringProperty> + <customPasswordProperty> + <name>abbyy.ocr.password</name> + <description>The password for the webservice abbyy ocr.</description> + </customPasswordProperty> + <customPasswordProperty> + <name>sigparser.apiKey</name> + <description>The api key to authenticate at the mail signature webservice sigparser.</description> + </customPasswordProperty> + <customStringProperty> + <name>sigparser.apiUrl</name> + <description>The url to the mail signature webservice sigparser.</description> + </customStringProperty> + </customConfigProperties> </preferences> diff --git a/process/Email_lib/process.js b/process/Email_lib/process.js index e980038b20d54714836d850c4d72c2aa07099675..c2f0d4b854054e0d60fae4b63f8ede19d5ff084d 100644 --- a/process/Email_lib/process.js +++ b/process/Email_lib/process.js @@ -87,6 +87,7 @@ function Email(pBody) this.sender = null; this.subject = null; this.body = pBody; + this.mailText = null; this.toRecipients = []; this.ccRecipients = []; this.bccRecipients = []; @@ -106,7 +107,12 @@ Email.fromRFC = function (pBase64RFC) var decoded = util.decodeBase64String(pBase64RFC); var mailData = mail.parseRFC(decoded); var newMail = new Email(mailData[mail.MAIL_HTMLTEXT]); + newMail.sender = mailData[mail.MAIL_SENDER]; + newMail.mailText = mailData[mail.MAIL_TEXT]; newMail.subject = mailData[mail.MAIL_SUBJECT]; + newMail.toRecipients = mailData[mail.RECIPIENT_TO]; + newMail.ccRecipients = mailData[mail.RECIPIENT_CC]; + newMail.bccRecipients = mailData[mail.RECIPIENT_BCC]; newMail.emlFile = decoded; return newMail; } diff --git a/process/Entity_lib/process.js b/process/Entity_lib/process.js index ada186ee6683b330ae33b2820391c83fea1fc94e..5f0e366b91d6f803b71fbfe651bb726f18614ec5 100644 --- a/process/Entity_lib/process.js +++ b/process/Entity_lib/process.js @@ -315,4 +315,25 @@ HasLinkedObjectTester.prototype.validate = function () HasLinkedObjectTester.prototype.toString = function () { return String(this.validate()); -} +}; + +/** +* Provides static methods for special handling of consumer in entities. +* +* @class +*/ +function EntityConsumerUtils(){} + +/** + * Method wich clears all fields behind the consumer. + * @param pConsumer the consumer which fields will be cleared. + * @return {void} + */ +EntityConsumerUtils.rmInsertedConsumerRows = function(pConsumer) +{ + var insertedRows = vars.get("$field." + pConsumer + ".insertedRows"); + insertedRows.forEach(function(row) + { + neon.deleteRecord(pConsumer, row["#UID"]); + }); +}; \ No newline at end of file diff --git a/process/KeywordRegistry_basic/process.js b/process/KeywordRegistry_basic/process.js index 0e4eb1424f24d5bc8442e6de58918f27dec570b2..0c9917ccc6aa44d68deb149a6a5e1ff0c6d878d4 100644 --- a/process/KeywordRegistry_basic/process.js +++ b/process/KeywordRegistry_basic/process.js @@ -103,6 +103,10 @@ $KeywordRegistry.offerProbability = function(){return "OfferProbability";}; $KeywordRegistry.communicationMedium = function(){return "CommunicationMedium";}; $KeywordRegistry.communicationMedium$mail = function(){return "COMMEMAIL";}; +$KeywordRegistry.communicationMedium$mobil = function(){return "COMMMOBIL";}; +$KeywordRegistry.communicationMedium$phone = function(){return "COMMPHONE";}; +$KeywordRegistry.communicationMedium$linkedin = function(){return "COMMLINKEDIN";}; +$KeywordRegistry.communicationMedium$internet = function(){return "COMMINTERNET";}; $KeywordRegistry.salesprojectPricePolitics = function(){return "SalesprojectPricePolitics";}; $KeywordRegistry.salesprojectWeakness = function(){return "SalesprojectWeakness";}; diff --git a/process/LeadQuickAcquisition_lib/LeadQuickAcquisition_lib.aod b/process/LeadQuickAcquisition_lib/LeadQuickAcquisition_lib.aod new file mode 100644 index 0000000000000000000000000000000000000000..dd1aecc728e349ceced88023f48b3c214a33ca1b --- /dev/null +++ b/process/LeadQuickAcquisition_lib/LeadQuickAcquisition_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>LeadQuickAcquisition_lib</name> + <majorModelMode>DISTRIBUTED</majorModelMode> + <process>%aditoprj%/process/LeadQuickAcquisition_lib/process.js</process> + <variants> + <element>LIBRARY</element> + </variants> +</process> diff --git a/process/LeadQuickAcquisition_lib/process.js b/process/LeadQuickAcquisition_lib/process.js new file mode 100644 index 0000000000000000000000000000000000000000..e52d1ff2fcae89269eeeea546e84106b050ab2d4 --- /dev/null +++ b/process/LeadQuickAcquisition_lib/process.js @@ -0,0 +1,276 @@ +import("WsValidation_lib"); +import("system.logging"); +import("system.project"); +import("system.neon"); +import("system.db"); +import("system.vars"); +import("system.mail"); +import("system.plugin"); +import("system.net"); +import("system.auth"); +import("system.util"); +import("system.fileIO"); +import("KeywordRegistry_basic"); +import("Sql_lib"); +import("Email_lib"); + +/** + * Object for storing the webservice response and + * give it in suitable format back. + * @class + */ +function WebserviceResponse() +{ + this.firstName = null; + this.lastName = null; + this.emailAddress = null; + this.phoneNumber = null; + this.mobilePhone = null; + this.address = null; + this.street = null; + this.buildingNo = null; + this.city = null; + this.state = null; + this.postalCode = null; + this.country = null; + this.title = null; + this.companyName = null; + this.website = null; + this.linkedInUrl = null; +} + +/** + * Provides static methods for generally handling mail signature + * or business card and calling the web serivce. + * @class + */ +function LeadQuickAcquisition(){} + +/** + * Method which calls the web service and gives the information back as object. + * @param pFile the file which will send to the webservice. + * @return {Object} the reply from the web service. + */ +LeadQuickAcquisition.callWebService = function(pFile) +{ + var response = new WebserviceResponse(); + + switch(pFile.mimeType) + { + case "image/bmp": + case "image/png": + case "image/jpeg": + case "application/pdf": + case "image/tiff": + case "image/gif": + response = LeadQuickAcquisition.handleBusinessCard(pFile.bindata); + break; + case "message/rfc822": + response = LeadQuickAcquisition.handleMailSig(Email.fromRFC(pFile.bindata)); + break; + default: + var convertedMail = plugin.run(null, "de.adito.aditoweb.plugin.msg2rfc.Msg2RfcPlugin", [pFile.bindata])[0]; + convertedMail = util.encodeBase64String(convertedMail); + response = LeadQuickAcquisition.handleMailSig(convertedMail); + } + + return response; +} + +/** + * Method which is especially for handling business cards. + * @param pFile the file which will send to the webservice. + * @return {Object} the reply from the web service. + */ +LeadQuickAcquisition.handleBusinessCard = function(pFile) +{ + var isEnabled = LeadQuickAcquisitionBusinessCardUtils.isEnabled(); + var response = null; + + if (isEnabled) + { + response = LeadQuickAcquisitionBusinessCardUtils._callWebServiceBusinessCard(pFile); + response = LeadQuickAcquisitionBusinessCardUtils._transformToResponse(response); + } + + return response; +} + +/** + * Method which is especially for handling mail signatures. + * @param pFile the file which will send to the webservice. + * @return {Object} the reply from the web service. + */ +LeadQuickAcquisition.handleMailSig = function(pFile) +{ + var isEnabled = LeadQuickAcquisitionMailSigUtils.isEnabled(); + var response = null; + + if (isEnabled) + { + response = LeadQuickAcquisitionMailSigUtils._callWebServiceMailSig(pFile); + response = LeadQuickAcquisitionMailSigUtils._transformToResponse(response); + } + + return response; +} + +/** + * Provides static methods for especially handling business cards. + * @class + */ +function LeadQuickAcquisitionBusinessCardUtils(){} + +/** + * Method which check whether the web service is enabled or not. + * @return {Boolean} whether the web service is enabled or not. + */ +LeadQuickAcquisitionBusinessCardUtils.isEnabled = function() +{ + var isEnabled = project.getInstanceConfigValue("custom.abbyy.ocr.isEnabled", "false"); + isEnabled = isEnabled == "true"; + + return isEnabled; +} + +/** + * Method which calls the webservice for business card. + * @param pFile the file which will send to the webservice. + * @return {Object} the reply from the web service. + */ +LeadQuickAcquisitionBusinessCardUtils._callWebServiceBusinessCard = function(pFile) +{ + var appId = project.getInstanceConfigValue("custom.abbyy.ocr.appId", ""); + var pwd = project.getInstanceConfigValue("custom.abbyy.ocr.password", ""); + var language = "German"; + + //var reply = '{"Company":"eisner","Email":"mustermann@motyp.de","Address":"Hauptstraße 21 ■12345 Berlin","Web":null,"Phone":null,"Fax":"028 01 83 999 019","Job":"Fliesenverlegemeister","Mobile":"0171 23 456 78","Name":"Max Mustermann"}'; + var reply = plugin.run(null,"de.adito.plugin.OCR.BusinessCardRecognizer", [appId, pwd, pFile ,language])[0]; + + //Error codes are defined in the java plugin. + if (reply == "552") + { + throw new Error("Abbyy ocr plugin has not enough credits!"); + } + else if (reply == "555") + { + throw new Error("The Abbyy ocr plugin request could not be completed!"); + } + else if (reply == "550") + { + throw new Error("The Abbyy ocr plugin credentials are wrong!") + } + + return reply; +} + +/** + * Method which transforms the web service response. + * @param pReply the file which will send to the webservice. + * @return {Object} the reply from the web service. + */ +LeadQuickAcquisitionBusinessCardUtils._transformToResponse = function(pReply) +{ + var response = new WebserviceResponse(); + var parsedData = JSON.parse(pReply); + + if(parsedData.Company) response.companyName = parsedData.Company; + if(parsedData.Email) response.emailAddress = parsedData.Email; + if(parsedData.Web) response.website = parsedData.Web; + if(parsedData.Phone) response.phoneNumber = parsedData.Phone; + if(parsedData.Mobile) response.mobilePhone = parsedData.Mobile; + if(parsedData.Name) + { + response.firstName = parsedData.Name.substring(0, parsedData.Name.indexOf(" ", 0)); + response.lastName = parsedData.Name.substring(parsedData.Name.lastIndexOf(" ", parsedData.Name.indexOf(" ", 0))+1, parsedData.Name.length-1); + } + if (WsValidationUtils.isWsEnabled(WsValidationType.get().TYPE_ADDRESS_NOMINATIM) && parsedData.Address) + { + response.address = parsedData.Address; + } + + return response; +} + +/** + * Provides static methods to call the webservice and + * transform the response for mail signatures. + * give it in suitable format back. + * @class + */ +function LeadQuickAcquisitionMailSigUtils(){} + +/** + * Method which check whether the web service is enabled or not. + * @return {Boolean} whether the web service is enabled or not. + */ +LeadQuickAcquisitionMailSigUtils.isEnabled = function() +{ + var isEnabled = project.getInstanceConfigValue("custom.sigparser.isEnabled", "false"); + isEnabled = isEnabled == "true"; + + return isEnabled; +} + +/** + * Method which calls the webservice for mail signatures. + * @param pFile the file which will send to the webservice. + * @return {Object} the reply from the web service. + */ +LeadQuickAcquisitionMailSigUtils._callWebServiceMailSig = function(pFile) +{ + var email = pFile; + var apiKey = project.getInstanceConfigValue("custom.sigparser.apiKey", ""); + var url = project.getPreferenceValue("custom.sigparser.apiUrl", ""); + + var emailText = email.mailText; + var senderName = email.sender.substring(0, email.sender.indexOf("<", 0)); + var senderMail = email.sender.substring(email.sender.indexOf("<", 0)+1, email.sender.indexOf(">", 0)); + + var inputValues = { + from_address: senderMail, + plainbody: emailText + }; + + if (senderName) + inputValues.from_name = senderName; + + var config = net.createConfigForRestWebserviceCall() + config.actionType(net.POST) + config.url(url) + config.addHeader("cache-control", "no-cache") + config.addHeader("x-api-key", apiKey) + config.dataTypeSend("application/json") + config.dataTypeJDitoAccept(util.DATA_TEXT) + config.dataTypeJDitoSend(util.DATA_TEXT) + config.requestEntity(JSON.stringify(inputValues)) + + var reply = net.callRestWebservice(config, auth.createConfigForNoAuth()); + + return reply; +} + +/** + * Method which transforms the response from the webservice. + * @param pReply the file which will send to the webservice. + * @return {Object} the reply from the web service. + */ +LeadQuickAcquisitionMailSigUtils._transformToResponse = function(pReply) +{ + var response = new WebserviceResponse(); + var parsedData = JSON.parse(pReply); + if (parsedData.contacts[0].companyName) response.companyName = parsedData.contacts[0].companyName; + if (parsedData.contacts[0].firstName) response.firstName = parsedData.contacts[0].firstName; + if (parsedData.contacts[0].lastName) response.lastName = parsedData.contacts[0].lastName; + if (parsedData.contacts[0].emailAddress) response.emailAddress = parsedData.contacts[0].emailAddress; + if (parsedData.contacts[0].phoneNumber) response.phoneNumber = parsedData.contacts[0].phoneNumber; + if (parsedData.contacts[0].mobilePhone) response.mobilePhone = parsedData.contacts[0].mobilePhone; + if (parsedData.contacts[0].addressParts != null) + { + var address = parsedData.contacts[0].addressParts.street; + response.street = address.substring(0, address.indexOf(" ", 0)); + response.buildingNo = address.substring((address.indexOf(" ", 0)+1)); + } + + return response; +} \ No newline at end of file