Как настроить Service Connect для автоматического импорта Заказов на Закупку из XLS файла?

Иногда эта задача может показаться слишком сложной, особенно для того, кто раньше никогда не сталкивался с xslt преобразованиями, однако, на самом деле всё не так драматично, как может показаться на первый взгляд. Очень надеюсь, что моё пошаговое описание с картинками убедит Вас в этом.

Начнём с задачи: имеется xls файл, который поставщик присылает по электронной почте. Будем исходить из того, что коды запасов в этом файле совпадают с кодами запасов, имеющимися в iScala.

Файл для импорта в формате Excel

Перейдём в административную консоль iScala, откроем «Интеграция» и добавим новый пакет рабочего потока:

Создание нового пакета рабочего потока Service Connect

Сохраним его под именем PurchaseOrderImport и добавим в него новый рабочий поток:

Добавление нового рабочего потока

Выберем пункт меню Инструменты — Утилита схемы

Вызов утилиты схемы

Выберем исходный Excel файл, назовём также файл схемы. Выберем конверсию образцов данных, как на картинке ниже:

Параметры утилиты схемы

В следующем окне выберем преобразование excel2xml

преобразование excel2xml

Укажем следующие параметры преобразования:

<DefaultNamespace>http://XLS</DefaultNamespace>
<FirstRowContainsFieldNames>true</FirstRowContainsFieldNames>

Параметры преобразования

Проверка результатов настройки преобразования:

Проверка результатов настройки преобразования

Установим флажок как на картинке ниже:

Установите флажок как на картинке

В результате в папке пользовательских схем создаётся схема примерно следующего содержимого:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:tns="http://XLS" attributeFormDefault="unqualified" elementFormDefault="qualified"
targetNamespace="http://XLS" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="table">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="row">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="StockCode" type="xs:string" />
              <xs:element name="InvoiceNo" type="xs:unsignedInt" />
              <xs:element name="InvoiceDate" type="xs:string" />
              <xs:element name="LineNo" type="xs:unsignedByte" />
              <xs:element name="StockItemDescription" type="xs:string" />
              <xs:element name="Quantity" type="xs:unsignedShort" />
              <xs:element name="Price" type="xs:string" />
              <xs:element name="Value" type="xs:string" />
              <xs:element name="Origin" type="xs:string" />
              <xs:element name="NetWeight" type="xs:string" />
              <xs:element name="GrossWeight" type="xs:string" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
      <xs:attribute name="SheetName" type="xs:string" use="required" />
    </xs:complexType>
  </xs:element>
</xs:schema>

В меню выберем свойства процесса:

свойства процесса

На закладке «Компания» установим конкретную компанию:

Закладка Компания

А на закладке «Финансовый год» — конкретный финансовый год:

Закладка Финансовый Год

С помощью элемента «Connection» соединим Start и Finish

С помощью элемента Connection соединим Start и Finish

Теперь процесс может быть сохранён:

Сохранить процесс

Назовём его PurchaseOrderFromExcelFile:

Назовём его PurchaseOrderFromExcelFile

Добавим элемент Conversion и укажем входную и выходную схему:

Добавим элемент Conversion и укажем входную и выходную схему:

С помощью элемента Connection соединим элементы схемы

С помощью элемента Connection соединим элементы схемы

Возьмём готовый шаблон преобразования «плоского» одноуровневого представления данных в Excel файле в иерархическое представление структуры заказа на закупку. Файл подготовлен мною, называется XLStoPurchaseOrder.xslt и выглядит следующим образом:

