public static VertexCalculateTaxRequest ToVertexCalculateTaxRequest(this OrderWorksheet order, List <OrderPromotion> promosOnOrder, string companyCode, VertexSaleMessageType type)
        {
            var itemLines     = order.LineItems.Select(li => ToVertexLineItem(li));
            var shippingLines = order.ShipEstimateResponse.ShipEstimates.Select(se =>
            {
                var firstLi = order.LineItems.First(li => li.ID == se.ShipEstimateItems.First().LineItemID);
                return(ToVertexLineItem(se, firstLi.ShippingAddress));
            });

            return(new VertexCalculateTaxRequest()
            {
                postingDate = DateTime.Now.ToString("yyyy-MM-dd"),
                saleMessageType = type,
                transactionType = VertexTransactionType.SALE,
                transactionId = order.Order.ID,
                seller = new VertexSeller()
                {
                    company = companyCode
                },
                customer = new VertexCustomer()
                {
                    customerCode = new VertexCustomerCode()
                    {
                        classCode = order.Order.FromUserID,
                        value = order.Order.FromUser.Email
                    },
                },
                lineItems = itemLines.Concat(shippingLines).ToList()
            });
        }
Exemple #2
0
        private static IList <LineItem> HandleLineItemPromoDiscounting(OrderWorksheet order, List <OrderPromotion> promosOnOrder)
        {
            List <string> allLineItemIDsWithDiscounts = promosOnOrder
                                                        .Where(promo => promo.LineItemLevel)
                                                        .Select(promo => promo.LineItemID)
                                                        .Distinct().ToList(); // X001, X002...

            List <string> allLineItemLevelPromoCodes = promosOnOrder
                                                       .Where(promo => promo.LineItemLevel)
                                                       .Select(promo => promo.Code)
                                                       .Distinct().ToList(); // 25OFF, SAVE15...

            Dictionary <string, decimal> totalWeightedLineItemDiscounts = new Dictionary <string, decimal>();

            foreach (string lineItemID in allLineItemIDsWithDiscounts)
            {
                // Initialize discounts at 0.00
                totalWeightedLineItemDiscounts.Add(lineItemID, 0M);
            }

            // Calculate discounts one promo code at a time
            foreach (string promoCode in allLineItemLevelPromoCodes)
            {
                CalculateDiscountByPromoCode(promoCode, order, promosOnOrder, totalWeightedLineItemDiscounts);
            }

            foreach (string lineItemID in allLineItemIDsWithDiscounts)
            {
                LineItem lineItemToUpdate = order.LineItems.FirstOrDefault(lineItem => lineItem.ID == lineItemID);
                lineItemToUpdate.LineTotal = lineItemToUpdate.LineSubtotal - totalWeightedLineItemDiscounts[lineItemID];
            }

            return(order.LineItems);
        }
