예제 #1
0
            /// <summary>
            /// Retrieves a customer discount item of the indicated type if it exists and creates one if not.
            /// </summary>
            /// <param name="salesLine">The sales line from which to find customer discount lines.</param>
            /// <param name="customerDiscountType">The customer discount type.</param>
            /// <param name="lineDiscountType">The line discount type.</param>
            /// <returns>
            /// The discount line.
            /// </returns>
            private static DiscountLine GetCustomerDiscountItem(SalesLine salesLine, CustomerDiscountType customerDiscountType, DiscountLineType lineDiscountType)
            {
                DiscountLine discount;
                var          discounts = from d in salesLine.DiscountLines
                                         where
                                         d.DiscountLineType == lineDiscountType &&
                                         d.CustomerDiscountType == customerDiscountType
                                         select d;

                // If the discount doesn't exist create a new one
                if (discounts.Count() == 0)
                {
                    discount = new DiscountLine
                    {
                        DiscountLineType     = lineDiscountType,
                        CustomerDiscountType = customerDiscountType,
                    };

                    salesLine.DiscountLines.Add(discount);
                }
                else
                {
                    // otherwise select it.
                    discount = discounts.First();
                }

                return(discount);
            }
예제 #2
0
            private static ProductPrice GetItemPrice(RequestContext requestContext, string itemId, string inventDimId, string unitOfMeasure, string customerAcctNumber)
            {
                SalesTransaction salesTransaction = new SalesTransaction
                {
                    Id         = Guid.NewGuid().ToString(),
                    CustomerId = customerAcctNumber,
                };

                SalesLine salesLine = new SalesLine()
                {
                    LineId = Guid.NewGuid().ToString("N"),
                    ItemId = itemId,
                    InventoryDimensionId    = inventDimId,
                    SalesOrderUnitOfMeasure = unitOfMeasure,
                    Quantity = 1m,
                };

                salesTransaction.SalesLines.Add(salesLine);

                GetIndependentPriceDiscountServiceRequest priceRequest = new GetIndependentPriceDiscountServiceRequest(salesTransaction);

                GetPriceServiceResponse pricingServiceResponse = requestContext.Execute <GetPriceServiceResponse>(priceRequest);

                SalesLine resultLine = pricingServiceResponse.Transaction.SalesLines[0];

                ProductPrice productPrice = GetProductPrice(
                    resultLine.ItemId,
                    resultLine.InventoryDimensionId,
                    resultLine.BasePrice,
                    resultLine.AgreementPrice,
                    resultLine.AdjustedPrice,
                    requestContext.GetChannelConfiguration().Currency);

                return(productPrice);
            }
        public Task <bool> Handle(RegisterNewSalesLineCommand message, CancellationToken cancellationToken)
        {
            if (!message.IsValid())
            {
                NotifyValidationErrors(message);
                return(Task.FromResult(false));
            }

            var SalesLine = new SalesLine(Guid.NewGuid(), message.IdSales, message.IdDisc, "", message.Quantity, message.PriceUnit, message.SalesPrice, message.Cashback);

            if (_SalesRepository.GetById(SalesLine.IdSales) != null)
            {
                Bus.RaiseEvent(new DomainNotification(message.MessageType, "Ordem de Venda não existe"));
                return(Task.FromResult(false));
            }

            if (_DiscMusicRepository.GetById(SalesLine.IdItem) != null)
            {
                Bus.RaiseEvent(new DomainNotification(message.MessageType, "O disco não existe"));
                return(Task.FromResult(false));
            }

            _SalesLineRepository.Add(SalesLine);

            if (Commit())
            {
                Bus.RaiseEvent(new SalesLineRegisteredEvent(SalesLine.Id, SalesLine.IdSales, SalesLine.IdItem, SalesLine.Quantity, SalesLine.PriceUnit, SalesLine.SalesPrice, SalesLine.Cashback));
            }

            return(Task.FromResult(false));
        }
예제 #4
0
            /// <summary>
            /// Clear all customer discount lines.
            /// </summary>
            /// <param name="salesLine">The sales line.</param>
            /// <param name="deleteTotalCustomerDiscount">A value indicating whether the customer total discount should be deleted as well.</param>
            private static void ClearCustomerDiscountLines(SalesLine salesLine, bool deleteTotalCustomerDiscount)
            {
                if (salesLine == null)
                {
                    throw new ArgumentNullException("salesLine");
                }

                // Create a list for items to be removed
                var deleteList = new List <DiscountLine>();

                foreach (DiscountLine discountLine in salesLine.DiscountLines)
                {
                    if (discountLine.DiscountLineType == DiscountLineType.CustomerDiscount)
                    {
                        if (discountLine.CustomerDiscountType == CustomerDiscountType.TotalDiscount && deleteTotalCustomerDiscount)
                        {
                            deleteList.Add(discountLine);
                        }
                        else if (discountLine.CustomerDiscountType != CustomerDiscountType.TotalDiscount)
                        {
                            deleteList.Add(discountLine);
                        }
                    }
                }

                // Remove peridic discounts.
                foreach (DiscountLine discountLine in deleteList)
                {
                    salesLine.DiscountLines.Remove(discountLine);
                }
            }
        public Task <bool> Handle(UpdateSalesLineCommand message, CancellationToken cancellationToken)
        {
            if (!message.IsValid())
            {
                NotifyValidationErrors(message);
                return(Task.FromResult(false));
            }

            var SalesLine         = new SalesLine(Guid.NewGuid(), message.IdSales, message.IdDisc, "", message.Quantity, message.PriceUnit, message.SalesPrice, message.Cashback);
            var existingSalesLine = _SalesLineRepository.GetById(SalesLine.Id);

            if (existingSalesLine != null && existingSalesLine.Id != SalesLine.Id)
            {
                if (!existingSalesLine.Equals(SalesLine))
                {
                    Bus.RaiseEvent(new DomainNotification(message.MessageType, "Ocorreu um erro ao salvar o item de venda"));
                    return(Task.FromResult(false));
                }
            }

            _SalesLineRepository.Update(SalesLine);

            if (Commit())
            {
                Bus.RaiseEvent(new SalesLineUpdatedEvent(SalesLine.Id, SalesLine.IdSales, SalesLine.IdItem, SalesLine.DiscName, SalesLine.Quantity, SalesLine.PriceUnit, SalesLine.Cashback));
            }

            return(Task.FromResult(false));
        }
