iiko / front.api.sdk

iikoFront Api SDK (part of iikoRMS)
34 stars 22 forks source link

ФР и округления на позицию в заказе #191

Closed igorpooh1978 closed 3 years ago

igorpooh1978 commented 3 years ago

Добрый день

Делаем интеграцию с НС Итак есть налоговая (не РФ), данные в неё буду отдавать через API ФР Итак в ChequeTask есть куча прекрасных полей, которые позволяют получить данные по продукту для печати на ФР. https://iiko.github.io/front.api.sdk/v6/html/T_Resto_Front_Api_Data_Device_Tasks_ChequeTask.htm

Используется API V6

Там есть поле ResultSum, в котором подается значение «После оформления всех скидок и надбавок» Но к сожалению есть ещё и List< ChequeSale> В котором не подается значение суммы после всех округлений, а только полная сумма, т.е. если товар стоил 1,5 и их 3, то там будет 4,5. Но к примеру после всех округлений Сумма д.б. стать 4, все. Сделать ничего не могу будет в конце расхождение Т.е. Там есть и скидки и надбавки и прочее, но нет суммы после округлений К примеру

В IOrder.Items заказа есть продукт Рис с овощами стоимостью 14,15 у него есть поля Price = 14,15, Sum = 14,15, Amount = 1, ResultSum = 14,14 (это было сделано для правильного расчета округления!)

В ChequeTask.Sales Рис с овощами стоимостью 14,15 у него есть поля Price = 14,15, Sum = 14,15, Amount = 1, (т.е. поля с округленными данными нет), таким образом при передаче данных в систему

Я подам к примеру сумму 200 рублей, но система посчитает все позиции и скажет, что вы не верно отдали сумму, т.к. расчет д.б к примеру 200,88 копеек, что приведет к ошибке

Мой вопрос, можно ли как-то выдернуть Order.Items.ResultSum в ChequeTask.Sales.Result чтобы они были равны и с минимальными извращениями, т.к. у вас очень много разных скидок и надбавок. Тоже касается и модификаторов

Сейчас мне видится только путь собирания всех модификаторов в отдельный список и тоже самое для продуктов и потом проходится выборкой (сложно и боюсь скидки и надбавки мне где-нибудь аукнутся).

Да и IncreaseSum и DiscountSum везде 0.

Может это можно как-то отрулить настройками, чтобы в ChequeTask.Sales.ResultSum был равен В IOrder.Items.ResultSum?

Кусок кода для формирования

        internal class AssignedModifiers
        {
            public Guid Id { get; set; }
            public decimal ResultSum { get; set; }
            public decimal Amount { get; set; }
            public decimal Price { get; set; }

        }