Exemple #3
0
        private static void CalculateDiscountByPromoCode(string promoCode, OrderWorksheet order, List <OrderPromotion> promosOnOrder, Dictionary <string, decimal> totalWeightedLineItemDiscounts)
        {
            // Total discounted from this code for all line items
            decimal totalDiscountedByOrderCloud = promosOnOrder
                                                  .Where(promo => promo.Code == promoCode)
                                                  .Select(promo => promo.Amount)
                                                  .Sum();

            // Line items discounted with this code
            List <string> eligibleLineItemIDs = promosOnOrder
                                                .Where(promo => promo.Code == promoCode)
                                                .Select(promo => promo.LineItemID)
                                                .ToList();

            // Subtotal of all of these line items before applying promo code
            decimal eligibleLineItemSubtotal = order.LineItems
                                               .Where(lineItem => eligibleLineItemIDs.Contains(lineItem.ID))
                                               .Select(lineItem => lineItem.LineSubtotal)
                                               .Sum();

            Dictionary <string, decimal> weightedLineItemDiscountsByPromoCode = new Dictionary <string, decimal>();

            HandleWeightedDiscounting(eligibleLineItemIDs, weightedLineItemDiscountsByPromoCode, order, eligibleLineItemSubtotal, totalDiscountedByOrderCloud);

            foreach (string lineItemID in eligibleLineItemIDs)
            {
                totalWeightedLineItemDiscounts[lineItemID] += weightedLineItemDiscountsByPromoCode[lineItemID];
            }
        }
        public static CreateTransactionModel ToAvalaraTransationModel(this OrderWorksheet order, string companyCode, DocumentType docType)
        {
            var buyerLocationID = order.Order.BillingAddress.ID;

            var standardLineItems     = order.LineItems.Where(li => li.Product.xp.ProductType == "Standard")?.ToList();
            var poLineItemIDs         = order.LineItems.Where(li => li.Product.xp.ProductType == "PurchaseOrder")?.Select(li => li?.ID)?.ToList();
            var standardShipEstimates = order.ShipEstimateResponse.ShipEstimates.Where(estimate =>
            {
                return(!estimate.ShipEstimateItems.Any(item => poLineItemIDs.Contains(item.LineItemID)));
            });

            var shipingLines = standardShipEstimates.Select(shipment =>
            {
                var(shipFrom, shipTo) = shipment.GetAddresses(order.LineItems);
                var method            = shipment.GetSelectedShippingMethod();
                return(method.ToLineItemModel(shipFrom, shipTo));
            });

            var hasResaleCert = ((int?)order.Order.BillingAddress.xp.AvalaraCertificateID != null);
            var exemptionNo   = hasResaleCert ? buyerLocationID : null;

            var productLines = standardLineItems.Select(lineItem =>
                                                        lineItem.ToLineItemModel(lineItem.ShipFromAddress, lineItem.ShippingAddress, exemptionNo));

            return(new CreateTransactionModel()
            {
                companyCode = companyCode,
                type = docType,
                customerCode = buyerLocationID,
                date = DateTime.Now,
                lines = productLines.Concat(shipingLines).ToList(),
                purchaseOrderNo = order.Order.ID
            });
        }
Exemple #5
0
        public static CreateTransactionModel ToAvalaraTransactionModel(this OrderWorksheet order, string companyCode, DocumentType docType, List <OrderPromotion> promosOnOrder)
        {
            var buyerLocationID = order.Order.BillingAddress.ID;

            var standardLineItems = order.LineItems.Where(li => li.Product.xp.ProductType == "Standard")?.ToList();

            var standardShipEstimates = order.ShipEstimateResponse?.ShipEstimates;

            var shippingLines = standardShipEstimates.Select(shipment =>
            {
                var(shipFrom, shipTo) = shipment.GetAddresses(order.LineItems);
                return(shipment.ToLineItemModel(shipFrom, shipTo));
            });

            string exemptionNo = null;             // can set to a resale cert id

            var productLines = standardLineItems.Select(lineItem =>
                                                        lineItem.ToLineItemModel(lineItem.ShipFromAddress, lineItem.ShippingAddress, exemptionNo));

            return(new CreateTransactionModel()
            {
                companyCode = companyCode,
                type = docType,
                customerCode = buyerLocationID,
                date = DateTime.Now,
                discount = GetOrderOnlyTotalDiscount(promosOnOrder),
                lines = productLines.Concat(shippingLines).ToList(),
                purchaseOrderNo = order.Order.ID
            });
        }
Exemple #6
0
        /// <summary>
        /// Calculates tax for an order without creating any records. Use this to display tax amount to user prior to order submit.
        /// </summary>
        public async Task <OrderTaxCalculation> CalculateEstimateAsync(OrderWorksheet orderWorksheet, List <OrderPromotion> promotions)
        {
            var orders = await CalculateTax(orderWorksheet);

            var orderTaxCalculation = orders.ToOrderTaxCalculation();

            return(orderTaxCalculation);
        }
Exemple #7
0
        private async Task <OrderTaxCalculation> CalculateTaxAsync(OrderWorksheet orderWorksheet, List <OrderPromotion> promotions, VertexSaleMessageType type)
        {
            var request  = orderWorksheet.ToVertexCalculateTaxRequest(promotions, _config.CompanyName, type);
            var response = await _client.CalculateTax(request);

            var orderTaxCalculation = response.ToOrderTaxCalculation();

            return(orderTaxCalculation);
        }
        private async Task HandleTaxTransactionCreationAsync(OrderWorksheet orderWorksheet)
        {
            var transaction = await _avalara.CreateTransactionAsync(orderWorksheet);

            await _oc.Orders.PatchAsync <HSOrder>(OrderDirection.Incoming, orderWorksheet.Order.ID, new PartialOrder()
            {
                TaxCost = transaction.totalTax ?? 0,  // Set this again just to make sure we have the most up to date info
                xp      = new { AvalaraTaxTransactionCode = transaction.code }
            });
        }