예제 #6
0
            internal static void ClearDiscountLinesOfType(SalesLine salesLine, DiscountLineType?lineType)
            {
                if (lineType == null)
                {
                    salesLine.DiscountLines.Clear();
                    return;
                }

                var remainingDiscounts = salesLine.DiscountLines.Where(l => l.DiscountLineType != lineType).ToList();

                salesLine.DiscountLines.Clear();
                foreach (var discount in remainingDiscounts)
                {
                    salesLine.DiscountLines.Add(discount);
                }

                if (lineType == DiscountLineType.PeriodicDiscount)
                {
                    salesLine.PeriodicDiscountPossibilities.Clear();
                    salesLine.QuantityDiscounted = 0;
                }

                if (lineType == DiscountLineType.CustomerDiscount)
                {
                    salesLine.LineMultilineDiscOnItem = LineMultilineDiscountOnItem.None;
                }
            }
예제 #7
0
            /// <summary>
            /// Update the discount items.
            /// </summary>
            /// <param name="saleItem">The item line that the discount line is added to.</param>
            /// <param name="discountItem">The new discount line to add.</param>
            internal static void UpdateDiscountLines(SalesLine saleItem, DiscountLine discountItem)
            {
                // Check if line discount is found, if so then update
                bool discountLineFound = false;

                foreach (var discLine in saleItem.DiscountLines)
                {
                    if (discLine.DiscountLineType == DiscountLineType.CustomerDiscount)
                    {
                        // If found then update
                        if ((discLine.DiscountLineType == discountItem.DiscountLineType) &&
                            (discLine.CustomerDiscountType == discountItem.CustomerDiscountType))
                        {
                            discLine.Percentage = discountItem.Percentage;
                            discLine.Amount     = discountItem.Amount;
                            discountLineFound   = true;
                        }
                    }
                }

                // If line discount is not found then add it.
                if (!discountLineFound)
                {
                    saleItem.DiscountLines.Add(discountItem);
                }

                if (discountItem.DiscountLineType == DiscountLineType.CustomerDiscount)
                {
                    saleItem.ResetLineMultilineDiscountOnItem();
                }

                saleItem.WasChanged = true;
            }
예제 #8
0
            /// <summary>
            /// Splits a SalesLine into two sales lines in cases where a discount only applies to a portion of the quantity.
            /// </summary>
            /// <param name="salesLine">The line to split.</param>
            /// <param name="quantityNeeded">The quantity to split away from this line.</param>
            /// <returns>The new SalesLine containing the needed quantity.</returns>
            private SalesLine SplitLine(SalesLine salesLine, decimal quantityNeeded)
            {
                if (salesLine.Quantity < 0)
                {
                    quantityNeeded *= -1;
                }

                // Create the duplicate sale line.
                SalesLine newLine = salesLine.Clone <SalesLine>();

                newLine.Quantity           = quantityNeeded;
                newLine.QuantityDiscounted = 0m;
                newLine.LineId             = Guid.NewGuid().ToString("N");
                newLine.OriginLineId       = salesLine.OriginLineId;

                if (salesLine.LineManualDiscountAmount != decimal.Zero && salesLine.Quantity > 0)
                {
                    newLine.LineManualDiscountAmount    = this.priceContext.CurrencyAndRoundingHelper.Round((salesLine.LineManualDiscountAmount / salesLine.Quantity) * newLine.Quantity);
                    salesLine.LineManualDiscountAmount -= newLine.LineManualDiscountAmount;
                }

                // Set the new quantity on the orgininal sale line item.
                salesLine.Quantity -= quantityNeeded;

                return(newLine);
            }