/// Собираем модификаторы с нормальным ResultSum
        private List<AssignedModifiers> GetAssignedModifiers(IOrder order)
        {
            List<AssignedModifiers> assignedModificators = new List<AssignedModifiers>();

            // get all assigned modificators in all products and grouped all
            foreach ( var oI in order.Items )
            {
                if ( !(oI is IOrderProductItem) ) continue;
                var product = oI as IOrderProductItem;
                if ( product.AssignedModifiers.Count > 0 )
                {
                    foreach ( var mod in product.AssignedModifiers )
                    {
                        if ( !(mod is IOrderModifierItem) ) continue;
                        var modifier = mod as IOrderModifierItem;

                        var asMod = assignedModificators.FirstOrDefault(ms => ms.Id.Equals(modifier.Product.Id) && ms.Price.Equals(modifier.Price));
                        if ( asMod == null )
                        {
                            assignedModificators.Add(new AssignedModifiers
                            {
                                Id = modifier.Product.Id ,
                                Amount = modifier.Amount ,
                                ResultSum = modifier.ResultSum,
                                Price = modifier.Product.Price
                            });
                        }
                        else
                        {
                            asMod.Amount += modifier.Amount;
                            asMod.ResultSum += modifier.ResultSum;

                        }

                    }

                }

            }
            return assignedModificators;
        }

  private eKassaRequest DoChequeAddItems(ChequeTask chequeTask , EnumCreateDocumentCommand command , Orders refundOrder = null)
        {
            string waiterName = chequeTask.CashierName;
            int guestCount = 0;

            IOrder order = PluginContext.Operations.TryGetOrderById(chequeTask.OrderId.GetValueOrDefault());

            List<AssignedModifiers> assignedModificators = GetAssignedModifiers(order);

            decimal cashless = chequeTask.CardPayments.Sum(st => st.Sum);
            decimal changes = chequeTask.ResultSum - cashless - chequeTask.ConsiderationSum - chequeTask.CashPayment - chequeTask.CreditSum; // , если с -, то
            decimal cashSum = chequeTask.CashPayment - Math.Abs(changes);

            decimal cashlessSum = chequeTask.CardPayments.Sum(sd => sd.Sum);
            DataCreateDocumentPaymentRequest paymentData = new DataCreateDocumentPaymentRequest()
            {
                BonusSum = 0 ,
                Cashier = waiterName ,
                CashlessSum = 0 ,
                CashSum = 0 ,
                Sum = chequeTask.ResultSum ,
                CreditSum = 0 ,
                PrepaymentSum = 0

            };

            paymentData.CashSum = cashSum;
            paymentData.CreditSum = chequeTask.CreditSum;
            paymentData.CashlessSum = cashless;
            paymentData.BonusSum = chequeTask.ConsiderationSum; // ????

            List<Item> chequeItems = new List<Item>();
            List<VatAmount> chequeVatAmounts = new List<VatAmount>();

                foreach ( ChequeSale saleItem in chequeTask.Sales )
                {

                    int qType = 0;
                    string productCode = saleItem.ProductId.ToString();
                    decimal taxPer = saleItem.Vat.GetValueOrDefault();

                IOrderProductItem productItemPrice = null;
                AssignedModifiers asModRes = null;
                if ( !chequeTask.IsPrepay )
                    {
                    // получаем дополнительные сведения о продукте НДС и ед. измерения, которые сверяются на основании QuantityTypeCompatibility

                    var gpi = PluginContext.Operations.TryGetProductById(saleItem.ProductId);

                    if ( gpi != null )
                        {
                            eKassaCashRegisterConfiguration.QuantityTypeCompatibility.TryGetValue(gpi.MeasuringUnitName , out string means);
                            int.TryParse(means , out qType);
                            taxPer = gpi.TaxPercent.GetValueOrDefault();

                        // Ищем соответствие order и chequeTask - тут засада если в ордере продукты шли не по-порядку, всё ломается!

                        foreach ( var oI in order.Items)
                        {
                            if ( !(oI is IOrderProductItem) ) continue;
                            var product = oI as IOrderProductItem;
                            if (
                                product.Product.Id.Equals(gpi.Id)
                            && product.Product.Price.Equals(saleItem.Price)
                           && product.Amount.Equals(saleItem.Amount)
                          )
                            {
                                productItemPrice = product as IOrderProductItem;
                                break;
                            }

                        }

                        // пробуем поискать в AssignedModifiers
                        if (productItemPrice == null)
                        {
                            asModRes = assignedModificators.FirstOrDefault(am =>
                                                                am.Id.Equals(gpi.Id)
                                                             && am.Price.Equals(saleItem.Price)
                                                             && am.Amount.Equals(saleItem.Amount)
                                                             );

                        }

                    }

                }
                decimal itemPrice = 0M;

                if ( productItemPrice != null )
                {

                        itemPrice = (decimal) Math.Round((double) (productItemPrice.ResultSum / productItemPrice.Amount) , 2 , MidpointRounding.ToEven);

                }
                else if (asModRes !=null)
                {
                    itemPrice = (decimal) Math.Round((double) (asModRes.ResultSum / asModRes.Amount) , 2 , MidpointRounding.ToEven);

                }
                else
                {
                    itemPrice = (decimal) Math.Round((double) (saleItem.Sum.Value / saleItem.Amount) , 2 , MidpointRounding.ToEven);
                }

                    chequeItems.Add(
                        new Item()
                        {
                            ItemName = saleItem.Name ,
                            ItemCodeType = 0 ,
                            ItemPrice = (decimal) itemPrice ,
                            ItemSum = itemPrice * saleItem.Amount.Value ,
                            ItemCode = saleItem.Code ,
                            ItemQuantity = saleItem.Amount.Value ,

                            ItemVatPercent = taxPer ,
                            ItemQuantityType = qType

                        }
                        );
                    VatAmount sVat = chequeVatAmounts.FirstOrDefault(sv => sv.VatPercent.Equals(taxPer));
                    if ( sVat == null )
                    {
                        chequeVatAmounts.Add(new VatAmount() { VatPercent = taxPer , VatSum = (itemPrice * saleItem.Amount.Value) });
                    }
                    else
                    {
                        sVat.VatSum += (itemPrice * saleItem.Amount.Value);
                    }

                }

            return ...;
}
vcpp commented 3 years ago

