public override async Task <bool> CreateOrderFromOrderProposal(OrderIdComponents orderId, SellerIdComponents sellerId, Uri orderProposalVersion, Order order) { // TODO more elegantly extract version UUID from orderProposalVersion (probably much further up the stack?) var version = new Guid(orderProposalVersion.ToString().Split('/').Last()); var result = await FakeBookingSystem.Database.BookOrderProposal( orderId.ClientId, sellerId.SellerIdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid, version); // TODO return enum to allow errors cases to be handled in the engine switch (result) { case FakeDatabaseBookOrderProposalResult.OrderSuccessfullyBooked: return(true); case FakeDatabaseBookOrderProposalResult.OrderProposalVersionOutdated: return(false); case FakeDatabaseBookOrderProposalResult.OrderProposalNotAccepted: throw new OpenBookingException(new OrderCreationFailedError(), "OrderProposal has not been accepted by the Seller"); case FakeDatabaseBookOrderProposalResult.OrderWasNotFound: throw new OpenBookingException(new UnknownOrderError()); default: throw new OpenBookingException(new OpenBookingError(), $"Unexpected FakeDatabaseDeleteOrderResult: {result}"); } }
public override async Task DeleteLease(OrderIdComponents orderId, SellerIdComponents sellerId) { // Note if no lease support, simply do nothing here await FakeBookingSystem.Database.DeleteLease( orderId.ClientId, orderId.uuid, sellerId.SellerIdLong ?? null /* Hack to allow this to work in Single Seller mode too */ ); }
public async Task <ResponseContent> DeleteOrderQuote(string clientId, Uri sellerId, string uuidString) { var orderId = new OrderIdComponents { ClientId = clientId, OrderType = OrderType.OrderQuote, uuid = ConvertToGuid(uuidString) }; using (await asyncDuplicateLock.LockAsync(GetParallelLockKey(orderId))) { await ProcessOrderQuoteDeletion(orderId, GetSellerIdComponentsFromApiKey(sellerId)); return(ResponseContent.OpenBookingNoContentResponse()); } }
//TODO: Should we move Seller into the Abstract level? Perhaps too much complexity private O ValidateFlowRequest <O>(string clientId, Uri authenticationSellerId, FlowStage stage, string uuid, OrderType orderType, O orderQuote) where O : Order, new() { var orderId = new OrderIdComponents { ClientId = clientId, uuid = uuid, OrderType = orderType }; // TODO: Add more request validation rules here SellerIdComponents sellerIdComponents = GetSellerIdComponentsFromApiKey(authenticationSellerId); ILegalEntity seller = settings.SellerStore.GetSellerById(sellerIdComponents); if (seller == null) { throw new OpenBookingException(new SellerNotFoundError()); } if (orderQuote?.Seller?.Id != null && seller?.Id != orderQuote?.Seller?.Id) { throw new OpenBookingException(new SellerMismatchError()); } // Check that taxMode is set in Seller if (!(seller?.TaxMode == TaxMode.TaxGross || seller?.TaxMode == TaxMode.TaxNet)) { throw new EngineConfigurationException("taxMode must always be set in the Seller"); } // Default to BusinessToConsumer if no customer provided TaxPayeeRelationship taxPayeeRelationship = orderQuote.Customer == null ? TaxPayeeRelationship.BusinessToConsumer : orderQuote.BrokerRole == BrokerType.ResellerBroker || orderQuote.Customer.IsOrganization ? TaxPayeeRelationship.BusinessToBusiness : TaxPayeeRelationship.BusinessToConsumer; var payer = orderQuote.BrokerRole == BrokerType.ResellerBroker ? orderQuote.Broker : orderQuote.Customer; return(ProcessFlowRequest <O>(new BookingFlowContext { Stage = stage, OrderId = orderId, OrderIdTemplate = settings.OrderIdTemplate, Seller = seller, SellerId = sellerIdComponents, TaxPayeeRelationship = taxPayeeRelationship, Payer = payer }, orderQuote)); }
/// <summary> /// Initiate customer cancellation for the specified OrderItems /// Note sellerId will always be null in Single Seller mode /// </summary> /// <returns>True if Order found, False if Order not found</returns> public override async Task <bool> CustomerCancelOrderItems(OrderIdComponents orderId, SellerIdComponents sellerId, List <OrderIdComponents> orderItemIds) { try { return(await FakeBookingSystem.Database.CancelOrderItems( orderId.ClientId, sellerId.SellerIdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid, orderItemIds.Where(x => x.OrderItemIdLong.HasValue).Select(x => x.OrderItemIdLong.Value).ToList(), true)); } catch (InvalidOperationException ex) { throw new OpenBookingException(new CancellationNotPermittedError(), ex.Message); } }
public void SingleIdTemplate_GetIdComponents_EnumToId() { var template = new OrderIdTemplate( "{+BaseUrl}api/{OrderType}/{uuid}", "{+BaseUrl}api/{OrderType}/{uuid}#/orderedItems/{OrderItemIdLong}" ); template.RequiredBaseUrl = new Uri("https://example.com/"); OrderIdComponents components = new OrderIdComponents { uuid = new Guid("3cbb8557-ca8f-4889-b21f-a59f860f8d25"), OrderItemIdLong = 123, OrderType = OrderType.Order }; var id = template.RenderOrderItemId(components); Assert.Equal(new Uri("https://example.com/api/orders/3cbb8557-ca8f-4889-b21f-a59f860f8d25#/orderedItems/123"), id); }
public void SingleIdTemplate_GetIdComponents_EnumToId() { var template = new OrderIdTemplate( "{+BaseUrl}api/{OrderType}/{uuid}", "{+BaseUrl}api/{OrderType}/{uuid}#/orderedItems/{OrderItemIdLong}" ); template.RequiredBaseUrl = new Uri("https://example.com/"); OrderIdComponents components = new OrderIdComponents { uuid = "asdf", OrderItemIdLong = 123, OrderType = OrderType.Order }; var id = template.RenderOrderItemId(components); Assert.Equal(new Uri("https://example.com/api/orders/asdf#/orderedItems/123"), id); }
private async Task <(OrderIdComponents orderId, SellerIdComponents sellerIdComponents, ILegalEntity seller)> ConstructIdsFromRequest(string clientId, Uri authenticationSellerId, string uuidString, OrderType orderType) { var orderId = new OrderIdComponents { ClientId = clientId, uuid = ConvertToGuid(uuidString), OrderType = orderType }; // TODO: Add more request validation rules here SellerIdComponents sellerIdComponents = GetSellerIdComponentsFromApiKey(authenticationSellerId); ILegalEntity seller = await settings.SellerStore.GetSellerById(sellerIdComponents); if (seller == null) { throw new OpenBookingException(new SellerNotFoundError()); } return(orderId, sellerIdComponents, seller); }
public async Task <ResponseContent> ProcessOrderProposalUpdate(string clientId, Uri sellerId, string uuidString, string orderProposalJson) { var orderId = new OrderIdComponents { ClientId = clientId, OrderType = OrderType.OrderProposal, uuid = ConvertToGuid(uuidString) }; using (await asyncDuplicateLock.LockAsync(GetParallelLockKey(orderId))) { OrderProposal orderProposal = OpenActiveSerializer.Deserialize <OrderProposal>(orderProposalJson); SellerIdComponents sellerIdComponents = GetSellerIdComponentsFromApiKey(sellerId); if (orderProposal == null || orderProposal.GetType() != typeof(Order)) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "OrderProposal is required for Order Cancellation"); } // Check for PatchContainsExcessiveProperties OrderProposal orderProposalWithOnlyAllowedProperties = new OrderProposal { OrderProposalStatus = orderProposal.OrderProposalStatus, OrderCustomerNote = orderProposal.OrderCustomerNote }; if (OpenActiveSerializer.Serialize <OrderProposal>(orderProposal) != OpenActiveSerializer.Serialize <OrderProposal>(orderProposalWithOnlyAllowedProperties)) { throw new OpenBookingException(new PatchContainsExcessivePropertiesError()); } // Check for PatchNotAllowedOnProperty if (orderProposal.OrderProposalStatus != OrderProposalStatus.CustomerRejected) { throw new OpenBookingException(new PatchNotAllowedOnPropertyError(), "Only 'https://openactive.io/CustomerRejected' is permitted for this property."); } await ProcessOrderProposalCustomerRejection(orderId, sellerIdComponents, settings.OrderIdTemplate); return(ResponseContent.OpenBookingNoContentResponse()); } }
public override async Task <DeleteOrderResult> DeleteOrder(OrderIdComponents orderId, SellerIdComponents sellerId) { var result = await FakeBookingSystem.Database.DeleteOrder( orderId.ClientId, orderId.uuid, sellerId.SellerIdLong ?? null /* Small hack to allow use of FakeDatabase when in Single Seller mode */); switch (result) { case FakeDatabaseDeleteOrderResult.OrderSuccessfullyDeleted: // "OrderWasAlreadyDeleted" is being treated as a success because the order did // exist - This maintains idempotency as requests that follow a successful request // will still return a 2xx. case FakeDatabaseDeleteOrderResult.OrderWasAlreadyDeleted: return(DeleteOrderResult.OrderSuccessfullyDeleted); case FakeDatabaseDeleteOrderResult.OrderWasNotFound: return(DeleteOrderResult.OrderDidNotExist); default: throw new OpenBookingException(new OpenBookingError(), $"Unexpected FakeDatabaseDeleteOrderResult: {result}"); } }
public async Task <ResponseContent> DeleteOrder(string clientId, Uri sellerId, string uuidString) { var orderId = new OrderIdComponents { ClientId = clientId, OrderType = OrderType.Order, uuid = ConvertToGuid(uuidString) }; using (await asyncDuplicateLock.LockAsync(GetParallelLockKey(orderId))) { var result = await ProcessOrderDeletion(orderId, GetSellerIdComponentsFromApiKey(sellerId)); switch (result) { case DeleteOrderResult.OrderSuccessfullyDeleted: return(ResponseContent.OpenBookingNoContentResponse()); case DeleteOrderResult.OrderDidNotExist: throw new OpenBookingException(new UnknownOrderError()); default: throw new OpenBookingException(new OpenBookingError(), $"Unexpected DeleteOrderResult: {result}"); } } }
protected abstract void ProcessOrderQuoteDeletion(OrderIdComponents orderId, SellerIdComponents sellerId);
public abstract void ProcessCustomerCancellation(OrderIdComponents orderId, SellerIdComponents sellerId, OrderIdTemplate orderIdTemplate, List <OrderIdComponents> orderItemIds);
public abstract bool CustomerCancelOrderItems(OrderIdComponents orderId, SellerIdComponents sellerId, OrderIdTemplate orderIdTemplate, List <OrderIdComponents> orderItemIds);
public abstract void DeleteLease(OrderIdComponents orderId, SellerIdComponents sellerId);
//TODO: Should we move Seller into the Abstract level? Perhaps too much complexity protected BookingFlowContext ValidateFlowRequest <TOrder>(OrderIdComponents orderId, SellerIdComponents sellerIdComponents, ILegalEntity seller, FlowStage stage, TOrder order) where TOrder : Order, new() { // If being called from Order Status then expect Seller to already be a full object var sellerIdFromOrder = stage == FlowStage.OrderStatus ? order?.Seller.Object?.Id : order?.Seller.IdReference; if (sellerIdFromOrder == null) { throw new OpenBookingException(new SellerMismatchError()); } if (seller?.Id != sellerIdFromOrder) { throw new OpenBookingException(new InvalidAuthorizationDetailsError()); } // Check that taxMode is set in Seller if (!(seller?.TaxMode == TaxMode.TaxGross || seller?.TaxMode == TaxMode.TaxNet)) { throw new InternalOpenBookingException(new InternalLibraryConfigurationError(), "taxMode must always be set in the Seller"); } // Default to BusinessToConsumer if no customer provided TaxPayeeRelationship taxPayeeRelationship = order.Customer == null ? TaxPayeeRelationship.BusinessToConsumer : order.BrokerRole == BrokerType.ResellerBroker || order.Customer.IsOrganization ? TaxPayeeRelationship.BusinessToBusiness : TaxPayeeRelationship.BusinessToConsumer; if (order.BrokerRole == null) { throw new OpenBookingException(new IncompleteBrokerDetailsError()); } if (order.BrokerRole == BrokerType.NoBroker && order.Broker != null) { throw new OpenBookingException(new IncompleteBrokerDetailsError()); // TODO: Placeholder for https://github.com/openactive/open-booking-api/issues/167 } // Throw error on incomplete customer details if C2, P or B if Broker type is not ResellerBroker if (order.BrokerRole != BrokerType.ResellerBroker) { if (stage != FlowStage.C1 && (order.Customer == null || string.IsNullOrWhiteSpace(order.Customer.Email))) { throw new OpenBookingException(new IncompleteCustomerDetailsError()); } } // Throw error on incomplete broker details if (order.BrokerRole != BrokerType.NoBroker && (order.Broker == null || string.IsNullOrWhiteSpace(order.Broker.Name))) { throw new OpenBookingException(new IncompleteBrokerDetailsError()); } // Throw error if TotalPaymentDue is not specified at B or P if (order.TotalPaymentDue?.Price.HasValue != true && (stage == FlowStage.B || stage == FlowStage.P)) { // TODO replace this with a more specific error throw new OpenBookingException(new OpenBookingError(), "TotalPaymentDue must have a price set"); } var payer = order.BrokerRole == BrokerType.ResellerBroker ? order.Broker : order.Customer; return(new BookingFlowContext { Stage = stage, OrderId = orderId, OrderIdTemplate = settings.OrderIdTemplate, Seller = seller, SellerId = sellerIdComponents, TaxPayeeRelationship = taxPayeeRelationship, Payer = payer }); }
public abstract Task <Order> ProcessOrderCreationFromOrderProposal(OrderIdComponents orderId, OrderIdTemplate orderIdTemplate, ILegalEntity seller, SellerIdComponents sellerId, Order order);
public abstract Task ProcessOrderProposalCustomerRejection(OrderIdComponents orderId, SellerIdComponents sellerId, OrderIdTemplate orderIdTemplate);
public override void DeleteOrder(OrderIdComponents orderId, SellerIdComponents sellerId) { FakeBookingSystem.Database.DeleteOrder(orderId.ClientId, orderId.uuid, sellerId.SellerIdLong ?? null /* Small hack to allow use of FakeDatabase when in Single Seller mode */); }
public async Task <ResponseContent> ProcessOrderUpdate(string clientId, Uri sellerId, string uuidString, string orderJson) { var orderId = new OrderIdComponents { ClientId = clientId, OrderType = OrderType.Order, uuid = ConvertToGuid(uuidString) }; using (await asyncDuplicateLock.LockAsync(GetParallelLockKey(orderId))) { Order order = OpenActiveSerializer.Deserialize <Order>(orderJson); SellerIdComponents sellerIdComponents = GetSellerIdComponentsFromApiKey(sellerId); if (order == null || order.GetType() != typeof(Order)) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Order is required for Order Cancellation"); } // Check for PatchContainsExcessiveProperties Order orderWithOnlyAllowedProperties = new Order { OrderedItem = order.OrderedItem.Select(x => new OrderItem { Id = x.Id, OrderItemStatus = x.OrderItemStatus }).ToList() }; if (OpenActiveSerializer.Serialize <Order>(order) != OpenActiveSerializer.Serialize <Order>(orderWithOnlyAllowedProperties)) { throw new OpenBookingException(new PatchContainsExcessivePropertiesError()); } // Check for PatchNotAllowedOnProperty if (!order.OrderedItem.TrueForAll(x => x.OrderItemStatus == OrderItemStatus.CustomerCancelled)) { throw new OpenBookingException(new PatchNotAllowedOnPropertyError(), "Only 'https://openactive.io/CustomerCancelled' is permitted for this property."); } List <OrderIdComponents> orderItemIds; try { orderItemIds = order.OrderedItem.Select(x => settings.OrderIdTemplate.GetOrderItemIdComponents(clientId, x.Id)).ToList(); } catch (ComponentFailedToParseException) { throw new OpenBookingException(new OrderItemIdInvalidError()); } // Check for mismatching UUIDs if (!orderItemIds.TrueForAll(x => x != null)) { throw new OpenBookingException(new OrderItemIdInvalidError()); } // Check for mismatching UUIDs if (!orderItemIds.TrueForAll(x => x.OrderType == OrderType.Order && x.uuid == orderId.uuid)) { throw new OpenBookingException(new OrderItemNotWithinOrderError()); } await ProcessCustomerCancellation(orderId, sellerIdComponents, settings.OrderIdTemplate, orderItemIds); return(ResponseContent.OpenBookingNoContentResponse()); } }
public override async Task <Order> GetOrderStatus(OrderIdComponents orderId, SellerIdComponents sellerId, ILegalEntity seller) { var(getOrderResult, dbOrder, dbOrderItems) = await FakeBookingSystem.Database.GetOrderAndOrderItems( orderId.ClientId, sellerId.SellerIdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid); if (getOrderResult == FakeDatabaseGetOrderResult.OrderWasNotFound) { throw new OpenBookingException(new UnknownOrderError()); } var orderIdUri = RenderOrderId(dbOrder.OrderMode == OrderMode.Proposal ? OrderType.OrderProposal : dbOrder.OrderMode == OrderMode.Lease ? OrderType.OrderQuote : OrderType.Order, new Guid(dbOrder.OrderId)); var orderItems = dbOrderItems.Select((orderItem) => new OrderItem { Id = dbOrder.OrderMode != OrderMode.Lease ? RenderOrderItemId(OrderType.Order, new Guid(dbOrder.OrderId), orderItem.Id) : null, AcceptedOffer = new Offer { Id = orderItem.OfferJsonLdId, Price = orderItem.Price }, OrderedItem = orderItem.OpportunityJsonLdId, OrderItemStatus = orderItem.Status == BookingStatus.Confirmed ? OrderItemStatus.OrderItemConfirmed : orderItem.Status == BookingStatus.CustomerCancelled ? OrderItemStatus.CustomerCancelled : orderItem.Status == BookingStatus.SellerCancelled ? OrderItemStatus.SellerCancelled : orderItem.Status == BookingStatus.Attended ? OrderItemStatus.AttendeeAttended : orderItem.Status == BookingStatus.Absent ? OrderItemStatus.AttendeeAbsent : (OrderItemStatus?)null }).ToList(); var order = RenderOrderFromDatabaseResult(orderIdUri, dbOrder, orderItems); // Map AcceptedOffer from object to IdReference var mappedOrderItems = order.OrderedItem.Select((orderItem) => new OrderItem { Id = orderItem.Id, AcceptedOffer = orderItem.AcceptedOffer.Object.Id, OrderedItem = orderItem.OrderedItem, OrderItemStatus = orderItem.OrderItemStatus }).ToList(); order.OrderedItem = mappedOrderItems; // These additional properties that are only available in the Order Status endpoint or B after P+A order.Seller = new ReferenceValue <ILegalEntity>(seller); order.BrokerRole = BrokerRoleToBrokerType(dbOrder.BrokerRole); order.Broker = order.BrokerRole == BrokerType.NoBroker ? null : new Organization { Name = dbOrder.BrokerName, Url = dbOrder.BrokerUrl, Telephone = dbOrder.BrokerTelephone }; order.BrokerRole = BrokerRoleToBrokerType(dbOrder.BrokerRole); if (dbOrder.CustomerType == CustomerType.Organization) { order.Customer = new Organization { Email = dbOrder.CustomerEmail, Name = dbOrder.CustomerOrganizationName, }; } else if (dbOrder.CustomerType == CustomerType.Person) { order.Customer = new Person { Email = dbOrder.CustomerEmail, Identifier = dbOrder.CustomerIdentifier, GivenName = dbOrder.CustomerGivenName, FamilyName = dbOrder.CustomerFamilyName, Telephone = dbOrder.CustomerTelephone, }; } // Payment Identifier is mandatory for non-free sessions if (dbOrder.PaymentIdentifier != null) { order.Payment = new Payment { Identifier = dbOrder.PaymentIdentifier, PaymentProviderId = dbOrder.PaymentProviderId, AccountId = dbOrder.PaymentAccountId, Name = dbOrder.PaymentName }; } return(order); }
/// <summary> /// Reject specified OrderProposal /// Note sellerId will always be null in Single Seller mode /// </summary> /// <returns>True if OrderProposal found, False if OrderProposal not found</returns> public override async Task <bool> CustomerRejectOrderProposal(OrderIdComponents orderId, SellerIdComponents sellerId) { return(await FakeBookingSystem.Database.RejectOrderProposal(orderId.ClientId, sellerId.SellerIdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid, true)); }
public override void DeleteLease(OrderIdComponents orderId, SellerIdComponents sellerId) { // Note if no lease support, simply do nothing here FakeBookingSystem.Database.DeleteLease(orderId.ClientId, orderId.uuid, sellerId.SellerIdLong.Value); }
public override async Task TriggerTestAction(OpenBookingSimulateAction simulateAction, OrderIdComponents orderId) { switch (simulateAction) { case SellerAcceptOrderProposalSimulateAction _: if (orderId.OrderType != OrderType.OrderProposal) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected OrderProposal"); } if (!await FakeBookingSystem.Database.AcceptOrderProposal(orderId.uuid)) { throw new OpenBookingException(new UnknownOrderError()); } break; case SellerAmendOrderProposalSimulateAction _: if (orderId.OrderType != OrderType.OrderProposal) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected OrderProposal"); } var version = Guid.NewGuid(); if (!await FakeBookingSystem.Database.AmendOrderProposal(orderId.uuid, version)) { throw new OpenBookingException(new UnknownOrderError()); } break; case SellerRejectOrderProposalSimulateAction _: if (orderId.OrderType != OrderType.OrderProposal) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected OrderProposal"); } if (!await FakeBookingSystem.Database.RejectOrderProposal(null, null, orderId.uuid, false)) { throw new OpenBookingException(new UnknownOrderError()); } break; case SellerRequestedCancellationWithMessageSimulateAction _: if (orderId.OrderType != OrderType.Order) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } if (!await FakeBookingSystem.Database.CancelOrderItems(null, null, orderId.uuid, null, false, true)) { throw new OpenBookingException(new UnknownOrderError()); } break; case SellerRequestedCancellationSimulateAction _: if (orderId.OrderType != OrderType.Order) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } if (!await FakeBookingSystem.Database.CancelOrderItems(null, null, orderId.uuid, null, false)) { throw new OpenBookingException(new UnknownOrderError()); } break; case AccessCodeUpdateSimulateAction _: if (orderId.OrderType != OrderType.Order) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } if (!await FakeBookingSystem.Database.UpdateAccess(orderId.uuid, updateAccessCode: true)) { throw new OpenBookingException(new UnknownOrderError()); } break; case AccessPassUpdateSimulateAction _: if (orderId.OrderType != OrderType.Order) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } if (!await FakeBookingSystem.Database.UpdateAccess(orderId.uuid, updateAccessPass: true)) { throw new OpenBookingException(new UnknownOrderError()); } break; case AttendeeAttendedSimulateAction _: if (orderId.OrderType != OrderType.Order) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } if (!await FakeBookingSystem.Database.UpdateOpportunityAttendance(orderId.uuid, true)) { throw new OpenBookingException(new UnknownOrderError()); } break; case AttendeeAbsentSimulateAction _: if (orderId.OrderType != OrderType.Order) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } if (!await FakeBookingSystem.Database.UpdateOpportunityAttendance(orderId.uuid, false)) { throw new OpenBookingException(new UnknownOrderError()); } break; case CustomerNoticeSimulateAction _: if (orderId.OrderType != OrderType.Order) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } if (!await FakeBookingSystem.Database.AddCustomerNotice(orderId.uuid)) { throw new OpenBookingException(new UnknownOrderError()); } break; case ReplacementSimulateAction _: if (orderId.OrderType != OrderType.Order) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } if (!await FakeBookingSystem.Database.ReplaceOrderOpportunity(orderId.uuid)) { throw new OpenBookingException(new UnknownOrderError()); } break; case AccessChannelUpdateSimulateAction _: if (orderId.OrderType != OrderType.Order) { throw new OpenBookingException(new UnexpectedOrderTypeError(), "Expected Order"); } if (!await FakeBookingSystem.Database.UpdateAccess(orderId.uuid, updateAccessChannel: true)) { throw new OpenBookingException(new UnknownOrderError()); } break; } }
private static string GetParallelLockKey(OrderIdComponents orderId) { // Use parsed GUID value and system defined ClientId rather than string to protect locks from abuse return($"{orderId.ClientId}|{orderId.uuid}".ToUpperInvariant()); }
protected abstract Task <Order> ProcessGetOrderStatus(OrderIdComponents orderId, SellerIdComponents sellerId, ILegalEntity seller);
protected abstract Task <DeleteOrderResult> ProcessOrderDeletion(OrderIdComponents orderId, SellerIdComponents sellerId);
/// <summary> /// Initiate customer cancellation for the specified OrderItems /// Note sellerId will always be null in Single Seller mode /// </summary> /// <returns>True if Order found, False if Order not found</returns> public override bool CustomerCancelOrderItems(OrderIdComponents orderId, SellerIdComponents sellerId, OrderIdTemplate orderIdTemplate, List <OrderIdComponents> orderItemIds) { //throw new OpenBookingException(new CancellationNotPermittedError()); return(FakeBookingSystem.Database.CancelOrderItem(orderId.ClientId, sellerId.SellerIdLong ?? null /* Hack to allow this to work in Single Seller mode too */, orderId.uuid, orderItemIds.Select(x => x.OrderItemIdLong.Value).ToList(), true)); }