예제 #9
0
            private static bool SetSalesInventory(RequestContext context, SalesLine salesLine, ItemAvailability itemAvailability, Dictionary <string, decimal> salesLineInventoryQuantities)
            {
                decimal salesLineInventoryQuantity = salesLineInventoryQuantities[salesLine.LineId];

                if (itemAvailability == null)
                {
                    var notification = new InventoryNotFoundNotification(salesLine.LineId);
                    context.Notify(notification);
                }
                else
                {
                    if (itemAvailability.AvailableQuantity < salesLineInventoryQuantity)
                    {
                        var notification = new InsufficientQuantityAvailableNotification(salesLine.LineId, salesLine.ItemId);
                        context.Notify(notification);
                    }

                    salesLine.InventoryLocationId = itemAvailability.InventoryLocationId;

                    itemAvailability.AvailableQuantity -= salesLineInventoryQuantity;

                    return(true);
                }

                return(false);
            }
        public mySale GetNewSale()
        {
            //Apenas com uma linha
            SalesLine line = new SalesLine()
            {
                LineNumber           = 1,
                ItemCode             = "ART1",
                ItemDescription      = "Artigo cust",
                Quantity             = 3,
                VATTax               = 23,
                UnitPriceExcludedVAT = 100,
                Discount1            = 1,
                Discount2            = 2,
                Discount3            = 3,
                DiscountValue        = 4,
            };

            mySale mySale = new mySale()
            {
                FiscalYear     = "2018",
                SectionCode    = "1",
                DocTypeAbbrev  = "FAT",
                EntityCode     = 1,
                Date           = DateTime.Now,
                ExpirationDate = DateTime.Now,
                CurrencyCode   = "EUR",
                Lines          = new List <SalesLine>()
                {
                    line
                },
                GetReportBytes = true,
            };

            return(mySale);
        }
예제 #11
0
            /// <summary>
            /// Gets maximum retail price from trade agreement.
            /// </summary>
            /// <param name="salesLine">The sales line.</param>
            /// <param name="context">The request context.</param>
            /// <returns>Maximum retail price from trade agreement.</returns>
            public decimal GetMaximumRetailPriceFromTradeAgreement(SalesLine salesLine, RequestContext context)
            {
                if (salesLine == null)
                {
                    throw new ArgumentNullException("salesLine");
                }

                decimal quantity = this.salesTransaction.ActiveSalesLines.Where(x => (x.ItemId == salesLine.ItemId) && (x.InventoryDimensionId == salesLine.InventoryDimensionId)).Sum(x => x.Quantity);

                if (quantity == decimal.Zero)
                {
                    quantity = 1;
                }

                if (!salesLine.BeginDateTime.IsValidAxDateTime())
                {
                    salesLine.BeginDateTime = context.GetNowInChannelTimeZone();
                }

                PriceResult priceResult = PricingEngine.GetActiveTradeAgreement(
                    this.pricingDataManager,
                    DiscountParameters.CreateAndInitialize(this.pricingDataManager),
                    this.channelConfiguration.Currency,
                    salesLine,
                    quantity,
                    this.customerId,
                    this.priceGroup,
                    salesLine.BeginDateTime);

                this.maxRetailPrice = priceResult.MaximumRetailPriceIndia;

                return(this.maxRetailPrice);
            }
예제 #12
0
            /// <summary>
            /// Creates a sales line from the xml element.
            /// </summary>
            /// <param name="xmlItemElement">The sales line line xml element.</param>
            /// <param name="order">The sales order.</param>
            /// <param name="lineId">The sales line identifier.</param>
            /// <param name="invoiceId">The invoice identifier.</param>
            /// <param name="invoiceBeginDate">The invoice begin date.</param>
            /// <param name="context">The request context.</param>
            private static void ParseAndCreateSalesLine(XElement xmlItemElement, SalesOrder order, int lineId, string invoiceId, DateTimeOffset invoiceBeginDate, RequestContext context)
            {
                SalesLine lineItem = new SalesLine();

                lineItem.LineId   = lineId.ToString();
                lineItem.RecordId = long.Parse(xmlItemElement.Attribute("RecId").Value);
                lineItem.ItemId   = xmlItemElement.Attribute("ItemId").Value;

                lineItem.Description          = xmlItemElement.Attribute("EcoResProductName").Value;
                lineItem.InventoryDimensionId = xmlItemElement.Attribute("InventDimId").Value;
                lineItem.ReturnInventTransId  = xmlItemElement.Attribute("InventTransId").Value;
                lineItem.ReturnTransactionId  = invoiceId;

                decimal quantityInvoiced = decimal.Parse(xmlItemElement.Attribute("Qty").Value);

                lineItem.Quantity            = decimal.Negate(quantityInvoiced);
                lineItem.UnitOfMeasureSymbol = xmlItemElement.Attribute("SalesUnit").Value;
                lineItem.ReturnQuantity      = lineItem.Quantity;
                lineItem.QuantityInvoiced    = quantityInvoiced;
                lineItem.Price = decimal.Parse(xmlItemElement.Attribute("SalesPrice").Value);
                lineItem.LineManualDiscountPercentage = decimal.Parse(xmlItemElement.Attribute("DiscPercent").Value);
                lineItem.DiscountAmount = decimal.Parse(xmlItemElement.Attribute("DiscAmount").Value);
                lineItem.NetAmount      = decimal.Parse(xmlItemElement.Attribute("LineAmount").Value);
                lineItem.TaxAmount      = decimal.Parse(xmlItemElement.Attribute("LineAmountTax").Value);

                lineItem.PeriodicDiscount           = Convert.ToDecimal(xmlItemElement.Attribute("PeriodicDiscount").Value);
                lineItem.PeriodicPercentageDiscount = Convert.ToDecimal(xmlItemElement.Attribute("PeriodicPercentageDiscount").Value);
                lineItem.LineDiscount                 = Convert.ToDecimal(xmlItemElement.Attribute("LineDscAmount").Value);
                lineItem.TotalDiscount                = Convert.ToDecimal(xmlItemElement.Attribute("TotalDiscount").Value);
                lineItem.TotalPercentageDiscount      = Convert.ToDecimal(xmlItemElement.Attribute("TotalPctDiscount").Value);
                lineItem.LineManualDiscountAmount     = Convert.ToDecimal(xmlItemElement.Attribute("LineManualDiscountAmount").Value);
                lineItem.LineManualDiscountPercentage = Convert.ToDecimal(xmlItemElement.Attribute("LineManualDiscountPercentage").Value);

                // lineItem.SalesMarkup = decimal.Parse(detail.Attribute("SalesMarkup").Value);
                lineItem.SalesTaxGroupId = xmlItemElement.Attribute("TaxGroup").Value;
                lineItem.ItemTaxGroupId  = xmlItemElement.Attribute("TaxItemGroup").Value;
                lineItem.BatchId         = xmlItemElement.Attribute("InventBatchId").Value;
                lineItem.SerialNumber    = xmlItemElement.Attribute("InventSerialId").Value;
                lineItem.BeginDateTime   = invoiceBeginDate;

                lineItem.DeliveryMode          = order.DeliveryMode;
                lineItem.RequestedDeliveryDate = order.RequestedDeliveryDate;
                lineItem.ShippingAddress       = order.ShippingAddress.Clone <Address>();
                lineItem.InventoryDimensionId  = xmlItemElement.Attribute("InventDimensionId").Value;

                Utilities.SetUpVariantAndProduct(context, lineItem.InventoryDimensionId, lineItem.ItemId, lineItem);

                foreach (XElement discountDetail in xmlItemElement.Elements("Discounts").Elements("Discount"))
                {
                    ParseAndCreateDiscountLine(discountDetail, lineItem);
                }

                // All taxable items need to use invoice date to have taxes calculated properly
                foreach (TaxableItem taxableItem in lineItem.ChargeLines)
                {
                    taxableItem.BeginDateTime = invoiceBeginDate;
                }

                order.SalesLines.Add(lineItem);
            }
