Knowing when a new record is a copy of another record while saving the new record on the backend

Often there are situations when copying a record in the UI you wish to perform additional actions on the backend. Imagine creating a copy of an offer record in a CRM system where on the backend you’d like to duplicate the offer line records as well.

In this case the default functionality of the ProDataset where the only thing you know about a copied record is, that it’s new row (by the ROW-STATE attribute value of ROW-CREATED). The ProDataset just does not distinguish between a brand new row and a copy of another record.

For this purpose, we optionally maintain the field “SmartCopiedFrom” with information about the source record of a copy. To leverage this functionality, the Temp-Table in which you are creating a copy of another record should have a character field named “SmartCopiedFrom”. This character field should not be sourced from a database field.

The field value is (by default) either assigned with the value of the “SmartRecordKey” field column which we maintain for SmartBusinessEntities or the return value of the BufferHelper:UniqueFindPredicate method.

You can influence the value that is assigned to the “SmartCopiedFrom” field by overriding the GetCurrentRecordKey method in your custom overrides of both the SmartBusinessEntityAdapter and the SmartDatasetChildAdapter types.

In case of a copied record, the value of this field is accessible in the Business Entity and data access object when saving the Field values. When the ROW-STATE of a record is ROW-CREATED this allows you to distinguish between a copy of an existing record and a brand new record and also provides the information about the source record.

Sample Business Entity Implementation

    /*------------------------------------------------------------------------------
        Purpose: Provides a hook for high level data validation before Update 
                 operations                                                                     
        Notes:   Invoked during SaveChanges (). When the ERROR flag of the ProDataset
                 is set, the Update operation will be cancelled before writing back
                 the data to the database using the DataAccess object                                                                      
    ------------------------------------------------------------------------------*/
    METHOD OVERRIDE PUBLIC VOID ValidateData ():
        
        DEFINE VARIABLE cEntityKey              AS CHARACTER                    NO-UNDO .
        DEFINE VARIABLE oRequest                AS FetchDataRequest             NO-UNDO . 
        DEFINE VARIABLE hSourceDataset          AS HANDLE                       NO-UNDO .
        DEFINE VARIABLE hQuery                  AS HANDLE                       NO-UNDO .


        DEFINE QUERY qQuery FOR eActivity . 
        
        /* Preselect, as we are changing the ActivityID of the copied record below */
        OPEN QUERY qQuery PRESELECT EACH eActivity .
        GET FIRST qQuery . 
        
        DO WHILE NOT QUERY qQuery:QUERY-OFF-END ON ERROR UNDO, THROW:


            /* Handle copies of the parent record from the UI */                 
            IF ROW-STATE (eActivity) = ROW-CREATED AND eActivity.SmartCopiedFrom > "":U THEN DO ON ERROR UNDO, THROW: 
            
                /* Turn on tracking changes, we are still in an SaveChanges call, so all modifications
                   will be handled by the Data Access object */
                THIS-OBJECT:TrackingChanges = TRUE . 
                
                /* Assign new ActivityID */
                IF CharacterType:IsNullOrEmpty (eActivity.ActivityID) THEN 
                    ASSIGN eActivity.ActivityID = GUID .  
            
                /* Use a distinct Business Entity instance as oterwise we'd be calling into ourselved */
                ASSIGN cEntityKey = SUBSTITUTE ("&1&2&3", THIS-OBJECT:GetClass():TypeName, CHR (1), GUID) .
            
                /* Fetch Source Record and Children */
                ASSIGN oRequest = NEW FetchDataRequest ("eActivity,eActivityAttribute",
                                                        "for each eActivity " + eActivity.SmartCopiedFrom) .
            
                ServiceInterface:FetchData (cEntityKey, 
                                            oRequest, 
                                            OUTPUT DATASET-HANDLE hSourceDataset) .
            
                ASSIGN hQuery = QueryHelper:CreatePreparedQuery (hSourceDataset::eActivityAttribute:HANDLE) .    
             
                /* Duplicate child records */
                DO WHILE NOT hQuery:QUERY-OFF-END:
                    CREATE eActivityAttribute .
                    
                    ASSIGN eActivityAttribute.ActivityAttributeID = GUID 
                           eActivityAttribute.ActivityID          = eActivity.ActivityID
                           eActivityAttribute.AttributeID         = hSourceDataset::eActivityAttribute::AttributeID  
                           eActivityAttribute.Name                = hSourceDataset::eActivityAttribute::Name         
                           eActivityAttribute.IsMandatory         = hSourceDataset::eActivityAttribute::IsMandatory  
                           eActivityAttribute.DefaultValue        = hSourceDataset::eActivityAttribute::DefaultValue .
                    RELEASE eActivityAttribute .
                    
                    hQuery:GET-NEXT () . 
                END . 
                             
                THIS-OBJECT:TrackingChanges = FALSE . 
                
                FINALLY:
                    ServiceManager:StopBusinessService (cEntityKey, NotRunningServiceEnum:Ignore) .
                    IF VALID-HANDLE (hSourceDataset) THEN 
                        DELETE OBJECT hSourceDataset . 	
                
                    hSourceDataset = ? . 
                
                    GarbageCollectorHelper:DeleteObject (hQuery) .                        	   
                END FINALLY.                             
            END. 
 
            FINALLY:
                GET NEXT qQuery .
            END FINALLY.
        END.                            
    END METHOD .