/// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            UploadStoreSalesTransactions codeExample = new UploadStoreSalesTransactions();

            Console.WriteLine(codeExample.Description);

            // The Google Ads customer ID for which the call is made.
            long customerId = long.Parse("INSERT_CUSTOMER_ID_HERE");

            // The type of user data in the job (first or third party). If you have an official
            // store sales partnership with Google, use StoreSalesUploadThirdParty.
            // Otherwise, use StoreSalesUploadFirstParty or omit this parameter.
            OfflineUserDataJobType offlineUserDataJobType =
                OfflineUserDataJobType.StoreSalesUploadFirstParty;

            // The ID of a store sales conversion action.
            long conversionActionId = long.Parse("INSERT_CONVERSION_ACTION_ID_HERE");

            // Optional (but recommended) external ID to identify the offline user data job.
            long?externalId = long.Parse("INSERT_EXTERNAL_ID_HERE");

            // OPTIONAL: If uploading third party data, also specify the following values:
            string advertiserUploadDateTime = "INSERT_ADVERTISER_UPLOAD_DATE_TIME_HERE";
            string bridgeMapVersionId       = "INSERT_BRIDGE_MAP_VERSION_ID_HERE";
            long?  partnerId = long.Parse("INSERT_PARTNER_ID_HERE");


            codeExample.Run(new GoogleAdsClient(), customerId, conversionActionId,
                            offlineUserDataJobType: offlineUserDataJobType,
                            externalId: externalId,
                            advertiserUploadDateTime: advertiserUploadDateTime,
                            bridgeMapVersionId: bridgeMapVersionId,
                            partnerId: partnerId);
        }
        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <param name="conversionActionId">The ID of a store sales conversion action.</param>
        /// <param name="customKey">Optional: If uploading data with custom key and values, also specify this value.</param>
        /// <param name="offlineUserDataJobType">The type of user data in the job (first or third
        ///     party). If you have an official store sales partnership with Google, use
        ///     StoreSalesUploadThirdParty. Otherwise, use StoreSalesUploadFirstParty or
        ///     omit this parameter.</param>
        /// <param name="externalId">Optional (but recommended) external ID to identify the offline
        ///     user data job.</param>
        /// <param name="advertiserUploadDateTime">Date and time the advertiser uploaded data to the
        ///     partner. Only required if uploading third party data.</param>
        /// <param name="bridgeMapVersionId">Version of partner IDs to be used for uploads. Only
        ///     required if uploading third party data.</param>
        /// <param name="partnerId">ID of the third party partner. Only required if uploading third
        ///     party data.</param>
        public void Run(GoogleAdsClient client, long customerId, long conversionActionId,
                        string customKey = null,
                        OfflineUserDataJobType offlineUserDataJobType =
                        OfflineUserDataJobType.StoreSalesUploadFirstParty,
                        long?externalId           = null, string advertiserUploadDateTime = null,
                        string bridgeMapVersionId = null, long?partnerId                  = null)
        {
            // Get the OfflineUserDataJobServiceClient.
            OfflineUserDataJobServiceClient offlineUserDataJobServiceClient =
                client.GetService(Services.V4.OfflineUserDataJobService);

            // Ensure that a valid job type is provided.
            if (offlineUserDataJobType != OfflineUserDataJobType.StoreSalesUploadFirstParty &
                offlineUserDataJobType != OfflineUserDataJobType.StoreSalesUploadThirdParty)
            {
                Console.WriteLine("Invalid job type specified, defaulting to First Party.");
                offlineUserDataJobType = OfflineUserDataJobType.StoreSalesUploadFirstParty;
            }

            try
            {
                // Creates an offline user data job for uploading transactions.
                string offlineUserDataJobResourceName =
                    CreateOfflineUserDataJob(offlineUserDataJobServiceClient, customerId,
                                             offlineUserDataJobType, externalId, advertiserUploadDateTime,
                                             bridgeMapVersionId, partnerId, customKey);

                // Adds transactions to the job.
                AddTransactionsToOfflineUserDataJob(offlineUserDataJobServiceClient, customerId,
                                                    offlineUserDataJobResourceName, conversionActionId);

                // Issues an asynchronous request to run the offline user data job.
                offlineUserDataJobServiceClient.RunOfflineUserDataJobAsync(
                    offlineUserDataJobResourceName);

                Console.WriteLine("Sent request to asynchronously run offline user data job " +
                                  $"{offlineUserDataJobResourceName}.");

                // Offline user data jobs may take up to 24 hours to complete, so instead of waiting
                // for the job to complete, retrieves and displays the job status once and then
                // prints the query to use to check the job again later.
                CheckJobStatus(client, customerId, offlineUserDataJobResourceName);
            }
            catch (GoogleAdsException e)
            {
                Console.WriteLine("Failure:");
                Console.WriteLine($"Message: {e.Message}");
                Console.WriteLine($"Failure: {e.Failure}");
                Console.WriteLine($"Request ID: {e.RequestId}");
                throw;
            }
        }
        /// <summary>
        /// Creates an offline user data job for uploading store sales transactions.
        /// </summary>
        /// <param name="offlineUserDataJobServiceClient">The offline user data job service
        ///     client.</param>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <param name="offlineUserDataJobType">The type of user data in the job (first or third
        ///     party). If you have an official store sales partnership with Google, use
        ///     StoreSalesUploadThirdParty. Otherwise, use StoreSalesUploadFirstParty or
        ///     omit this parameter.</param>
        /// <param name="externalId">Optional (but recommended) external ID to identify the offline
        ///     user data job.</param>
        /// <param name="advertiserUploadDateTime">Date and time the advertiser uploaded data to the
        ///     partner. Only required if uploading third party data.</param>
        /// <param name="bridgeMapVersionId">Version of partner IDs to be used for uploads. Only
        ///     required if uploading third party data.</param>
        /// <param name="partnerId">ID of the third party partner. Only required if uploading third
        ///     party data.</param>
        /// <returns>The resource name of the created job.</returns>
        private string CreateOfflineUserDataJob(
            OfflineUserDataJobServiceClient offlineUserDataJobServiceClient, long customerId,
            OfflineUserDataJobType offlineUserDataJobType, long?externalId,
            string advertiserUploadDateTime, string bridgeMapVersionId, long?partnerId)
        {
            // TIP: If you are migrating from the AdWords API, please note that Google Ads API uses
            // the term "fraction" instead of "rate". For example, loyaltyRate in the AdWords API is
            // called loyaltyFraction in the Google Ads API.

            // Please refer to https://support.google.com/google-ads/answer/7506124 for additional
            // details.
            StoreSalesMetadata storeSalesMetadata = new StoreSalesMetadata()
            {
                // Sets the fraction of your overall sales that you (or the advertiser, in the third
                // party case) can associate with a customer (email, phone number, address, etc.) in
                // your database or loyalty program.
                // For example, set this to 0.7 if you have 100 transactions over 30 days, and out
                // of those 100 transactions, you can identify 70 by an email address or phone
                // number.
                LoyaltyFraction = 0.7,
                // Sets the fraction of sales you're uploading out of the overall sales that you (or
                // the advertiser, in the third party case) can associate with a customer. In most
                // cases, you will set this to 1.0.
                // Continuing the example above for loyalty fraction, a value of 1.0 here indicates
                // that you are uploading all 70 of the transactions that can be identified by an
                // email address or phone number.
                TransactionUploadFraction = 1.0
            };

            // Creates additional metadata required for uploading third party data.
            if (offlineUserDataJobType == OfflineUserDataJobType.StoreSalesUploadThirdParty)
            {
                StoreSalesThirdPartyMetadata storeSalesThirdPartyMetadata =
                    new StoreSalesThirdPartyMetadata()
                {
                    // The date/time must be in the format "yyyy-MM-dd hh:mm:ss".
                    AdvertiserUploadDateTime = advertiserUploadDateTime,

                    // Sets the fraction of transactions you received from the advertiser that
                    // have valid formatting and values. This captures any transactions the
                    // advertiser provided to you but which you are unable to upload to Google
                    // due to formatting errors or missing data.
                    // In most cases, you will set this to 1.0.
                    ValidTransactionFraction = 1.0,

                    // Sets the fraction of valid transactions (as defined above) you received
                    // from the advertiser that you (the third party) have matched to an
                    // external user ID on your side.
                    // In most cases, you will set this to 1.0.
                    PartnerMatchFraction = 1.0,

                    // Sets the fraction of transactions you (the third party) are uploading out
                    // of the transactions you received from the advertiser that meet both of
                    // the following criteria:
                    // 1. Are valid in terms of formatting and values. See valid transaction
                    // fraction above.
                    // 2. You matched to an external user ID on your side. See partner match
                    // fraction above.
                    // In most cases, you will set this to 1.0.
                    PartnerUploadFraction = 1.0,

                    // Sets the version of partner IDs to be used for uploads.
                    // Please speak with your Google representative to get the values to use for
                    // the bridge map version and partner IDs.
                    BridgeMapVersionId = bridgeMapVersionId,

                    // Sets the third party partner ID uploading the transactions.
                    PartnerId = partnerId
                };

                storeSalesMetadata.ThirdPartyMetadata = storeSalesThirdPartyMetadata;
            }

            // Creates a new offline user data job.
            OfflineUserDataJob offlineUserDataJob = new OfflineUserDataJob()
            {
                Type = offlineUserDataJobType,
                StoreSalesMetadata = storeSalesMetadata
            };

            if (externalId.HasValue)
            {
                offlineUserDataJob.ExternalId = externalId.Value;
            }

            // Issues a request to create the offline user data job.
            CreateOfflineUserDataJobResponse createOfflineUserDataJobResponse =
                offlineUserDataJobServiceClient.CreateOfflineUserDataJob(
                    customerId.ToString(), offlineUserDataJob);
            string offlineUserDataJobResourceName = createOfflineUserDataJobResponse.ResourceName;

            Console.WriteLine("Created an offline user data job with resource name: " +
                              $"{offlineUserDataJobResourceName}.");
            return(offlineUserDataJobResourceName);
        }