예제 #13
0
            private static decimal AllocateLoyaltyDiscountLines(SalesLine salesLine, IEnumerable <DiscountLine> loyaltyDiscountLineList, RoundingRule roundingRule)
            {
                decimal loyaltyDiscountEffectiveAmount = decimal.Zero;
                decimal loyaltyDiscountPercentage      = decimal.Zero;
                decimal grossAmountDiscountable        = (salesLine.Price * salesLine.Quantity) - salesLine.PeriodicDiscount - salesLine.LineDiscount - salesLine.TotalDiscount;

                if (loyaltyDiscountLineList.Any() && grossAmountDiscountable != decimal.Zero)
                {
                    // Round 1: amount off fist
                    foreach (DiscountLine loyaltyLine in loyaltyDiscountLineList)
                    {
                        loyaltyDiscountEffectiveAmount += loyaltyLine.SetEffectiveAmountForAmountOff(grossAmountDiscountable - loyaltyDiscountEffectiveAmount, salesLine.Quantity, roundingRule);
                    }

                    // Round 2: percent off, plus percentage
                    decimal grossAmountBase = grossAmountDiscountable - loyaltyDiscountEffectiveAmount;
                    foreach (DiscountLine loyaltyLine in loyaltyDiscountLineList)
                    {
                        if (grossAmountDiscountable != loyaltyDiscountEffectiveAmount)
                        {
                            decimal maxDiscountAmount = grossAmountDiscountable - loyaltyDiscountEffectiveAmount;
                            loyaltyDiscountEffectiveAmount += loyaltyLine.AddEffectiveAmountForPercentOff(grossAmountBase, maxDiscountAmount, roundingRule);
                        }
                    }

                    loyaltyDiscountPercentage = (loyaltyDiscountEffectiveAmount / grossAmountDiscountable) * 100m;
                }

                salesLine.LoyaltyDiscountAmount     = loyaltyDiscountEffectiveAmount;
                salesLine.LoyaltyPercentageDiscount = Math.Round(loyaltyDiscountPercentage, 2);

                return(loyaltyDiscountEffectiveAmount);
            }
예제 #14
0
            private static decimal AllocateTotalDiscountLines(SalesLine salesLine, List <DiscountLine> totalDiscountItemList, RoundingRule roundingRule)
            {
                decimal totalDiscountEffectiveAmount = 0;
                decimal totalDiscountPercentage      = 0;
                decimal grossAmountDiscountable      = (salesLine.Price * salesLine.Quantity) - salesLine.PeriodicDiscount - salesLine.LineDiscount;

                if (totalDiscountItemList.Any() && grossAmountDiscountable != decimal.Zero)
                {
                    // Round 1: amount off fist.
                    foreach (DiscountLine totalLine in totalDiscountItemList)
                    {
                        totalDiscountEffectiveAmount += totalLine.SetEffectiveAmountForAmountOff(grossAmountDiscountable - totalDiscountEffectiveAmount, salesLine.Quantity, roundingRule);
                    }

                    // Round 2: percent off, plus percentage.
                    decimal grossAmountBase = grossAmountDiscountable - totalDiscountEffectiveAmount;
                    foreach (DiscountLine totalLine in totalDiscountItemList)
                    {
                        if (grossAmountDiscountable != totalDiscountEffectiveAmount)
                        {
                            decimal maxDiscountAmount = grossAmountDiscountable - totalDiscountEffectiveAmount;
                            totalDiscountEffectiveAmount += totalLine.AddEffectiveAmountForPercentOff(grossAmountBase, maxDiscountAmount, roundingRule);
                        }
                    }

                    totalDiscountPercentage = (totalDiscountEffectiveAmount / grossAmountDiscountable) * 100m;
                }

                salesLine.TotalDiscount           = totalDiscountEffectiveAmount;
                salesLine.TotalPercentageDiscount = Math.Round(totalDiscountPercentage, 2);

                return(totalDiscountEffectiveAmount);
            }