Содержимое файла XLStoPurchaseOrder.xslt
<?schema_locations source_url="" source_root="" destination_url="" destination_root=""?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:msg="http://Epicor.com/InternalMessage/1.1" xmlns:src="http://XLS"
xmlns:dta="http://www.scala.net/PurchaseOrder/1.1">
 <xsl:output method="xml" version="1.0" indent="yes" omit-xml-declaration="no"></xsl:output>
 <xsl:template match="/">
 <xsl:apply-templates select="*"></xsl:apply-templates>
 </xsl:template>

 <!-- Переменные (преобразование дат) -->
 <xsl:variable name="OrdDate" select="concat(substring(msg:msg/msg:req/msg:dta/src:table/src:row/src:OrdDate,7,4),'-',substring(msg:msg/msg:req/msg:dta/src:table/src:row/src:OrdDate,4,2),'-',substring(msg:msg/msg:req/msg:dta/src:table/src:row/src:OrdDate,1,2))"></xsl:variable>
 <xsl:variable name="DelivDate" select="concat(substring(msg:msg/msg:req/msg:dta/src:table/src:row/src:DelivDate,7,4),'-',substring(msg:msg/msg:req/msg:dta/src:table/src:row/src:DelivDate,4,2),'-',substring(msg:msg/msg:req/msg:dta/src:table/src:row/src:DelivDate,1,2))"></xsl:variable>

 <xsl:template match="msg:msg">
 <xsl:element name="msg:msg">
 <xsl:for-each select="msg:req">
 <xsl:element name="msg:req">
 <xsl:if test="msg:ctx/*">
 <xsl:element name="msg:ctx">
 <xsl:copy-of select="msg:ctx/*"></xsl:copy-of>
 </xsl:element>
 </xsl:if>
 <xsl:element name="msg:dta">
 <xsl:element name="dta:PurchaseOrder">
 <!-- Заголовок заказа на закупку -->
 <xsl:element name="dta:OrderHeader">
 <xsl:element name="dta:PurchOrdNum">
 <xsl:value-of select="msg:dta/src:table/src:row/src:PurchOrdNum"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:OrdType">
 <xsl:text>1</xsl:text>
 </xsl:element>
 <xsl:element name="dta:SuppCode">
 <xsl:text>001500</xsl:text>
 </xsl:element>
 <xsl:element name="dta:Remark1">
 <xsl:value-of select="msg:dta/src:table/src:row/src:Remark1"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:Remark2">
 <xsl:value-of select="msg:dta/src:table/src:row/src:Remark2"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:POPrintedFlag">
 <xsl:value-of select="msg:dta/src:table/src:row/src:POPrintedFlag"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:ReminderConfFlag">
 <xsl:value-of select="msg:dta/src:table/src:row/src:ReminderConfFlag"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:ReminderShipFlag">
 <xsl:value-of select="msg:dta/src:table/src:row/src:ReminderShipFlag"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:DispNoteFlag">
 <xsl:value-of select="msg:dta/src:table/src:row/src:DispNoteFlag"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:InvEnteredFlag">
 <xsl:value-of select="msg:dta/src:table/src:row/src:InvEnteredFlag"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:PaymTerm">
 <xsl:value-of select="msg:dta/src:table/src:row/src:PaymTerm"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:DelivTerm">
 <xsl:value-of select="msg:dta/src:table/src:row/src:DelivTerm"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:DelivWay">
 <xsl:value-of select="msg:dta/src:table/src:row/src:DelivWay"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:OrdDate">
 <xsl:value-of select="$OrdDate"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:DelivDate">
 <xsl:value-of select="$DelivDate"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:OurRef">
 <xsl:value-of select="msg:dta/src:table/src:row/src:OurRef"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:YourRef">
 <xsl:value-of select="msg:dta/src:table/src:row/src:YourRef"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:OrdDisc">
 <xsl:value-of select="msg:dta/src:table/src:row/src:OrdDisc"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:OrdAmt">
 <xsl:value-of select="msg:dta/src:table/src:row/src:OrdAmt"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:LanCode">
 <xsl:value-of select="msg:dta/src:table/src:row/src:LanCode"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:CurrCode">
 <xsl:value-of select="msg:dta/src:table/src:row/src:CurrCode"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:Warehouse">
 <xsl:value-of select="msg:dta/src:table/src:row/src:Warehouse"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:UserOption1">
 <xsl:value-of select="msg:dta/src:table/src:row/src:UserOption1"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:UserOption2">
 <xsl:value-of select="msg:dta/src:table/src:row/src:UserOption2"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:UserOption3">
 <xsl:value-of select="msg:dta/src:table/src:row/src:UserOption3"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:CurrContract">
 <xsl:value-of select="msg:dta/src:table/src:row/src:CurrContract"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:FixedExchRate">
 <xsl:value-of select="msg:dta/src:table/src:row/src:FixedExchRate"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:ContractNum">
 <xsl:value-of select="msg:dta/src:table/src:row/src:ContractNum"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:Purchaser">
 <xsl:value-of select="msg:dta/src:table/src:row/src:Purchaser"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:ProjNum">
 <xsl:value-of select="msg:dta/src:table/src:row/src:ProjNum"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:SiteCode">
 <xsl:value-of select="msg:dta/src:table/src:row/src:SiteCode"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:CustPONum">
 <xsl:value-of select="msg:dta/src:table/src:row/src:CustPONum"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:SuppOrdNum">
 <xsl:value-of select="msg:dta/src:table/src:row/src:SuppOrdNum"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:ExtRemark1">
 <xsl:value-of select="msg:dta/src:table/src:row/src:ExtRemark1"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:ExtRemark2">
 <xsl:value-of select="msg:dta/src:table/src:row/src:ExtRemark2"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:CFOP">
 <xsl:value-of select="msg:dta/src:table/src:row/src:CFOP"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:FiscNoteNo">
 <xsl:value-of select="msg:dta/src:table/src:row/src:FiscNoteNo"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:FiscNoteDate">
 <xsl:value-of select="msg:dta/src:table/src:row/src:FiscNoteDate"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:NetWeight">
 <xsl:value-of select="msg:dta/src:table/src:row/src:OrderNetWeight"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:GrossWeight">
 <xsl:value-of select="msg:dta/src:table/src:row/src:OrderGrossWeight"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:NoOfBoxes">
 <xsl:value-of select="msg:dta/src:table/src:row/src:NoOfBoxes"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:NumOfPallets">
 <xsl:value-of select="msg:dta/src:table/src:row/src:NumOfPallets"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:TransportCompany">
 <xsl:value-of select="msg:dta/src:table/src:row/src:TransportCompany"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:FreightAmount">
 <xsl:value-of select="msg:dta/src:table/src:row/src:FreightAmount"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:Packing">
 <xsl:value-of select="msg:dta/src:table/src:row/src:Packing"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:OrgDelivDate">
 <xsl:value-of select="msg:dta/src:table/src:row/src:OrgDelivDate"></xsl:value-of>
 </xsl:element>
 </xsl:element>
 <!-- Very important here (переход на другой уровень вложенности) -->
 <xsl:apply-templates select="msg:dta/src:table"></xsl:apply-templates>


 </xsl:element>
 </xsl:element>
 <xsl:element name="msg:wfl">
 <xsl:if test="msg:wfl/msg:usr/*">
 <xsl:element name="msg:usr">
 <xsl:copy-of select="msg:wfl/msg:usr/*"></xsl:copy-of>
 </xsl:element>
 </xsl:if>
 <xsl:if test="msg:wfl/msg:ElemID">
 <xsl:element name="msg:ElemID">
 <xsl:value-of select="msg:wfl/msg:ElemID"></xsl:value-of>
 </xsl:element>
 </xsl:if>
 <xsl:if test="msg:wfl/msg:PrcID">
 <xsl:element name="msg:PrcID">
 <xsl:value-of select="msg:wfl/msg:PrcID"></xsl:value-of>
 </xsl:element>
 </xsl:if>
 <xsl:if test="msg:wfl/msg:SubWF/*">
 <xsl:element name="msg:SubWF">
 <xsl:copy-of select="msg:wfl/msg:SubWF/*"></xsl:copy-of>
 </xsl:element>
 </xsl:if>
 <xsl:if test="msg:wfl/msg:RefID">
 <xsl:element name="msg:RefID">
 <xsl:value-of select="msg:wfl/msg:RefID"></xsl:value-of>
 </xsl:element>
 </xsl:if>
 <xsl:if test="msg:wfl/msg:Task/*">
 <xsl:element name="msg:Task">
 <xsl:copy-of select="msg:wfl/msg:Task/*"></xsl:copy-of>
 </xsl:element>
 </xsl:if>
 <xsl:if test="msg:wfl/msg:Ack">
 <xsl:element name="msg:Ack">
 <xsl:value-of select="msg:wfl/msg:Ack"></xsl:value-of>
 </xsl:element>
 </xsl:if>
 <xsl:if test="msg:wfl/msg:MsgType">
 <xsl:element name="msg:MsgType">
 <xsl:value-of select="msg:wfl/msg:MsgType"></xsl:value-of>
 </xsl:element>
 </xsl:if>
 <xsl:if test="msg:wfl/msg:Action">
 <xsl:element name="msg:Action">
 <xsl:value-of select="msg:wfl/msg:Action"></xsl:value-of>
 </xsl:element>
 </xsl:if>
 <xsl:if test="msg:wfl/msg:DataPaging/*">
 <xsl:element name="msg:DataPaging">
 <xsl:copy-of select="msg:wfl/msg:DataPaging/*"></xsl:copy-of>
 </xsl:element>
 </xsl:if>
 </xsl:element>
 </xsl:element>
 </xsl:for-each>
 <xsl:if test="msg:trc/*">
 <xsl:element name="msg:trc">
 <xsl:copy-of select="msg:trc/*"></xsl:copy-of>
 </xsl:element>
 </xsl:if>
 </xsl:element>
 </xsl:template>

 <!-- Уровень Строк заказа на закупку) -->
 <xsl:template match="msg:dta/src:table">
 <xsl:element name="dta:OrderLines">
 <xsl:apply-templates select="src:row"></xsl:apply-templates>
 </xsl:element>
 </xsl:template>

 <!-- Уровень Отдельной строки заказа на закупку) -->
 <xsl:template match="src:row">
 <xsl:element name="dta:OrderLine">
 <xsl:element name="dta:LineNum">
 <xsl:value-of select="src:LineNum"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:StructLineNum">
 <xsl:value-of select="src:StructLineNum"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:LineType">
 <xsl:value-of select="src:LineType"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:StockCode">
 <xsl:value-of select="src:StockCode"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:DescrLine1">
 <xsl:value-of select="src:DescrLine1"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:DescrLine2">
 <xsl:value-of select="src:DescrLine2"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:UnitPrice">
 <xsl:value-of select="src:Price"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:UnitCode">
 <xsl:value-of select="src:UnitCode"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:QtyOrdered">
 <xsl:value-of select="src:Quantity"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:QtyReceived">
 <xsl:value-of select="src:QtyReceived"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:QtyInvoiced">
 <xsl:value-of select="src:QtyInvoiced"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:AccCode">
 <xsl:value-of select="src:AccCode"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:AccCode2">
 <xsl:value-of select="src:AccCode2"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:Disc">
 <xsl:value-of select="src:Disc"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:DelivDateCommit">
 <xsl:value-of select="src:DelivDateCommit"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:DelivDateActual">
 <xsl:value-of select="src:DelivDateActual"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:QtyUnitInDeliv">
 <xsl:value-of select="src:QtyUnitInDeliv"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:CostPriceMult">
 <xsl:value-of select="src:CostPriceMult"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:DelivDateReq">
 <xsl:value-of select="src:DelivDateReq"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:NetWeight">
 <xsl:value-of select="src:NetWeight"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:GrossWeight">
 <xsl:value-of select="src:GrossWeight"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:Volume">
 <xsl:value-of select="src:Volume"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:WarehouseId">
 <xsl:value-of select="src:WarehouseId"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:ProjectRef">
 <xsl:value-of select="src:ProjectRef"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:OrdRef">
 <xsl:value-of select="src:OrdRef"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:UnitConv">
 <xsl:value-of select="src:UnitConv"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:DeliveredQtySCUnit">
 <xsl:value-of select="src:DeliveredQtySCUnit"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:OrderedQtySCUnit">
 <xsl:value-of select="src:OrderedQtySCUnit"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:OrdLineRef">
 <xsl:value-of select="src:OrdLineRef"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:Contract">
 <xsl:value-of select="src:WarehouseId"></xsl:value-of>
 </xsl:element>
 <xsl:element name="dta:Contract">
 <xsl:value-of select="src:WarehouseId"></xsl:value-of>
 </xsl:element>
 </xsl:element>
 </xsl:template>

