public static async Task <OperationResult <IRealTimeInventory> > PurchaseAsync(this IRealTimeInventory realTimeInventory, IInventoryStorage inventoryStorage, string productId, int quantity)
        {
            if (quantity < 0)
            {
                return(InventoryServiceErrorMessageGenerator.Generate(ErrorType.NEGATIVE_PURCHASE_FOR_PURCHASEFROMRESERVATION, realTimeInventory, quantity).ToFailedOperationResult(realTimeInventory, productId));
            }
            var newQuantity = realTimeInventory.Quantity - quantity;

            var newReserved = Math.Max(0, realTimeInventory.Reserved - quantity);

            //todo we still want to sell even though there is reservation
            //var newReserved = realTimeInventory.Reserved - quantity;
            //if(newReserved<0) throw new Exception("provided " + quantity + ", available reservations must be less than or equal to quantity for product " + productId);

            if (newQuantity - realTimeInventory.Holds < 0)
            {
                return(InventoryServiceErrorMessageGenerator.Generate(ErrorType.PURCHASE_EXCEED_QUANTITY_FOR_PURCHASEFROMRESERVATION, realTimeInventory, quantity).ToFailedOperationResult(realTimeInventory, productId));
            }

            var newrealTimeInventory = new RealTimeInventory(productId, newQuantity, newReserved, realTimeInventory.Holds);
            var result = await inventoryStorage.WriteInventoryAsync(newrealTimeInventory);

            if (!result.IsSuccessful)
            {
                return(InventoryServiceErrorMessageGenerator.Generate(ErrorType.UNABLE_TO_UPDATE_INVENTORY_STORAGE, realTimeInventory, quantity, result.Errors).ToFailedOperationResult(realTimeInventory, productId));
            }

            return(newrealTimeInventory.ToOperationResult(isSuccessful: true));
        }
        public static async Task <OperationResult <IRealTimeInventory> > PurchaseFromHoldsAsync(this IRealTimeInventory realTimeInventory, IInventoryStorage inventoryStorage, string productId, int quantity)
        {
            if (quantity < 0)
            {
                return(InventoryServiceErrorMessageGenerator.Generate(ErrorType.NEGATIVE_PURCHASE_FOR_PURCHASEFROMHOLD, realTimeInventory, quantity).ToFailedOperationResult(realTimeInventory, productId));
            }
            var newQuantity = realTimeInventory.Quantity - quantity;
            var newHolds    = realTimeInventory.Holds - quantity;

            if (newQuantity < 0 || newHolds < 0)
            {
                return(InventoryServiceErrorMessageGenerator.Generate(ErrorType.PURCHASE_EXCEED_QUANTITY_FOR_PURCHASEFROMHOLD, realTimeInventory, quantity).ToFailedOperationResult(realTimeInventory, productId));
            }

            var newrealTimeInventory = new RealTimeInventory(productId, newQuantity, realTimeInventory.Reserved,
                                                             newHolds);

            var result = await inventoryStorage.WriteInventoryAsync(newrealTimeInventory).ConfigureAwait(false);

            if (!result.IsSuccessful)
            {
                return(InventoryServiceErrorMessageGenerator.Generate(ErrorType.UNABLE_TO_UPDATE_INVENTORY_STORAGE, realTimeInventory, quantity, result.Errors).ToFailedOperationResult(realTimeInventory, productId));
            }

            return(newrealTimeInventory.ToOperationResult(isSuccessful: true));
        }
        public static async Task <OperationResult <IRealTimeInventory> > UpdateQuantityAsync(this IRealTimeInventory realTimeInventory, IInventoryStorage inventoryStorage, string productId, int quantity)
        {
            var newQuantity = realTimeInventory.Quantity + quantity;

            var newRealTimeInventory = new RealTimeInventory(productId, newQuantity, realTimeInventory.Reserved, realTimeInventory.Holds);
            var result = await inventoryStorage.WriteInventoryAsync(newRealTimeInventory);

            if (!result.IsSuccessful)
            {
                return(InventoryServiceErrorMessageGenerator.Generate(ErrorType.UNABLE_TO_UPDATE_INVENTORY_STORAGE, realTimeInventory, quantity, result.Errors).ToFailedOperationResult(realTimeInventory, productId));
            }

            return(newRealTimeInventory.ToOperationResult(isSuccessful: true));
        }
        public static async Task <OperationResult <IRealTimeInventory> > PlaceHoldAsync(this IRealTimeInventory realTimeInventory, IInventoryStorage inventoryStorage, string productId, int toHold)
        {
            var newHolds = realTimeInventory.Holds + toHold;

            if (newHolds > realTimeInventory.Quantity)
            {
                return(InventoryServiceErrorMessageGenerator.Generate(ErrorType.HOLD_EXCEED_QUANTITY_FOR_HOLD, realTimeInventory, toHold).ToFailedOperationResult(realTimeInventory, productId));
            }
            var newRealTimeInventory = new RealTimeInventory(productId, realTimeInventory.Quantity, realTimeInventory.Reserved, newHolds);

            var result = await inventoryStorage.WriteInventoryAsync(newRealTimeInventory);

            if (!result.IsSuccessful)
            {
                return(InventoryServiceErrorMessageGenerator.Generate(ErrorType.UNABLE_TO_UPDATE_INVENTORY_STORAGE, realTimeInventory, toHold, result.Errors).ToFailedOperationResult(realTimeInventory, productId));
            }

            return(newRealTimeInventory.ToOperationResult(isSuccessful: true));
        }
        public static async Task <OperationResult <IRealTimeInventory> > ReserveAsync(this IRealTimeInventory realTimeInventory, IInventoryStorage inventoryStorage, string productId, int reservationQuantity)
        {
            var newReserved = Math.Max(0, realTimeInventory.Reserved + reservationQuantity);

            if ((reservationQuantity > 0) && (newReserved > realTimeInventory.Quantity - realTimeInventory.Holds))
            {
                return(InventoryServiceErrorMessageGenerator.Generate(ErrorType.RESERVATION_EXCEED_QUANTITY, realTimeInventory, reservationQuantity).ToFailedOperationResult(realTimeInventory, productId));
            }

            var newRealTimeInventory = new RealTimeInventory(productId, realTimeInventory.Quantity, newReserved, realTimeInventory.Holds);

            var result = await inventoryStorage.WriteInventoryAsync(newRealTimeInventory);

            if (!result.IsSuccessful)
            {
                return(InventoryServiceErrorMessageGenerator.Generate(ErrorType.UNABLE_TO_UPDATE_INVENTORY_STORAGE, realTimeInventory, reservationQuantity, result.Errors).ToFailedOperationResult(realTimeInventory, productId));
            }

            return(newRealTimeInventory.ToOperationResult(isSuccessful: true));
        }