예제 #15
0
 /// <summary>
 /// Adds the specified SalesLine object to the collection of lines included in this group.
 /// </summary>
 /// <param name="line">The line item to add.</param>
 public void Add(SalesLine line)
 {
     if (line != null)
     {
         this.salesLines.Add(line);
         this.Quantity += line.Quantity;
     }
 }
 /// <summary>
 /// Clears the line level delivery values.
 /// </summary>
 /// <param name="salesLine">The sales line.</param>
 private static void ClearLineLevelDeliveryValues(SalesLine salesLine)
 {
     if (salesLine != null)
     {
         salesLine.DeliveryMode       = null;
         salesLine.ShippingAddress    = null;
         salesLine.FulfillmentStoreId = null;
     }
 }
예제 #17
0
            /// <summary>
            /// Populates the fields for total amount, total discount, and total taxes on the sales line.
            /// </summary>
            /// <param name="salesTransaction">The parent transaction for the sales line.</param>
            /// <param name="salesLine">The sales line to total.</param>
            /// <param name="salesRoundingRule">Delegate which can do sales rounding.</param>
            public static void CalculateLine(SalesTransaction salesTransaction, SalesLine salesLine, RoundingRule salesRoundingRule)
            {
                if (salesTransaction == null)
                {
                    throw new ArgumentNullException("salesTransaction");
                }

                CalculateLine(salesTransaction.BeginDateTime, salesTransaction.LineDiscountCalculationType, salesLine, salesRoundingRule, true);
            }
예제 #18
0
            /// <summary>
            /// Calculates the specific reason code on an entity like sales line, tender line etc.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <param name="sourceId">The source identifier.</param>
            /// <param name="tableRefType">The table identifier for the specific reason code.</param>
            /// <param name="refRelation">The first reference relation key corresponding to the entity and table.</param>
            /// <param name="refRelation2">The second reference relation key corresponding to the entity and table.</param>
            /// <param name="refRelation3">The third reference relation key corresponding to the entity and table.</param>
            /// <param name="requiredReasonCodes">The collection to which required reason codes are added.</param>
            /// <param name="reasonCodeRequirements">The required specific reason codes map.</param>
            /// <param name="presentReasonCodeLines">The collection with the already present reason code lines.</param>
            /// <param name="salesLine">The sales line when applicable.</param>
            private static void CalculateReasonCodesSpecificToEntity(
                CalculateRequiredReasonCodesServiceRequest request,
                string sourceId,
                ReasonCodeTableRefType tableRefType,
                string refRelation,
                string refRelation2,
                string refRelation3,
                IDictionary <string, ReasonCode> requiredReasonCodes,
                HashSet <ReasonCodeRequirement> reasonCodeRequirements,
                IEnumerable <ReasonCodeLine> presentReasonCodeLines,
                SalesLine salesLine)
            {
                GetReasonCodesByTableRefTypeDataRequest getReasonCodesByTableRefTypeDataRequest = new GetReasonCodesByTableRefTypeDataRequest(tableRefType, refRelation, refRelation2, refRelation3, QueryResultSettings.AllRecords);
                IEnumerable <ReasonCode> reasonCodes = request.RequestContext.Runtime.Execute <EntityDataServiceResponse <ReasonCode> >(getReasonCodesByTableRefTypeDataRequest, request.RequestContext).PagedEntityCollection.Results;

                reasonCodes = AddLinkedReasonCodes(reasonCodes, request.RequestContext);

                var triggeredReasonCodes = CalculateTriggeredReasonCodes(reasonCodes, presentReasonCodeLines, request.RequestContext);

                reasonCodes = reasonCodes.Union(triggeredReasonCodes).Distinct();

                reasonCodes = AddLinkedReasonCodes(reasonCodes, request.RequestContext);

                foreach (var reasonCode in reasonCodes)
                {
                    if (presentReasonCodeLines.Any(rc => string.Equals(rc.ReasonCodeId, reasonCode.ReasonCodeId, StringComparison.OrdinalIgnoreCase)))
                    {
                        continue;
                    }

                    bool entitySpecificApplicability = true;

                    switch (tableRefType)
                    {
                    case ReasonCodeTableRefType.Item:
                        entitySpecificApplicability = HasSalesLineSpecificApplicability(reasonCode, salesLine);
                        break;
                    }

                    if (entitySpecificApplicability &&
                        ShouldReasonCodeBeApplied(reasonCode, request.SalesTransaction))
                    {
                        var reasonCodeRequirement = new ReasonCodeRequirement()
                        {
                            ReasonCodeId      = reasonCode.ReasonCodeId,
                            SourceId          = sourceId,
                            TableRefTypeValue = (int)tableRefType,
                        };

                        reasonCodeRequirements.Add(reasonCodeRequirement);
                        requiredReasonCodes[reasonCode.ReasonCodeId] = reasonCode;
                    }
                }
            }