</xsl:stylesheet>


Файл нужно положить в папку, как на картинке ниже

Местоположение файла преобразования

Как только файл появится в вышеупомянутой папке, он становится доступным в выпадающем списке в окне параметров преобразования:

окно параметров преобразования

Откорректируем файл (закомментируем ненужные поля, укажем код поставшика, приведём в соответствие названия полей из источника), например: <xsl:value-of select=»src:Quantity»></xsl:value-of> вместо <xsl:value-of select=»src:QtyOrdered»></xsl:value-of>

Корректировка файла преобразования. Начало

Корректировка файла преобразования. Окончание

Сохраним процесс. Даже если мы ничего не корректировали в редакторе, но что-то меняли в файле преобразования (внешним редактором), я рекомендую после этого зайти в рабочий процесс, который использует данное преобразование и принудительно сохранить его. Так система поймёт, что нужно заново прочитать все файлы, используемые процессом:

Сохранение процесса

Теперь необходимо объяснить системе, в каких случаях использовать созданный рабочий поток. Первое, что необходимо сделать, это создать новый тип сообщения:

Создание нового типа сообщения

Назовём его PurchaseOrderFromExcel и добавим действие под названием Process:

Назовём новый тип сообщения PurchaseOrderFromExcel и добавим действие под названием Process

