public byte[] Serialize(LeveledFileLogger LeveledLog)
        {
            TLVWriter ArticleWriter = new TLVWriter();

              // анализ флагов перед сериализацией
              if (Quantity.Value != Quantity.Initial || PriceWithoutDiscount.Value != PriceWithoutDiscount.Initial) // количество товара или цена без скидки была изменено
            Flags.Value |= FiscalArticleFlags.Changed; // устанавливаем флаг того, что позиция была изменена

              if (Price < 0) throw new ArgumentException("Цена товара не может быть отрицательной", "Цена товара");

              //if (Quantity != 0 && Amount != 0) убрал 10.12.2013 при тестировании событий "добавления товара с нулевой ценой" и "удаления товара путем обнуления количества"
              {
            ArticleWriter.AppendTLVString((byte)OCPFiscalArticleTags.GoodsCode, GoodsCode, EncodingName); LeveledLog.Message("GoodsCode = '{0}'", GoodsCode);
            if (GoodsName.Length != 0)
            {
              ArticleWriter.AppendTLVString((byte)OCPFiscalArticleTags.GoodsName, GoodsName, EncodingName);
              LeveledLog.Message("GoodsName = '{0}'", GoodsName);
            }
            ArticleWriter.AppendTLVInt32((byte)OCPFiscalArticleTags.Quantity, Quantity.Value); LeveledLog.Message("Quantity = {0}", Quantity.Value);
            ArticleWriter.AppendTLVInt32((byte)OCPFiscalArticleTags.PriceWithoutDiscount, PriceWithoutDiscount.Value); LeveledLog.Message("PriceWithoutDiscount = {0}", PriceWithoutDiscount.Value);
            ArticleWriter.AppendTLVInt32((byte)OCPFiscalArticleTags.AmountWithoutDiscount, AmountWithoutDiscount.Value); LeveledLog.Message("AmountWithoutDiscount = {0}", AmountWithoutDiscount.Value);

            if (QuantityPrecision != 3)
            {
              ArticleWriter.AppendTLVByte((byte)OCPFiscalArticleTags.QuantityPrecision, QuantityPrecision); LeveledLog.Message("QuantityPrecision = {0}", QuantityPrecision);
            }
            if (Flags.Value != FiscalArticleFlags.None)
            {
              ArticleWriter.AppendTLVByte((byte)OCPFiscalArticleTags.Flags, (byte)Flags.Value); LeveledLog.Message("Flags = {0} ({1})", Flags.Value, (int)Flags.Value);
            }
            if (DiscountForPriceCurrent.Value != 0)
            {
              ArticleWriter.AppendTLVInt32((byte)OCPFiscalArticleTags.DiscountForPrice, DiscountForPriceCurrent.Value);
              LeveledLog.Message("DiscountForPriceCurrent = {0}", DiscountForPriceCurrent.Value);
            }
            if (DiscountForAmount.Value != 0)
            {
              ArticleWriter.AppendTLVInt32((byte)OCPFiscalArticleTags.DiscountForAmount, DiscountForAmount.Value);
              LeveledLog.Message("DiscountForAmount = {0}", DiscountForAmount.Value);
            }
            if (Bonuses.Value != 0)
            {
              ArticleWriter.AppendTLVInt32((byte)OCPFiscalArticleTags.Bonuses, Bonuses.Value);
              LeveledLog.Message("Bonuses = {0}", Bonuses.Value);
            }

            if (Fiscal.Payments.IsMixedPayment || paymentType.Received) // тип оплаты возвращаем тогда, когда используется смешанная оплату
            {
              ArticleWriter.AppendTLVByte((byte)OCPFiscalArticleTags.PaymentType, (byte)PaymentType.Value);
              LeveledLog.Message("PaymentType = {0} ({1})", PaymentType.Value, (int)PaymentType.Value);
            }

            //ArticleWriter.AppendTLVString((byte)CMPFiscalArticleTags.MeasureUnit, MeasureUnit, EncodingName); LeveledLog.Message("MeasureUnit = {0}", MeasureUnit); не передаётся обратно для экономии трафика
              }

              return ArticleWriter.Bytes;
        }
        // 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));
            }
        }
        /*private void LeveledLog.Message(string Message)
        {
          const byte Level = 4;
          SessionLog.Message("".PadLeft(Level * 2) + Message);
        }
        private void LeveledLog.Message(string Message, params object[] Arguments)
        {
          LeveledLog.Message(string.Format(Message, Arguments));
        }*/
        public void Deserialize(byte[] Data, LeveledFileLogger LeveledLog)
        {
            byte Tag = 0;
              byte[] Value = null;

              TLVReader ArticleReader = new TLVReader(Data);
              while (ArticleReader.NextValue(out Tag, out Value))
            switch ((OCPFiscalArticleTags)Tag)
            {
              case OCPFiscalArticleTags.GoodsCode:
            GoodsCode = TLVReader.ValueToString(Value, EncodingName);
            LeveledLog.Message("GoodsCode = '{0}'", GoodsCode);
            break;
              case OCPFiscalArticleTags.Quantity:
            Quantity.Receive(TLVReader.ValueToInt32(Value));
            LeveledLog.Message("Quantity = {0}", Quantity.Value);
            break;
              case OCPFiscalArticleTags.PriceWithoutDiscount:
            PriceWithoutDiscount.Receive(TLVReader.ValueToUInt32(Value));
            LeveledLog.Message("PriceWithoutDiscount = {0}", PriceWithoutDiscount.Value);
            break;
              case OCPFiscalArticleTags.AmountWithoutDiscount:
            AmountWithoutDiscount.Receive(TLVReader.ValueToInt32(Value));
            LeveledLog.Message("AmountWithoutDiscount = {0}", AmountWithoutDiscount.Value);
            break;
              case OCPFiscalArticleTags.GoodsName:
            GoodsName = TLVReader.ValueToString(Value, EncodingName);
            LeveledLog.Message("GoodsName = '{0}'", GoodsName);
            break;
              case OCPFiscalArticleTags.Flags:
            Flags.Receive((FiscalArticleFlags)TLVReader.ValueToByte(Value));
            LeveledLog.Message("Flags = {0} {1}", Flags.Value, (int)Flags.Value);
            break;
              case OCPFiscalArticleTags.DiscountForPrice:
            DiscountForPriceCurrent.Receive(TLVReader.ValueToInt32(Value));
            LeveledLog.Message("DiscountForPrice = {0}", DiscountForPriceCurrent.Value);
            break;
              case OCPFiscalArticleTags.DiscountForAmount:
            DiscountForAmount.Receive(TLVReader.ValueToInt32(Value));
            LeveledLog.Message("DiscountForAmount = {0}", DiscountForAmount.Value);
            break;
              case OCPFiscalArticleTags.Bonuses:
            Bonuses.Receive(TLVReader.ValueToInt32(Value));
            LeveledLog.Message("Bonuses = {0}", Bonuses.Value);
            break;
              case OCPFiscalArticleTags.PaymentType:
            paymentType.Receive((PaymentTypes)TLVReader.ValueToByte(Value));
            LeveledLog.Message("PaymentType = {0} ({1})", paymentType.Value, (int)paymentType.Value);
            break;
              case OCPFiscalArticleTags.MeasureUnit:
            MeasureUnit = Functions.LimitString(TLVReader.ValueToString(Value, EncodingName), 8).ToLower();
            LeveledLog.Message("MeasureUnit = '{0}'", MeasureUnit);
            break;
              case OCPFiscalArticleTags.QuantityPrecision:
            QuantityPrecision = TLVReader.ValueToByte(Value);
            LeveledLog.Message("QuantityPrecision = {0}", QuantityPrecision);
            if (QuantityPrecision < 0 || QuantityPrecision > 3)
              Fiscal.ErrorMessage.Add("Неверное значение QuantityPrecision = {0}", QuantityPrecision);
            break;
              default:
            throw new Exception(string.Format("Unknown tag 0x{0:X} received", Tag));
            }
        }
        public byte[] Serialize(LeveledFileLogger LeveledLog)
        {
            var NextLeveledLog = LeveledLog.CloneNextLevel();
              TLVWriter FiscalWriter = new TLVWriter();

              if (Quantity > 0 && Articles.Count > 0) // сериализуем только если есть позиции в чеке и они ненулевые
              {
            foreach (FiscalArticle Article in Articles)
            {
              LeveledLog.Message("Article = {");
              FiscalWriter.AppendTLV((byte)CMPFiscalReceiptTags.Article, Article.Serialize(NextLeveledLog));
              LeveledLog.Message("}");
            }

            if (Flags.Value != FiscalReceiptFlags.None)
            {
              FiscalWriter.AppendTLVByte((byte)CMPFiscalReceiptTags.Flags, (byte)Flags.Value);
              LeveledLog.Message("Flags = {0} ({1})", Flags.Value, (byte)Flags.Value);
            }
            if (AmountWithoutDiscount.Value != 0 || Articles.Count > 0)
            {
              FiscalWriter.AppendTLVInt32((byte)CMPFiscalReceiptTags.AmountWithoutDiscount, (Int32)AmountWithoutDiscount.Value);
              LeveledLog.Message("AmountWithoutDiscount = {0}", AmountWithoutDiscount.Value);
            }
            if (DiscountForAmount.Value != 0)
            {
              FiscalWriter.AppendTLVInt32((byte)CMPFiscalReceiptTags.DiscountForAmount, (Int32)DiscountForAmount.Value);
              LeveledLog.Message("DiscountForAmount = {0}", DiscountForAmount.Value);
            }
            LeveledLog.Message("Payments = {");
            FiscalWriter.AppendTLV((byte)CMPFiscalReceiptTags.Payments, Payments.Serialize(NextLeveledLog));
            LeveledLog.Message("}");
              }

              return FiscalWriter.Bytes;
        }