cancel
Showing results for 
Search instead for 
Did you mean: 

SAP Marketing Cloud: Always use email address from most reliable source for golden record

former_member558862
Participant

We are currently trying to find some way to always consider the email address from the most reliable origin for the best record. In our case the email address from SAP Commerce Cloud (SAP_HYBRIS_CONSUMER) is the only valid one. It represents the most current email address used for the webshop login.

Even though SAP_HYBRIS_CONSUMER has the highest priority of all our origins, sometimes an email address from a lower priorizized origin is added to the best record. The reason for that is probably described here:

https://launchpad.support.sap.com/#/notes/2850695.

See some example here:

The email from the commerce origin (priority 20) is test..., however, the golden record has the dmaz... email address which is part of two other lower prioritized origins (form priority 25, custom service priority 35).

When checking the permissions, you can see the following:

There is an explicit opt-in for the dmaz... email address (golden record) and an explicit opt-out for the test... one.

Our expected result would be that the test... email address is used for the golden record because it's the one from the most reliable source. The dmaz... email address is not up-to-date anymore. We don't want to consider any permission status to determine the email address for the golden record. If there is an email address for SAP_HYBRIS_CONSUMER - this one should always be used for the golden record. If there is no SAP_HYBRIS_CONSUMER origin, then the one with the origin with the highest priority should count. Or let's say it like this: The golden record should always consider the email address from the origin with the highest priority and it should not take any permission status into consideration.

Now I would like to find a way to implement this. I thought that custom logic "Update interaction contact (Best Record)" may help here. I mainly copied the example coding and changed it a little bit at the end. I was not able to check for the actual priority so what I tried to implement was this:

  1. At first, I'm checking for the interaction contact type so that the custom email logic is not executed for accounts/companies
  2. Use email address from SAP_HYBRIS_CONSUMER origin if available
  3. Since some contacts were also migrated from another source (DOI_SERVICE_ID), this is the origin with the next priority - so if no email from the SAP_HYBRIS_CONSUMER origin is available, the DOI_SERVICE_ID origin should be considered
  4. Otherwise the email on the golden record should be empty