Рис с овощами стоимостью 14,15 у него есть поля Price = 14,15, Sum = 14,15, Amount = 1, ResultSum = 14,14 (это было сделано для правильного расчета округления!)

Что за округление 15,15 в 15,14? Хотелось бы посмотреть пример целиком, имея доступ к БД с номенклатурой, настройками и всем прочим. Можете показать копию папки CashServer? Если в общий доступ выкладывать нельзя, отправьте на api@iiko.ru со ссылкой на эту страницу.

letolica commented 3 years ago

Итак в ChequeTask есть куча прекрасных полей, которые позволяют получить данные по продукту для печати на ФР.

И дополнительный вопрос: вы смотрели, какое значение приходит в поле ChequeTask.RoundSum?

igorpooh1978 commented 3 years ago

Добрый день. Про БД несколько сложно . Про RoundSum - да такое поле известно, но в полях налоговой таких полей как округление - нет и они ждут финальной суммы со всеми скидками и надбавками. (ещё раз замечу, что это не РФ).

letolica commented 3 years ago

Заполнение тех или иных полей зависит в том числе от настроек ФР в iikoOffice. Если случайным образом выбрана поддержка ФФД, то сумма округления (настройка "Округлять стоимость заказа до целого в пользу гостя") уйдет как раз в ChequeTask.RoundSum. Но вы правы, вне РФ это не используется. Так что рассмотрим без поддержки ОФД.

В CheqeTask много полей, и всех их достаточно для оплаты заказа на ФР. Т.е. все скидки/надбавки и округления там есть, если они есть в заказе. Например, если в ФР включена печать НДС, то вся информация о скидках/надбавках хранится исключительно в ChequeSale: ChequeSale.DiscountSum и ChequeSale.IncreaseSum. Поле ChequeSale.Sum ВСЕГДА хранит в себе итоговую стоимость позиции заказа (с учетом скидок/надбавок и округлений). Если же в ФР не включена печать НДС, а в заказе есть не только категориальные скидки/надбавки на блюда, но и общие скидки/надбавки на заказ, а также округления, то категориальные скидки/надбавки будут храниться в ChequeSale.DiscountSum или ChequeSale.IncreaseSum соответственно, а скидки/надбавки, действующие целиком на весь заказ (сюда же входят округления), в BillTask.DiscountSum и BillTask.IncreaseSum.

letolica commented 3 years ago

В котором не подается значение суммы после всех округлений, а только полная сумма, т.е. если товар стоил 1,5 и их 3, то там будет 4,5. Но к примеру после всех округлений Сумма д.б. стать 4

Попробовала воспроизвести ситуацию. В настройках валюты включена настройка "Округлять стоимость заказа до целого в пользу гостя". Добавляю 3 блюда по 1.5. Итоговая стоимость заказа: 4, сумма скидки-округления: 0.5.

1. В настройках ФР выключена настройка "Печать НДС". В ChequeTask: BillTask.DiscountSum = 0.5 ChequeSale.DiscountSum = 0 ChequeSale.Sum = 4 ChequeSale.Price = 1.5 ChequeSale.Amount = 3

2. Включаю в настройках ФР "Печать НДС" и оплачиваю такой же заказ. В ChequeTask: BillTask.DiscountSum = 0 ChequeSale.DiscountSum = 0.5 ChequeSale.Sum = 4 ChequeSale.Price = 1.5 ChequeSale.Amount = 3

igorpooh1978 commented 3 years ago

Да спасибо