Exemple #9
0
        public async Task <TransactionModel> GetEstimateAsync(OrderWorksheet orderWorksheet)
        {
            if (ShouldMockAvalaraResponse())
            {
                return(CreateMockTransactionModel());
            }
            var taxEstimate = await CreateTransactionAsync(DocumentType.SalesOrder, orderWorksheet);

            return(taxEstimate);
        }
Exemple #10
0
        public async Task <OrderTaxCalculation> CalculateEstimateAsync(OrderWorksheet orderWorksheet, List <OrderPromotion> promotions)
        {
            if (ShouldMockAvalaraResponse())
            {
                return(CreateMockTransactionModel());
            }
            var taxEstimate = await CreateTransactionAsync(DocumentType.SalesOrder, orderWorksheet, promotions);

            return(taxEstimate);
        }
Exemple #11
0
        public async Task <OrderTaxCalculation> CommitTransactionAsync(OrderWorksheet orderWorksheet, List <OrderPromotion> promotions)
        {
            if (ShouldMockAvalaraResponse())
            {
                return(CreateMockTransactionModel());
            }

            var transaction = await CreateTransactionAsync(DocumentType.SalesInvoice, orderWorksheet, promotions);

            return(transaction);
        }
Exemple #12
0
        public async Task <TransactionModel> CreateTransactionAsync(OrderWorksheet orderWorksheet)
        {
            if (ShouldMockAvalaraResponse())
            {
                return(CreateMockTransactionModel());
            }

            var transaction = await CreateTransactionAsync(DocumentType.SalesInvoice, orderWorksheet);

            return(transaction);
        }
Exemple #13
0
        /// <summary>
        ///	Returns a list of TaxJarOrders because each order has a single to and from address. They therefor coorespond to OrderCloud LineItems.
        /// </summary>
        public static List <TaxJarOrder> ToTaxJarOrders(this OrderWorksheet order)
        {
            var itemLines     = order.LineItems.Select(li => ToTaxJarOrders(li, order.Order.ID));
            var shippingLines = order.ShipEstimateResponse.ShipEstimates.Select(se =>
            {
                var firstLineItem = order.LineItems.First(li => li.ID == se.ShipEstimateItems.First().LineItemID);
                return(ToTaxJarOrders(se, firstLineItem, order.Order.ID));
            });

            return(itemLines.Concat(shippingLines).ToList());
        }
Exemple #14
0
        private async Task HandleTaxTransactionCreationAsync(OrderWorksheet orderWorksheet)
        {
            var promotions = await _oc.Orders.ListAllPromotionsAsync(OrderDirection.All, orderWorksheet.Order.ID);

            var taxCalculation = await _taxCalculator.CommitTransactionAsync(orderWorksheet, promotions);

            await _oc.Orders.PatchAsync <HSOrder>(OrderDirection.Incoming, orderWorksheet.Order.ID, new PartialOrder()
            {
                TaxCost = taxCalculation.TotalTax,  // Set this again just to make sure we have the most up to date info
                xp      = new { ExternalTaxTransactionID = taxCalculation.ExternalTransactionID }
            });
        }
Exemple #15
0
        /// <summary>
        /// Creates a tax transaction record in the calculating system. Use this once on purchase, payment capture, or fulfillment.
        /// </summary>
        public async Task <OrderTaxCalculation> CommitTransactionAsync(OrderWorksheet orderWorksheet, List <OrderPromotion> promotions)
        {
            var orders = await CalculateTax(orderWorksheet);

            foreach (var response in orders)
            {
                response.request.TransactionDate = DateTime.Now.ToString("yyyy/MM/dd");
                response.request.SalesTax        = response.response.TaxableAmount;
            }

            await Throttler.RunAsync(orders, 100, 8, async order => await MakeRequest(() => _client.CreateOrderAsync(order)));

            var orderTaxCalculation = orders.ToOrderTaxCalculation();

            return(orderTaxCalculation);
        }