예제 #19
0
 /// <summary>
 /// Set the price or original price depending on if price is overridden or keyed-in.
 /// </summary>
 /// <param name="line">Sales line to set price on.</param>
 /// <param name="priceToSet">Price to set.</param>
 private static void SetPriceOnSalesLine(SalesLine line, decimal priceToSet)
 {
     if (line.IsPriceOverridden)
     {
         line.OriginalPrice = priceToSet;
     }
     else
     {
         line.Price = priceToSet;
     }
 }
예제 #20
0
            private static bool IsMatchUnitOfMeasure(SalesLine line, PriceAdjustment adjustment)
            {
                bool isMatch = string.IsNullOrWhiteSpace(adjustment.UnitOfMeasure);

                if (!isMatch)
                {
                    isMatch = string.Equals(adjustment.UnitOfMeasure, line.SalesOrderUnitOfMeasure, StringComparison.OrdinalIgnoreCase);
                }

                return(isMatch);
            }
예제 #21
0
            internal static string GetUnitOfMeasure(SalesLine salesLine)
            {
                string unitOfMeasure = salesLine.SalesOrderUnitOfMeasure;

                if (string.IsNullOrWhiteSpace(unitOfMeasure))
                {
                    unitOfMeasure = salesLine.UnitOfMeasureSymbol ?? string.Empty;
                }

                return(unitOfMeasure);
            }
예제 #22
0
            /// <summary>
            /// Calculate the manual line discount.
            /// </summary>
            /// <param name="transaction">The transaction receiving total discount lines.</param>
            /// <param name="saleItem">The sale item that contains the discount lines.</param>
            /// <param name="lineDiscountItem">The line discount amount to discount the transaction.</param>
            private void AddLineDiscount(SalesTransaction transaction, SalesLine saleItem, DiscountLine lineDiscountItem)
            {
                Item item = PriceContextHelper.GetItem(this.priceContext, saleItem.ItemId);
                bool isDiscountAllowed = item != null ? !item.NoDiscountAllowed : true;

                if (isDiscountAllowed)
                {
                    saleItem.DiscountLines.Add(lineDiscountItem);
                    SalesLineTotaller.CalculateLine(transaction, saleItem, d => this.priceContext.CurrencyAndRoundingHelper.Round(d));
                }
            }
예제 #23
0
            /// <summary>
            /// Attempts to apply a DiscountLine to a SalesLine that has a larger quantity than the required quantity, splitting the line if it is found.
            /// </summary>
            /// <param name="transaction">The current transaction containing the lines.</param>
            /// <param name="availableLines">The available line indices on the transaction.</param>
            /// <param name="quantityNeeded">The quantity needed for the DiscountLine.</param>
            /// <param name="discount">The DiscountLine and original quantity needed.</param>
            /// <param name="isReturn">True if it's return.</param>
            /// <param name="requiresExistingCompoundedDiscounts">A flag indicating whether it requires existing compounded discounts on the line to compound on top of.</param>
            /// <returns>True if a match was found and the discount was applied, false otherwise.</returns>
            private bool ApplyDiscountLineForLargerMatch(
                SalesTransaction transaction,
                HashSet <int> availableLines,
                ref decimal quantityNeeded,
                DiscountLineQuantity discount,
                bool isReturn,
                bool requiresExistingCompoundedDiscounts)
            {
                bool discountApplied = false;

                foreach (int x in availableLines.ToList().OrderBy(p => this.salesLines[p].Quantity))
                {
                    SalesLine salesLine = this.salesLines[x];

                    if (!IsQuantityMatchSalesOrReturn(isReturn, salesLine.Quantity))
                    {
                        continue;
                    }

                    decimal lineQuantity = Math.Abs(salesLine.Quantity);
                    if (lineQuantity > quantityNeeded)
                    {
                        if (discount.DiscountLine.ConcurrencyMode != ConcurrencyMode.Compounded ||
                            CanApplyCompoundedDiscount(this.salesLines[x].DiscountLines, discount.DiscountLine, requiresExistingCompoundedDiscounts))
                        {
                            // Perform the split of this line
                            SalesLine newLine = this.SplitLine(this.salesLines[x], quantityNeeded);

                            // Add the new line to the transaction and to the available lines for discounts.  Set the line number to the next available number.
                            newLine.LineNumber = transaction.SalesLines.Max(p => p.LineNumber) + 1;
                            transaction.SalesLines.Add(newLine);
                            this.salesLines.Add(newLine);

                            DiscountLine discountLine = discount.DiscountLine.Clone <DiscountLine>();
                            discountLine.SaleLineNumber = newLine.LineNumber;
                            newLine.DiscountLines.Add(discountLine);
                            newLine.QuantityDiscounted = quantityNeeded * Math.Sign(salesLine.Quantity);

                            // If this is a compounding discount, add the new line to the available lines.
                            if (discount.DiscountLine.ConcurrencyMode == ConcurrencyMode.Compounded)
                            {
                                availableLines.Add(this.salesLines.Count - 1);
                            }

                            discountApplied = true;
                            quantityNeeded  = 0;
                            break;
                        }
                    }
                }

                return(discountApplied);
            }
