@using System @using System.Collections.Generic @using System.Globalization @using System.Linq @using Resto.Front.PrintTemplates.Cheques.Razor @using Resto.Front.PrintTemplates.Cheques.Razor.TemplateModels @inherits TemplateBase @{ var order = Model.Order; } @* Header (begin) *@ @if (Model.AdditionalServiceChequeInfo == null) { if (Model.RepeatBillNumber == 0) {
@Resources.BillHeaderTitle
} else {
@string.Format(Resources.RepeateBillHeaderTitleFormat, Model.RepeatBillNumber)
} } @if (Model.AdditionalServiceChequeInfo != null) { @string.Format(Resources.AdditionalServiceHeaderOrderItemsAddedPattern, FormatLongDateTime(Model.CommonInfo.CurrentTime)) } @if (!string.IsNullOrWhiteSpace(order.ExternalNumber)) { @string.Format(Resources.BillHeaderOrderExternalNumberPattern, order.ExternalNumber) } @string.Format(Resources.BillHeaderWaiterPattern, order.Waiter.GetNameOrEmpty()) @foreach (var clientInfo in from discountItem in order.DiscountItems where discountItem.CardInfo != null select discountItem.CardInfo into cardInfo select string.IsNullOrWhiteSpace(cardInfo.MaskedCard) ? cardInfo.Owner : string.Format("{0} ({1})", cardInfo.Owner, cardInfo.MaskedCard) into clientInfo where !string.IsNullOrWhiteSpace(clientInfo) select clientInfo) { @string.Format(Resources.ClientFormat, clientInfo) } @if (Model.AdditionalServiceChequeInfo == null) { @Raw(string.Join(Environment.NewLine, Model.Extensions.AfterHeader)) } @if (Model.AdditionalServiceChequeInfo != null) { if (order.ClientBinding != null && !string.IsNullOrWhiteSpace(order.ClientBinding.CardNumber)) { @string.Format(Resources.CardPattern, order.ClientBinding.CardNumber) }
@Resources.AdditionalServiceHeaderTitle
} @* Header (end) *@ @* Body (begin) *@ @Guests() @Summaries()
@* Body (end) *@ @* Footer (begin) *@ @if (Model.AdditionalServiceChequeInfo == null) { @Raw(string.Join(Environment.NewLine, Model.Extensions.BeforeFooter)) }
@Model.CommonInfo.CafeSetup.BillFooter
@if (Model.AdditionalServiceChequeInfo == null) { @Raw(string.Join(Environment.NewLine, Model.Extensions.AfterFooter)) } @* Footer (end) *@
@helper Guests() { var order = Model.Order; Func orderItemsFilter; if (Model.AdditionalServiceChequeInfo != null) { orderItemsFilter = orderItem => Model.AdditionalServiceChequeInfo.AddedOrderItems.Contains(orderItem); } else { orderItemsFilter = orderItem => orderItem.DeletionInfo == null; } var guestsWithItems = Model.Order.Table.Section.DisplayGuests ? order.Guests.Select(guest => new { Guest = guest, Items = guest.Items.Where(item => orderItemsFilter(item) && OrderItemsToPrintFilter(item, order.DiscountItems) && OrderItemsPrechequePrintableFilter(item)) }) .Where(guestWithItems => guestWithItems.Items.Any()).ToList() : EnumerableEx.Return(new { Guest = order.Guests.FirstOrDefault(), Items = order.Guests.SelectMany(g => g.Items.Where(item => orderItemsFilter(item) && OrderItemsToPrintFilter(item, order.DiscountItems) && OrderItemsPrechequePrintableFilter(item))) }) .Where(guestWithItems => guestWithItems.Items.Any()).ToList(); if (!guestsWithItems.Any()) { return; } @Resources.NameColumnHeader @Resources.ProductAmount @Resources.ResultSum if (guestsWithItems.Count == 1) { @SingleGuest(guestsWithItems.Single().Items.ToList()) } else { @OneOfMultipleGuests(guestsWithItems.First().Guest, guestsWithItems.First().Items.ToList()) foreach (var guestWithItems in guestsWithItems.Skip(1)) { @OneOfMultipleGuests(guestWithItems.Guest, guestWithItems.Items.ToList()) } } } @helper SingleGuest(IEnumerable items) { foreach (var comboGroup in items.OrderBy(i => i.OrderRank).GroupBy(i => i.Combo)) { var combo = comboGroup.Key; var isPartOfCombo = combo != null; var additionalSpace = isPartOfCombo ? " " : string.Empty; if (isPartOfCombo) { @combo.Name @FormatAmount(combo.Amount) @FormatMoney(combo.Price * combo.Amount) } foreach (var orderItemGroup in comboGroup.OrderBy(item => item.OrderRank).GroupBy(_ => _, CreateComparer(AreOrderItemsEqual))) { var totalAmount = orderItemGroup.Sum(orderItem => orderItem.Amount); var totalCost = orderItemGroup.Sum(orderItem => orderItem.Cost); var productItem = orderItemGroup.Key as IProductItem; if (productItem != null && productItem.CompoundsInfo != null && productItem.CompoundsInfo.IsPrimaryComponent) { @(additionalSpace + string.Format("{0} {1}", productItem.CompoundsInfo.ModifierSchemaName, productItem.ProductSize == null ? string.Empty : productItem.ProductSize.Name)) // для разделенной пиццы комменты печатаем под схемой if (Model.Order.Table.Section.PrintProductItemCommentInCheque && productItem.Comment != null && !productItem.Comment.Deleted) { @(additionalSpace + productItem.Comment.Text)
} // у пиццы не может быть удаленных модификаторов, поэтому берем весь список foreach (var orderEntry in productItem.ModifierEntries.Where(orderEntry => ModifiersFilter(orderEntry, productItem, true))) { var productName = orderEntry.ProductCustomName ?? orderEntry.Product.Name; @(additionalSpace + " " + productName) if (orderEntry.Amount != 1m) { @FormatAmount(orderEntry.Amount) } else { } if (orderEntry.Cost != 0m) { @FormatMoney(orderEntry.Cost) } else { } @PrintOrderEntryAllergens(orderEntry) @CategorizedDiscountsForOrderEntryGroup(new[] { orderEntry }, isPartOfCombo) } } if (productItem != null && productItem.CompoundsInfo != null) { @(additionalSpace + " 1/2 " + GetOrderEntryNameWithProductSize(productItem)) } else { @(additionalSpace + GetOrderEntryNameWithProductSize(orderItemGroup.Key)) } @FormatAmount(totalAmount) if (!isPartOfCombo) { @FormatMoney(totalCost) } else { } if (productItem != null) { @PrintOrderEntryAllergens(productItem) } @CategorizedDiscountsForOrderEntryGroup(orderItemGroup.ToList(), isPartOfCombo) // здесь комменты для обычных блюд и целых пицц if (Model.Order.Table.Section.PrintProductItemCommentInCheque && productItem != null && productItem.Comment != null && !productItem.Comment.Deleted && productItem.CompoundsInfo == null) { @(additionalSpace + productItem.Comment.Text)
} foreach (var orderEntry in orderItemGroup.Key.GetNotDeletedChildren().Where(orderEntry => ModifiersFilter(orderEntry, orderItemGroup.Key)).ToList()) { var productName = orderEntry.ProductCustomName ?? orderEntry.Product.Name; @(additionalSpace + " " + productName) if (orderEntry.Amount != 1m) { @FormatAmount(orderEntry.Amount) } else { } if (orderEntry.Cost != 0m) { @FormatMoney(orderEntry.Cost) } else { } @PrintOrderEntryAllergens(orderEntry) @CategorizedDiscountsForOrderEntryGroup(new[] { orderEntry }, isPartOfCombo) } } } } @helper CategorizedDiscountsForOrderEntryGroup(ICollection entries, bool isPartOfCombo) { var orderEntry = entries.First(); var additionalSpace = isPartOfCombo ? " " : string.Empty; if (orderEntry.Cost != 0m) { var categorizedDiscounts = from discountItem in Model.Order.DiscountItems where discountItem.IsCategorized && discountItem.PrintDetailedInPrecheque let discountSum = entries.Sum(entry => discountItem.GetDiscountSumFor(entry)) where discountSum != 0m select new { IsDiscount = discountSum > 0m, Sum = Math.Abs(discountSum), Percent = Math.Abs(CalculatePercent(entries.Sum(entry => entry.Cost), discountSum)), Name = discountItem.Type.PrintableName, discountItem.Type.DiscountBySum } into discount orderby discount.IsDiscount descending select discount; foreach (var categorizedDiscount in categorizedDiscounts) { @(additionalSpace + GetFormattedDiscountDescriptionForOrderItem(categorizedDiscount.IsDiscount, categorizedDiscount.Name, categorizedDiscount.DiscountBySum, categorizedDiscount.Percent)) @GetFormattedDiscountSum(categorizedDiscount.IsDiscount, categorizedDiscount.Sum) } } } @helper OneOfMultipleGuests(IGuest guest, ICollection items) { @guest.Name @SingleGuest(items) var includedEntries = items.SelectMany(item => item.ExpandIncludedEntries()).ToList(); var total = includedEntries.Sum(orderEntry => orderEntry.Cost); var totalWithoutCategorizedDiscounts = total - (from orderEntry in includedEntries from discountItem in Model.Order.DiscountItems where discountItem.IsCategorized select discountItem.GetDiscountSumFor(orderEntry)).Sum(); var totalWithoutDiscounts = totalWithoutCategorizedDiscounts - (from orderEntry in includedEntries from discountItem in Model.Order.DiscountItems where !discountItem.IsCategorized select discountItem.GetDiscountSumFor(orderEntry)).Sum(); if (totalWithoutCategorizedDiscounts != totalWithoutDiscounts) { @Resources.BillFooterTotalPlain @FormatMoney(totalWithoutCategorizedDiscounts) var nonCategorizedDiscounts = from discountItem in Model.Order.DiscountItems where !discountItem.IsCategorized let discountSum = includedEntries.Sum(orderEntry => discountItem.GetDiscountSumFor(orderEntry)) select new { IsDiscount = discountSum > 0m, Sum = Math.Abs(discountSum), Percent = Math.Abs(CalculatePercent(includedEntries.Sum(entry => entry.Cost), discountSum)), Name = discountItem.Type.PrintableName, discountItem.Type.DiscountBySum } into discount orderby discount.IsDiscount descending select discount; foreach (var nonCategorizedDiscount in nonCategorizedDiscounts) { @(nonCategorizedDiscount.DiscountBySum ? GetFormattedDiscountDescriptionShort(nonCategorizedDiscount.IsDiscount, nonCategorizedDiscount.Name) : GetFormattedDiscountDescriptionDetailed(nonCategorizedDiscount.IsDiscount, nonCategorizedDiscount.Name, nonCategorizedDiscount.Percent)) @GetFormattedDiscountSum(nonCategorizedDiscount.IsDiscount, nonCategorizedDiscount.Sum) } } @string.Format(Model.AdditionalServiceChequeInfo == null ? Resources.BillFooterTotalGuestPattern : Resources.AdditionalServiceFooterTotalGuestPattern, guest.Name) @FormatMoney(totalWithoutDiscounts) } @helper Summaries() { var order = Model.Order; var fullSum = order.GetFullSum() - order.DiscountItems.Where(di => !di.Type.PrintProductItemInPrecheque).Sum(di => di.GetDiscountSum()); var categorizedDiscountItems = new List(); var nonCategorizedDiscountItems = new List(); foreach (var discountItem in order.DiscountItems.Where(di => di.Type.PrintProductItemInPrecheque && di.DiscountSums.Count > 0)) { if (discountItem.IsCategorized) { categorizedDiscountItems.Add(discountItem); } else { nonCategorizedDiscountItems.Add(discountItem); } } var subTotal = fullSum - categorizedDiscountItems.Sum(di => di.GetDiscountSum()); var totalWithoutDiscounts = subTotal - nonCategorizedDiscountItems.Sum(di => di.GetDiscountSum()); var prepay = order.PrePayments.Sum(prepayItem => prepayItem.Sum); var total = Math.Max(totalWithoutDiscounts + order.GetVatSumExcludedFromPrice() - prepay, 0m); if (Model.DiscountMarketingCampaigns != null) { total -= Model.DiscountMarketingCampaigns.TotalDiscount; totalWithoutDiscounts -= Model.DiscountMarketingCampaigns.TotalDiscount; } var vatSumsByVat = (Model.AdditionalServiceChequeInfo == null ? order.GetIncludedEntries() : Model.AdditionalServiceChequeInfo.AddedOrderItems.SelectMany(item => item.ExpandIncludedEntries())) .Where(orderEntry => !orderEntry.VatIncludedInPrice) .GroupBy(orderEntry => orderEntry.Vat) .Where(group => group.Key != 0m) .Select(group => new { Vat = group.Key, Sum = group.Sum(orderEntry => orderEntry.ExcludedVat) }) .ToList(); var vatSum = vatSumsByVat.Sum(vatWithSum => vatWithSum.Sum); if ((prepay != 0m || fullSum != total) && Model.AdditionalServiceChequeInfo == null) { @Resources.BillFooterFullSum @FormatMoney(fullSum) } @PrintOrderDiscounts(categorizedDiscountItems, fullSum) if (categorizedDiscountItems.Any()) { @Resources.BillFooterTotalPlain @FormatMoney(subTotal) } @PrintOrderDiscounts(nonCategorizedDiscountItems, fullSum) if (Model.DiscountMarketingCampaigns != null) { foreach (var discountMarketingCampaign in Model.DiscountMarketingCampaigns.Campaigns) { @discountMarketingCampaign.Name @("-" + FormatMoney(discountMarketingCampaign.TotalDiscount)) } } if (prepay != 0m && (categorizedDiscountItems.Any() || nonCategorizedDiscountItems.Any())) { @Resources.BillFooterTotalWithoutDiscounts @FormatMoney(totalWithoutDiscounts) } if (vatSum != 0m) { foreach (var vatWithSum in vatSumsByVat) { @string.Format(Resources.VatFormat, vatWithSum.Vat) @string.Format(FormatMoney(vatWithSum.Sum)) } if (vatSumsByVat.Count > 1) { @Resources.VatSum @FormatMoney(vatSum) } } if (Model.AdditionalServiceChequeInfo != null) { @Resources.AdditionalServiceAddedFooterTotalUpper @FormatMoney(Model.AdditionalServiceChequeInfo.AddedOrderItems.SelectMany(item => item.ExpandIncludedEntries()).Sum(orderEntry => orderEntry.Cost) + vatSum) } if (prepay != 0m) { @Resources.Prepay @FormatMoney(prepay) } @(Model.AdditionalServiceChequeInfo == null ? Resources.BillFooterTotal : Resources.AdditionalServiceFooterTotalUpper) @FormatMoney(total) foreach (var rate in order.FixedCurrencyRates) { var currencyIsoName = rate.Key.IsoName; var defaultCurrencyIsoName = Model.CommonInfo.CafeSetup.CurrencyIsoName; @string.Format(Resources.CurrencyRateFormat, currencyIsoName, rate.Value.ToString("f4", CultureInfo.CurrentCulture), defaultCurrencyIsoName) @string.Format(Resources.CurrencyFormat, currencyIsoName, FormatMoney(GetSumInAdditionalCurrency(rate.Key, rate.Value, total), currencyIsoName)) } if (Model.AdditionalServiceChequeInfo != null && order.ClientBinding != null && order.ClientBinding.PaymentLimit.HasValue) { @Resources.AdditionalServiceLimit @FormatMoney(order.ClientBinding.PaymentLimit.Value - total) } if (Model.DiscountMarketingCampaigns != null) { foreach (var discountMarketingCampaign in Model.DiscountMarketingCampaigns.Campaigns.Where(campaign => !string.IsNullOrWhiteSpace(campaign.BillComment))) { @discountMarketingCampaign.BillComment } } } @helper PrintOrderDiscounts(IEnumerable discountItems, decimal fullSum) { foreach (var discountItem in discountItems.OrderByDescending(discountItem => discountItem.IsDiscount())) { @GetFormattedDiscountDescriptionShort(discountItem.IsDiscount(), discountItem.Type.PrintableName) @GetFormattedDiscountSum(discountItem.IsDiscount(), Math.Abs(discountItem.GetDiscountSum())) } } @helper PrintOrderEntryAllergens(IOrderEntry orderEntry) { var totalAllergens = orderEntry.Allergens.ToArray(); if (totalAllergens.Any()) { @( string.Format(Resources.AllergenGroupsFormat, string.Join(", ", totalAllergens)) ) } } @functions { /// /// Отфильтровывает флаерные блюда, для которых нет настройки "Печать в пречеке блюд по флаеру" /// private static bool OrderItemsToPrintFilter(IOrderItem orderItem, IEnumerable discountItems) { return !(orderItem is IProductItem) || discountItems .Where(discountItem => discountItem.DiscountSums.ContainsKey(orderItem)) .All(discountItem => discountItem.Type.PrintProductItemInPrecheque); } private static bool OrderItemsPrechequePrintableFilter(IOrderItem orderItem) { if (orderItem.Cost != 0m) return true; return orderItem.Product.PrechequePrintable; } private bool AreOrderItemsEqual(IOrderItem x, IOrderItem y) { if (ReferenceEquals(x, y)) return true; if (x == null) return y == null; if (y == null) return false; var xProductItem = x as IProductItem; var yProductItem = y as IProductItem; if (xProductItem == null || yProductItem == null || !ProductItemCanBeMerged(xProductItem) || !ProductItemCanBeMerged(yProductItem)) return false; if (xProductItem.Product.Name != yProductItem.Product.Name) return false; if (xProductItem.ProductCustomName != yProductItem.ProductCustomName) return false; if (xProductItem.ProductSize == null ^ yProductItem.ProductSize == null) return false; if (xProductItem.ProductSize != null && yProductItem.ProductSize != null && xProductItem.ProductSize.Name != yProductItem.ProductSize.Name) return false; if (xProductItem.Price != yProductItem.Price) return false; if (xProductItem.Price == 0m) return true; var categorizedDiscounts = Model.Order.DiscountItems .Where(discountItem => discountItem.IsCategorized && discountItem.PrintDetailedInPrecheque && discountItem.DiscountSums.Count > 0) .ToList(); var xCategorizedDiscountItems = categorizedDiscounts.Where(discountItem => discountItem.DiscountSums.ContainsKey(x)); var yCategorizedDiscountItems = categorizedDiscounts.Where(discountItem => discountItem.DiscountSums.ContainsKey(y)); return new HashSet(xCategorizedDiscountItems).SetEquals(yCategorizedDiscountItems); } private bool ProductItemCanBeMerged(IProductItem productItem) { return productItem.CompoundsInfo == null && productItem.Amount - Math.Truncate(productItem.Amount) == 0m && productItem.GetNotDeletedChildren().Where(orderEntry => ModifiersFilter(orderEntry, productItem)).IsEmpty() && (productItem.Comment == null || productItem.Comment.Deleted || !Model.Order.Table.Section.PrintProductItemCommentInCheque); } private static bool CommonModifiersFilter(bool isCommonModifier, IProductItem parent, bool onlyCommonModifiers) { if (parent.CompoundsInfo != null && parent.CompoundsInfo.IsPrimaryComponent) { if (onlyCommonModifiers && !isCommonModifier) return false; if (!onlyCommonModifiers && isCommonModifier) return false; return true; } return true; } private static bool ModifiersFilter(IOrderEntry orderEntry, IOrderItem parent, bool onlyCommonModifiers = false) { var parentProductItem = parent as IProductItem; if (parentProductItem != null && !CommonModifiersFilter(((IModifierEntry)orderEntry).IsCommonModifier, parentProductItem, onlyCommonModifiers)) return false; if (orderEntry.Cost > 0m) return true; if (!orderEntry.Product.PrechequePrintable) return false; var modifierEntry = orderEntry as IModifierEntry; if (modifierEntry == null) return true; if (modifierEntry.ChildModifier == null) return true; if (!modifierEntry.ChildModifier.HideIfDefaultAmount) return true; var amountPerItem = modifierEntry.ChildModifier.AmountIndependentOfParentAmount ? modifierEntry.Amount : modifierEntry.Amount / GetParentAmount(parent, onlyCommonModifiers); return amountPerItem != modifierEntry.ChildModifier.DefaultAmount; } private static string GetFormattedDiscountDescriptionForOrderItem(bool isDiscount, string discountName, bool discountBySum, decimal absolutePercent) { return string.Format(" {0}", discountName); } private static string GetFormattedDiscountDescriptionShort(bool isDiscount, string discountName) { return string.Format(discountName); } private static string GetFormattedDiscountDescriptionDetailed(bool isDiscount, string discountName, decimal absolutePercent) { return string.Format(discountName); } private static string GetFormattedDiscountSum(bool isDiscount, decimal absoluteSum) { return (isDiscount ? "-" : "+") + FormatMoney(absoluteSum); } private static string GetOrderEntryNameWithProductSize(IOrderEntry orderEntry) { var productName = orderEntry.ProductCustomName ?? orderEntry.Product.Name; var productItem = orderEntry as IProductItem; return (productItem == null || productItem.ProductSize == null || productItem.CompoundsInfo != null) ? productName : string.Format(Resources.ProductNameWithSizeFormat, productName, productItem.ProductSize.Name); } private static decimal GetParentAmount(IOrderItem parent, bool onlyCommonModifiers) { return onlyCommonModifiers ? parent.Amount * 2 : parent.Amount; } }