Exemple #16
0
        public static CreateTransactionModel ToAvalaraTransactionModel(this OrderWorksheet order, string companyCode, DocumentType docType, List <OrderPromotion> promosOnOrder)
        {
            var buyerLocationID = order.Order.BillingAddress.ID;

            var standardLineItems = order.LineItems.Where(li => li.Product.xp.ProductType == "Standard")?.ToList();

            if (promosOnOrder.Any(promo => promo.LineItemLevel))
            {
                order.LineItems = HandleLineItemPromoDiscounting(order, promosOnOrder);
            }

            var standardShipEstimates = order.ShipEstimateResponse?.ShipEstimates;

            var shippingLines = standardShipEstimates.Select(shipment =>
            {
                var(shipFrom, shipTo) = shipment.GetAddresses(order.LineItems);
                var method            = shipment.GetSelectedShippingMethod();
                return(method.ToLineItemModel(shipFrom, shipTo));
            });

            var hasResaleCert = ((int?)order.Order.BillingAddress.xp?.AvalaraCertificateID != null);
            var exemptionNo   = hasResaleCert ? buyerLocationID : null;

            var productLines = standardLineItems.Select(lineItem =>
                                                        lineItem.ToLineItemModel(lineItem.ShipFromAddress, lineItem.ShippingAddress, exemptionNo));

            return(new CreateTransactionModel()
            {
                companyCode = companyCode,
                type = docType,
                customerCode = buyerLocationID,
                date = DateTime.Now,
                discount = GetOrderOnlyTotalDiscount(order, promosOnOrder),
                lines = productLines.Concat(shippingLines).ToList(),
                purchaseOrderNo = order.Order.ID
            });
        }
Exemple #17
0
        private async Task <IEnumerable <(TaxJarOrder request, TaxResponseAttributes response)> > CalculateTax(OrderWorksheet orderWorksheet)
        {
            var orders    = orderWorksheet.ToTaxJarOrders();
            var responses = await Throttler.RunAsync(orders, 100, 8, async order =>
            {
                var tax = await MakeRequest(() => _client.TaxForOrderAsync(order));
                return(order, tax);
            });

            return(responses);
        }
Exemple #18
0
        private async Task <TransactionModel> CreateTransactionAsync(DocumentType docType, OrderWorksheet orderWorksheet)
        {
            var standardLineItems = orderWorksheet.LineItems.Where(li => li.Product.xp.ProductType == "Standard")?.ToList();

            if (standardLineItems.Any())
            {
                try
                {
                    if (ShouldMockAvalaraResponse())
                    {
                        return(CreateMockTransactionModel());
                    }

                    var promosOnOrder = await _oc.Orders.ListAllPromotionsAsync(OrderDirection.Incoming, orderWorksheet.Order.ID);

                    var createTransactionModel = orderWorksheet.ToAvalaraTransactionModel(_companyCode, docType, promosOnOrder);
                    var transaction            = await _avaTax.CreateTransactionAsync("", createTransactionModel);

                    return(transaction);
                }
                catch (AvaTaxError e)
                {
                    throw new CatalystBaseException("AvalaraTaxError", e.error.error.message, e.error.error, 400);
                }
            }
            else
            {
                return(new TransactionModel
                {
                    code = "NotTaxable",
                    totalTax = 0
                });
            }
        }
Exemple #19
0
 /// <summary>
 /// Calculates tax for an order without creating any records. Use this to display tax amount to user prior to order submit.
 /// </summary>
 public async Task <OrderTaxCalculation> CalculateEstimateAsync(OrderWorksheet orderWorksheet, List <OrderPromotion> promotions) =>
 await CalculateTaxAsync(orderWorksheet, promotions, VertexSaleMessageType.QUOTATION);
Exemple #20
0
 /// <summary>
 /// Creates a tax transaction record in the calculating system. Use this once on purchase, payment capture, or fulfillment.
 /// </summary>
 public async Task <OrderTaxCalculation> CommitTransactionAsync(OrderWorksheet orderWorksheet, List <OrderPromotion> promotions) =>
 await CalculateTaxAsync(orderWorksheet, promotions, VertexSaleMessageType.INVOICE);
Exemple #21
0
 private static decimal GetOrderOnlyTotalDiscount(OrderWorksheet order, List <OrderPromotion> promosOnOrder)
 {
     return(promosOnOrder
            .Where(promo => promo.LineItemID == null && !promo.LineItemLevel)
            .Select(promo => promo.Amount).Sum());
 }
