private CitationSeverity CalculateSeverity(ITrafficState trafficState, ILegalEntity citee)
    {
        int speedDiff = trafficState.IncidentSpeed - trafficState.SpeedLimit;

        // If it's the citee's birthday, knock 5 MPH off of their speed diff.
        if (citee.BirthDate.Month == _calendar.CurrentDate.Month &&
            citee.BirthDate.Day == _calendar.CurrentDate.Day)
        {
            speedDiff -= 5;
        }

        CitationSeverity severity;

        if (speedDiff > 25)
        {
            severity = CitationSeverity.Large;
        }
        else if (speedDiff > 10)
        {
            severity = CitationSeverity.Medium;
        }
        else if (speedDiff > 0)
        {
            severity = CitationSeverity.Small;
        }
        else
        {
            severity = CitationSeverity.None;
        }

        return(severity);
    }
 public override IProduct CloneForNewOwner(ILegalEntity owner)
 {
     if (!base.Template)
     {
         throw new ProductUseException("Owners are not allowed to materialize Products out of nothing!");
     }
     return(new Drink(this, owner));
 }
 public SpeedingCitation(ICalendarProvider calendar, ITicketer issuer, ILegalEntity citee, CitationSeverity severity)
 {
     _calendar    = calendar ?? throw new ArgumentNullException(nameof(calendar));
     Issuer       = issuer ?? throw new ArgumentNullException(nameof(issuer));
     Citee        = citee ?? throw new ArgumentNullException(nameof(citee));
     Severity     = severity;
     CitationDate = _calendar.CurrentDate;
 }
    public IEnumerable <ICitation> IssueCitations(ITrafficState trafficState, ILegalEntity citee)
    {
        List <ICitation> citations;

        citations = TrafficLaws
                    .Where(x => x.ShouldIssueCitation(trafficState, citee))
                    .Select(x => x.CreateCitation(trafficState, citee, this))
                    .ToList();

        return(citations);
    }
        //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));
        }
Beispiel #6
0
        public IEnumerable <IProduct> PackageProductFor(ILegalEntity customer, uint quantity)
        {
            var products = new List <IProduct>();

            if (quantity > Quantity)
            {
                throw new ArgumentOutOfRangeException("You don't have that many stocked!");
            }
            Quantity -= quantity;
            for (int i = 0; i < quantity; i++)
            {
                products.Add(Product.CloneForNewOwner(customer));
            }
            return(products);
        }
Beispiel #7
0
        // Note that the order of "Organization, Person" must match the generated properties that accept ILegalEntity
        public static SingleValues <Organization, Person> GetSingleValues(this ILegalEntity legalEntity)
        {
            switch (legalEntity)
            {
            case Organization organization:
                return(organization);

            case Person person:
                return(person);

            case null:
                return(default);

            default:
                throw new InvalidCastException($"Supplied ILegalEntity is of unknown type {legalEntity.GetType().ToString()}");
            }
        }
Beispiel #8
0
        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);
        }
Beispiel #9
0
        //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
            });
        }
 protected Drink(Drink drink, ILegalEntity owner) : base((Product)drink, owner)
 {
 }
 public abstract IProduct CloneForNewOwner(ILegalEntity owner);
 protected Product(Product product, ILegalEntity owner) : this(product.Name, product.Description, product.ManufacturingCost, product.Servings, product.VAT, product.ExtraStoreMarkupFactor, owner)
 {
     Template = false;
 }
Beispiel #13
0
 public abstract Task <Order> ProcessOrderCreationFromOrderProposal(OrderIdComponents orderId, OrderIdTemplate orderIdTemplate, ILegalEntity seller, SellerIdComponents sellerId, Order order);
    public bool ShouldIssueCitation(ITrafficState trafficState, ILegalEntity citee)
    {
        var severity = CalculateSeverity(trafficState, citee);

        return(severity != CitationSeverity.None);
    }
Beispiel #15
0
 protected abstract Task <Order> ProcessGetOrderStatus(OrderIdComponents orderId, SellerIdComponents sellerId, ILegalEntity seller);
Beispiel #16
0
        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);
        }
 public ICitation CreateCitation(ITrafficState trafficState, ILegalEntity citee, ITicketer issuer)
 {
     return(new SpeedingCitation(_calendar, issuer, citee, CalculateSeverity(trafficState, citee)));
 }