* If you want to change any attribute value of an interaction contact's best record mentioned in table
* NEW_CONTACT_ROOT you can fetch additional information on this contact from the tables CONTACT_DATA,
* INTERACTION, ADDITIONAL_FACET, ATTRIBUTE_RSTRCTN and OLD_CONTACT_ROOT.
* For each interaction contact in NEW_CONTACT_ROOT its row number is mentioned in field RECORD_IDX.
* For each RECORD_IDX there can be several entries in tables CONTACT_DATA, INTERACTION,
* ADDITIONAL_FACET, ATTRIBUTE_RSTRCTN and OLD_CONTACT_ROOT, which themselves also have a field RECORD_IDX,
* and moreover the tables are populated in ascending order by field RECORD_IDX.
* If for instance you want to use additional information for the 3rd interaction contact mentioned in
* table  NEW_CONTACT_ROOT, you can access the corresponding lines in tables CONTACT_DATA, INTERACTION,
* ADDITIONAL_FACET, ATTRIBUTE_RSTRCTN and OLD_CONTACT_ROOT, by the ABAP statement
* READ TABLE xyz WITH KEY record_idx = 3 TRANSPORTING NO FIELDS  BINARY SEARCH.
* If the SY-SUBRC value is different from zero, you can loop over table xyz starting from line SY-TABIX
* until you reach a line whose RECORD_IDX value is greater than 3. 'xyz' you would of course replace by
* any of the tables CONTACT_DATA, INTERACTION, ADDITIONAL_FACET, ATTRIBUTE_RSTRCTN or OLD_CONTACT_ROOT name.
* All lines with RECORD_IDX value equal to 3 would then belong to your 3rd interaction contact.
*
* Table CONTACT_DATA may contain common contact data already persisted and/or curently imported referring
* to a specific main facet (= contact ID + Origin of contact ID). The mentioned timestamp may specify the
* last date + time when these data were updated.
*
* Table INTERACTION contains the common contact data of the currently imported interaction (it will be
* empty if pure contact data instead of interactions are currently imported).
*
* Table ADDITIONAL_FACET contains additional facets (for instance email addresses, telephone numbers, or
* other identifiers) belonging to the common data of a main facet available in table CONTACT_DATA. The
* fields ID_GROUP and ID_ORIGIN_GROUP are the main facet mentioned in fields ID + ID_ORIGIN of table
* CONTACT_DATA.
*
* Table ATTRIBUTE_RSTRCTN may contain additional information on values appearing in common data fields
* SMTP_ADDR, TELNR_LONG, TELNR_MOBILE or FAXNR_LONG. Field ATTRIBUTE will specify the name of the
* respective common data field. Field DB_FACET_INVALID has the value 'X' if the contact has a
* corresponding facet which is flagged invalid on the data base (the facet may be flagged invalid due to
* an interaction indicating that the contact was not reachable under this specific identifier. See BAdI
* CUAN_IMPORT_IA_4_IC). The value 'Y' (=yes) or 'N' (=no) in field VALUE_MP_OPT_IN reflects the marketing
* permission given by the contact for this specific identifier. If field VALUE_INVALID has the value 'X',
* the specific ID has an invalid format, or its facet is flagged invalid, or it is a unique ID stored in
* a container contact (and therefore not attached to the current contact as interaction contact facet).
*
* Table OLD_CONTACT_ROOT contains the common contact data on the data base, reflecting the situation
* before the update with the values in table NEW_CONTACT_ROOT. Of course if a interaction contact is new
* to the system and currently imported first time, table OLD_CONTACT_ROOT will not have any entry
* referring to them. On the other hand, if the system detects based on currently imported interaction
* contact data that 2 already persisted interaction contacts must be duplicates of one single contact,
* they will be merged to one single contact, and the two original best records will be available in
* table OLD_CONTACT_ROOT.
*
* CBO_WRITE_OBJECT provides a reference to a Write API Factory for Custom Business Objects (CBOs).
* With this reference you get write access to your own CBOs and for example instantiate a particular CBO.
*

*   Always store the email address from SAP Commerce Cloud (= current webshop login email address) in the golden record

    DATA:
      lr_ic_root_new    TYPE REF TO cuan_s_ce_ic_rt_badi,
      lv_contact_number TYPE        cuan_s_ce_ic_rt_badi-record_idx,
      lv_index          LIKE        sy-tabix,
      lr_facet_data     TYPE REF TO cuan_s_ce_ic_badi,
      lt_facet_data     TYPE        cuan_t_ce_ic_badi,
      ls_interaction    TYPE        cuan_s_ce_ia_badi,
      lr_add_facet      TYPE REF TO cuan_s_ce_ic_facet_add_badi,
      lt_add_facet      TYPE        cuan_t_ce_ic_facet_add_badi,
      lr_attr_rstrctn   TYPE REF TO cuan_s_ce_ic_atrst_badi,
      lt_attr_rstrctn   TYPE        cuan_t_ce_ic_atrst_badi,
      lr_mkta           TYPE REF TO cuan_s_ce_ic_mkta_badi,
      lt_mkta           TYPE        cuan_t_ce_ic_mkta_badi,
      lt_old_ic_root    TYPE        cuan_t_ce_ic_rt_badi,
      lr_old_ic_root    TYPE REF TO cuan_s_ce_ic_rt_badi.

*   Process all updated interaction contacts.
    LOOP AT new_contact_root REFERENCE INTO lr_ic_root_new.

      lv_contact_number = lr_ic_root_new->record_idx.
      CLEAR: lt_facet_data,
             lt_add_facet,
             lt_attr_rstrctn,
             lt_mkta,
             lt_old_ic_root.