Exemple #22
0
        private static void HandleWeightedDiscounting(List <string> eligibleLineItemIDs, Dictionary <string, decimal> weightedLineItemDiscountsByPromoCode, OrderWorksheet order, decimal eligibleLineItemSubtotal, decimal totalDiscountedByOrderCloud)
        {
            foreach (string lineItemID in eligibleLineItemIDs)
            {
                weightedLineItemDiscountsByPromoCode.Add(lineItemID, 0M);                 // Initialize discount for this promo code at 0.00
                LineItem lineItem = order.LineItems.FirstOrDefault(lineItem => lineItem.ID == lineItemID);

                // Determine amount of promo code discount to apply to this line item and round
                decimal lineItemRateOfSubtotal = lineItem.LineSubtotal / eligibleLineItemSubtotal;
                decimal weightedDiscount       = Math.Round(lineItemRateOfSubtotal * totalDiscountedByOrderCloud, 2);
                weightedLineItemDiscountsByPromoCode[lineItemID] += weightedDiscount;
            }

            decimal totalWeightedDiscountApplied = weightedLineItemDiscountsByPromoCode.Sum(discount => discount.Value);

            if (totalDiscountedByOrderCloud != totalWeightedDiscountApplied)
            {
                // If a small discrepancy occurs due to rounding, resolve it by adding/subtracting the difference to the item that was discounted the most
                decimal difference = totalDiscountedByOrderCloud - totalWeightedDiscountApplied;
                string  lineItemIDToApplyDiscountDifference = weightedLineItemDiscountsByPromoCode.Aggregate((x, y) => x.Value > y.Value ? x : y).Key;
                weightedLineItemDiscountsByPromoCode[lineItemIDToApplyDiscountDifference] += difference;
            }
        }
Exemple #23
0
        public async Task <TransactionModel> CreateTransactionAsync(OrderWorksheet orderWorksheet)
        {
            var transaction = await CreateTransactionAsync(DocumentType.SalesInvoice, orderWorksheet);

            return(transaction);
        }
Exemple #24
0
        public async Task <TransactionModel> GetEstimateAsync(OrderWorksheet orderWorksheet)
        {
            var taxEstimate = await CreateTransactionAsync(DocumentType.SalesOrder, orderWorksheet);

            return(taxEstimate);
        }
Exemple #25
0
        private async Task <TransactionModel> CreateTransactionAsync(DocumentType docType, OrderWorksheet orderWorksheet)
        {
            var standardLineItems = orderWorksheet.LineItems.Where(li => li.Product.xp.ProductType == "Standard")?.ToList();

            if (standardLineItems.Any())
            {
                try
                {
                    var createTransactionModel = orderWorksheet.ToAvalaraTransationModel(_companyCode, docType);
                    var transaction            = await _avaTax.CreateTransactionAsync("", createTransactionModel);

                    return(transaction);
                }
                catch (AvaTaxError e)
                {
                    throw new CatalystBaseException("AvalaraTaxError", 400, e.error.error.message, e.error.error);
                }
            }
            else
            {
                return(new TransactionModel
                {
                    code = "NotTaxable",
                    totalTax = 0
                });
            }
        }
Exemple #26
0
        private async Task <OrderTaxCalculation> CreateTransactionAsync(DocumentType docType, OrderWorksheet orderWorksheet, List <OrderPromotion> promotions)
        {
            var standardLineItems = orderWorksheet.LineItems.Where(li => li.Product.xp.ProductType == "Standard")?.ToList();

            if (standardLineItems.Any())
            {
                try
                {
                    if (ShouldMockAvalaraResponse())
                    {
                        return(CreateMockTransactionModel());
                    }
                    var createTransactionModel = orderWorksheet.ToAvalaraTransactionModel(_companyCode, docType, promotions);
                    var transaction            = await _avaTax.CreateTransactionAsync("", createTransactionModel);

                    return(transaction.ToOrderTaxCalculation());
                }
                catch (AvaTaxError e)
                {
                    throw new CatalystBaseException("AvalaraTaxError", e.error.error.message, e.error.error, 400);
                }
            }
            else
            {
                return(new OrderTaxCalculation
                {
                    OrderID = orderWorksheet.Order.ID,
                    ExternalTransactionID = "NotTaxable",
                    TotalTax = 0
                });
            }
        }