web
You’re offline. This is a read only version of the page.
close
Skip to main content
Community site session details

Community site session details

Session Id :
Supply chain | Supply Chain Management, Commerce
Unanswered

How to retrieve and email a customreceipt type receipt

(0) ShareShare
ReportReport
Posted on by 1,455

I have to email a CustomReceiptType1 receipt from a custom POS operation button. We are on V10.0.22.

The print part has no problem picking up the custom receipt type. However if the hardwareStation is setup as NONE and the customReceiptType is flag to allow for email, then it should email the receipt.

If you read "RetailSDK\Documents\SampleExtensionsInstructions\Receipts\readme.txt" it only give instructions for the printing part as if it can only print custom receipt types.

If you look at "RetailSDK\SampleExtensions\CommerceRuntime\Extensions.ReceiptsSample\CustomReceiptService.cs" and scroll down to method "GetCustomEmailReceiptsAsync", you see the following comment:

/// When adding support for new receipt type here, please make sure to mark the new receipt type as email compatible by updating 'RetailReceiptTypeConfiguration' table in HQ.
/// To update, override 'populateRetailReceiptTypeConfigurationTable' method on the table and use Commerce Parameters -> General -> Initialize.

I did the above custom using chain of command. I clicked on the initialize button they said to run but it never reaches the 'populateRetailReceiptTypeConfigurationTable' method on that table. So don't know if the latter is outdated. I created then a custom Initialize button that calls the 'populateRetailReceiptTypeConfigurationTable' method on the table. If I look in the DB the customReceiptType1 (code 101) is now showing it is enabled for email. I also looked in the channel DB table on my Dev box and even there it shows that it can be enabled. So the distribution schedule did push it to the channel DB.

However when I try to apply the logic for finding the custom receipt type receipt as shown in "RetailSDK\SampleExtensions\CommerceRuntime\Extensions.ReceiptsSample\CustomReceiptService.cs", method "GetCustomEmailReceiptsAsync", I get errors that the CustomReceiptType1 is not a valid receipt type.

IF I run the part that tries to first find the original receipts with GetEmailReceiptServiceRequest but pass in the CustomReceiptType1 as the receipt type, I get error :

***

Microsoft_Dynamics_Commerce_Runtime_ReceiptTypeNotSupported
Receipt for an invalid receipt type CustomReceipt1 was requested

***

If I run just the custom receipt part because I'm not actually interested in other receipt types that can be emailed, I only want the receipt for CustomReceiptType1, I also get an error that the receipt type is not valid:

***