*     Get common data of main facets of current contact into LT_FACET_DATA.
      READ TABLE contact_data WITH KEY record_idx = lv_contact_number TRANSPORTING NO FIELDS BINARY SEARCH.
      IF sy-subrc EQ 0.
        lv_index = sy-tabix.
        DO.
          READ TABLE contact_data REFERENCE INTO lr_facet_data INDEX lv_index.
          IF sy-subrc EQ 0 AND lr_facet_data->record_idx EQ lv_contact_number.
            APPEND lr_facet_data->* TO lt_facet_data.
            lv_index =  lv_index + 1.
          ELSE.
            EXIT.
          ENDIF.
        ENDDO.
        SORT lt_facet_data BY id_origin id ASCENDING timestamp DESCENDING .
      ENDIF.

*     Get imported interaction of current contact into LS_INTERACTION.
      READ TABLE interaction WITH KEY record_idx = lv_contact_number INTO ls_interaction BINARY SEARCH.
      IF sy-subrc NE 0.
        CLEAR ls_interaction.
      ENDIF.

*     Get additional facets of current contact into LT_ADD_FACET.
      READ TABLE additional_facet WITH KEY record_idx = lv_contact_number TRANSPORTING NO FIELDS BINARY SEARCH.
      IF sy-subrc EQ 0.
        lv_index = sy-tabix.
        DO.
          READ TABLE additional_facet REFERENCE INTO lr_add_facet INDEX lv_index.
          IF sy-subrc EQ 0 AND lr_add_facet->record_idx EQ lv_contact_number.
            APPEND lr_add_facet->* TO lt_add_facet.
            lv_index =  lv_index + 1.
          ELSE.
            EXIT.
          ENDIF.
        ENDDO.
        SORT lt_add_facet ASCENDING BY id_origin id.
      ENDIF.

*     Get IC best record attributes' restrictions of current contact into LT_ATTR_RSTRCTN.
      READ TABLE attribute_rstrctn WITH KEY record_idx = lv_contact_number TRANSPORTING NO FIELDS BINARY SEARCH.
      IF sy-subrc EQ 0.
        lv_index = sy-tabix.
        DO.
          READ TABLE attribute_rstrctn REFERENCE INTO lr_attr_rstrctn INDEX lv_index.
          IF sy-subrc EQ 0 AND lr_attr_rstrctn->record_idx EQ lv_contact_number.
            APPEND lr_attr_rstrctn->* TO lt_attr_rstrctn.
            lv_index =  lv_index + 1.
          ELSE.
            EXIT.
          ENDIF.
        ENDDO.
        SORT lt_attr_rstrctn ASCENDING BY attribute value.
      ENDIF.

*     Get marketing areas of current contact.
      READ TABLE marketing_area WITH KEY record_idx = lv_contact_number TRANSPORTING NO FIELDS BINARY SEARCH.
      IF sy-subrc EQ 0.
        lv_index = sy-tabix.
        DO.
          READ TABLE marketing_area REFERENCE INTO lr_mkta INDEX lv_index.
          IF sy-subrc EQ 0 AND lr_mkta->record_idx EQ lv_contact_number.
            APPEND lr_mkta->* TO lt_mkta.
            lv_index =  lv_index + 1.
          ELSE.
            EXIT.
          ENDIF.
        ENDDO.
        SORT lt_mkta ASCENDING BY mkt_area_id.
      ENDIF.

*     Get old best record(s) of current contact into LT_OLD_IC_ROOT.
      READ TABLE old_contact_root WITH KEY record_idx = lv_contact_number TRANSPORTING NO FIELDS BINARY SEARCH.
      IF sy-subrc EQ 0.
        lv_index = sy-tabix.
        DO.
          READ TABLE old_contact_root REFERENCE INTO lr_old_ic_root INDEX lv_index.
          IF sy-subrc EQ 0 AND lr_old_ic_root->record_idx EQ lv_contact_number.
            APPEND lr_old_ic_root->* TO lt_old_ic_root.
            lv_index =  lv_index + 1.
          ELSE.
            EXIT.
          ENDIF.
        ENDDO.
        SORT lt_old_ic_root DESCENDING BY validation_status.
      ENDIF.

