Training
Module
Create transactions in Store Commerce in Dynamics 365 Commerce - Training
Suspend and recall transactions, and work with serialized items using Store Commerce in Dynamics 365 Commerce.
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
This article describes how to use triggers to capture events that occur before and after any Microsoft Dynamics 365 Commerce Store Commerce app operations.
You can use triggers to capture events that occur before or after Store Commerce app operations. Using triggers supports several business logic scenarios that enable you to do the following:
The following table lists the available triggers and denotes whether they can be canceled.
Trigger | Type | Description |
---|---|---|
ApplicationStartTrigger | Noncancelable | Executed after the POS application is started. You can revert or cancel whatever executed previously. |
ApplicationSuspendTrigger | Noncancelable | Executed after the POS application is suspended. |
PreLogOnTriggerTrigger | Cancelable | Executed before the POS log on. |
PostLogOnTriggerTrigger | Noncancelable | Executed after the POS log on. |
PostLogOffTrigger | Noncancelable | Executed after the POS log off. |
PreLockTerminalTrigger | Cancelable | Executed before the POS register lock. |
PostLockTerminalTrigger | Non-Cancelable | Executed after the POS register lock. |
PreUnlockTerminalTrigger | Cancelable | Executed before the POS register is unlocked. |
PostDeviceActivationTrigger | Non-Cancelable | Executed after the POS activation. |
PreElevateUserTrigger | Cancelable | This trigger is executed before the manager override and only works for non-Microsoft Microsoft Entra user authentication. If Microsoft Entra is enabled, this trigger won't work. |
PreRegisterAuditEventTrigger | Cancelable | Executed before the audit event. |
PostRegisterAuditEventTrigger | Non-Cancelable | Executed after the audit event. |
PreOpenUrlTrigger | Cancelable | Executed before the open URL operation. |
Trigger | Type | Description |
---|---|---|
PreTenderDeclarationTrigger | Cancelable | Executed before the POS tender declaration. |
PostTenderDeclarationTrigger | Noncancelable | Executed after the POS tender declaration. |
PreFloatEntryTrigger | Cancelable | Executed before the POS float entry. |
PostFloatEntryTrigger | Noncancelable | Executed after the POS float entry. |
Trigger | Type | Description | Release |
---|---|---|---|
PreCustomerAddTrigger | Cancelable | Executed before the Create customer form is opened. | |
PostCustomerAddTrigger | Noncancelable | Executed after the customer is created. | |
PreCustomerClearTrigger | Cancelable | Executed before the customer cleared from the cart. | |
PostCustomerClearTrigger | Noncancelable | Executed after the customer cleared from the cart. | |
PreCustomerSetTrigger | Cancelable | Executed before the customer is added to the cart. | |
PreCustomerSearchTrigger | Cancelable | Executed before customer search is performed. | |
PostCustomerSearchTrigger | Noncancelable | Executed after customer search is performed. | |
PostIssueLoyaltyCardTrigger | Noncancelable | Executed after the loyalty card is issued. | |
PreCustomerSaveTrigger | Cancelable | Executed after the user selects the Save button, and before the customer is created or updated. The isNewCustomer value in the option indicates whether the operation is creating a new customer or editing an existing customer. |
|
PostCustomerSaveTrigger | Noncancelable | Executed after the customer is created or updated. | |
PreSaveCustomerAddressTrigger | Cancelable | Executed before the customer address is saved. | |
PreGetLoyaltyCardBalanceTrigger | Cancelable | Executed before getting the loyalty card balance. | |
PostGetLoyaltyCardBalanceTrigger | Noncancelable | Executed after getting the loyalty card balance. | |
PreDisplayLoyaltyCardBalanceTrigger | Cancelable | Executed before displaying the loyalty card balance. | |
PreCustomerEditTrigger | Cancelable | Executed before editing the Customer. | 10.0.19 |
Trigger | Type | Description |
---|---|---|
PreLineDiscountAmountTrigger | Cancelable | Executed before line discount amount is added to the cart line. |
PostLineDiscountAmountTrigger | Noncancelable | Executed after line discount amount added to the cart line. |
PreLineDiscountPercentTrigger | Cancelable | Executed before line discount percent added to the cart line. |
PostLineDiscountPercentTrigger | Noncancelable | Executed after line discount percent added to the cart line. |
PreTotalDiscountAmountTrigger | Cancelable | Executed before total discount percent added to the cart. |
PostTotalDiscountAmountTrigger | Noncancelable | Executed after total amount percent added to the cart. |
PreTotalDiscountPercentTrigger | Cancelable | Executed before total discount percent added to the cart. |
PostTotalDiscountPercentTrigger | Noncancelable | Executed after total discount percent added to the cart. |
PreAddCouponTrigger | Cancelable | Executed before adding discount coupon to the cart. |
PostAddCouponTrigger | Noncancelable | Executed after adding discount coupon to the cart. |
Trigger | Type | Description |
---|---|---|
PreOperationTrigger | Cancelable | Generic trigger executed before all POS operations. You can use this trigger if there is no specific trigger available for a POS operation. |
PreOperationValidationTrigger | Cancelable | Generic trigger executed before the operation validation begins. |
OperationFailureTrigger | Noncancelable | Generic trigger executed after any POS operation failed. |
PostOperationTrigger | Noncancelable | Generic trigger executed after all POS operations. You can use this trigger if there is no specific trigger available for a POS operation. |
Trigger | Type | Description |
---|---|---|
PreAddTenderLineTrigger | Cancelable | Executed before the payment line is added to the cart. |
PrePaymentTrigger | Cancelable | Executed after you select the pay button in POS. |
PostPaymentTrigger | Noncancelable | Executed after all the payment processing is done. |
PreVoidPaymentTrigger | Cancelable | Executed before the payment line is voided in POS. |
PostVoidPaymentTrigger | Noncancelable | Executed after the payment line is voided in POS. |
PreTenderPaymentTrigger (10.0.21) | Cancelable | Executed after the tender amount is selected in the payment view. |
Trigger | Type | Description |
---|---|---|
PrePrintReceiptCopyTrigger | Cancelable | Executed before the receipt copy is printed from the show journal screen or receipt view screen. |
PostReceiptPromptTrigger | Noncancelable | Executed after the receipt prompt (for example, "Do you want to print the receipt?"). |
Trigger | Type | Description |
---|---|---|
PostGetSerialNumberTrigger | Noncancelable | Executed after the serial number is added to the cart line. |
PreProductSaleTrigger | Cancelable | Executed before the product is added to the cart. |
PostProductSaleTrigger | Noncancelable | Executed after the product is added to the cart. |
PreReturnProductTrigger | Cancelable | Executed before the return product is added to the cart. |
PostReturnProductTrigger | Noncancelable | Executed after the return product is added to the cart. |
PreSetQuantityTrigger | Cancelable | Executed before the quantity information is updated in the cart line. |
PostSetQuantityTrigger | Noncancelable | Executed after the quantity information is updated in the cart line. |
PrePriceOverrideTrigger | Cancelable | Executed before the price is overridden for a cart line. |
PostPriceOverrideTrigger | Noncancelable | Executed after the price is overridden for a cart line. |
PreClearQuantityTrigger | Cancelable | Executed before the quantity information is cleared from the cart line. |
PostClearQuantityTrigger | Noncancelable | Executed after the quantity information is cleared from the cart line. |
PreVoidProductsTrigger | Cancelable | Executed before the product is voided from the cart. |
PostVoidProductsTrigger | Noncancelable | Executed after the product is voided from the cart. |
PostPriceCheckTrigger | Noncancelable | Executed after the price check for the product is executed. |
Trigger | Type | Description |
---|---|---|
PreRecallCustomerOrderTrigger | Cancelable | Executed before the customer order is recalled. |
PostRecallCustomerOrderTrigger | Noncancelable | Executed after the customer order is recalled. |
PrePickUpCustomerOrderLinesTrigger | Cancelable | Executed before the customer order lines are picked. |
PreChangeShippingOriginTrigger | Cancelable | Executed before the shipping origin is changed during a customer order. |
PreGetFulfillmentLinesTrigger | Cancelable | Executed before the Order fulfillment lines are loaded in the Order fulfillment view. |
PreShipFulfillmentLinesTrigger | Cancelable | Executed before the shipping is done from the Order fulfillment view by selecting the Ship button. |
PostShipFulfillmentLinesTrigger | Non-Cancelable | Executed after the shipping is done from the Order fulfillment view by selecting the Ship button. |
PreMarkFulfillmentLinesAsPackedTrigger | Cancelable | Executed before the mark as packed option is triggered from the order fulfillment view by selecting the Pack button. |
PostMarkFulfillmentLinesAsPackedTrigger | Non-Cancelable | Executed after the mark as packed option is triggered from the order fulfillment view by selecting the Pack button. |
PreCreatePackingSlipTrigger | Cancelable | Executed before the create packing slip option is triggered from the order fulfillment view by selecting the Pack button. |
PostCreatePackingSlipTrigger | Non-Cancelable | Executed after the create packing slip option is triggered from the order fulfillment view by selecting the Pack button. |
PostReturnInvoicedSalesLinesTrigger | Non-Cancelable | Executed after one or more invoices selected for return. |
PreResendEmailReceiptTrigger (10.0.13) | Cancelable | Executed before sending the email from the Show journal view. |
PreRecallCustomerQuoteTrigger (10.0.18) | Cancelable | Executed before the customer quote is recalled from the recall order view. |
PostRecallCustomerQuoteTrigger (10.0.18) | Non-Cancelable | Executed after the customer quote is recalled from the recall order view. |
Trigger | Type | Description | Release |
---|---|---|---|
PostOpenShiftTrigger | Noncancelable | This trigger is executed after the new shift is opened. | |
PreCloseShiftTrigger | Cancelable | This trigger is executed before the shift is closed. | |
PreResumeShiftTrigger | Cancelable | This trigger is executed before the shift is resumed. | 10.0.16 |
PostResumeShiftTrigger | Noncancelable | This trigger is executed after the shift is resumed. | 10.0.16 |
Trigger | Type | Description |
---|---|---|
PreOverrideLineProductTaxTrigger | Cancelable | Executed before the tax amount or code is overridden for a cart line. |
PostOverrideLineProductTaxTrigger | Noncancelable | Executed after the tax amount or code is overridden for a cart line. |
PreOverrideTransactionTaxTrigger | Cancelable | Executed before the tax amount or code is overridden for a cart or transaction. |
PostOverrideTransactionTaxTrigger | Noncancelable | Executed before the tax amount or code is overridden for a cart or transaction. |
Trigger | Type | Description |
---|---|---|
BeginTransactionTrigger | Noncancelable | Executed before the transaction is initialized. |
PreConfirmReturnTransactionTrigger | Cancelable | Executed before the return transaction is confirmed. |
PreReturnTransactionTrigger | Cancelable | Executed before the return transaction is processed. |
PostReturnTransactionTrigger | Noncancelable | Executed after the return transaction is processed. |
PreEndTransactionTrigger | Cancelable | Executed before the end transaction request is called to commit the changes to the database and close the transaction. |
PostEndTransactionTrigger | Noncancelable | Executed after the end transaction request is called to commit the changes to the database and close the transaction. |
PreVoidTransactionTrigger | Cancelable | Executed before the transaction is voided. |
PostVoidTransactionTrigger | Noncancelable | Executed after the transaction is voided. |
PreSuspendTransactionTrigger | Cancelable | Executed before the transaction is suspended. |
PostSuspendTransactionTrigger | Noncancelable | Executed after the transaction is suspended. |
PreRecallTransactionTrigger | Cancelable | Executed before the transaction or order is recalled. |
PostRecallTransactionTrigger | Noncancelable | Executed after the transaction or order is recalled. |
PostCartCheckoutTrigger | Noncancelable | Executed after the checkout process is completed. |
PreRecallTransactionTrigger | Cancelable | Executed before the customer order is recalled. |
PostRecallTransactionTrigger | Non-Cancelable | Executed after the customer order is recalled. |
PreSelectTransactionPaymentMethodTrigger | Cancelable | When the user selects the Totals button in the Cart view - totals panel, the available payment methods are shown and this trigger is executed before the dialog box is shown. You can use extension code to modify the available payment methods displayed from this trigger. |
PreShipSelectedCartLinesTrigger | Cancelable | Executed when the product is selected for shipping. |
Trigger | Type | Description |
---|---|---|
PostGetReasonCodeLine | Cancelable | This trigger is executed after the reason code line value is entered (before the reason code is added to the cart). |
Trigger | Type | Description |
---|---|---|
PreCreateTransferOrderTrigger | Cancelable | This trigger is executed before the transfer order is created (executed after the order input). |
PreUpdateTransferOrderTrigger | Cancelable | This trigger is executed before the transfer order is updated. |
Trigger | Type | Description | Release |
---|---|---|---|
PreCreateInventoryDocumentTrigger | Cancelable | This trigger is executed before the inbound/outbound document is created (executed after the order input). | 10.0.15 |
PreUpdateInventoryDocumentTrigger | Cancelable | This trigger is executed before the inbound/outbound document is updated. | 10.0.15 |
Trigger | Type | Description | Release |
---|---|---|---|
PreAdjustStockCountLineQuantityTrigger | Cancelable | This trigger is executed before the stock count for a line is adjusted. | 10.0.16 |
PreSaveStockCountJournalTrigger | Cancelable | This trigger is executed before the stock count journal is saved. | 10.0.16 |
In this example, a custom receipt is printed when the user suspends a transaction. This example implements the PostSuspendTransactionTrigger trigger and prints the custom receipt using the existing print peripheral API.
To implement this scenario, you must complete these steps.
To implement a trigger, follow these steps.
Open Visual Studio 2015 in administrator mode.
Open the ModernPOS solution from …\RetailSDK\POS.
Under the POS.Extensions project, create a new folder named SuspendReceiptSample.
Under SuspendReceiptSample, create new folder named TriggersHandlers.
In the TriggersHandlers folder, add a new Typescript file named PostSuspendTransactionTrigger.ts.
Add the following import statements to import the relevant entities and context.
import * as Triggers from "PosApi/Extend/Triggers/TransactionTriggers";
import { ObjectExtensions } from "PosApi/TypeExtensions";
import { ClientEntities, ProxyEntities } from "PosApi/Entities";
import { PrinterPrintRequest, PrinterPrintResponse } from "PosApi/Consume/Peripherals";
import { GetHardwareProfileClientRequest, GetHardwareProfileClientResponse } from "PosApi/Consume/Device";
import { GetReceiptsClientRequest, GetReceiptsClientResponse } from "PosApi/Consume/SalesOrders";
Create a new class named PostSuspendTransactionTrigger and extend it from PostSuspendTransactionTrigger.
export default class PostSuspendTransactionTrigger extends Triggers.PostSuspendTransactionTrigger { }
Implement the trigger's execute method to get the receipt profile and print the custom receipt:
public execute(options: Triggers.IPostSuspendTransactionTriggerOptions): Promise < void> {
this.context.logger.logVerbose("Executing PostSuspendTransactionTrigger with options " + JSON.stringify(options) + ".");
if(ObjectExtensions.isNullOrUndefined(options) || ObjectExtensions.isNullOrUndefined(options.cart)) {
// This will never happen, but is included to demonstrate how to return a rejected promise when validation fails.
let error: ClientEntities.ExtensionError
= new ClientEntities.ExtensionError("The options provided to the PostSuspendTransactionTrigger were invalid.");
return Promise.reject(error);
} else {
return this.context.runtime.executeAsync(new GetHardwareProfileClientRequest())
.then((response: ClientEntities.ICancelableDataResult<GetHardwareProfileClientResponse>)
: Promise<ClientEntities.ICancelableDataResult<GetReceiptsClientResponse>> => {
let hardwareProfile: ProxyEntities.HardwareProfile = response.data.result;
// Gets the receipts.
let salesOrderId: string = options.cart.Id;
let receiptRetrievalCriteria: ProxyEntities.ReceiptRetrievalCriteria = {
IsCopy: false,
IsRemoteTransaction: false,
IsPreview: false,
QueryBySalesId: true,
ReceiptTypeValue: ProxyEntities.ReceiptType.CustomReceipt7,
HardwareProfileId: hardwareProfile.ProfileId
};
let getReceiptsClientRequest: GetReceiptsClientRequest<GetReceiptsClientResponse> =
new GetReceiptsClientRequest(salesOrderId, receiptRetrievalCriteria);
return this.context.runtime.executeAsync(getReceiptsClientRequest);
})
.then((response: ClientEntities.ICancelableDataResult<GetReceiptsClientResponse>)
: Promise<ClientEntities.ICancelableDataResult<PrinterPrintResponse>> => {
let receipts: ProxyEntities.Receipt[] = response.data.result;
// Prints the receipts.
let printerPrintRequest: PrinterPrintRequest<PrinterPrintResponse> = new PrinterPrintRequest(receipts);
return this.context.runtime.executeAsync(printerPrintRequest);
}).then((): Promise<void> => {
// Resolves to a void result when fulfilled.
return Promise.resolve();
}).catch((reason: any): Promise<void> => {
// Resolves to a void result when rejected. This matches existing POS printing behavior.
this.context.logger.logError("PostSuspendTransactionTrigger execute error: " + JSON.stringify(reason));
return Promise.resolve();
});
}
}
The entire example should look like the following.
import * as Triggers from "PosApi/Extend/Triggers/TransactionTriggers";
import { ObjectExtensions } from "PosApi/TypeExtensions";
import { ClientEntities, ProxyEntities } from "PosApi/Entities";
import { PrinterPrintRequest, PrinterPrintResponse } from "PosApi/Consume/Peripherals";
import { GetHardwareProfileClientRequest, GetHardwareProfileClientResponse } from "PosApi/Consume/Device";
import { GetReceiptsClientRequest, GetReceiptsClientResponse } from "PosApi/Consume/SalesOrders";
export default class PostSuspendTransactionTrigger extends Triggers.PostSuspendTransactionTrigger {
/**
* Executes the trigger functionality.
* @param {Triggers.IPostSuspendTransactionTriggerOptions} options The options provided to the trigger.
*/
public execute(options: Triggers.IPostSuspendTransactionTriggerOptions): Promise<void> {
this.context.logger.logVerbose("Executing PostSuspendTransactionTrigger with options " + JSON.stringify(options) + ".");
if (ObjectExtensions.isNullOrUndefined(options) || ObjectExtensions.isNullOrUndefined(options.cart)) {
// This will never happen, but is included to demonstrate how to return a rejected promise when validation fails.
let error: ClientEntities.ExtensionError
= new ClientEntities.ExtensionError("The options provided to the PostSuspendTransactionTrigger were invalid.");
return Promise.reject(error);
} else {
return this.context.runtime.executeAsync(new GetHardwareProfileClientRequest())
.then((response: ClientEntities.ICancelableDataResult<GetHardwareProfileClientResponse>)
: Promise<ClientEntities.ICancelableDataResult<GetReceiptsClientResponse>> => {
let hardwareProfile: ProxyEntities.HardwareProfile = response.data.result;
// Gets the receipts.
let salesOrderId: string = options.cart.Id;
let receiptRetrievalCriteria: ProxyEntities.ReceiptRetrievalCriteria = {
IsCopy: false,
IsRemoteTransaction: false,
IsPreview: false,
QueryBySalesId: true,
ReceiptTypeValue: ProxyEntities.ReceiptType.CustomReceipt7,
HardwareProfileId: hardwareProfile.ProfileId
};
let getReceiptsClientRequest: GetReceiptsClientRequest<GetReceiptsClientResponse> =
new GetReceiptsClientRequest(salesOrderId, receiptRetrievalCriteria);
return this.context.runtime.executeAsync(getReceiptsClientRequest);
})
.then((response: ClientEntities.ICancelableDataResult<GetReceiptsClientResponse>)
: Promise<ClientEntities.ICancelableDataResult<PrinterPrintResponse>> => {
let receipts: ProxyEntities.Receipt[] = response.data.result;
// Prints the receipts.
let printerPrintRequest: PrinterPrintRequest<PrinterPrintResponse> = new PrinterPrintRequest(receipts);
return this.context.runtime.executeAsync(printerPrintRequest);
}).then((): Promise<void> => {
// Resolves to a void result when fulfilled.
return Promise.resolve();
}).catch((reason: any): Promise<void> => {
// Resolves to a void result when rejected. This matches existing POS printing behavior.
this.context.logger.logError("PostSuspendTransactionTrigger execute error: " + JSON.stringify(reason));
return Promise.resolve();
});
}
}
}
Create a new .json file under the SuspendReceiptSample folder and name it manifest.json.
Replace the autogenerated code in manifest.json with the following code.
{
"$schema": "../manifestSchema.json",
"name": "Pos_Extensibility_SuspendTransactionReceiptSample",
"publisher": "Microsoft",
"version": "7.2.0",
"minimumPosVersion": "7.2.0.0",
"components": {
"extend": {
"triggers": [
{
"triggerType": "PostSuspendTransaction",
"modulePath": "TriggersHandlers/PostSuspendTransactionTrigger"
}
]
}
}
}
Open the extensions.json file in the POS.Extensions project and update it with the SuspendReceiptSample samples, so that during runtime POS will include this extension.
{
"extensionPackages": [
{
"baseUrl": "SampleExtensions2"
},
{
"baseUrl": "SuspendReceiptSample"
}
]
}
Open the tsconfig.json file and comment out the extension package folders from the exclude list. POS will use this file to include or exclude the extension. By default, the list contains all the excluded extensions list. If you want to include an extension that is part of the POS, then add the extension folder name and comment out the extension from the extension, as shown.
"exclude": [
"AuditEventExtensionSample",
"B2BSample",
"CustomerSearchWithAttributesSample",
"FiscalRegisterSample",
"PaymentSample",
"PromotionsSample",
"SalesTransactionSignatureSample",
"SampleExtensions",
//"SampleExtensions2",
"StoreHoursSample",
"SuspendTransactionReceiptSample"
//"POSAPIExtension",
//"CustomColumnExtensions",
//"EODSample",
//"ProdDetailsCustomColumnExtensions",
//"SerachExtension",
//"SuspendReceiptSample"
],
Compile and rebuild the project.
The following procedure describes how to override an existing CRT request to print a receipt for suspended transactions.
To override the CRT receipt request to generate the receipt data, follow these steps.
Start Visual Studio 2015.
On the File menu, select Open > Project/Solution. Find the template project (SampleCRTExtension.csproj).
Rename the template project Runtime.Extensions.SuspendReceiptSample.
Optional: Change the default namespace.
Rename the output assembly Contoso.Commerce.Runtime.SuspendReceiptSample.
Inside the project, add a new request class file, and name it GetCustomReceiptsRequestHandler.cs.
Copy the following code, and paste it inside the class. Before you copy it, delete the automatically generated code.
namespace Contoso
{
namespace Commerce.Runtime.ReceiptsSample
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Dynamics.Commerce.Runtime;
using Microsoft.Dynamics.Commerce.Runtime.DataModel;
using Microsoft.Dynamics.Commerce.Runtime.Messages;
using Microsoft.Dynamics.Commerce.Runtime.Services.Messages;
using Microsoft.Dynamics.Commerce.Runtime.Workflow;
/// <summary>
/// The request handler for GetCustomReceiptsRequestHandler class.
/// </summary>
/// <remarks>
/// This is an example of how to print custom types of receipts. In this example the receipt is for a transaction as opposed to
/// a sales order. The implementation converts the transaction to a sales order so that existing receipt fields can be used.
/// </remarks>
public class GetCustomReceiptsRequestHandler : SingleRequestHandler<GetCustomReceiptsRequest, GetReceiptResponse>
{
}
}
}
Copy the following code, and paste it inside the class to add a new method to read the transaction from the cart table, because suspended transactions aren't yet created in the commerce transaction table. You must then convert the cart transaction to sales transaction. This conversion is required because the receipt object can understand only sales transactions.
Note
If you're printing a custom receipt for a completed transaction, you have don't have to get the cart transaction and convert it to a sales transaction. It has already been converted to a sales transaction, because the transaction is completed.
private SalesOrder GetSalesOrderForTransactionWithId(RequestContext requestContext, string transactionId)
{
SalesOrder salesOrder = new SalesOrder();
var getCartRequest = new GetCartRequest(new CartSearchCriteria(transactionId), QueryResultSettings.SingleRecord);
var getCartResponse = requestContext.Execute<GetCartResponse>(getCartRequest);
SalesTransaction salesTransaction = getCartResponse.Transactions.SingleOrDefault(); ;
if (salesTransaction != null)
{
// The sales transaction is converted into a sales order so that existing receipt fields can be used.
salesOrder.CopyFrom<SalesTransaction>(salesTransaction);
}
else
{
throw new DataValidationException(
DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ObjectNotFound,
string.Format("Unable to get the sales transaction. ID: {0}", transactionId));
}
return salesOrder;
}
Copy the following code, and paste it into the class to add a new method to construct the receipt format by using the sales transaction information, based on the custom receipt format that is defined in HQ.
private Collection<Receipt> GetCustomReceipts(SalesOrder salesOrder, ReceiptRetrievalCriteria criteria)
{
Collection<Receipt> result = new Collection<Receipt>();
var getReceiptServiceRequest = new GetReceiptServiceRequest(
salesOrder,
new Collection<ReceiptType> { criteria.ReceiptType },
salesOrder.TenderLines,
criteria.IsCopy,
criteria.IsPreview,
criteria.HardwareProfileId);
ReadOnlyCollection<Receipt> customReceipts = this.Context.Execute<GetReceiptServiceResponse>(getReceiptServiceRequest).Receipts;
result.AddRange(customReceipts);
return result;
}
Copy the following code, and paste it into the class to add a new method to override the get receipt response. This method processes the request and returns the response.
protected override GetReceiptResponse Process(GetCustomReceiptsRequest request)
{
ThrowIf.Null(request, "request");
ThrowIf.Null(request.ReceiptRetrievalCriteria, "request.ReceiptRetrievalCriteria");
// The sales order that we are printing receipts for is retrieved.
SalesOrder salesOrder = this.GetSalesOrderForTransactionWithId(request.RequestContext, request.TransactionId);
// Custom receipts are printed.
Collection<Receipt> result = new Collection<Receipt>();
switch (request.ReceiptRetrievalCriteria.ReceiptType)
{
// An example of getting custom receipts.
case ReceiptType.CustomReceipt7:
{
IEnumerable<Receipt> customReceipts = this.GetCustomReceipts(salesOrder, request.ReceiptRetrievalCriteria);
result.AddRange(customReceipts);
}
break;
default:
// Add more logic to handle more types of custom receipt types.
break;
}
return new GetReceiptResponse(new ReadOnlyCollection<Receipt>(result));
}
The overall code should look like this.
namespace Contoso
{
namespace Commerce.Runtime.ReceiptsSample
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Dynamics.Commerce.Runtime;
using Microsoft.Dynamics.Commerce.Runtime.DataModel;
using Microsoft.Dynamics.Commerce.Runtime.Messages;
using Microsoft.Dynamics.Commerce.Runtime.Services.Messages;
using Microsoft.Dynamics.Commerce.Runtime.Workflow;
/// <summary>
/// The request handler for GetCustomReceiptsRequestHandler class.
/// </summary>
/// <remarks>
/// This is an example of how to print custom types of receipts. In this example the receipt is for a transaction as opposed to
/// a sales order. The implementation converts the transaction to a sales order so that existing receipt fields can be used.
/// </remarks>
public class GetCustomReceiptsRequestHandler : SingleRequestHandler<GetCustomReceiptsRequest, GetReceiptResponse>
{
/// <summary>
/// Processes the GetCustomReceiptsRequest to return the set of receipts. The request should not be null.
/// </summary>
/// <param name="request">The request parameter.</param>
/// <returns>The GetReceiptResponse.</returns>
protected override GetReceiptResponse Process(GetCustomReceiptsRequest request)
{
ThrowIf.Null(request, "request");
ThrowIf.Null(request.ReceiptRetrievalCriteria, "request.ReceiptRetrievalCriteria");
// The sales order that we are printing receipts for is retrieved.
SalesOrder salesOrder = this.GetSalesOrderForTransactionWithId(request.RequestContext, request.TransactionId);
// Custom receipts are printed.
Collection<Receipt> result = new Collection<Receipt>();
switch (request.ReceiptRetrievalCriteria.ReceiptType)
{
// An example of getting custom receipts.
case ReceiptType.CustomReceipt7:
{
IEnumerable<Receipt> customReceipts = this.GetCustomReceipts(salesOrder, request.ReceiptRetrievalCriteria);
result.AddRange(customReceipts);
}
break;
default:
// Add more logic to handle more types of custom receipt types.
break;
}
return new GetReceiptResponse(new ReadOnlyCollection<Receipt>(result));
}
/// <summary>
/// Gets a sales order for the transaction with the given identifier.
/// </summary>
/// <param name="requestContext">The request context.</param>
/// <param name="transactionId">The transaction identifier.</param>
/// <returns>The sales order.</returns>
private SalesOrder GetSalesOrderForTransactionWithId(RequestContext requestContext, string transactionId)
{
SalesOrder salesOrder = new SalesOrder();
var getCartRequest = new GetCartRequest(new CartSearchCriteria(transactionId), QueryResultSettings.SingleRecord);
var getCartResponse = requestContext.Execute<GetCartResponse>(getCartRequest);
SalesTransaction salesTransaction = getCartResponse.Transactions.SingleOrDefault(); ;
if (salesTransaction != null)
{
// The sales transaction is converted into a sales order so that existing receipt fields can be used.
salesOrder.CopyFrom<SalesTransaction>(salesTransaction);
}
else
{
throw new DataValidationException(
DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ObjectNotFound,
string.Format("Unable to get the sales transaction. ID: {0}", transactionId));
}
return salesOrder;
}
/// <summary>
/// An example to get a custom receipt.
/// </summary>
/// <param name="salesOrder">The sales order that we are printing receipts for.</param>
/// <param name="criteria">The receipt retrieval criteria.</param>
/// <returns>A collection of receipts.</returns>
private Collection<Receipt> GetCustomReceipts(SalesOrder salesOrder, ReceiptRetrievalCriteria criteria)
{
Collection<Receipt> result = new Collection<Receipt>();
var getReceiptServiceRequest = new GetReceiptServiceRequest(
salesOrder,
new Collection<ReceiptType> { criteria.ReceiptType },
salesOrder.TenderLines,
criteria.IsCopy,
criteria.IsPreview,
criteria.HardwareProfileId);
ReadOnlyCollection<Receipt> customReceipts = this.Context.Execute<GetReceiptServiceResponse>(getReceiptServiceRequest).Receipts;
result.AddRange(customReceipts);
return result;
}
}
}
}
Compile and build the project.
Go to the output directory, and copy the output assembly.
Navigate to the …\RetailServer\webroot\bin\ext folder, and paste the assembly.
Also paste the assembly in the …\RetailSDK\References folder.
Open the commerceruntime.ext.config file, and add the custom assembly information under the <composition> section.
<add source="assembly" value="Contoso.Commerce.Runtime.SuspendReceiptSample" />
Save and close the file.
Restart the Commerce Scale Unit to load the new assembly.
To add the custom receipt layout, follow these steps.
To configure the XPS printer for quick testing, follow these steps.
To validate the extension, follow these steps.
Training
Module
Create transactions in Store Commerce in Dynamics 365 Commerce - Training
Suspend and recall transactions, and work with serialized items using Store Commerce in Dynamics 365 Commerce.