/// <summary> /// Скидка при оплате бонусами /// </summary> Int64 CalculateDiscountForBonuses(FiscalArticle Article) { Int64 Result = 0, CurrentDiscount = 0; if (!GetDiscountForSpendBonuses(Article, out CurrentDiscount)) // ищем скидку на товар в БД товаров-исключений CurrentDiscount = BonusPaymentDiscounts[Card.StatusID]; if (CurrentDiscount > 0) { Result = Calc.NormalizePrice(Article.PriceWithoutDiscount * CurrentDiscount); RetailScreen.Add("Скидка {0}% за оплату бонусами\n", (int)Calc.ValueToNaturalAmount(CurrentDiscount)); //PrinterMessage += string.Format("{0}% оплату бонусами\n", Parameters.ValueToStringPercent(CurrentDiscount)); // max 24 symbols: 123456789012345678901234 } return Result; }
// скидка в счастливые часы Int64 CalculateDiscountForHappyHours(FiscalArticle Article) { Int64 Result = 0, ActionDiscount = 0; string HappyDescription = ""; if (DateInHappyHours(Transaction.TerminalDateTime)) // если время в списке счасливых часов, то { ActionDiscount = HappyHourDiscounts[Card.StatusID]; HappyDescription = "счастливые часы"; } else if (DateInHappyDayOfWeek(Transaction.TerminalDateTime)) // если дата в списке счасливых дней недели { ActionDiscount = HappyDayOfWeekDiscounts[Card.StatusID]; HappyDescription = "выходные"; } else if (DateInHappyHolidays(Transaction.TerminalDateTime)) // если дата в списке праздников { ActionDiscount = HappyHolidayDiscounts[Card.StatusID]; HappyDescription = "праздничные дни"; } if (ActionDiscount > 0) { Result = Calc.NormalizePrice(Article.PriceWithoutDiscount * ActionDiscount); RetailScreen.Add("+{0}% бонусов за покупку в {1}\n", Calc.ValueToStringPercent(ActionDiscount), HappyDescription); //PrinterMessage += string.Format("{0}% за счастливые часы\n", Parameters.ValueToStringPercent(ActionDiscount)); // max 24 symbols: 123456789012345678901234 /*if (Result != 0) Transaction.ActiveActions.Add(new TransactionAction(Actions.BonusesForHappy, new ActionParameterValue(ActionParameters.ApproximateAmountOfBonuses, Calc.ValueToCurrencyAmount(Calc.NormalizePriceQuantity(Result * Article.Quantity)), HappyDescription)));*/ } return Result; }
// бонусы за оплату банковской картой Int64 CalculateDiscountForBankingCard(FiscalArticle Article) { Int64 Result = 0; if (Article.PaymentType == PaymentTypes.BankingCard) // если товар оплачивается банковской картой { Int64 DiscountForPaymentByBankingCard; if (DiscountPaymentByBankingCard.TryGetValue(Card.StatusID, out DiscountForPaymentByBankingCard)) Result = Calc.NormalizePrice(Article.PriceWithoutDiscount * DiscountForPaymentByBankingCard); RetailScreen.Add("+{0}% бонусов за покупку банковской картой\n", Calc.ValueToStringPercent(DiscountForPaymentByBankingCard)); } return Result; }
// скидка за день рождения Int64 CalculateDiscountForBirthday(FiscalArticle Article) { Int64 Result = 0; if (Card.Contract.LoyaltyClient.Birthday != DateTime.MinValue && Card.Contract.LoyaltyClient.Birthday.Day == Transaction.TerminalDateTime.Day && Card.Contract.LoyaltyClient.Birthday.Month == Transaction.TerminalDateTime.Month) { Result = Calc.NormalizePrice(Article.PriceWithoutDiscount * BirthdayDiscounts[Card.StatusID]); RetailScreen.Add("+{0}% бонусов за покупку в день рождения\n", Calc.ValueToStringPercent(BirthdayDiscounts[Card.StatusID])); //PrinterMessage += string.Format("{0}% за день рождения!\n", Parameters.ValueToStringPercent(Parameters.BirthdayDiscount)); // max 24 symbols: 123456789012345678901234 /*if (Result != 0) Transaction.ActiveActions.Add(new TransactionAction(Actions.BonusesForBirthday, new ActionParameterValue(ActionParameters.ApproximateAmountOfBonuses, Calc.ValueToCurrencyAmount(Calc.NormalizePriceQuantity(Result * Article.Quantity)), Card.Contract.LoyaltyClient.Birthday.ToString())));*/ } return Result; }
/// <summary>Ищет товарную позицию в чеке (сравнение по коду и типу оплаты). Возвращет null, если позиция не найдена</summary> public FiscalArticle FindArticle(FiscalArticle ToFind) { foreach (FiscalArticle Article in Articles) if (Article != null && Article.IsAnalog(ToFind)) return Article; return null; }
// скидка за сумму артикула Int64 CalculateDiscountForAmount(FiscalArticle Article, Int64 FiscalTotalAmount) { Int64 Result = 0, ActionLimit = BigPurchaseLimits[Card.StatusID], ActionDiscount = BigPurchaseDiscounts[Card.StatusID]; if (FiscalTotalAmount >= ActionLimit) { Result = Calc.NormalizePrice(Article.PriceWithoutDiscount * ActionDiscount); RetailScreen.Add("+{0}% бонусов за сумму выше {1} {2}\n", Calc.ValueToStringPercent(ActionDiscount), (int)Calc.ValueToNaturalAmount(ActionLimit), Transaction.Terminal.ServicePoint.Region.Currency.ShortName); //PrinterMessage += string.Format("{0}% за сумму > {1}\n", Parameters.ValueToStringPercent(ActionDiscount), (int)Parameters.ValueToCurrencyAmount(ActionLimit)); // max 24 symbols: 123456789012345678901234 /*if (Result != 0) Transaction.ActiveActions.Add(new TransactionAction(Actions.BonusesForPurchase, new ActionParameterValue(ActionParameters.ApproximateAmountOfBonuses, Calc.ValueToCurrencyAmount(Calc.NormalizePriceQuantity(Result * Article.Quantity))), new ActionParameterValue(ActionParameters.MainParameter, Calc.ValueToCurrencyAmount(ActionLimit))));*/ } return Result; }
/// <summary> /// true, если на товар действует специальная скидка в обход всех акций /// </summary> public bool GetSpecialDiscountForBonusAwarding(FiscalArticle Article, out Int64 Discount) { Discount = 0; LogicalProductInfo ap = Article.Product; if (ProductsBonusAwardDiscount != null && ap != null) { try { if (ProductsBonusAwardDiscount.GetPartForCurrent(Transaction).TryGetValue(ap.ID, out Discount)) { Discount = Calc.NormalizePrice(Article.PriceWithoutDiscount * Discount); return true; } } catch { } // продукт не найден } return false; }
/// <summary>Возвращает true, если переданная позиция является аналогом переданной позиции (сравнение по коду и по типу оплаты)</summary> public bool IsAnalog(FiscalArticle Article) { return GoodsCode == Article.GoodsCode && PaymentType.Value == Article.PaymentType.Value; }
/// <summary> /// Возвращает количество товара, ограниченное в соотв. с параметрами. На возвращённое количество товара можно начислять бонусы. /// В случае изменения количества товара на экран кассира отправляется информация о превышении /// </summary> private Int64 GetLimitedQuantityForPurchase(FiscalArticle Article) { Int64 Result = Article.Quantity; LogicalProductInfo Product = Article.Product; if (Product != null) { string CounterName = GetCounterNameForLimitedProduct(Product); Int64 CurrentCounterValue = Card.Counters[CounterName].CurrentValue; Int64 Limit = ProductQuantityDailyLimits[Product.ID]; if (CurrentCounterValue >= Limit) Result = 0; else { if (CurrentCounterValue + Result > Limit) Result = Limit - CurrentCounterValue; // в других случаях Quantity не меняем } if (Result != Article.Quantity) RetailScreen.Add("Внимание! Превышен допустимый лимит {0} литров при покупке {1}. Начисление бонусов за {1} сегодня производиться не будет\n", Calc.ValueToNaturalQuantity(Limit), Product.Name); } return Result; }
/// <summary>Проверяет, входит ли переданный артикул в продукты, потребление которых необходимо ограничить</summary> bool IsArticleInLimitedProducts(FiscalArticle Article) { if (Article.Product == null) return false; foreach (var kvp in ProductQuantityDailyLimits) if (Products.IsCodeInProductID(Article.GoodsCode, RetailSystem.ID, kvp.Key)) return true; return false; }
/// <summary> /// Получить для артикула, продаваемого на терминале, специальную скидку при оплате бонусами. /// Скидка будет возвращена в Discount /// </summary> /// <returns>true, если скидка найдена</returns> bool GetDiscountForSpendBonuses(FiscalArticle Article, out Int64 Discount) { LogicalProductInfo Product = Article.Product; Discount = 0; if (Product != null) return ProductsBonusSpendDiscount.GetPartForCurrent(Transaction).TryGetValue(Product.ID, out Discount); else return false; }
// parse TLV for fiscal receipt internal void Deserialize(byte[] Data, LeveledFileLogger LeveledLog) { var NextLeveledLog = LeveledLog.CloneNextLevel(); PaymentsReceived = false; byte Tag = 0; byte[] Value = null; TLVReader FiscalReader = new TLVReader(Data); while (FiscalReader.NextValue(out Tag, out Value)) switch ((CMPFiscalReceiptTags)Tag) { case CMPFiscalReceiptTags.Article: // может быть несколько позиций чека FiscalArticle Article = new FiscalArticle(this); LeveledLog.Message("Article = {"); Article.Deserialize(Value, NextLeveledLog); // в артикуле у нас все полученные значения LeveledLog.Message("}"); this.Articles.Add(Article); break; case CMPFiscalReceiptTags.Flags: Flags.Receive((FiscalReceiptFlags)TLVReader.ValueToByte(Value)); LeveledLog.Message("Flags = {0} ({1})", Flags.Value, (int)Flags.Value); break; case CMPFiscalReceiptTags.AmountWithoutDiscount: AmountWithoutDiscount.Receive(TLVReader.ValueToInt32(Value)); LeveledLog.Message("AmountWithoutDiscount = {0}", AmountWithoutDiscount.Value); break; case CMPFiscalReceiptTags.DiscountForAmount: DiscountForAmount.Receive(TLVReader.ValueToInt32(Value)); LeveledLog.Message("DiscountForAmount = {0}", DiscountForAmount.Value); break; case CMPFiscalReceiptTags.Payments: // performed payments LeveledLog.Message("Payments = {"); Payments.Deserialize(Value, NextLeveledLog); LeveledLog.Message("}"); PaymentsReceived = true; break; default: throw new Exception(string.Format("Unknown tag 0x{0:X} received", Tag)); } }
/// <summary>ищет среди позиций чека, позицию с GoodsCode и PaymentType совпадающими с переданной и заменяет её в исходной коллекции на клон переданной</summary> public void ReplaceArticle(FiscalArticle Article) { for (int i = 0; i < Articles.Count; i++) if (Articles[i].GoodsCode == Article.GoodsCode && Articles[i].PaymentType.Value == Article.PaymentType.Value) { Articles[i] = Article.Clone(Articles[i].Fiscal); return; } throw new Exception(string.Format("Could not find Article {0} with payment type {1}", Article.GoodsCode, Article.PaymentType.Value)); //return false; }
// скидка за статус Int64 CalculateDiscountForStatus(FiscalArticle Article) { Int64 ActionDiscount = Article.IsFuel ? StatusDiscountsFuel[Card.StatusID] : StatusDiscountsNonFuel[Card.StatusID]; RetailScreen.Add("+{0}% бонусов по статусу - за покупку '{1}'\n", Calc.ValueToStringPercent(ActionDiscount), Article.GoodsNameForTerminalPrinter/* (Article.IsFuel ? "топлива" : "товаров")*/); //PrinterMessage += string.Format("{0}% по статусу\n", Parameters.ValueToStringPercent(ActionDiscount)); // max 24 symbols: 123456789012345678901234 Int64 Result = Calc.NormalizePrice(Article.PriceWithoutDiscount * ActionDiscount); /*if (Result != 0) Transaction.ActiveActions.Add(new TransactionAction(Actions.BonusesForStatus, new ActionParameterValue(ActionParameters.ApproximateAmountOfBonuses, Calc.ValueToCurrencyAmount(Calc.NormalizePriceQuantity(Result * Article.Quantity)))));*/ return Result; }
/// <summary> /// true, если на товар запрещены начисления бонусов /// </summary> public bool IsBonusAwardForbidden(FiscalArticle Article) { Int64 Dummy; LogicalProductInfo ap = Article.Product; if (ap != null) return ProductsBonusAwardForbidden.GetPartForCurrent(Transaction).TryGetValue(ap.ID, out Dummy); return false; }
/// <summary>Заменяет параметры артикула this, использующиеся для вычислений, на параметры переданного артикула</summary> public bool Combine(FiscalArticle Article) { if (Article != null) { if (Article.Quantity.Received) Quantity = new DVInt64(Article.Quantity.Value); if (Article.PriceWithoutDiscount.Received) PriceWithoutDiscount = new DVInt64(Article.PriceWithoutDiscount.Value); if (Article.DiscountForPrice.Received) DiscountForPrice = new DVInt64(Article.DiscountForPrice.Value); if (Article.AmountWithoutDiscount.Received) AmountWithoutDiscount = new DVInt64(Article.AmountWithoutDiscount.Value); if (Article.DiscountForAmount.Received) DiscountForAmount = new DVInt64(Article.DiscountForAmount.Value); if (Article.Bonuses.Received) Bonuses = new DVInt64(Article.Bonuses.Value); if (Article.Flags.Received) Flags = new DVFiscalArticleFlags((FiscalArticleFlags)Article.Flags.Value); if (Article.paymentType.Received) paymentType = new DVPaymentTypes(Article.paymentType.Value); } return Fiscal.ErrorMessage.IsEmpty; }
/// <summary>true, если за товар нельзя платить бонусами</summary> public bool IsForbiddenBonusPayment(FiscalArticle Article) { LogicalProductInfo ap = Article.Product; if (ProductsBonusSpendForbidden != null && ap != null) { try { Int64 Dummy; return ProductsBonusSpendForbidden.GetPartForCurrent(Transaction).TryGetValue(ap.ID, out Dummy); } catch { } // продукт не найден } return false; }
internal static FiscalArticle Substract(FiscalReceipt FR, FiscalArticle A, FiscalArticle B) { var Result = A.Clone(FR); Result.Quantity.Value = A.Quantity.Value - B.Quantity.Value; Result.PriceWithoutDiscount.Value = A.Price; Result.DiscountForPriceCurrent.Value = 0; Result.AmountWithoutDiscount.Value = A.Amount - B.Amount; Result.DiscountForAmount.Value = 0; Result.Bonuses.Value = A.Bonuses.Value - B.Bonuses.Value; return Result; }
/// <summary>Добавить запись в список товаров фискального чека</summary> public FiscalArticle AddArticle(string GoodsCode, PaymentTypes PaymentType, string GoodsName, Int64 Quantity, Int64 Price, Int64 Amount, Int64 DiscountForPrice, Int64 DiscountForAmount, Int64 Bonuses) { FiscalArticle Article = new FiscalArticle(this); Articles.Add(Article); Article.Flags.Value = FiscalArticleFlags.Added; Article.GoodsCode = GoodsCode; Article.PaymentType = new DVPaymentTypes(PaymentType); Article.GoodsName = GoodsName; Article.Quantity = new DVInt64(Quantity); Article.PriceWithoutDiscount = new DVInt64(Price); Article.AmountWithoutDiscount = new DVInt64(Amount); Article.DiscountForPrice = new DVInt64(DiscountForPrice); Article.DiscountForAmount = new DVInt64(DiscountForAmount); Article.Bonuses = new DVInt64(Bonuses); return Article; }