*     Custom logic only for contact persons or sales responsibles and not for companies/accounts
*     00 = undefined | 01 = person | 02 = company
      IF lr_ic_root_new->ic_type NE '02'.
*     Email address on golden record should be empty if none is found for SAP Commerce Cloud or DOI service.
          CLEAR lr_ic_root_new->smtp_addr.
*     Store in best record always the email address from SAP Commerce Cloud if available.
          READ TABLE lt_add_facet REFERENCE INTO lr_add_facet
                                        WITH KEY id_origin       = 'EMAIL'
                                                 id_origin_group = 'SAP_HYBRIS_CONSUMER'.
          IF sy-subrc EQ 0.
            lr_ic_root_new->smtp_addr = lr_add_facet->id.
          ELSE.
*     If the contact was not migrated from SAP Commerce Cloud - use the email address from the old DOI service.
            READ TABLE lt_add_facet REFERENCE INTO lr_add_facet
                                          WITH KEY id_origin       = 'EMAIL'
                                                   id_origin_group = 'DOI_SERVICE_ID'.
            IF sy-subrc EQ 0.
                lr_ic_root_new->smtp_addr = lr_add_facet->id.
            ENDIF.
          ENDIF.
      ENDIF.

*     Example: If there is no email address from CRM business partner data,
*               take any other email address with positive opt-in (=marketing permission).
*
*      IF lr_ic_root_new->smtp_addr IS INITIAL.
*        READ TABLE lt_attr_rstrctn REFERENCE INTO lr_attr_rstrctn
*                                         WITH KEY attribute       = 'SMTP_ADDR'
*                                                  value_mp_opt_in = 'Y'
*                                                  value_invalid   = abap_false.
*        IF sy-subrc EQ 0.
*          lr_ic_root_new->smtp_addr = lr_attr_rstrctn->value.
*        ENDIF.
*      ENDIF.

    ENDLOOP.

So my questions now:

  1. When exactly is this BAdI executed? Is it always when a new contact is created or an existing one is updated?
  2. Let's say I upload a contact with origin 'SAP_HYBRIS_CONSUMER', ID '123' and email address 'Christian.Needs.Help@gmail.com'. The uploaded data would be contained in CONTACT_DATA. When the BAdI is executed for the uploaded data - would ADDITIONAL_FACET already contain an entry for the uploaded email address like ID_ORIGIN 'EMAIL', ID_ORIGIN_GROUP 'SAP_HYBRIS_CONSUMER' and ID ''Christian.Needs.Help@gmail.com'?
  3. What would be if the email address was initially provided by the SAP_HYBRIS_CONSUMER origin but removed due to a hard bounce? Would it still find something in table ADDITIONAL_FACET? I would expect some record with ID_ORIGIN 'EMAIL' and ID_ORIGIN_GROUP 'SAP_HYBRIS_CONSUMER' then. But the ID value would probably be empty?
  4. Did someone maybe implement a similar requirement and can validate my coding?
  5. When transporting this custom logic to production, it will probably only apply to the existing interaction contacts if they are explicitly updated, right? So I would need to make some dummy change on the interaction contacts so that the best record is recalculated considering the custom logic. Or is there some other better way?

Unfortunatelly, the documentation could not answer those questions for me. So maybe the community can help here. Thank you very much for assistance.

Christian

Accepted Solutions (1)

Accepted Solutions (1)

