public override IPurchasable Select(IEnumerable <IPurchasableItemOptions> options)
        {
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            TraceMethodInfo("Start");

            var purchaseMetadata = WebFormMetadata.FirstOrDefault(IsPurchaseMetadata) ?? new Entity();

            if (WebFormSession.QuoteId != Guid.Empty)
            {
                TraceMethodInfo("Web form session has quote {0}. Creating purchasable from quote.", WebFormSession.QuoteId);

                return(SelectFromQuote(new EntityReference("quote", WebFormSession.QuoteId), purchaseMetadata, options));
            }

            var serviceContext = Dependencies.GetServiceContext();

            Entity purchaseEntity;

            if (!TryGetPurchaseEntity(serviceContext, purchaseMetadata, out purchaseEntity))
            {
                TraceMethodError("Failed to retrieve purchase entity from web form step. Returning null.");

                return(null);
            }

            TraceMethodInfo("Found purchase entity: {0}:{1}", purchaseEntity.LogicalName, purchaseEntity.Id);

            var requiredProductsRelationship = purchaseMetadata.GetAttributeValue <string>(PurchaseMetadataAttributes.RequiredProductsRelationship);

            if (!string.IsNullOrEmpty(requiredProductsRelationship))
            {
                return(SelectFromProductsDirectlyRelatedToPurchaseEntity(serviceContext, purchaseEntity, requiredProductsRelationship, purchaseMetadata, options));
            }

            var lineItemRelationship = purchaseMetadata.GetAttributeValue <string>(PurchaseMetadataAttributes.LineItemRelationship);

            if (!string.IsNullOrEmpty(lineItemRelationship))
            {
                return(SelectFromLineItemEntities(serviceContext, purchaseEntity, lineItemRelationship, purchaseMetadata, options));
            }

            if (purchaseEntity.LogicalName == "product")
            {
                return(SelectFromProduct(purchaseEntity, purchaseMetadata, options));
            }

            if (purchaseEntity.LogicalName == "quote")
            {
                return(SelectFromQuote(purchaseEntity.ToEntityReference(), purchaseMetadata, options));
            }

            TraceMethodError("Failed to retrieve purchasable using web form step metadata. Returning null.");

            return(null);
        }
        public override void CompletePurchase(bool fulfillOrder = false, bool createInvoice = false)
        {
            TraceMethodInfo("Start");

            var purchaseMetadata = WebFormMetadata.FirstOrDefault(IsPurchaseMetadata);

            if (purchaseMetadata == null)
            {
                return;
            }

            if (WebFormSession.QuoteId == Guid.Empty)
            {
                throw new InvalidOperationException("Failed to retrieve purchase quote from web form session.");
            }

            fulfillOrder  = purchaseMetadata.GetAttributeValue <bool?>(PurchaseMetadataAttributes.FulfillOrderOnPayment).GetValueOrDefault(fulfillOrder);
            createInvoice = purchaseMetadata.GetAttributeValue <bool?>(PurchaseMetadataAttributes.CreateInvoiceOnPayment).GetValueOrDefault(createInvoice);

            var orderRelationshipName   = purchaseMetadata.GetAttributeValue <string>(PurchaseMetadataAttributes.TargetEntityOrderRelationship);
            var setOrderRelationship    = !string.IsNullOrEmpty(orderRelationshipName);
            var invoiceRelationshipName = purchaseMetadata.GetAttributeValue <string>(PurchaseMetadataAttributes.TargetEntityInvoiceRelationship);
            var setInvoiceRelationship  = !string.IsNullOrEmpty(invoiceRelationshipName);

            if (!(fulfillOrder || setOrderRelationship || createInvoice || setInvoiceRelationship))
            {
                // Nothing to do, return.
                return;
            }

            var quote          = new EntityReference("quote", WebFormSession.QuoteId);
            var serviceContext = Dependencies.GetServiceContext();

            var order = serviceContext.CreateQuery("salesorder")
                        .Where(e => e.GetAttributeValue <EntityReference>("quoteid") == quote)
                        .OrderByDescending(e => e.GetAttributeValue <DateTime>("createdon"))
                        .FirstOrDefault();

            if (order == null)
            {
                TraceMethodError("Unable to retrieve associated order for quote {0}.", quote.Id);

                return;
            }

            var serviceContextForUpdates = Dependencies.GetServiceContextForWrite();
            var hasUpdates = false;

            var targetUpdate = new Entity(Target.LogicalName)
            {
                Id = Target.Id
            };

            serviceContextForUpdates.Attach(targetUpdate);

            if (setOrderRelationship)
            {
                var orderUpdate = new Entity(order.LogicalName)
                {
                    Id = order.Id
                };

                serviceContextForUpdates.Attach(orderUpdate);
                serviceContextForUpdates.AddLink(orderUpdate, new Relationship(orderRelationshipName), targetUpdate);

                hasUpdates = true;
            }

            if (createInvoice)
            {
                var convertOrderRequest = new ConvertSalesOrderToInvoiceRequest()
                {
                    ColumnSet    = new ColumnSet("invoiceid"),
                    SalesOrderId = order.Id
                };

                var convertOrderResponse = (ConvertSalesOrderToInvoiceResponse)serviceContext.Execute(convertOrderRequest);

                var invoice = convertOrderResponse.Entity;

                var setStateRequest = new SetStateRequest()
                {
                    EntityMoniker = invoice.ToEntityReference(),
                    State         = new OptionSetValue(2),
                    Status        = new OptionSetValue(100001)
                };

                var setStateResponse = (SetStateResponse)serviceContext.Execute(setStateRequest);

                invoice = serviceContext.CreateQuery("invoice").Where(i => i.GetAttributeValue <Guid>("invoiceid") == convertOrderResponse.Entity.Id).FirstOrDefault();

                if (setInvoiceRelationship && convertOrderResponse != null && convertOrderResponse.Entity != null)
                {
                    var invoiceUpdate = new Entity(invoice.LogicalName)
                    {
                        Id = convertOrderResponse.Entity.Id
                    };

                    serviceContextForUpdates.Attach(invoiceUpdate);
                    serviceContextForUpdates.AddLink(invoiceUpdate, new Relationship(invoiceRelationshipName), targetUpdate);

                    hasUpdates = true;
                }
            }

            if (hasUpdates)
            {
                serviceContextForUpdates.SaveChanges();
            }

            if (fulfillOrder)
            {
                var orderClose = new Entity("orderclose");

                orderClose["salesorderid"] = order.ToEntityReference();

                serviceContext.Execute(new FulfillSalesOrderRequest
                {
                    OrderClose = orderClose,
                    Status     = new OptionSetValue(-1),
                });
            }
        }