예제 #24
0
            /// <summary>
            /// Executes the workflow for a get price check for a product.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <returns>The response.</returns>
            protected override PriceCheckResponse Process(PriceCheckRequest request)
            {
                ThrowIf.Null(request, "request");

                ItemBarcode itemBarcode = null;

                if (string.IsNullOrEmpty(request.Barcode) && string.IsNullOrEmpty(request.ItemId))
                {
                    throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ItemIdBarcodeMissing, "Either an item identifier or barcode is required.");
                }

                if (string.IsNullOrEmpty(request.ItemId))
                {
                    GetProductBarcodeDataRequest dataRequest = new GetProductBarcodeDataRequest(request.Barcode);
                    itemBarcode = this.Context.Runtime.Execute <GetProductBarcodeDataResponse>(dataRequest, this.Context).Barcode;
                }

                SalesTransaction salesTransaction = new SalesTransaction()
                {
                    Id         = Guid.NewGuid().ToString(),
                    CustomerId = request.CustomerAccountNumber,
                };

                SalesLine salesLine = new SalesLine()
                {
                    ItemId = request.ItemId,
                    InventoryDimensionId    = request.InventoryDimensionId ?? itemBarcode.InventoryDimensionId,
                    SalesOrderUnitOfMeasure = request.UnitOfMeasureSymbol ?? itemBarcode.UnitId,
                    Quantity = 1m,
                    LineId   = Guid.NewGuid().ToString()
                };

                salesTransaction.SalesLines.Add(salesLine);

                GetIndependentPriceDiscountServiceRequest priceRequest = new GetIndependentPriceDiscountServiceRequest(salesTransaction);

                GetPriceServiceResponse pricingServiceResponse = this.Context.Execute <GetPriceServiceResponse>(priceRequest);

                SalesLine resultLine = pricingServiceResponse.Transaction.SalesLines[0];

                ProductPrice productPrice = GetProductPrice(
                    resultLine.ItemId,
                    resultLine.InventoryDimensionId,
                    resultLine.BasePrice,
                    resultLine.TotalAmount,
                    this.Context.GetChannelConfiguration().Currency);

                var productPrices = new List <ProductPrice> {
                    productPrice
                };

                return(new PriceCheckResponse(productPrices.AsPagedResult()));
            }
예제 #25
0
            private static SalesLine ConstructSalesLine(string itemId, string lineId = "1")
            {
                SalesLine salesLine = new SalesLine();

                salesLine.OriginalSalesOrderUnitOfMeasure = "ea";
                salesLine.SalesOrderUnitOfMeasure         = "ea";
                salesLine.ItemId   = itemId;
                salesLine.Quantity = 1;
                salesLine.UnitOfMeasureConversion = UnitOfMeasureConversion.CreateDefaultUnitOfMeasureConversion();
                salesLine.LineId = lineId;
                return(salesLine);
            }
예제 #26
0
            public void OnExecuting(Request request)
            {
                CreateSalesOrderServiceRequest createSalesOrderServiceRequest = request as CreateSalesOrderServiceRequest;
                SalesTransaction salesTransaction    = createSalesOrderServiceRequest.Transaction;
                string           returnTransactionId = "";
                string           returnStore         = "";
                string           returnTerminalId    = "";
                SalesOrder       returnSalesOrder    = null;

                Dictionary <string, Dictionary <string, decimal> > payments       = new Dictionary <string, Dictionary <string, decimal> >();
                Dictionary <string, Dictionary <string, decimal> > returnPayments = new Dictionary <string, Dictionary <string, decimal> >();

                if (salesTransaction != null && salesTransaction.IsReturnByReceipt)
                {
                    SalesLine salesLine = salesTransaction.SalesLines.First(line => !string.IsNullOrEmpty(line.ReturnTransactionId));
                    // Find the orginal transaction id.
                    returnTransactionId = salesLine.ReturnTransactionId;
                    returnStore         = salesLine.ReturnStore;
                    returnTerminalId    = salesLine.ReturnTerminalId;

                    if (!String.IsNullOrEmpty(returnTransactionId))
                    {
                        var getSalesOrderDetailsByTransactionIdServiceRequest = new GetSalesOrderDetailsByTransactionIdServiceRequest(returnTransactionId, SearchLocation.All);
                        var getSalesOrderDetailsServiceResponse = request.RequestContext.Execute <GetSalesOrderDetailsServiceResponse>(getSalesOrderDetailsByTransactionIdServiceRequest);
                        returnSalesOrder = getSalesOrderDetailsServiceResponse.SalesOrder;
                    }

                    if (!string.IsNullOrEmpty(returnStore) && !string.IsNullOrEmpty(salesTransaction.StoreId) && salesTransaction.StoreId != returnStore)
                    {
                        throw new CommerceException("Microsoft_Dynamics_Commerce_ReturnInDifferentStore", ExceptionSeverity.Warning, null, "Custom error")
                              {
                                  LocalizedMessage           = "Return in different store from original is not allowed.",
                                  LocalizedMessageParameters = new object[] { }
                              };
                    }

                    if (returnSalesOrder != null && salesTransaction != null)
                    {
                        buildPayments(salesTransaction, ref payments);
                        buildPayments(returnSalesOrder, ref returnPayments);
                    }

                    if (!comparePaymentsWithReturnPayments(payments, returnPayments))
                    {
                        throw new CommerceException("Microsoft_Dynamics_Commerce_ReturnPaymentInconsistent", ExceptionSeverity.Warning, null, "Custom error")
                              {
                                  LocalizedMessage           = "The payment methods or amounts of return are inconsistent with original.",
                                  LocalizedMessageParameters = new object[] { }
                              };
                    }
                }
            }
 private static ProductPrice ActivePriceFromSalesLine(long productId, SalesLine salesLine)
 {
     return(new ProductPrice
     {
         UnitOfMeasure = salesLine.SalesOrderUnitOfMeasure,
         ItemId = salesLine.ItemId,
         InventoryDimensionId = salesLine.InventoryDimensionId,
         BasePrice = salesLine.BasePrice,
         TradeAgreementPrice = salesLine.AgreementPrice,
         AdjustedPrice = salesLine.AdjustedPrice,
         ProductId = productId
     });
 }