andreas_bettin
Explorer
Hello Christian,
  1. When exactly is this BAdI executed? Is it always when a new contact is created or an existing one is updated? Answer: It is always executed if interaction contact is created or updated during finalize event.
  2. Let's say I upload a contact with origin 'SAP_HYBRIS_CONSUMER', ID '123' and email address 'EmailAddress'. The uploaded data would be contained in CONTACT_DATA. When the BAdI is executed for the uploaded data - would ADDITIONAL_FACET already contain an entry for the uploaded email address like ID_ORIGIN 'EMAIL', ID_ORIGIN_GROUP 'SAP_HYBRIS_CONSUMER' and ID 'EmailAddress'? Answer: Facet like attributes are also filled into ADDITIONAL_FACET
  3. What would be if the email address was initially provided by the SAP_HYBRIS_CONSUMER origin but removed due to a hard bounce? Would it still find something in table ADDITIONAL_FACET? I would expect some record with ID_ORIGIN 'EMAIL' and ID_ORIGIN_GROUP 'SAP_HYBRIS_CONSUMER' then. But the ID value would probably be empty? Answer: Hard bounce facet is also filled in ADDITIONAL_FACET
  4. Did someone maybe implement a similar requirement and can validate my coding?
  5. When transporting this custom logic to production, it will probably only apply to the existing interaction contacts if they are explicitly updated, right? So I would need to make some dummy change on the interaction contacts so that the best record is recalculated considering the custom logic. Or is there some other better way? Answer: Yes the new logic will be executed if an update comes in
former_member558862
Participant
0 Kudos

Hi andreas.bettin. Thanks for the information so far. Some further question in regards to...

3. This means that the EMAIL facet for the bounced email address would still be available and also filled with the bounced email address? So in my coding, I would probably write the bounced email address to the golden record which would be wrong. Is there any chance to find out those bounced ones on the coding layer of the described BAdI?

5. Do you maybe have a proposal about the "easiest" dummy contact update?

Thank you & best regards
Christian

Answers (2)

Answers (2)

andreas_bettin
Explorer
0 Kudos

Hello Christian,

to answer your open questions:

2. You can select the corresponding entry of the email address in internal table lt_attr_rstrctn to check if the email address is invalid.

Select the entry where ID_ORIGIN = 'EMAIL' and VALUE = <your email address>

If the found entry’s VALUE_INVALID is ‘X’, then the email address is invalid with respect to the format or due to a bounce hard.

If the found entry’s DB_FACET_INVALID is ‘X’, then the email address is invalid due to a bounce hard.

5. No there is no special dummy update. If you use an OData service that only supports a full update you to send the complete data with a newer timestamp again. In case of an OData service that allows patching for sure one single attribute is enough.

former_member558862
Participant
0 Kudos

I did some testing and can update my questions partly:

1) When exactly is this BAdI executed? Is it always when a new contact is created or an existing one is updated?

It's definitly executed when a new contact is created and also if an existing contact is updated. I suppose that it's executed after the best record was prepared by the standard - so it's some additional coding at the very end of the process.

2) Let's say I upload a contact with origin 'SAP_HYBRIS_CONSUMER', ID '123' and email address .... The uploaded data would be contained in CONTACT_DATA. When the BAdI is executed for the uploaded data - would ADDITIONAL_FACET already contain an entry for the uploaded email address like ID_ORIGIN 'EMAIL', ID_ORIGIN_GROUP 'SAP_HYBRIS_CONSUMER' and ID ...?

I assume that the ADDITIONAL_FACET table is already filled in this case because the email on the best record is set if I upload a new contact with SAP_HYBRIS_CONSUMER or DOI_SERVICE_ID origin. Instead, if I upload a new contact with a different source origin, the email field of the best record stays empty even though an email address was contained in the upload.

5) When transporting this custom logic to production, it will probably only apply to the existing interaction contacts if they are explicitly updated, right? So I would need to make some dummy change on the interaction contacts so that the best record is recalculated considering the custom logic. Or is there some other better way?

By just publishing the custom logic existing contacts and their best record are not updated as already supposed. So I'm currently trying to identify the easiest way to dummy change all contacts in order to have the best records up-to-date considering the new custom logic.