Теперь добавим новую карту сообщений:

Новая карта сообщений

Новая карта сообщений

При нажатии на кнопку «Выбор» можно увидеть список всех возможных вариантов. Лучше всего снять все флажки кроме «Процессы»:

Лучше всего снять все флажки кроме Процессы

После выбора процесса возвращаемся в предыдущий экран

Новая карта сообщений

Следующий шаг — добавление входного канала:

Добавление нового входного канала

Назовём его PurchaseOrderFromSupplierXLS. Тип — Файл

Новый входной канал

Конфигурируем канал. Закладка «Свойства коммуникатора» должна выглядеть примерно так:

Свойства коммуникатора

Для «КнфгПреобразов.» указываем те же свойства, которые мы указывали при использовании утилиты схемы:

указываем те же свойства, которые мы указывали при использовании утилиты схемы

Возвращаемся обратно:

Свойства коммуникатора

На закладке «Свойства Сообщения» указываем тип сообщения, который мы недавно добавляли

На закладке Свойства Сообщения указываем тип сообщения, который мы недавно добавляли

Входной канал сконфигурирован

Входной канал настроен

Входной канал настроен

Копируем исходный XLS файл в папку входного канала:

Папка входного канала

Переходим в Административную консоль и в разделе «Отслеживание документов» видим результат:

Отслеживание обработки сообщений

Посмотрим, что внутри:

Подробности обработки сообщения

Подробности обработки сообщения

Возвращаемся в редактор рабочих потоков. Добавляем менеджер «Purchase Order In»:

Добавляем менеджер Purchase Order In

Конфигурируем его свойства

Свойства менеджера

С помощью элемента Connection соединим элементы схемы и сохраняем рабочий поток.

С помощью элемента Connection соединим элементы схемы

Снова копируем исходный XLS файл в папку входного канала. Смотрим результат:

Отслеживание обработки сообщений

Подробности обработки сообщения

Теперь посмотрим, что произошло в iScala:

Запрос по заказу на закупку

Мы получили то, что хотели. На всё это у меня вместе с подготовкой картинок ушло чуть более одного часа. Чтобы подготовить это описание — ещё 2 часа. Думаю, что и у Вас получится.

Если у Вас возникли какие-либо вопросы, не стесняйтесь их задать.

 

Список опубликованных процедур: