...
Code Block |
---|
[ "IndividualId1", "IndividualId2" ] |
Process:
EXPIRIENCE API:
Required header “validator“ with value: rbesb
Request Epsilon for a token (It is needed to be moved to middleware Mulesoft)
For each individual id
Get from CDP - Golden Individual - saved under “profile”
Get from CDP - Contacts - export from its contact and save under “emails”, “phones”
Map enrollments - if there are any children, gather all enrollments and save them under “enrollChildren”
Sync process:
Check Contact
Query Object: Account with several conditions (SFSC)
Code Block var boc = if (vars.profile.BrandOrgCode == 'MJNTHA') "TH_Household" else if (vars.profile.BrandOrgCode == 'MJNIDN') "ID_Household" else if (vars.profile.BrandOrgCode == 'MJNMYS') "MY_Household" else if (vars.profile.BrandOrgCode == 'MJNSGP') "MY_Household" else if (vars.profile.BrandOrgCode == 'MJNBRN') "MY_Household" else if (vars.profile.BrandOrgCode == 'MJNVNM') "VN_Household" else null
Code Block SELECT Id, OwnerId, Inactive__c FROM Account WHERE IsDeleted=false AND RecordMarket__c= ++ boc ++ AND ++ QUERIES EMAILS OR PHONES (Depending if values exists)
It is saved later in a variable: “contactQueryResponse”
If contacts do not exist = logger. If contacts exist then:
Filter contacts if Inactive__c == “false”. If false then = logger, if true then:
Load data from vars to payload
Query data for parents (SFSC):
Code Block SELECT Id, RecordMarket__c, Inactive__c FROM Contact WHERE Is_Primary_Contact__c=true AND IsDeleted=false AND AccountId='" + vars.'account_id' + "'
Data is saved into a variable: “contactQueryResponse”
If parents exist: for each parent there are transformations made. If parents do not exist it just logs proper information.
Mapping Household
Prepare new_account variable: (Variables inside DW)
Code Block fullname: profileFirstName ++ profileMiddleName ++ profileLastName Coutry_TXT mapping: { "THA": "Thailand", "VNM": "Vietnam", "SGP": "Singapore", "MYS": "Malaysia", "BRN": "Brunei", "IDN": "Indonesia", "PHL": "Philippines", "USA": "USA" }
Mapping:
Code Block First_Name__c: profileFirstName Last_Name__c: profileLastName Middle_Name__c: profileMiddleName Name: fullName Id: def_data.account_id RecordTypeId: profileBrandOrgCode Address_Line_1__c: profileAddressLine1 Address_Line_2__c: profileAddressLine2 Address_Remarks__c: profileJsonExternalData.AddressRemarks Mobile_Phone__c: profileMobilePhone Home_phone__c: profileHomePhone Home_phone__c: profileHomePhone splitBy("x"))[0] Home_Phone_Extension__c: (profileHomePhone splitBy("x"))[1])) Other_Phone_1__c: profileWorkPhone Other_Phone_1__c: profileOtherPhone Account_Email__c: profileEmailAddress Member_Country__c: COUNTRY_TXT[vars.profileCountryCode]
If values are not blank then it is appended to the payload. In another case, the whole key is deleted from the request. All data are in vars. (It can cause memory issues later if data is larger).
Prepare cityData variable:
Code Block District__c: profileAddressLine3 -> with additonal logic of splitting data Region__c: profileAddressLine3 -> with additional logic of splitting data Name: profileCity State_Province__c: profileState Zip_Code__c: profilePostalCode Country__c: COUNTRY_TXT[vars.profileCountryCode] //This element should not work there is no COUNTRY_TXT in dataweave
If Profile ID SF is equal to profile ID from CDP
For each: JsonExternalData
If payload profile id equals correctly and mom status is not empty and mom status is equal to first time then
Mom_status equals:
"1st Time"
In other case logger
if Profile ID SF is NOT equal to profile ID from CDP
If ProfileSubscriptions and JsonExternalData are not empty for each profile subscription do:
If optStatus is true and channelCode contains channelCode(??? payload.OptStatuss == true and !(payload.ChannelCode contains payload.ChannelCode) ???) prepare parse_subs which is currently empty object in other case it also empty object
Code Block %dw 2.0 output application/json var s_optstatus = payload.OptStatus var s_channel = payload.ChannelCode var upsert_channel = false --- {} // subscribes[s_channel] = subscriptions[s]; // upsert_channel = true;
Prepare subscriptions in payload
Code Block var most_recent = (vars.enrollChildren orderBy ((item, index) -> -item.enrollDate as DateTime as Number {unit: "milliseconds"}))[0]
Adding to new_account object (and saving to payload)
Code Block Mailing_Opt_Out__c: true (hardcoded) Account_Email_Opt_out__c: true (hardcoded) Do_not_SMS_Mobile__c: true (hardcoded) Do_Not_Call_Mobile__c: true (hardcoded) Do_Not_Call_Home_Phone__c: true (hardcoded) Do_Not_Call_Other_Phone_1__c: true (hardcoded) Enfa_Status__c: most_recent.'Enfa_Status__c' <- added if most_recent.'Enfa_Status__c' == "Active" Enfa_Date_Joined__c: most_recent.enrollDate <- added if most_recent.'Enfa_Status__c' == "Active" Sustagen_Status__c: most_recent.'Enfa_Status__c' Sustagen_Date_Joined__c: most_recent.enrollDate Mom_Status__c: vars.Mom_Status <- added if !isBlank(vars.Mom_Status)
The query is being prepared for the city - Long logic - Object City__c
Query City (SF)
Does city query have at least 3 query conditions? if true then
Query City
if value is not empty it is appending additional data to vars with account
In other cases it logs information
If false then
Creates/Updates a household data into SF (Object Account
CDP - SFDC Sync - Upsert contact If parentIdQueryResponse is empty this one is skipped in other cases:
Create def_data variable:
Code Block if (vars.createContactParentResponse !=null) { 'contact_id': vars.createContactParentResponse.id, 'account_id': vars.def_data.account_id } else if (vars.updateContactParentResponse !=null) { 'contact_id': vars.updateContactParentResponse.id, 'account_id': vars.def_data.account_id } else "Parent Response not chosen"
If a Child should be created - Sync children - (MAPPING ONLY)
Mapping - Most data is from payload. Need to be validated when running
Code Block var CHILD = { 'MJNTHA': "0129000000168Ip", 'MJNIDN': '0129000000168Ij', 'MJNBRN': '0129000000168Il', 'MJNMYS': '0129000000168Il', 'MJNSGP': '0129000000168Il', 'MJNPHL': '', 'MJNVNM': '0129000000168Ir' } --- Family_Relationship__c: CaregiverTypeCode FirstName: ChildFirstName LastName: ChildLastName Middle_Name__c: ChildMiddleName RecordTypeId: CHILD[vars.profile.BrandOrgCode] AccountId: 'def_data'.'acc_id' Gender__c: GenderCode Birthdate: BirthDueDate Last_Consumption_Brand__c: PreferredBrand prod_id: PreferredProduct Feeding_Method__c: PreferredProduct Born_Hospital__c: HospitalId Baby_Hospital__c: HospitalId Attending_Doctor__c: HcpProfileId
In this process, there are multiple for each using extra_data and even extra_data.Children (Why not payload.Children)
Big choice which contains
!isEmpty(vars.new_children) and vars.create_child == true - only logger (Missing implementation?)
isEmpty(vars.new_children) and vars.create_child == true - Only logger - no children to upsert
!isBlank(vars.def_data.contact_id) - Contains logger that it should be update updated but creates Contact Parent
default - Creates Contact Parent It is double the same logic
Contains Logger TODO - What is missing Syncing children? enrollments?
End of the process
Python process: (I took all-singapore.py file)
Checks files inside of SFTP
Downloads them (PGP)
Decrypt them and save them somewhere
Runs process-singapore.py
Inside of process-singapore.py
Basic variables are being saved like base URL, tokens, etc.
Starts logging into file
Calling function
processFile
Opening file and reading IndividualIDs and saving it into the IndividualIDS list
Depending on the situation in data inside of the file it can skip if there is a blank row, if there are not enough fields or if there is an empty Individual ID
Creating a token towards CDP
Getting a golden record from CDP (function:
cdpGetIndivGolden
)Headers Authorization, Brand-Org-Code, Accept-Language.
A request is made for
'/profiles/individuals/golden/' + individualID
It checks if in response there is correct status and if so then returns json data to previous method.
Saves response in json file
Getting a list of email addresses for processing record (
cdpGetContacts
) - Using goldenRecord (it is really emails and phone numbers)For each golden record - key: JsonExternalData and inside of it key: Profiles
Gets profile ids
Builds a headers Authorization, Brand-Org-Code, Account-Source, Source-Account-Number, Accept-Language
Requests towards:
'/profiles/' + profile['ProfileId']
If everything is correct processes phones (adding it to list) and the same for emails
If there is non values it provides null values.
Code Block if len(emailLst) == 0 and len(phoneLst) != 0: return 'null', ";".join(phoneLst) if len(emailLst) != 0 and len(phoneLst) == 0: return ";".join(emailLst), 'null' if len(emailLst) != 0 and len(phoneLst) != 0: return ";".join(emailLst), ";".join(phoneLst)
Every time there is no match or any error it creates logs.
if there is no profiles it logs it as an error
Creates profile in SFDC (
createInSFDC
) (Parameters profiled, golden record, brand org code (MJNSGP), email list, mobile list, sf_token, sf_instance)Creates headers
Build logic about mapping (
cdpGetEnrollments
)For each profile in golden record → json external data → profiles. For each json external data in profile. For each children in json external data. In each child enrollments in children.
Code Block for profile in goldenRecord['JsonExternalData']['Profiles']: for jed in profile['JsonExternalData']: for child in jed['Children']: for cenroll in child['ChildEnrollments']:
Puts data into variable
Gathers enrollment subscription id and child ID
Gathers data for birthDate of the child and child
If a child is not in the parent variable (that we add values) we append it. In else, we check if the enrollment date of the current is newer than that saved if so we swap them:
Code Block if keyenroll not in enrolled_child: enrolled_child[keyenroll] = enrollment else: if enrollment['EnrollDate'] > enrolled_child[keyenroll]['EnrollDate']: enrolled_child[keyenroll] = enrollment
After process it being returned back
Builds body which is:
Code Block { 'cid' : cid, 'emlid' : emlid, 'mobileid' : mobileid, 'profile_id' : profileID, 'oid' : oid, #'dev' : dev, 'sf_token' : sf_token, 'sf_instance' : sf_instance, 'op' : 'check_contact', 'profile' : json.dumps(goldenRecord), 'profile_mkt' : brandOrgCode, 'boc' : brandOrgCode, 'enrollment': json.dumps(enrollment), 'all' : 'true', 'tag' : 'oblist-add' }
Creates request towards
https://mjn-cdp-pop.navomi.com/ajax/sync_profile
(This hits the JAVASCRIPT (https://bitbucket.org/rbdigital/navomi-pop-up-sync-profile/src/master/routes/ajax.js - You can find under /ajax/sync_profile)JAVASCRIPT /ajax/sync_profile
Sets header content-type as application/json
Creates a new current date
Puts data into object API.setSession in which puts everything to an object.
Runs salesforce lib with function
sync_profile
and in there is whole logic with additional checks and mappings (corresponding to mapping for this process is “check_contact”)In salesforce lib, there is an op check if is equal to this value and then process values query SF for Account details for emails and phones. If emails and phones exist then a query is made and a function is called:
check_contact_cb
. If there is no data then depending on condition it creates household.Inside function
check_contact_cb
First is the condition that checks if the result has a size > 0 (if it is 0 it creates a household ) LOWER YOU CAN FIND HOUSEHOLD OPERATION
Next, it checks if accounts are not inactive and saves them into variable active_account
If an active account exists then (if not there is another condition that creates a household or does nothing)
JS makes a request again to SF to check if there are any Contact for this account (This seems that is searching for parent contact) Why they did do this way they could gather all data by Python and then send it to JS (or simply only use Python)
If such parent contact exists it creates household
Contains handler of error.
That ends everything
ABOUT CREATE HOUSEHOLD (_create_household
)
In here we have mapping all details from body and additional queries to SF and syncing such object.
It:
Code Block Sets ID if exists, and puts flag for update as true First_Name__c = FirstName Middle_Name__c = MiddleName Last_Name__c = LastName Name = FirstName + MiddleName + LastName Address_Line_1__c = AddressLine1 Address_Line_2__c = AddressLine2 Address_Remarks__c = JsonExternalData.AddressRemarks Makes query for City_data for reqion, district if address line 3 exists (?) Then again does something with query according to field AddressLine5 (?) Mobile_Phone__c = MobilePhone Home_phone__c = HomePhone.split('x') [0] Home_Phone_Extension__c = HomePhone.split('x') [1] Other_Phone_1__c = WorkPhone Other_Phone_2__c = OtherPhone Account_Email__c = EmailAddress Member_Country__c = COUNTRY_TXT[profile.CountryCode] There are olso parse subscription After city query there is also appended: SA_City__c = ret.records[0].Id Enfa_Status_Changed_NAV__c = false Sustagen_Status_Changed_NAV__c = false
And then sends it to SFSC.