예제 #28
0
            /// <summary>
            /// Gets the return reason code for the transaction or empty, if none.
            /// </summary>
            /// <param name="salesTransaction">The sales transaction to get the reason code from.</param>
            /// <param name="context">The request context.</param>
            /// <returns>The reason code for the transaction or empty if none.</returns>
            private static string GetReturnReasonCodeId(SalesTransaction salesTransaction, RequestContext context)
            {
                // transaction server expects only one reason code, so we just take it from the first line that has reason codes
                SalesLine firstLineWithReasonCode = salesTransaction.SalesLines.FirstOrDefault(sl => sl.ReasonCodeLines.Any());

                GetReturnOrderReasonCodesDataRequest getReturnOrderReasonCodesDataRequest = new GetReturnOrderReasonCodesDataRequest(QueryResultSettings.AllRecords);
                ReadOnlyCollection <ReasonCode>      returnOrderReasonCodes = context.Runtime.Execute <EntityDataServiceResponse <ReasonCode> >(getReturnOrderReasonCodesDataRequest, context).PagedEntityCollection.Results;

                ReasonCodeLine returnReasonCodeLine = firstLineWithReasonCode != null
                    ? firstLineWithReasonCode.ReasonCodeLines.FirstOrDefault(reasonCodeLine => returnOrderReasonCodes.Any(returnOrderReasonCode => returnOrderReasonCode.ReasonCodeId == reasonCodeLine.ReasonCodeId))
                    : null;

                return((returnReasonCodeLine != null && !string.IsNullOrWhiteSpace(returnReasonCodeLine.ReasonCodeId)) ? returnReasonCodeLine.ReasonCodeId : null);
            }
예제 #29
0
            private static bool IsAdjustmentActiveOnSalesLine(
                SalesLine line,
                PriceAdjustment adjustment,
                DateTimeOffset defaultDate)
            {
                var activeDate = line.SalesDate ?? defaultDate;

                return(InternalValidationPeriod.ValidateDateAgainstValidationPeriod(
                           (DateValidationType)adjustment.DateValidationType,
                           adjustment.ValidationPeriod,
                           adjustment.ValidFromDate,
                           adjustment.ValidToDate,
                           activeDate));
            }
예제 #30
0
            private static void ResolveAndApplyPriceForSalesLine(SalesLine item, IEnumerable <PriceLine> itemPriceLines, ICurrencyOperations currencyAndRoundingHelper)
            {
                var agreementLine = itemPriceLines.OfType <TradeAgreementPriceLine>().FirstOrDefault();
                var baseLine      = itemPriceLines.OfType <BasePriceLine>().FirstOrDefault();

                bool hasTradeAgreementPrice = agreementLine != null;
                bool hasBasePrice           = baseLine != null;

                item.AgreementPrice = hasTradeAgreementPrice ? agreementLine.Value : 0m;
                item.BasePrice      = hasBasePrice ? baseLine.Value : 0m;

                // use the trade agreement price if any, otherwise use the base price
                if (hasTradeAgreementPrice)
                {
                    SetPriceOnSalesLine(item, item.AgreementPrice);
                    item.TradeAgreementPriceGroup = agreementLine.CustPriceGroup;
                }
                else if (hasBasePrice)
                {
                    SetPriceOnSalesLine(item, item.BasePrice);
                    item.AgreementPrice = item.BasePrice;
                }
                else
                {
                    SetPriceOnSalesLine(item, 0);
                }

                // now try to apply any price adjustments
                var adjustmentLines = itemPriceLines.OfType <PriceAdjustmentPriceLine>();

                item.AdjustedPrice = PriceAdjustmentCalculator.CalculatePromotionPrice(adjustmentLines, item.Price);

                if (Math.Abs(item.AdjustedPrice) < Math.Abs(item.Price))
                {
                    SetPriceOnSalesLine(item, item.AdjustedPrice);
                    item.TradeAgreementPriceGroup = null;
                }

                // round prices
                item.Price = currencyAndRoundingHelper.Round(item.Price);
                if (item.OriginalPrice.HasValue)
                {
                    item.OriginalPrice = currencyAndRoundingHelper.Round(item.OriginalPrice.Value);
                }

                item.BasePrice      = currencyAndRoundingHelper.Round(item.BasePrice);
                item.AgreementPrice = currencyAndRoundingHelper.Round(item.AgreementPrice);
                item.AdjustedPrice  = currencyAndRoundingHelper.Round(item.AdjustedPrice);
            }