Exception: Microsoft.Dynamics.Commerce.Runtime.ConfigurationException: Unknown request type Microsoft.Dynamics.Commerce.Runtime.Messages.GetCustomReceiptsRequest.

   at Microsoft.Dynamics.Commerce.Runtime.CommerceRuntime.<Execute>d__43`1.MoveNext()

***

I'm working with Microsoft on this but is not getting anywhere at this point.  And it is now becoming urgent to get this to work. The last correspondence yesterday was that I need to check to see if feature 'Email any receipt type and customize emailed receipts ' is enabled. It is enabled on my Dev box. So it is definitely not the feature that is causing the standard triggers not to recognize that the customReceiptType1 is email-enabled.

Setting in D365:

T1.PNG

Please note that on the CRT and POS side the receipt type change to ReceiptType.CustomReceipt1. However the value behind the ENUM is still 101 for both.

"RetailSDK\SampleExtensions\CommerceRuntime\Extensions.SuspendTransactionReceiptSample" is a great example, however it only prints, no email part.

Below is my code. Can anybody who has successfully retrieved a CustomReceiptType receipt for email, please look at my code and let me know what is wrong?  Or is the standard code not able to email customReceiptType receipts even though their is example code. Maybe the example code is outdated.

Please note that because I have to add a custom POS operation, I have a custom request/response trigger pair that is called from a custom RetailServer controller that is again called via the DataServices on the POS side when a button linked to the custom POS operation is clicked.

namespace TMCCustomReceiptTriggerHandlersCRT
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.Threading.Tasks;
    using Microsoft.Dynamics.Commerce.Runtime;
    using Microsoft.Dynamics.Commerce.Runtime.DataModel;
    using Microsoft.Dynamics.Commerce.Runtime.Messages;
    using Microsoft.Dynamics.Commerce.Runtime.Services;
    using Microsoft.Dynamics.Commerce.Runtime.Services.Messages;
    using Microsoft.Dynamics.Commerce.Runtime.Workflow.Orders;

    class GetCustomReceiptTypesRequestHandler : IRequestHandlerAsync
    {
        public IEnumerable SupportedRequestTypes
        {
            get
            {
                return new[]
                {
                    typeof(CustomReceiptRequestTMC)
                };
            }
        }

        public async Task Execute(Request request)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            Type reqType = ((object)request).GetType();

            if (reqType == typeof(CustomReceiptRequestTMC))
            {
                return await this.GetReceipts((CustomReceiptRequestTMC)request).ConfigureAwait(false);
            }
            else
            {
                throw new NotSupportedException(string.Format((IFormatProvider)CultureInfo.InvariantCulture, "Request '{0}' is not supported.", (object)((object)request).GetType()));
            }
        }


      //  protected override async Task Process(GetCustomReceiptsRequest request)
        private async Task GetReceipts(CustomReceiptRequestTMC request)
        {
            SalesOrder salesOrder;

            ThrowIf.Null(request.receiptCriteria, "request.ReceiptRetrievalCriteria");

            //1. The sales order that we are printing receipts for is retrieved.
            salesOrder = await this.GetSalesOrderForTransactionWithId(request.RequestContext, request.transactionId).ConfigureAwait(false);
            if (salesOrder == null)
            {
                throw new DataValidationException(
                    DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ObjectNotFound,
                    string.Format("Unable to get the sales order created. ID: {0}", request.transactionId));
            }

            Collection result = new Collection();

            // 2. Now we can handle any additional receipt here.
            switch (request.receiptCriteria.ReceiptType)
            {
                // Proforma Receipt 
                case ReceiptType.CustomReceipt1:
                    {
                        IEnumerable customReceipts = await this.GetCustomReceiptsAsync(salesOrder, request).ConfigureAwait(false);

                        result.AddRange(customReceipts);
                    }

                    break;

                default:
                    // Add more logic to handle more types of custom receipt types.
                    break;
            }

            return new CustomReceiptResponseTMC(new ReadOnlyCollection(result));
        }

        private async Task GetSalesOrderForTransactionWithId(RequestContext requestContext, string transactionId)
        {
            SalesOrder salesOrder = new SalesOrder();
            SalesTransaction salesTransaction = await SalesTransactionRepository.LoadSalesTransaction(requestContext, transactionId).ConfigureAwait(false);

            if (salesTransaction != null)
            {
                // The sales transaction is converted into a sales order so that existing receipt fields can be used.
                salesOrder.CopyFrom(salesTransaction);
            }
            else
            {
                throw new DataValidationException(
                DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ObjectNotFound,
                string.Format("Unable to get the sales transaction. ID: {0}", transactionId));
            }
            return salesOrder;
        }

        private async Task<Collection> GetCustomReceiptsAsync(SalesOrder salesOrder, CustomReceiptRequestTMC request)
        {
            //Full sample code: RetailSDK\SampleExtensions\CommerceRuntime\Extensions.SuspendTransactionReceiptSample folder.
            // Also "RetailSDK\SampleExtensions\CommerceRuntime\Extensions.ReceiptsSample"

            Collection result = new Collection();
            ReceiptRetrievalCriteria criteria = request.receiptCriteria;
            RequestContext requestContext = request.RequestContext;

            // Call receipt service to get the custom receipt if printed.
            if (criteria.HardwareProfileId != "NONE")
            {
                var getReceiptServiceRequest = new GetReceiptServiceRequest(
                    salesOrder,
                    new Collection { criteria.ReceiptType },
                    salesOrder.TenderLines,
                    criteria.IsCopy,
                    criteria.IsPreview,
                    criteria.HardwareProfileId,
                    includeExternalReceipt: false,
                    requestedReceiptType: criteria.ReceiptType);
                var customReceiptsResponse = await requestContext.ExecuteAsync(getReceiptServiceRequest).ConfigureAwait(false);
                ReadOnlyCollection customReceipts = customReceiptsResponse.Receipts;

                // Add the custom receipt to the result collection.
                result.AddRange(customReceipts);
            }
            else
            {
                List vList = await this.GetCustomEmailReceiptsAsync(request).ConfigureAwait(false);

                ReadOnlyCollection customEmailReceipts = vList.AsReadOnly();
            }
            return result;
        }

        private async Task<List> GetCustomEmailReceiptsAsync(CustomReceiptRequestTMC request)
        {
            List result = new List(); ;

            ReceiptRetrievalCriteria criteria = request.receiptCriteria;
            criteria.QueryBySalesId = true;
            
            GetCustomReceiptsRequest getCustomReceiptsRequest = new GetCustomReceiptsRequest(request.transactionId, criteria);
            switch (criteria.ReceiptType)
            {
                case ReceiptType.CustomReceipt1:
                    {
                        var customReceipts = (await request.RequestContext.Runtime.ExecuteAsync(
                            getCustomReceiptsRequest, request.RequestContext).ConfigureAwait(false)).Receipts;

                        result.AddRange(customReceipts);
                    }
                    break;
            }

            return result;
        }

    }
}

The above works for GetReceiptServiceRequest when there is a hardware station ID other than NONE.

This is how I populate the receiptCriteria on the POS side in the handler of the custom POS operation ID:

let receiptRetrievalCriteria: ProxyEntities.ReceiptRetrievalCriteria = {
                                IsCopy: false,
                                IsRemoteTransaction: false,
                                IsPreview: false,
                                QueryBySalesId: true,
                                ReceiptTypeValue: ProxyEntities.ReceiptType.CustomReceipt1,
                                HardwareProfileId: hardwareProfile.ProfileId
                            };
                            let getCustomReceiptRequest: StoreOperations.TMCGetCustomReceiptActionRequest =
                                new StoreOperations.TMCGetCustomReceiptActionRequest(receiptRetrievalCriteria, salesOrderId, vRequestfor);

                            return me.context.runtime.executeAsync(getCustomReceiptRequest);

To print the receipt retrieved by GetReceiptserviceRequest when there is a hardware station ID other than NONE, I call the following code

public PrintReceipt(context: IExtensionContext, receipts: ProxyEntities.Receipt[]): Promise {

        // Prints the receipts.
        let printerPrintRequest: PrinterPrintRequest = new PrinterPrintRequest(receipts);

        return context.runtime.executeAsync(printerPrintRequest);
 
    }

The above works. I used the logic in the "RetailSDK\POS\Extensions\SuspendTransactionReceiptSample" example. 

But as I said this example has no code for emailing the custom receipt type receipt.

If I can finally get the retrieval of the customReceiptType1 working, how do I email it?

I called GetReceiptEmailAddressClientRequest/GetReceiptEmailAddressClientResponse pair to prompt for the email address, but that is how far I got on the POS side. I do not see any trigger that is exposed that I can use to do the actual email of the receipt.

So any help is much appreciated. I HAVE TO get the email part to work. The customers want emails, not printouts. This has now become urgent. I will continue to try and work with Microsoft in the meantime but I don't have much hope at this point that I will get it to work from that end. It doesn't look like they have done it with a CustomReceiptType.  It is just suppose dto work in theory.

So anybody who did it in real life?

I have the same question (0)
  • ToddB Profile Picture
    on at
    RE: How to retrieve and email a customreceipt type receipt

    Hi Retha,

    As you are already working with Microsoft on this I will leave it here for the community to respond.

  • Retha Profile Picture
    1,455 on at
    RE: How to retrieve and email a customreceipt type receipt

    There was a missing piece in the Microsoft documentation. they will update the documentation. When feature 'Email any receipt type and customize emailed receipts ' is enabled, one has to pass in the receipt layout ID as well.

    I did a test on my Dev box and it seems that at least one cannot add a receipt profile for the same receipt format and receipt type.

    So this should be okay.

    I finally got the email to work. I had to send the email in my CRT code because there is no request/response trigger exposed to me on the POS side like there is for the printing.

    Here is the handler. I added a change to pass in the email address so that it is prompted for on the POS side for future use when it needs to prompt:

    namespace TMCCustomReceiptTriggerHandlersCRT
    {
        using System;
        using System.Collections.Generic;
        using System.Collections.ObjectModel;
        using System.Globalization;
        using System.Linq;
        using System.Threading.Tasks;
        using Microsoft.Dynamics.Commerce.Runtime;
        using Microsoft.Dynamics.Commerce.Runtime.DataModel;
        using Microsoft.Dynamics.Commerce.Runtime.DataServices.Messages;
        using Microsoft.Dynamics.Commerce.Runtime.Messages;
        using Microsoft.Dynamics.Commerce.Runtime.RealtimeServices.Messages;
        using Microsoft.Dynamics.Commerce.Runtime.Services.Messages;
        using Microsoft.Dynamics.Commerce.Runtime.TransactionService.Serialization;
        using Microsoft.Dynamics.Commerce.Runtime.Workflow;
        using Microsoft.Dynamics.Commerce.Runtime.Workflow.Orders;
    
        class GetCustomReceiptTypesRequestHandler : IRequestHandlerAsync
        {
            public IEnumerable SupportedRequestTypes
            {
                get
                {
                    return new[]
                    {
                        typeof(CustomReceiptRequestTMC)
                    };
                }
            }
    
            public async Task Execute(Request request)
            {
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }
    
                Type reqType = ((object)request).GetType();
    
                if (reqType == typeof(CustomReceiptRequestTMC))
                {
                    return await this.GetReceipts((CustomReceiptRequestTMC)request).ConfigureAwait(false);
                }
                else
                {
                    throw new NotSupportedException(string.Format((IFormatProvider)CultureInfo.InvariantCulture, "Request '{0}' is not supported.", (object)((object)request).GetType()));
                }
            }
    
    
            private async Task GetReceipts(CustomReceiptRequestTMC request)
            {
                SalesOrder salesOrder;
                RequestContext requestContext = request.RequestContext;
                int vPrintBehaviour = 1; //1	DO_NOT_PRINT
    
                ThrowIf.Null(request.receiptCriteria, "request.ReceiptRetrievalCriteria");
    
                //1. The sales order that we are printing/emailing receipts for, is retrieved.
                salesOrder = await this.GetSalesOrderForTransactionWithId(request).ConfigureAwait(false);
                if (salesOrder == null)
                {
                    throw new DataValidationException(
                        DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ObjectNotFound,
                        string.Format("Unable to get the sales order created. ID: {0}", request.orderId));
                }
    
                Collection result = new Collection();
    
                // 2. Retrieve the custom receipts for the receipt type passed in
                if(request.receiptCriteria.ReceiptType != 0)
                {
                    // need to determine if the receiptFormat linked to the hardwarestation is flagged to print
                    vPrintBehaviour = await this.GetPrintBehaviour(requestContext, request.receiptCriteria.ReceiptType, request.receiptCriteria.HardwareProfileId).ConfigureAwait(false); 
    
                    // Proforma Receipt = CustomReceipt1
                    // Packingslip receipt = CustomReceipt2
                    // code must pull any custom receipt type that is passed in
                    /* Print behaviour AX enum: RetailPrintBehaviourBase
                     * enumValue	Name
                        0	ALWAYS_PRINT
                        1	DO_NOT_PRINT
                        2	PROMPT_USER
                        3	AS_REQUIRED
                     * */
                    if (vPrintBehaviour != 1)
                    {
                        IEnumerable customReceipts = await this.GetPrintReceiptsAsync(salesOrder, request).ConfigureAwait(false);
                        result.AddRange(customReceipts);
                    }
    
                    // The next code is for emailing of the receipts where applicable as determined by the RetailEmailBehaviorBase
                    Boolean vOK = await this.GetCustomEmailReceiptsAsync(salesOrder, request).ConfigureAwait(false);
                }
    
                return new CustomReceiptResponseTMC(new ReadOnlyCollection(result));
            }
    
            private async Task GetSalesOrderForTransactionWithId(CustomReceiptRequestTMC _request)
            {
                SalesOrder salesOrder = new SalesOrder();
    
                if (_request.requestFor == "Packslip")
                {
                    // Get the packingslip
                    var GetFulfillmentByPackslipRequest = new GetFulfillmentLinesByPackingSlipIdRealtimeRequest(_request.orderId, _request.packslipId);
                    GetFulfillmentLinesByPackingSlipIdRealtimeResponse responsePackLines = await (RequestContextExtensions.ExecuteAsync(_request.RequestContext, GetFulfillmentByPackslipRequest)).ConfigureAwait(false);
                    // Get the salesOrder linked to packingslip
                    var recallCustOrderRequest = new RecallCustomerOrderRealtimeRequest(_request.orderId, false);
                    SalesOrder recallOrder = (await ((Task)RequestContextExtensions.ExecuteAsync(_request.RequestContext, recallCustOrderRequest)).ConfigureAwait(false)).SalesOrder;
                    if (recallOrder == null)
                    {
                        throw new DataValidationException((DataValidationErrors)17, string.Format("Unable to get the sales order created. Sales ID: {0}", (object)_request.orderId));
                    }
    
                    salesOrder = this.SetPackingQuantity(recallOrder, responsePackLines.FulfillmentLines);
    
                    /* IMPORTANT: Rather use teh standard code that already calculates the totals for the standard packslip receipt
                     * 
                    InvokeExtensionMethodRealtimeRequest extensionRequest = new InvokeExtensionMethodRealtimeRequest("TMC_GetFulfillmentLinesByPackingSlipId", _request.orderId, _request.packslipId);
                    InvokeExtensionMethodRealtimeResponse response = await _request.RequestContext.ExecuteAsync(extensionRequest).ConfigureAwait(false); ;
                    ReadOnlyCollection results = response.Result;
                    string vResultXmlStr = (string)results[0];
                    salesOrder = SerializationHelper.DeserializeObjectDataContractFromXml(vResultXmlStr);
                    *
                    */
                }
                else
                {
                    SalesTransaction salesTransaction = await SalesTransactionRepository.LoadSalesTransaction(_request.RequestContext, _request.orderId).ConfigureAwait(false);
    
                    if (salesTransaction != null)
                    {
                        // The sales transaction is converted into a sales order so that existing receipt fields can be used.
                        salesOrder.CopyFrom(salesTransaction);
                    }
                    else
                    {
                        // Just get the sales order for the orderId
                        var recallCustOrderRequest = new RecallCustomerOrderRealtimeRequest(_request.orderId, false);
                        salesOrder = (await ((Task)RequestContextExtensions.ExecuteAsync(_request.RequestContext, recallCustOrderRequest)).ConfigureAwait(false)).SalesOrder;
                    }
                }
                return salesOrder;
            }
    
            // copied from Microsoft.Dynamics.Commerce.Runtime.Workflow.GetReceiptRequestHandler
            private SalesOrder SetPackingQuantity(
                  SalesOrder salesOrder,
                  IEnumerable packingLines)
            {
                Dictionary dictionary = this.GetLineIdQuantityDictionary(packingLines);
                ((SalesTransaction)salesOrder).SalesLines = new Collection((IList)new List(((IEnumerable)((SalesTransaction)salesOrder).SalesLines).Where((Func)(l => dictionary.ContainsKey(l.LineNumber)))));
                foreach (SalesLine salesLine in ((SalesTransaction)salesOrder).SalesLines)
                    salesLine.QuantityPacking = new Decimal?(dictionary[salesLine.LineNumber]);
                return salesOrder;
            }
    
            // copied from Microsoft.Dynamics.Commerce.Runtime.Workflow.GetReceiptRequestHandler
            private Dictionary GetLineIdQuantityDictionary(
                  IEnumerable packingLines)
            {
                Dictionary dictionary = new Dictionary();
                foreach (FulfillmentLineParameter packingLine in packingLines)
                {
                    if (!packingLine.Quantity.HasValue)
                        throw new DataValidationException((DataValidationErrors)161, "The quantity of the line to print packing slip must not be null.");
                    dictionary.Add(packingLine.SalesLineNumber, packingLine.Quantity.Value);
                }
                return dictionary;
            }
    
            /// 
            ///  01/24/2022 - ticket 8743 - determine the printer behaviour flag on the receiptLayout
            /// 
            /// 
            /// 
            /// 
            /// 
            private async Task GetPrintBehaviour(RequestContext _context, ReceiptType _receiptType, string _hardwareProfileId)
            {
                int vPrintBehaviour = 0; //0	ALWAYS_PRINT
    
                // need to determine if the receiptFormat linked to the hardwarestation is flagged to print
                var hardwareProfileRequest = new GetHardwareProfileDataRequest(_hardwareProfileId, QueryResultSettings.SingleRecord);
                SingleEntityDataServiceResponse hwResponse = await _context.ExecuteAsync<SingleEntityDataServiceResponse>(hardwareProfileRequest).ConfigureAwait(false);
                IEnumerable printersWithReceiptProfile = hwResponse.Entity.Printers.Where((Func)(p => !p.DeviceType.Equals((object)(DeviceType)0) && !string.IsNullOrWhiteSpace(p.ReceiptProfileId)));
                if (!IEnumerableExtensions.IsNullOrEmpty(printersWithReceiptProfile))
                {
                    Dictionary receiptLayoutPreviewReceiptsMap = new Dictionary();
                    foreach (HardwareProfilePrinter hardwareProfilePrinter in printersWithReceiptProfile)
                    {
                        var getReceiptLayoutIdRequest = new GetReceiptLayoutIdDataRequest(hardwareProfilePrinter.ReceiptProfileId, _receiptType, QueryResultSettings.SingleRecord);
                        string receiptLayoutId = (await _context.Runtime.ExecuteAsync<SingleEntityDataServiceResponse>(getReceiptLayoutIdRequest, _context).ConfigureAwait(false)).Entity;
                        if (!string.IsNullOrEmpty(receiptLayoutId))
                        {
                            var receiptLayoutInfoRequest = new GetReceiptInfoDataRequest(receiptLayoutId, true, QueryResultSettings.SingleRecord);
                            ReceiptInfo receiptFormat = (await _context.Runtime.ExecuteAsync<SingleEntityDataServiceResponse>(receiptLayoutInfoRequest, _context).ConfigureAwait(false)).Entity;
                            if (receiptFormat != null)
                            {
                                vPrintBehaviour = receiptFormat.PrintBehavior;
                                // only take the first one that has a receiptLayoutId on it
                                break;
                            }
                        }
                    }
                }
    
                return vPrintBehaviour;
    
            }
    
            /// 
            ///  01/20/2022 - ticket 8743 - pull the receipts to be printed 
            /// 
            /// 
            /// 
            /// 
            private async Task<Collection> GetPrintReceiptsAsync(SalesOrder salesOrder, CustomReceiptRequestTMC request)
            {
                //Full sample code: RetailSDK\SampleExtensions\CommerceRuntime\Extensions.SuspendTransactionReceiptSample folder.
                // Also "RetailSDK\SampleExtensions\CommerceRuntime\Extensions.ReceiptsSample"
    
                Collection result = new Collection();
                ReceiptRetrievalCriteria criteria = request.receiptCriteria;
                RequestContext requestContext = request.RequestContext;
    
                // Call receipt service to get the custom receipt if printed.
                if (criteria.HardwareProfileId != "NONE")
                {
                    var getReceiptServiceRequest = new GetReceiptServiceRequest(
                        salesOrder,
                        new Collection { criteria.ReceiptType },
                        salesOrder.TenderLines,
                        criteria.IsCopy,
                        criteria.IsPreview,
                        criteria.HardwareProfileId,
                        includeExternalReceipt: false,
                        requestedReceiptType: criteria.ReceiptType);
                    var customReceiptsResponse = await requestContext.ExecuteAsync(getReceiptServiceRequest).ConfigureAwait(false);
                    ReadOnlyCollection customReceipts = customReceiptsResponse.Receipts;
    
                    // Add the custom receipt to the result collection.
                    result.AddRange(customReceipts);
                }
                return result;
            }
    
            /// 
            ///  01/14/2022 - ticket 8743 - process the custom receipts for Email 
            /// 
            /// 
            /// 
            /// 
            private async Task GetCustomEmailReceiptsAsync(SalesOrder salesOrder, CustomReceiptRequestTMC _request)
            {
                ReceiptRetrievalCriteria criteria = _request.receiptCriteria;
                RequestContext requestContext = _request.RequestContext;
    
                ICollection extProp = salesOrder.ExtensionProperties;
                string vReceiptEmail = _request.emailAddress;
                string emailMessage = string.Empty;
                bool vEmail = false;
    
                if(string.IsNullOrWhiteSpace(vReceiptEmail))
                {
                    vReceiptEmail = salesOrder.ReceiptEmail;
                }
                if (string.IsNullOrWhiteSpace(vReceiptEmail))
                {
                    foreach (CommerceProperty prop in extProp)
                    {
                        if (prop.Key == "RetailReceiptEmail")
                        {
                            if (prop.Value.StringValue != "")
                            {
                                vReceiptEmail = prop.Value.StringValue;
                            }
                        }
                    }
                }
                if (string.IsNullOrWhiteSpace(vReceiptEmail))
                {
                    var getCustomerRequest = new GetCustomerDataRequest((salesOrder).CustomerId);
                    Customer custEntity = (await ((Task<SingleEntityDataServiceResponse>)RequestContextExtensions.ExecuteAsync<SingleEntityDataServiceResponse>(requestContext, getCustomerRequest)).ConfigureAwait(false))?.Entity;
                    if (custEntity != null)
                    {
                        vReceiptEmail = custEntity.ReceiptEmail;
                    }
                }
    
                if (!string.IsNullOrWhiteSpace(vReceiptEmail))
                {
                    //Get the receipt layout ID for the receiptType passed in and receipt profile ID on the user's current store
                    var orgUnitDataRequest = new SearchOrgUnitDataRequest(requestContext.GetPrincipal().ChannelId);
                    var OrgUnitResponse = await RequestContextExtensions.ExecuteAsync<EntityDataServiceResponse>(requestContext, orgUnitDataRequest).ConfigureAwait(false);
                    OrgUnit store = OrgUnitResponse.SingleOrDefault();
                    if (!string.IsNullOrWhiteSpace(store.EmailReceiptProfileId))
                    {
                        var getReceiptLayoutIdRequest = new GetReceiptLayoutIdDataRequest(store.EmailReceiptProfileId, criteria.ReceiptType, QueryResultSettings.SingleRecord);
                        string receiptLayoutId = (await requestContext.Runtime.ExecuteAsync<SingleEntityDataServiceResponse>(getReceiptLayoutIdRequest, requestContext).ConfigureAwait(false)).Entity;
    
                        if (!string.IsNullOrWhiteSpace(receiptLayoutId))
                        {
                            var receiptLayoutInfoRequest = new GetReceiptInfoDataRequest(receiptLayoutId, true, QueryResultSettings.SingleRecord);
                            ReceiptInfo receiptFormat = (await requestContext.Runtime.ExecuteAsync<SingleEntityDataServiceResponse>(receiptLayoutInfoRequest, requestContext).ConfigureAwait(false)).Entity;
                            if(receiptFormat != null)
                            {
                                if (receiptFormat.EmailBehavior != ReceiptEmailBehavior.DoNotEmail)
                                {
                                    vEmail = true;
                                }
                            }
    
                            if (vEmail)
                            {
                                GetEmailReceiptServiceRequest getEmailReceiptServiceRequest = new GetEmailReceiptServiceRequest(
                                salesOrder,
                                new Collection { criteria.ReceiptType },
                                salesOrder.TenderLines,
                                criteria.IsCopy,
                                receiptLayoutId);
                                GetEmailReceiptServiceResponse emailReceiptsResponse = await requestContext.ExecuteAsync(getEmailReceiptServiceRequest).ConfigureAwait(false);
    
                                if (emailReceiptsResponse.Receipts == null)
                                    emailMessage = string.Empty;
                                else if (emailReceiptsResponse.Receipts.Results.Count == 0)
                                {
                                    emailMessage = string.Empty;
                                }
                                else
                                {
                                    PagedResult customReceipts = emailReceiptsResponse.Receipts;
                                    emailMessage = ReceiptWorkflowHelper.ConvertReceiptToHtml(((IEnumerable)customReceipts).First());
                                    string str = await ReceiptWorkflowHelper.GetLanguageFromSalesOrder(requestContext, salesOrder).ConfigureAwait(false);
    
                                    Collection collectionNP = new Collection();
                                    NameValuePair nameValuePair1 = new NameValuePair()
                                    {
                                        Name = "message",
                                        Value = emailMessage
                                    };
                                    collectionNP.Add(nameValuePair1);
                                    // The receiptEmailTemplate is hardcoded in Microsoft.Dynamics.Commerce.Runtime.Workflow.GetReceiptRequestHandler
                                    string receiptEmailTemplate = "EmailRecpt";
    
                                    SendEmailRealtimeRequest sendEmailRequest = new SendEmailRealtimeRequest(vReceiptEmail, (ICollection)collectionNP, str, (object)string.Empty, receiptEmailTemplate);
                                    sendEmailRequest.RequestContext = requestContext;
                                    NullResponse nullResponse = await (RequestContextExtensions.ExecuteAsync(requestContext, sendEmailRequest)).ConfigureAwait(false);
                                    emailMessage = string.Empty;
                                }
                            }
                        }
                    }
                }
                return true;
            }
    
        }
    }
    
    :

    The code on the POS side that I use to to get the receipts as well as prompt for the email address when applicable. I added it in a common class so that I can call it when needed

        public static GetReceipts(_context: IExtensionContext, _salesId: string, _packslipID: string, _RequestedFor: string, _EmailPrompt: boolean): Promise {
            return this.GetEmailAddressSend(_context, _EmailPrompt)
                .then((responseEmail: string) => {
                    let vEmailAddress: string = responseEmail;
    
                    return _context.runtime.executeAsync(new GetHardwareProfileClientRequest())
                        .then((response: ClientEntities.ICancelableDataResult)
                            : Promise<ClientEntities.ICancelableDataResult> => {
    
                            let hardwareProfile: ProxyEntities.HardwareProfile = response.data.result;
    
                            let vReceiptTypeToUse: ProxyEntities.ReceiptType;
    
                            switch (_RequestedFor) {
                                case "Proforma": {
                                    vReceiptTypeToUse = ProxyEntities.ReceiptType.CustomReceipt1;
                                    break;
                                }
                                case "Packslip": {
                                    vReceiptTypeToUse = ProxyEntities.ReceiptType.CustomReceipt2;
                                    break;
                                }
                                default: {
                                    let vError: ClientEntities.ExtensionError
                                        = new ClientEntities.ExtensionError("The requested for receipts is invalid, there will be no custom receipt receupt type for it.");
                                    _context.logger.logError("CommonHelper.GetReceipts execute error: "   JSON.stringify(vError));
    
                                    return Promise.reject(vError);
                                }
                            }
    
                            // Gets the receipts. 
                            let salesOrderId: string = _salesId;
                            let pId: string = _packslipID;
    
                            if (hardwareProfile) {
                                let vRequestfor: string = _RequestedFor;
                                let receiptRetrievalCriteria: ProxyEntities.ReceiptRetrievalCriteria = {
                                    IsCopy: false,
                                    IsRemoteTransaction: false,
                                    IsPreview: false,
                                    QueryBySalesId: true,
                                    ReceiptTypeValue: vReceiptTypeToUse,
                                    HardwareProfileId: hardwareProfile.ProfileId
                                };
                                let getCustomReceiptRequest: StoreOperations.TMCGetCustomReceiptActionRequest =
                                    new StoreOperations.TMCGetCustomReceiptActionRequest(
                                        receiptRetrievalCriteria,
                                        salesOrderId,
                                        pId,
                                        vRequestfor,
                                        vEmailAddress);
    
                                return _context.runtime.executeAsync(getCustomReceiptRequest);
                            }
                            else {
                                MessageService.ShowMessage(this, _context.resources.getString("tmc_109"), _context.resources.getString("tmc_110"));
                                let vError2: ClientEntities.ExtensionError
                                    = new ClientEntities.ExtensionError("No Hardware profile setup");
    
                                return Promise.reject(vError2);
                            }
                        })
                        .then((response: ClientEntities.ICancelableDataResult)
                            : Promise => {
                            let receipts: ProxyEntities.Receipt[] = response.data.result;
    
                            // Prints the receipts.
                            if (receipts != null && receipts.length > 0) {
                                return this.PrintReceipt(_context, receipts);
                            }
                            else {
                                return Promise.resolve({ canceled: false });
                            }
                        }).then((): Promise => {
                            // Resolves to a void result when fulfilled.
                            return Promise.resolve();
                        }).catch((reason: any): Promise => {
                            // Resolves to a void result when rejected. This matches existing POS printing behavior.
                            _context.logger.logError("CommonHelper on GetReceipts execute error: "   JSON.stringify(reason));
                            return Promise.resolve();
                        });
                });
    
        }
    
        public static GetEmailAddressSend(context: IExtensionContext, _EmailPrompt: boolean): Promise {
            let cRequest: GetReceiptEmailAddressClientRequest =
                new GetReceiptEmailAddressClientRequest(context.logger.getNewCorrelationId());
    
            if (_EmailPrompt) {
                return context.runtime.executeAsync(cRequest)
                    .then((cResponse: ClientEntities.ICancelableDataResult)
                        : Promise => {
                        let vEmailAddress: string = "";
    
                        if (!ObjectExtensions.isNullOrUndefined(cResponse)) {
                            vEmailAddress = cResponse.data.result.emailAddress;
                            context.logger.logInformational("GetEmailAddress on CommonHelper successful: "   JSON.stringify(vEmailAddress));
                        }
                        return Promise.resolve(vEmailAddress);
                    })
                    .catch((reason: any): any => {
                        context.logger.logError("GetEmailAddress and email on CommonHelper: "   JSON.stringify(reason));
                        return Promise.resolve("");
                    });
            }
            else {
                return Promise.resolve("");
            }
        }
    
         public static PrintReceipt(context: IExtensionContext, receipts: ProxyEntities.Receipt[]): Promise {
    
            // Prints the receipts.
            let printerPrintRequest: PrinterPrintRequest = new PrinterPrintRequest(receipts);
    
            return context.runtime.executeAsync(printerPrintRequest);
    
        }
    

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Responsible AI policies

As AI tools become more common, we’re introducing a Responsible AI Use…

Abhilash Warrier – Community Spotlight

We are honored to recognize Abhilash Warrier as our Community Spotlight honoree for…

Leaderboard > Supply chain | Supply Chain Management, Commerce

#1
CA Neeraj Kumar Profile Picture

CA Neeraj Kumar 810

#2
André Arnaud de Calavon Profile Picture

André Arnaud de Cal... 243 Super User 2025 Season 2

#3
Sagar Suman Profile Picture

Sagar Suman 217 Super User 2025 Season 2

Last 30 days Overall leaderboard

Featured topics

Product updates

Dynamics 365 release plans