Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форумы общения пользователей продуктов АТОЛ _ Frontol для Win32 — предприятия торговли _ Примеры создания сценариев с помощью языка Java Script

Автор: АТОЛ: Герман Бойцов 18.6.2007, 14:44

Пример №1. Использование параметра E (идентификатор события) и получение значений счетчиков в сценарии

// Пример использования идентификатора события в сценарии
function EventToText(E)
{
switch (E) {
case 1: return "Открытие чека";
case 2: return "Добавление позиции";
case 3: return "Редактирование позиции";
case 4: return "Закрытие чека";
case 5: return "Отмена чека";
case 6: return "Ввод оплаты";
case 7: return "Сторно оплаты";
case 8: return "Ввод карты";
case 9: return "Отмена карты";
case 10: return "Ввод клиента";
case 11: return "Отмена клиента";
case 12: return "Ввод ручной скидки";
}
}

// Пример обращения к значениям счетчиков в сценарии
function CountersToText(RO)
{
Text = "Всего счетчиков " + RO.Counter.Count + "\n";
Text += "Ин." +
"\tКод" +
"\tВида"+
"\tЗначение" +
"\t\"Наименование Вида\"\n";
for (RO.Counter.Index = 1; RO.Counter.Index <= RO.Counter.Count; RO.Counter.Index++)
Text += RO.Counter.Index +
"\t" + RO.Counter.Code + // Код счетчика
"\t" + RO.Counter.TypeCode + // Код вида счетчика
"\t" + RO.Counter.Value + // Значение счетчика
"\t\"" + RO.Counter.TypeName + // Наименование вида счетчика
"\"\n";
return Text;
}

// Функция, с предопределенным именем, которая будет вызвана Frontol в начале обработки события
function BeforeAct(AO, RO, E)
{
Text = "До события " + EventToText(E) + "\n" + CountersToText(RO);
AO.ShowMessage(Text); // Вывод сообщения на экран кассира
}

// Функция, с предопределенным именем, которая будет вызвана Frontol в конце обработки события
function AfterAct(AO, RO, E)
{
Text = "После события " + EventToText(E) + "\n" + CountersToText(RO);
AO.ShowMessage(Text);
}



Пример №2. Учет накоплений и скидка на их основе

Создаем сценарий на момент действия Закрытие чека

function BeforeAct(AO, RO, E)
{
}
function AfterAct(AO, RO, E)
{
RO.Counter.AddValueByTypeCode(8, RO.SummWD * 0.0004);

/* 8 - код вида счетчиков в котором будут учитываться накопления.
В этом виде должны быть счетчики, привязанные к клиентам.
RO.SummWD - это сумма чека.
0.004 - например будем накапливать по 0.4% с чека. */
}




Создаем Объект скидки, в котором указываем этот сценарий.

Теперь, в зависимости от величины накоплений, сделаем разные скидки:

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

В Условии задаем на закладке Счетчики диапазон значений и вид счетчика.

При наличии в чеке счетчика требуемого вида со значением удовлетворяющим диапазону будет начислена заданная ставкой скидка. Для этой группы условий тоже необходим Объект скидки (можно использовать тот же самый что и для сценария).

Автор: POUL 18.6.2007, 14:55

Позновательно. Предложение выкладывать сюда скрипты работающих сценариев и их описания.

Автор: POUL 21.6.2007, 16:23

При введении бонусной карты скрипт подсчитывает максимальную скидку чека по минимальным ценам всех позиций и выводит сообщения со значением максимальной скидки на чек и количеством бонусов на карте.

Код
function BeforeAct(AO, RO, E)
{
if (RO.BuyCount>0)

    {var WMPrice = 0;
     var MaxSkidka = 0;
           for (RO.Pos.Index = 1;
           RO.Pos.Index <=
           RO.Pos.Count;
           RO.Pos.Index ++)

           {if (RO.Pos.Storno == 1)
             {continue;}
             else
             var WMPrice = WMPrice + RO.Pos.WareMinPrice * RO.Pos.Quantity;}

              AO.ShowMessage ("Максимальная скидка: " + (RO.SummForD - WMPrice) + " руб.")}

else AO.ShowMessage ("Введите карту после регистрации ВСЕХ покупок!");
  }

function AfterAct(AO, RO, E)
{
for (RO.Counter.Index = 1;
      RO.Counter.Index <=
      RO.Counter.Count;
      RO.Counter.Index ++)

      {if (RO.Counter.TypeCode == 1)
{AO.ShowMessage ("Баланс карты: " + RO.Counter.Value + " руб.");
AO.DisplayScroll ("Баланс карты: " + RO.Counter.Value + " руб.");}
}
}

Автор: POUL 26.2.2008, 23:39

Программировать надо. На Ява-скрbпте smile.gif

Код
function BeforeAct(AO, RO, E)
{
}
function AfterAct(AO, RO, E)
{
if (RO.Card.Count != 0)
{RO.Counter.AddValueByTypeCode(//Ваш тип кода счетчика//, 1);}
}


Момент действия - закрытие чека.
Также необходимо написать еще несколько сценариев для проверок и ограничений.

Автор: POUL 27.11.2008, 11:06

скрипт проверки наличия введенной карты

Код
function BeforeAct(AO, RO, E)
{
if (RO.Card.Count != 0)
AO.ShowError ("Карта уже введена");
}

function AfterAct(AO, RO, E)
{

}

Автор: POUL 1.5.2009, 22:08

Скрипты для бонусной программы.
Общая идея:
Клиент - владелец карты приводит другого клиента, которому тут же выдается дисконтная карта. Сначала вводится карта приводимого, потом приводящего. При закрытии чека карта приводящего приписывается к карте приводимого. При покупке приводимым на карты обоих записываются по 10% от суммы чека, при возврате списываются обратно. Записанные бонусы можно использовать для оплаты товаров.

При вводе карты:


Код
function BeforeAct(AO, RO, E, O)
{
if ((O.Value.substr (0, 7) != 2997777) || (O.Value.length != 13))    //Проверяем префикс карты и длину
AO.ShowError ("Карта не из этого магазина!");

if (RO.Card.Count >=2)                                               //Проверяем количество введенных в чек карт
AO.ShowError ("Ввод карты запрещен");

if (RO.Card.Count == 1)                                              //Если количество введенных карт в чеке = 1

{
   enter2ndkard = true;                                              //Разрешаем ввод 2-й карты
   Karta = O.Value.slice (7, O.Value.length);                        //Отбрасываем первые 7 знаков вводимой карты

   for (RO.Card.Index = 1;                                           //Перебираем введенные карты
        RO.Card.Index <=
        RO.Card.Count;
        RO.Card.Index ++)

      {if (RO.Card.Index == 1)                                       //Находим первую введенную карту

       for (RO.Card.Counter.Index = 1;                               //Перебираем счетчики на этой карте
            RO.Card.Counter.Index <=
            RO.Card.Counter.Count;
            RO.Card.Counter.Index ++)

         {if (RO.Card.Counter.TypeCode == 1)                         //Находим счетчик с кодом типа счетчика = 1
             {if (RO.Card.Counter.Value != 0)                        //Если он не = 0
                 enter2ndkard = false;}                              //Запрещаем ввод 2-й карты

          if (RO.Card.Counter.TypeCode == 2)                                  //Находим счетчик с кодом типа счетчика = 2
      {if ((RO.Card.Counter.Value == 0) && (enter2ndkard))                    //Если значение найденного счетчика = 0 и разрешен ввод 2-й карты
          RO.UserValues.Set ("Karta", Karta);                                 //Объявляем пользовательскую переменную с хвостом от вводимой карты
       if ((RO.Card.Counter.Value != (Karta || 0)) && (!enter2ndkard))        //Если значение найденного счетчика не равно хвосту от вводимой карты и не = 0, а первый счетчик не = 0
          AO.ShowError ("Ввод второй карты запрещен!");}}}           //Вываливаем ошибку
}
}


function AfterAct(AO, RO, E, O)
{

if (RO.Card.Count == 1)                                                                                  //Если количество введенных карт = 1

   for (RO.Counter.Index = 1;                                                                            //Перебираем счетчики на карте
        RO.Counter.Index <=
        RO.Counter.Count;
        RO.Counter.Index ++)

     {if (RO.Counter.TypeCode == 1)                                                                      //Находим счетчик с кодом типа счетчика = 1
          AO.ShowMessage ("Балланс карты: " + ((Math.round (RO.Counter.Value*100))/100) + " руб.");      //Выводим сообщение о сумме счетчика

      if (RO.Counter.TypeCode == 2)                                                                      //Находим счетчик с кодом типа счетчика = 2
      if (RO.Counter.Value != 0)                                                                         //Если он не нулевой и не null
        {KarPr = String (RO.Counter.Value);                                                              //Переводим его значение в строку
          do
          {KarPr = 0 + KarPr}                                                                            //Добиваем нулями слева
          while (KarPr.length < 6);                                                                      //До длины в 6 знаков
                                                                                                         ///////////////////////////////////////////////
         var WShell = new ActiveXObject("WScript.Shell");                                                //Программно нажимаем F8, вводим префикс карты,
             WShell.SendKeys ("^({F8})2997777" + KarPr + "~"); }}                                        //добитый нулями слева хвост карты из второго
                                                                                                         //счетчика и нажимаем ввод
}                                                                                                        //То есть программно вводим карту приведшего
                                                                                                         //Записанную на второй счетчик
function FuncAct(AO, RO)                                                                                 //////////////////////////////////////////////
{
}

function NoAction(AO, RO, POS)
{
}


297777 - префикс карты, который и не нужен и не влезал в счетчик. Отбрасывается при записи и пририсовывается при восстановлении.

При закрытии чека:


Код
function BeforeAct(AO, RO, E, O)
{
}



function AfterAct(AO, RO, E, O)
{
if (RO.Card.Count != 0)                                                              //Если количество введенных карт не = 0
{
   for (RO.Card.Index = 1;                                                           //Перебираем карты в чеке
        RO.Card.Index <=
        RO.Card.Count;
        RO.Card.Index ++)

  {
      if (RO.Card.Index == 1)                                                       //Находим первую введенную карту
   {
         for (RO.Card.Counter.Index = 1;                                            //Перебираем счетчики на первой карте
              RO.Card.Counter.Index <=
              RO.Card.Counter.Count;
              RO.Card.Counter.Index ++)


          {if (RO.Card.Counter.TypeCode == 1)                                       //Находим 1-й счетчик 1-й карты
           {if ((RO.ReceiptTypeCode == 1) || (RO.ReceiptTypeCode == 4))             //Если это чек продажи (код типа = 1) или возврата (код типа = 4)
                {RO.Card.Counter.AddValue ((RO.TotalDiscSumm)*(-1));                //Списываем с 1-го счетчика 1-й карты сумму скидки на чек
                 RO.Card.Counter.AddValue(Math.round(RO.SummWD * 10)/100);}         //Записываем в 1-й счетчик 1-й карты округленные 10% суммы чека со скидками

            if (RO.ReceiptTypeCode == 2)                                            //Если это чек возврата (код типа = 2)
               {RO.Card.Counter.AddValue(RO.TotalDiscSumm);                         //Записываем на 1-й счетчик 1-й карты сумму скидки на чек
                RO.Card.Counter.AddValue(Math.round(RO.SummWD * (-10))/100);}}      //Списываем с 1-го счетчика 1-й карты округленные 10% суммы чека со скидками


           if (RO.Card.Counter.TypeCode == 2)                                        //Находим счетчик с кодом типа счетчика = 2
              {if (RO.Card.Counter.Value == 0)                                       //Если его значение = 0, то есть карта карты приводящего нет
               {Karta = Number (RO.UserValues.Get ("Karta"));                        //Вытаскиваем пользовательскую переменную, объявленную в воде карты и переводим ее в число
                if (Karta != 0)                                                      //Если мы вытащини не 0
                RO.Card.Counter.AddValueByTypeCode (2, Karta);}}}                    //Записываем в счетчик с кодом типа счетчика = 2 хвост номера карты приведшего
   }

      if (RO.Card.Index == 2)                                                        //Находим вторую введенную карту
   {
         for (RO.Card.Counter.Index = 1;                                             //Перебираем счетчики на первой карте
              RO.Card.Counter.Index <=
              RO.Card.Counter.Count;
              RO.Card.Counter.Index ++)

            {if (RO.Card.Counter.TypeCode == 1)                                      //Если это счетчик №1
               {if ((RO.ReceiptTypeCode == 1) || (RO.ReceiptTypeCode == 4))          //Если это чек продажи (код типа = 1) или возврата (код типа = 4)
                     RO.Card.Counter.AddValue(Math.round(RO.SummWD * 10)/100);       //Записываем в 1-й счетчик 2-й карты округленные 10% суммы чека со скидками
                if (RO.ReceiptTypeCode == 2)                                         //Если это чек возврата (код типа = 2)
                    RO.Card.Counter.AddValue(Math.round(RO.SummWD * (-10))/100);}}   //Списываем с 1-го счетчика 2-й карты округленные 10% суммы чека со скидками
   }
  }
}
}

function FuncAct(AO, RO)
{
}

function NoAction(AO, RO, POS)
{
}


Скрипты можно соптимизировать, но сил и желания нет.
Может кому пригодится.

Автор: Владимир Д. 2.5.2009, 16:27

Интересная идея! +1 goodpost.gif

Автор: Лаврухин Алексей 3.12.2009, 16:33

Пример решенной задачи с помощью скриптов: http://blog.atol.ru/2009/12/02/diskont-na-javascript-primer/

Автор: Andrey_malin 7.4.2010, 3:15

Думаю будет полезно привести классический пример работы со счётчиками.
Задача: Есть карты для которых из товароучётной программы выгружается максимальная сумма покупки (что-то вроде материальной помощи или
покупки под зарплату).
Вот пример скрипта реализующего эту задачу.

Код
function BeforeAct(AO, RO, E, O)
{
for (RO.Counter.Index = 1;
      RO.Counter.Index <= RO.Counter.Count;
      RO.Counter.Index++)
      {
       switch (RO.Counter.TypeCode){                 //Здесь определяем коды используемых счётчико
              case 4: MaxLimit = RO.Counter.Value; //Счётчик содержащий максимальную сумму покупки.
                      break
              case 6: SumNakop = RO.Counter.Value; //Счётчик содержащий израсходованную сумму.
                      break         }
       }

MaxSkidka = MaxLimit - SumNakop;                 //Определяем масимальную сумму скидки на чек.

for (RO.Counter.Index = 1;                              //Расчёт значения предоставляемой скидки.
      RO.Counter.Index <= RO.Counter.Count;
      RO.Counter.Index++)
      {
       if (RO.Counter.TypeCode == 7)
          {
           if (RO.SummWD <= MaxSkidka)
              {
               RO.Counter.AddValue(-RO.Counter.Value);
               RO.Counter.AddValue(RO.SummWD);
               MaxSkidka = RO.SummWD;
              }
           else
              {
               RO.Counter.AddValue(-RO.Counter.Value);
               RO.Counter.AddValue(MaxSkidka);
              }
          }
      }
for (RO.Counter.Index = 1;                            
      RO.Counter.Index <= RO.Counter.Count;
      RO.Counter.Index++)
      {
       if (RO.Counter.TypeCode == 6)
          RO.Counter.AddValue(MaxSkidka);   //Увеличиваем значение счётчика расходования средств на сумму покупки.
      }
}


Для использования скрипта потребуется создать три счётчика, ставку скидки и условие.

Счётчики:
1: Сумма лимита из товароучётной программы (Автосоздание по карте);
2: Сумма израсходованных средств (Автосоздание карта);
3: Сумма скидки (Автосоздания нет).

Ставка скидки:
1: Тип -$, значение счётчик 3, автоматическая.

Условие:
1: Карта введена, ставка 1.

В объекте скидки указываем только Условие. Момент действия скрипта Ввод платежа.
Так же потребуется скрипт с моментом действия Ввод карты.
Код
function BeforeAct(AO, RO, E, O)
{
if (RO.Card.Count >0)                                  
   AO.ShowError("Карта уже введена!!!");

for (RO.Counter.Index =1;                                 //Проверяем наличие средств для оплаты по карте.
           RO.Counter.Index <= RO.Counter.Count;
           RO.Counter.Index++)
           {switch (RO.Counter.TypeCode) {
                   case 4:{Limit = RO.Counter.Value;
                           break}
                   case 6:{Rashod = RO.Counter.Value;
                           break}
                           }
           }
      if (Limit == Rashod)
         AO.ShowError("Лимит исчерпан!");

}

Автор: awerta 2.12.2010, 17:43

Может кому понадобиться!
Скидка на позицию, равная 20% от суммы чека или равная 100 руб., если 20 % от чека больше чем 100 руб.
С условием, что в чеке присутствует товар с минимальной ценой равной розничной, и стоимость этого товара исключается из общей суммы чека.
Плюс присутствует проверка на то, что такая скидка уже была сделана.


Код
function qwert(AO, RO, POS)
{
Summa = 0;
for (RO.Pos.Index = 1;
           RO.Pos.Index <=
           RO.Pos.Count;
           RO.Pos.Index ++)
{
if (RO.Pos.Storno == 1)
   {
   continue;
   }
else
   {
            if (RO.Pos.WareMinPrice == RO.Pos.Price)
               {

               }
            else
               {
               Summa = Summa + RO.Pos.SummForD;
               }
    }
}
for (RO.Counter.Index = 1; RO.Counter.Index <= RO.Counter.Count; RO.Counter.Index++)
    {
    if (RO.Counter.TypeCode == 3)
       {
       if (RO.Counter.Value == 0)
            {
            RO.Counter.AddValueByCode(3,1,1);
            Summa = Summa*0.2;
            if (Summa > 100)
            {
            Summa = 100;
            }
            return Summa;
            
            }
       }
    }
}

Автор: АТОЛ: Царюков Роман 2.12.2010, 17:46

желательно делать комментарии в коде....
для чего, например нужен счетчик, к чему этот счетчик привязан и т.д....

Автор: POUL 3.12.2010, 0:18

Вот этот момент совсем не понял:

Цитата
if (RO.Pos.WareMinPrice == RO.Pos.Price)
{

}


Можете объяснить?

Автор: 80Serg 16.9.2011, 1:41

Здравствуйте, товарищи. На нашем предприятии возникла необходимость в такой скидке: скидка действует на определенное количество товара, например от 1 до 4 штук. Т.е если покупатель приобретает от 1-ой до 4-х штук товара, то возникает скидка. Однако, если покупатель берет 5 штук и более, скидка на первые 4 позиции не должна быть отменена. Т.е. скидка должна остаться для 4-х штук товара, но на пятую штуку и далее уже не распространяться.
Формулами и другими способами сделать не получается, поэтому предлагаю такую схему:

1. Создается Сценарий, который в зависимости от некоторого параметра будет выдавать разную скидку.
2. Создается Ставка скидки, где в качестве ставки указывается ссылка на сценарий. Ставка идет на позицию, автоматическая.
3. Создается Условие в одноименной справочнике, где прописывается Ставка и Сценарий, а самих условий нет.
4. Создается Классификатор, который косвенно будет задавать параметры скидки.
5. К классификатору привязываются товары.

Имя классификатора содержит параметры скидки и строится по следующему шаблону: НачКол_КонКол_ПроцентСкидки.
Например, классификатор с именем 1_4_10, означает, что скидка действует на товар с количеством от 1 до 4 и составляет 10% на товар.

Сценарий возникает при расчете автоматической скидки, т.е. момент действия сценария - Нет, а сам алгоритм создается в свободной процедуре NoAction, переименованной по своему усмотрению (у меня - DiscountVariable).
Итак при регистрации товара возникает сценарий автоматической скидки. В сценарии проверяется, есть ли у данного товара классификатор, если есть то это значит, что есть причина начислить скидку и параметры скидки нужно взять из наименования классификатора. Разбирается строка с именем классификатора и заполняются переменные - параметры скидки ( numBegin, numEnd, discount ). Далее по формуле считается скидка на данный товар (т.к. сценарий вызывается для каждой позиции в чеке. Кстати стоит объединение позиций). Формула такая:
ПроцентСкидкиНаТовар = ( ( ЦенаБезСкидки*ОбщееКолво – (КолвоСоСкидкой*ЦенаСоСкидкой + КолвоБезСкидки*ЦенаБезСкидки) ) / ЦенаБезСкидки*ОбщееКолво) * 100. Рассчитанная скидка возвращается в переменной TotalDisc.

Хочется еще сказать, что классификаторы загружаются из 1С. Я создал там справочник схемы скидок, каждый элемент которого соответствует классификатору. При этом во Фронтоле неизменными остаются элементы справочников Ставки, Условия, Сценарии, а классификаторы и Объекты скидок, каждый раз (при загрузке товаров) удаляются и создаются снова.

код файла goods.spr привожу ниже сценария.

Сценарий :

CODE
function BeforeAct(AO, RO, E, O)
{
}

function AfterAct(AO, RO, E, O)
{
}

function FuncAct(AO, RO)
{
}

function DiscountVariable(AO, RO, POS)

{
numBegin = 0;
numEnd = 0;
discount = 0;

param = "";
paramInt = 0;

CurrentClassifier = 0;
numFind = 0;

for (POS.Classifier.Index = 1;
POS.Classifier.Index <= POS.Classifier.Count;
POS.Classifier.Index++)
{
CurrentClassifier = POS.Classifier;
break;
}

if (CurrentClassifier != 0)
{

Name = CurrentClassifier.Name;
i = 1;
do
{
numFind = Name.search("_");
if (numFind != -1)
{
param = Name.substr(0, numFind);
Name = Name.substr(numFind + 1);
}
else
param = Name;

paramInt = parseInt(param);
if (i == 1)
numBegin = paramInt;
if (i == 2)
numEnd = paramInt;
if (i == 3)
discount = paramInt;

i++;
}
while(numFind != -1);
}

TotalDisc = 0;

WareQuantity = POS.Quantity;
WarePriceWODisc = POS.Price;
WarePriceWithDisc = WarePriceWODisc - (WarePriceWODisc * discount / 100);

WareQuantityWODisc = WareQuantity;
WareQuantityWithDisc = 0;
if (WareQuantity <= numEnd)
{
WareQuantityWODisc = 0;
WareQuantityWithDisc = WareQuantity;
}
else
{
WareQuantityWODisc = WareQuantity - numEnd;
WareQuantityWithDisc = numEnd;
}

TotalDisc =( (WarePriceWODisc * WareQuantity - (WareQuantityWithDisc * WarePriceWithDisc + WareQuantityWODisc * WarePriceWODisc ) ) / (WarePriceWODisc * WareQuantity) ) * 100;
return TotalDisc;
}



goods.spr:


CODE
##@@&&
#
$$$ADDQUANTITY
00098354;5706773223251;Ящик HOME BOX 32 л голубой РТ 2232-ГП;Ящик HOME BOX 32 л голубой РТ 2232-ГП;614,90;0;0;0,1,1,0,0,0,0,1,1,,1;;;;;;1;;;1;0;;;;;;;;;;;;;;;
00095462;4607126319229;Ящик HOUSE 60л штабелируемый оранжевый РТ9954/М-ОР;Ящик HOUSE 60л штабелируемый оранжевый РТ9954/М-ОР;288,60;0;0;0,1,1,0,0,0,0,1,1,,1;;;;;;1;;;1;0;;;;;;;;;;;;;;;
00095460;4607126319014;Ящик JUNIOR на роликах 30л красный РТ9034-КР;Ящик JUNIOR на роликах 30л красный РТ9034-КР;410,80;0;0;0,1,1,0,0,0,0,1,1,,1;;;;;;1;;;1;0;;;;;;;;;;;;;;;
00095461;4607126319045;Ящик JUNIOR на роликах 60л оранжевый РТ9046-ОРАНЖ;Ящик JUNIOR на роликах 60л оранжевый РТ9046-ОРАНЖ;494,00;0;0;0,1,1,0,0,0,0,1,1,,1;;;;;;1;;;1;0;;;;;;;;;;;;;;;
00078280;2200100584488;Ящик Multibox XS с крышкой многофункциональная флауер пауер;Ящик Multibox XS с крышкой многофункциональная флауер пауер;102,70;0;0;0,1,1,0,0,0,0,1,1,,1;;;;;;1;;;1;0;;;;;;;;;;;;;;;
00108670;4607016810720;Ящик д/игрушек 600*400*360 на колесах 4312068;Ящик д/игрушек 600*400*360 на колесах 4312068;800,80;0;0;0,1,1,0,0,0,0,1,1,,1;;;;;;1;;;1;0;;;;;;;;;;;;;;;
00108669;4607016814261;Ящик д/игрушек на кол.600*400*360 с апплик. 4313068;Ящик д/игрушек на кол.600*400*360 с апплик. 4313068;738,20;0;0;0,1,1,0,0,0,0,1,1,,1;;;;;;1;;;1;0;;;;;;;;;;;;;;;
00107499;8697444382032;Ящик д/инст.клас. 16 MANO C.O-16;Ящик д/инст.клас. 16 MANO C.O-16;480,70;0;0;0,1,1,0,0,0,0,1,1,,1;;;;;;1;;;1;0;;;;;;;;;;;;;;;
00107514;8697444384340;Ящик д/инст.клас. 3-х эт 20 MANO BL.O-20;Ящик д/инст.клас. 3-х эт 20 MANO BL.O-20;1358,50;0;0;0,1,1,0,0,0,0,1,1,,1;;;;;;1;;;1;0;;;;;;;;;;;;;;;
00108671;4607016815367;Ящик д/инструментов 510*260*270 4312560;Ящик д/инструментов 510*260*270 4312560;517,00;0;0;0,1,1,0,0,0,0,1,1,,1;;;;;;1;;;1;0;;;;;;;;;;;;;;;
00107101;4607016812502;Ящик складной с перфориров. стенками 32л. 4312282;Ящик складной с перфориров. стенками 32л. 4312282;318,10;0;0;0,1,1,0,0,0,0,1,1,,1;;;;;;1;;;1;0;;;;;;;;;;;;;;;
00107079;4607016812090;Ящик Хобби 27*19*4см 4312285;Ящик Хобби 27*19*4см 4312285;112,80;0;0;0,1,1,0,0,0,0,1,1,,1;;;;;;1;;;1;0;;;;;;;;;;;;;;;
$$$DELETEALLCLASSIFIERLINKS
$$$DELETEALLDISCOBJECTS
$$$DELETEALLCLASSIFIERS
$$$ADDCLASSIFIERS
8;;0;2_8_6 ;;
$$$ADDDISCOBJECTS
8;;;;;;;;8;;;;;1;;;
$$$ADDCLASSIFIERS
5;;0;1_7_3 ;;
$$$ADDDISCOBJECTS
5;;;;;;;;5;;;;;1;;;
$$$ADDCLASSIFIERLINKS
5;1;00080608;
5;1;00103202;
5;1;00096178;
5;1;00085378;
5;1;00104275;
5;1;00086399;
5;1;00030047;
5;1;00070107;
5;1;00070108;
5;1;00049050;
5;1;00085377;
5;1;00086805;
5;1;00104087;
5;1;00096796;
5;1;00094931;
5;1;00096180;
5;1;00095222;
5;1;00031502;
5;1;00085712;
5;1;00080584;
5;1;00085376;
5;1;00101488;
5;1;00031504;

Автор: POUL 8.12.2011, 23:15

Для начинающих ссылка на адекватный справочник по JS:

http://wdh.suncloud.ru/js01.htm

Автор: Peter1234 21.5.2012, 16:05

Мой первый скрипт. Надеюсь, кому-то пригодится. Плюс будет интересно услышать комментарии от гуру.

Схема работы следующая.

1. Выпускаются карты сертификатов с какой-то нумерацией, идущей подряд.
2. Сертификатам присваивается какой-то номинал (в примере скрипта два номинала – 5000 руб. и 6000 руб.).
3. Карты продаются/выдаются клиентам без регистрации через фронтол. В моем случае просто стопку карт продали организации по безналу, а она раздала своим сотрудникам.
4. Клиенты приходят отоваривать сертификаты. Они могут набрать товара на любую сумму: меньше номинала сертификата, больше или равно. Клиенты могут одновременно отоварить два и более сертификата разных номиналов.
5. При покупке на сумму меньше номинала сертификата чек будет закрыт на сумму покупки с видом оплаты «Сертификат». При покупке на сумму, равную сертификату, чек будет закрыт по номиналу сертификата. При покупке на сумму больше номинала сертификата, будет зарегистрирована оплата на сумму сертификата, а остальное надо будет доплатить наличными или по безналу.
6. Сертификат у покупателя изымается.
7. Нигде не контролируется, что сертификат уже был отоварен. Поэтому изымать сертификат надо обязательно.

Последовательность действий:

1. Создаем сценарий с моментом действия «Ввод платежа».

Код
function inArray(data, array) {
    for (var i = 0; i < array.length; i++) {
        if (data == array[i]) return true;
    }
    return false;
}

function BeforeAct(AO, RO, E, O) {
    var certificateSumm = 0;
    var groupCode = [1, 2, 3]; // Коды групп карт, которые являются сертификатами
    var certificateValue = [5000, 5000, 6000]; // Номиналы сертификатов. К массиву обращаться по [код группы - 1]. В соответствующей ячейке будет сумма.

    // Проверим, что вводится оплата по сертификату, в нашем случае это вид оплаты с кодом 2
    if (O.Code == 2) {
        // Вычислим сумму номиналов всех сертификатов
        for (RO.Card.Index = 1; RO.Card.Index <= RO.Card.Count; RO.Card.Index++) {
            // Проверяем, является ли введенная карта сертификатом (по коду группы карт)
            if (inArray(RO.Card.GroupCode, groupCode)) {
                certificateSumm += certificateValue[RO.Card.GroupCode - 1];
            }
        }
        // Если сумма сертификатов равно 0, то отменяем оплату по сертификату
        if (certificateSumm == 0) {
            AO.ShowError("Сертификат не введен!");
        }
        // Если сумма чека больше или равна сумме сертификатов, то закрываем суммой сертификатов.
        // Если сумма чека меньше, то закрываем суммой чека.
        if (RO.SummWD >= certificateSumm) {
            RO.AddPayment(2, certificateSumm);
        } else {
            RO.AddPayment(2, RO.SummWD)
        }
        AO.Cancel()
    }
}

function AfterAct(AO, RO, E, O)
{
}

function FuncAct(AO, RO)
{
}

function NoAction(AO, RO, POS)
{
}


2. Создаем объект скидок, выбираем в «Оплата» «Сертификатом» в действии «Сценарий» - наш созданный сценарий.
3. Создаем группу карт с параметром «Авторегистрация карт», ввод «Ридером», указываем минимальный и максимальный префикс.
4. Код созданной группы будет проверяться в сценарии. По нему мы будем определять, что карта является сертификатом. В сценарии есть массив groupCode, в который надо внести все коды групп карт данных сертификатов.
5. «В Системных справочниках» в «Видах оплаты» создаем вид оплаты «Сертификатом», «Тип оплаты в ККМ» - 1, «Операция» - Нет. Сдача не выдается, денежный ящик не меняется. Код вида оплаты проверяется в сценарии тут: if (O.Code == 2). В данном случае код вида оплаты «Сертификатом» - 2.

PS. Спасибо Павлу Сусину за примеры.

Автор: POUL 1.11.2012, 20:14

Глубокоуважаемые коллеги!!!
Убедительная просьба обратить ваше драгоценное внимание на название темы.
Выкладывайте, пожалуйста, сюда примеры работы с внутренним программированием во Фронтоле.
Для решения ваших личных в этой области проблем, остается почти весь остальной форум!
Спасибо за понимание.

Автор: Владимир Шалаев 14.11.2012, 10:41

Существует группа карт "Накопительные карты", имеющая свой код Х.
При любой покупке, на карту добавляется сумма покупки. При достижении определенной суммы, выдается определенная скидка.
Задача: написать скрипт, который при возврате товара, вычитает цену товара из накоплений карты.
момент действия: Закрытие документа.
Функция может быть как BeforeAct, так и AfterAct.

/*Скрипт при каждом закрытии документа определяет его тип.
Если тип документа "ВОЗВРАТ", то скрипт проверяет наличие
накопительных карт в документе. Если накопительная карта найдена,
то из ее суммы накоплений вычитается сумма возврата (с учетом скидки).*/

Код
function BeforeAct(AO, RO, E, O)
{

   if(RO.ReceiptType == "ВОЗВРАТ")
   {//Если название операции "ВОЗВРАТ", то обрабатываем этот случай
        for(RO.Card.Index = 1; RO.Card.Index <= RO.Card.Count; RO.Card.Index++)
        {//Запускаем цикл по всем картам, имеющимся в документе
              if((RO.Card.GroupCode==X)) //где X - код группы накопительных карт
              {//Находим счетчик накопительных карт. Код группы таких счетчиков - X
                   for(RO.Card.Counter.Index = 1; RO.Card.Counter.Index <= RO.Card.Counter.Count; RO.Card.Counter.Index++)
                   { //Запускаем цикл по всем счетчикам карты
                         if(RO.Card.Counter.TypeName == "Накопления по картам")
                         {
                         RO.Card.Counter.AddValue(-RO.SummWD);
                         } //Окончание условия обработки счетчика "Накопления по картам"

                   } //Окончание цикла перебора всех счетчиков карты

              } //Окончание условия проверки кода группы карт (нам нужен код группы 509)

        } //Окончание цикла перебора всех карт данного документа

   } //Окончание условия обработки типа документа "ВОЗВРАТ"

} //Конец функции

function AfterAct(AO, RO, E, O)
{}
function FuncAct(AO, RO)
{}
function NoAction(AO, RO, POS)
{}

Автор: POUL 14.11.2012, 13:35

Такие вещи надо писать в AfterAct
Потому что если это будет в таком виде, возможна следующая ситуация:
Проигрался BeforAct, пошла печать чека, ФР вернул ошибку, кассир жмет закрыть чек еще раз, опять проигрывается BeforAct и так далее со всеми вытекающими последствиями.

Автор: Владимир Шалаев 15.11.2012, 10:25

Цитата(POUL @ 14.11.2012, 14:35) *
Такие вещи надо писать в AfterAct
Потому что если это будет в таком виде, возможна следующая ситуация:
Проигрался BeforAct, пошла печать чека, ФР вернул ошибку, кассир жмет закрыть чек еще раз, опять проигрывается BeforAct и так далее со всеми вытекающими последствиями.

Спасибо! Это ценное замечание!!

Автор: POUL 15.11.2012, 10:38

Ваш Кэп.

Автор: Лазарев Максим Викторович 25.1.2013, 13:18

Скрипт, блокирующий продажу алкоголя с 22.00 до 10.00 часов

Данный скрипт вытаскивает первые n символов из названия товара в позиции и сравнивает с заданными значениями в скрипте.
Скрипт писался специально, т.к. товарный справочник на кассе без классификатора (всё в куче, особенность товароучетки)
Вы сами можете "допилить" данный скрипт под свои наименования товаров.

Код
unction BeforeAct(AO, RO, E, O, CO, POS)
{
var now = new Date();
var hour = now.getHours();
var s = O.Ware.Name;
var vin = s.substr(0, 3); //Вино
var vod = s.substr(0, 5); //Водка
var kon = s.substr(0, 4); //Коньяк
var piv = s.substr(0, 3); //Пиво
var nas = s.substr(0, 4); //Настойка
var bal = s.substr(0, 4); //Бальзам
var vis = s.substr(0, 3); //Виски
var lik = s.substr(0, 3); //Ликер
var rom = s.substr(0, 3); //Ром
var tek = s.substr(0, 3); //Текила
var brd = s.substr(0, 3); //Бренди
var kok = s.substr(0, 4); //Коктейль
var djn = s.substr(0, 4); //ДжинТоник
var shp = s.substr(0, 5); //Шампанское
var por = s.substr(0, 3); //Портвейн
var ver = s.substr(0, 5); //Вермут
var bo = s.substr(0, 15); //Балтика 0 (продавать можно)
var so = s.substr(0, 28); //Сибирская корона 0 (продавать можно)
var jag1 = s.substr(0, 5); //Ягуар
var jag2 = s.substr(0, 6); //Ягуар


if ((vin == "ВИН" || vod == "ВОДКА" || kon == "КОН " || piv == "ПИВ" || nas == "НАСТ" ||
  bal == "БАЛЬ" || vis == "ВИС" || lik == "ЛИК" || rom  == "РОМ" || tek == "ТЕК" || brd == "БРЕ" ||
  kok == "КОКТ" || djn == "ДЖИН" || shp == "ШАМПА" || por == "ПОР" || ver == "ВЕРМУ" || jag1 == "ЭНЕР " || jag2 == "ЭНЕРГ ") &&
  (bo != "ПИВО БАЛТИКА їО") && (so != "ПИВО Сибирская Корона БЕЗАЛК") && (hour >= 22 || hour < 10))
   AO.ShowError("Нельзя продавать алкоголь с 22.00 до 10.00!!!");
}

function AfterAct(AO, RO, E, O, CO)
{
}

function FuncAct(AO, RO, CO)
{
}

function NoAction(AO, RO, POS, CO, UserParam)
{
}

Автор: mozer 7.2.2013, 4:20

Скрипт организует скидку 20% на каждую 2-ю позицию из классификатора. Скидка применяется к позициям по возрастанию цены, то есть сначала идет скидка на дешёвый товар.
Необходимые условия:
1. Номенклатура дожна принадлежать только 1 классификатору
2. Создать условие по классификатору, в качестве ставки указать ставку которая вычисляет скидку по данному скрипту. Ставка СУММОВАЯ, НЕ ПРОЦЕНТНАЯ
Условий в пункте 2 может быть сколько угодно, тогда скрипт вычислит скидку по каждому классификатору отдельно.
3. Скрипт с моментом действия НЕТ.

Код
function BeforeAct(AO, RO, E, O)
{
}

function AfterAct(AO, RO, E, O)
{
}

function FuncAct(AO, RO)
{
}

//Функция сравнения для сортировки массива позиций
//Умышлено сортируем по возрастанию цены позиции
function Compare(A,B)
{
    if (A.Price < B.Price)
    {
        return -1;
    }
    if (A.Price > B.Price)
    {
        return 1;
    }
    return 0;
}

//Объект позиция. Содержит свойства:
//Идентификатор товара
//Цена товара без скидок
function MyPosition(ID,Price)
{
    this.ID = ID;
    this.Price = Price;
}
//Функция собирает массив позиций
//принадлежащих к одному класификатору.
//Сортирует массив в порядке возрастания цены.
//позиция попадает в массив столько раз, сколько товара взял покупатель
function collectArray(RO,POS)
{
    arrayPosition = new Array;
    //Соберем массив строк принадлежащих одному класификатору
    POS.Classifier.Index = 1;
    for(RO.Pos.Index = 1; RO.Pos.Index <= RO.Pos.Count; RO.Pos.Index++)
    {
        //Если позиция сторнирована или не принадлежит классификаторам, пропустим ее.
        if ((RO.Pos.Storno == 1)||RO.Pos.Classifier.Count == 0)
        {
            continue;
        }
        //Установливаем на первый классификатор
        RO.Pos.Classifier.Index = 1;
        if (RO.Pos.Classifier.Code == POS.Classifier.Code)
        {

            for (var IndexCount = 1; IndexCount <= RO.Pos.Quantity; IndexCount++)
            {
                MyPos = new MyPosition(RO.Pos.PosID,RO.Pos.Price);
                arrayPosition.push(MyPos);
            }
        }
    }
    //Сортируем массив в порядке возрастания цены
    arrayPosition.sort(Compare);
    //обрабатываем массив,для определения скидок по позициям.
    //Найдем количество позиций со скидкой. Каждый второй товар со скидкой.
    CountDiscounWare = arrayPosition.length / 2;
    arrayDiscounPosition = new Array;
    for(var IndexDiscount = 1; IndexDiscount <= CountDiscounWare; IndexDiscount++)
    {
        arrayDiscounPosition.push(arrayPosition.shift());
    }
    return arrayDiscounPosition;

}

//Основная функция ее подставляем в ставку
function MyAction(AO, RO, POS)
{
    var Discount = 0;
    //Если позиция не относится ни к одному класссификатору, тогда и скидки на нее нет.
    if (POS.Classifier.Count == 0)
    {
        return Discount;
    }
    //Сформируем массив скидок
    var arrayDiscounPosition = collectArray(RO,POS);
    //Обрабатываем полученый массив. В массиве может быть не одна текущая позиция, если количество больше 1
    for (var index = 0; index < arrayDiscounPosition.length; index++)
    {
        if ((arrayDiscounPosition[index].ID == POS.PosID))
        {
            Discount = Discount + arrayDiscounPosition[index].Price*0.2;
        }
    }
    return Discount;
}

Автор: Вованыч 11.12.2013, 19:02

Цитата(mozer @ 7.2.2013, 5:20) *
Скрипт организует скидку 20% на каждую 2-ю позицию из классификатора. Скидка применяется к позициям по возрастанию цены, то есть сначала идет скидка на дешёвый товар.
Необходимые условия:
1. Номенклатура дожна принадлежать только 1 классификатору
2. Создать условие по классификатору, в качестве ставки указать ставку которая вычисляет скидку по данному скрипту. Ставка СУММОВАЯ, НЕ ПРОЦЕНТНАЯ
Условий в пункте 2 может быть сколько угодно, тогда скрипт вычислит скидку по каждому классификатору отдельно.
3. Скрипт с моментом действия НЕТ.
Код
function BeforeAct(AO, RO, E, O)
{
}

function AfterAct(AO, RO, E, O)
{
}

function FuncAct(AO, RO)
{
}

//Функция сравнения для сортировки массива позиций
//Умышлено сортируем по возрастанию цены позиции
function Compare(A,B)
{
    if (A.Price < B.Price)
    {
        return -1;
    }
    if (A.Price > B.Price)
    {
        return 1;
    }
    return 0;
}

//Объект позиция. Содержит свойства:
//Идентификатор товара
//Цена товара без скидок
function MyPosition(ID,Price)
{
    this.ID = ID;
    this.Price = Price;
}
//Функция собирает массив позиций
//принадлежащих к одному класификатору.
//Сортирует массив в порядке возрастания цены.
//позиция попадает в массив столько раз, сколько товара взял покупатель
function collectArray(RO,POS)
{
    arrayPosition = new Array;
    //Соберем массив строк принадлежащих одному класификатору
    POS.Classifier.Index = 1;
    for(RO.Pos.Index = 1; RO.Pos.Index <= RO.Pos.Count; RO.Pos.Index++)
    {
        //Если позиция сторнирована или не принадлежит классификаторам, пропустим ее.
        if ((RO.Pos.Storno == 1)||RO.Pos.Classifier.Count == 0)
        {
            continue;
        }
        //Установливаем на первый классификатор
        RO.Pos.Classifier.Index = 1;
        if (RO.Pos.Classifier.Code == POS.Classifier.Code)
        {

            for (var IndexCount = 1; IndexCount <= RO.Pos.Quantity; IndexCount++)
            {
                MyPos = new MyPosition(RO.Pos.PosID,RO.Pos.Price);
                arrayPosition.push(MyPos);
            }
        }
    }
    //Сортируем массив в порядке возрастания цены
    arrayPosition.sort(Compare);
    //обрабатываем массив,для определения скидок по позициям.
    //Найдем количество позиций со скидкой. Каждый второй товар со скидкой.
    CountDiscounWare = arrayPosition.length / 2;
    arrayDiscounPosition = new Array;
    for(var IndexDiscount = 1; IndexDiscount <= CountDiscounWare; IndexDiscount++)
    {
        arrayDiscounPosition.push(arrayPosition.shift());
    }
    return arrayDiscounPosition;

}

//Основная функция ее подставляем в ставку
function MyAction(AO, RO, POS)
{
    var Discount = 0;
    //Если позиция не относится ни к одному класссификатору, тогда и скидки на нее нет.
    if (POS.Classifier.Count == 0)
    {
        return Discount;
    }
    //Сформируем массив скидок
    var arrayDiscounPosition = collectArray(RO,POS);
    //Обрабатываем полученый массив. В массиве может быть не одна текущая позиция, если количество больше 1
    for (var index = 0; index < arrayDiscounPosition.length; index++)
    {
        if ((arrayDiscounPosition[index].ID == POS.PosID))
        {
            Discount = Discount + arrayDiscounPosition[index].Price*0.2;
        }
    }
    return Discount;
}

Очень нужный скриптик.., но почему то у меня фронтол вылетает если в чеке более 7 товаров забить принадлежащих одному классификатору.

Автор: АТОЛ: Средних Владимир 12.12.2013, 12:02

Цитата(Вованыч @ 11.12.2013, 20:02) *
Очень нужный скриптик.., но почему то у меня фронтол вылетает если в чеке более 7 товаров забить принадлежащих одному классификатору.

Версия фронтола, которая "вылетает"?

Автор: Вованыч 12.12.2013, 12:40

Цитата(АТОЛ: Средних Владимир @ 12.12.2013, 12:02) *
Версия фронтола, которая "вылетает"?

Начинал на 4.9.13
обновился до 4.9.14 ситуация та же.
Ошибку описывал в отдельной теме. Ошибка чтения памяти.
Патч ставил с форума, ситуация та же.

Автор: ДмитрийН 23.1.2014, 12:23

Подскажите пожалуйста, а как написать скрипт чтобы в определенное время выскакивало сообщение?

Автор: АТОЛ: Царюков Роман 23.1.2014, 12:25

Цитата(ДмитрийН @ 23.1.2014, 13:23) *
Подскажите пожалуйста, а как написать скрипт чтобы в определенное время выскакивало сообщение?

http://forum.atol.ru/index.php?showtopic=6492&view=findpost&p=163789

Автор: POUL 21.3.2014, 20:46

Во Фронтоле начиная с релиза, по-моему 4.9.7 (могу ошибаться) доступно чтение данных из базы путем SQL запросов без использования внешних провайдеров.
Пример использования:

Код
    QueryCode = "";
    QueryName = "";
    Query = CO.NewDBQuery;
    Query.SQL = "SELECT CODE, NAME FROM SPRT WHERE PARENTID IN (SELECT ID FROM SPRT WHERE ISWARE = 0 AND NAME = "Контрацепция") ORDER BY 1";
    Query.Open();
    if (Query.FieldByName("CODE") != null)
    {
     do
     {
      QueryCode = QueryCode + String (Query.FieldByName("CODE")) + "\n";
      QueryName = QueryName + String (Query.FieldByName("NAME")) + "\n";
      n = Query.RecordCount;
      Query.Next();
     }
     while (n != Query.RecordCount)
     Query.Close();
    }
    if (QueryCode == "" || QueryName == "")
    {
     Query.Close();
     AO.ShowError ("Результат запроса пуст!");
    }
    QueryCode = QueryCode.substring (0, QueryCode.length - 1);
    QueryName = QueryName.substring (0, QueryName.length - 1);

    SelectedSol = Number (AO.SelectString ("Выберите товар:", QueryName, QueryCode));


Повторюсь. Доступно только чтение данных.

Автор: Romeo 18.8.2014, 14:51

Цитата(Лазарев Максим Викторович @ 25.1.2013, 14:18) *
Скрипт, блокирующий продажу алкоголя с 22.00 до 10.00 часов

Данный скрипт вытаскивает первые n символов из названия товара в позиции и сравнивает с заданными значениями в скрипте.
Скрипт писался специально, т.к. товарный справочник на кассе без классификатора (всё в куче, особенность товароучетки)
Вы сами можете "допилить" данный скрипт под свои наименования товаров.

CODE
unction BeforeAct(AO, RO, E, O, CO, POS)
{
var now = new Date();
var hour = now.getHours();
var s = O.Ware.Name;
var vin = s.substr(0, 3); //Вино
var vod = s.substr(0, 5); //Водка
var kon = s.substr(0, 4); //Коньяк
var piv = s.substr(0, 3); //Пиво
var nas = s.substr(0, 4); //Настойка
var bal = s.substr(0, 4); //Бальзам
var vis = s.substr(0, 3); //Виски
var lik = s.substr(0, 3); //Ликер
var rom = s.substr(0, 3); //Ром
var tek = s.substr(0, 3); //Текила
var brd = s.substr(0, 3); //Бренди
var kok = s.substr(0, 4); //Коктейль
var djn = s.substr(0, 4); //ДжинТоник
var shp = s.substr(0, 5); //Шампанское
var por = s.substr(0, 3); //Портвейн
var ver = s.substr(0, 5); //Вермут
var bo = s.substr(0, 15); //Балтика 0 (продавать можно)
var so = s.substr(0, 28); //Сибирская корона 0 (продавать можно)
var jag1 = s.substr(0, 5); //Ягуар
var jag2 = s.substr(0, 6); //Ягуар


if ((vin == "ВИН" || vod == "ВОДКА" || kon == "КОН " || piv == "ПИВ" || nas == "НАСТ" ||
bal == "БАЛЬ" || vis == "ВИС" || lik == "ЛИК" || rom == "РОМ" || tek == "ТЕК" || brd == "БРЕ" ||
kok == "КОКТ" || djn == "ДЖИН" || shp == "ШАМПА" || por == "ПОР" || ver == "ВЕРМУ" || jag1 == "ЭНЕР " || jag2 == "ЭНЕРГ ") &&
(bo != "ПИВО БАЛТИКА їО") && (so != "ПИВО Сибирская Корона БЕЗАЛК") && (hour >= 22 || hour < 10))
AO.ShowError("Нельзя продавать алкоголь с 22.00 до 10.00!!!");
}

function AfterAct(AO, RO, E, O, CO)
{
}

function FuncAct(AO, RO, CO)
{
}

function NoAction(AO, RO, POS, CO, UserParam)
{
}


По наименованию ??? blink.gif
Странно может получиться, особенно в случаях:
vin == "ВИН" - Виноград
kon == "КОН " - Конфеты
vis == "ВИС" - Вискас
por == "ПОР" - Порошок стиральный
... и прочие rolleyes.gif rolleyes.gif rolleyes.gif

Автор: АТОЛ: Царюков Роман 18.8.2014, 14:56

а с чего Вы взяли, что у них есть такие товары?
скрипт писали под себя, поэтому я думаю автор скрипта уверен в своем справочнике товаров

Автор: АТОЛ: Царюков Роман 20.3.2015, 8:50

http://fs.atol.ru/DocLib/Frontol.%20Курс%20доллара.%20Пример%20работы%20с%20XML.aspx

Автор: АТОЛ: Царюков Роман 20.3.2015, 15:15

http://fs.atol.ru/DocLib/Frontol.%20Голосовой%20движок.aspx

Автор: АТОЛ: Царюков Роман 15.5.2015, 17:02

http://fs.atol.ru/DocLib/Frontol.%20Информеры%20Яндекс.%20Пример%20работы%20с%20XML.aspx
еще один пример с XML

Автор: Волгоград.Инфософт 11.5.2016, 13:15

Задача:
Перед печатью картинки на ФР проверить принадлежность к формату bmp, ширину и высоту картинки в пикселях а так же количество бит цвета на пиксель.

Решаемая проблема:
Фискальные регистраторы умеют печатать только монохромные (один бит цвета на пиксель) картинки в формате bmp. Размер картинки может быть слишком широк для чековой ленты и слишком длинен для приемлемого времени печати. Проверка параметров файла, позволит принять решение о печати.

Пример получаемой информации:



Пример вызова:

Код
function FuncAct(AO, RO, CO) {
    
    // конструктор объекта
    // параметр - полный путь к файлу картинки - строка
    // var bytes0 = new propertyBMPfile("d:\\picture\\0.bmp");
    
    // DepthColor = bytes0.nDepthColor; // получение свойства "глубина цвета" - число
    // Width = bytes0.nWidth;           // получение свойства "ширина растра" - число
    // Heigth = bytes0.nHeigth;         // получение свойства "высота растра" - число
    // Path = bytes0.fullPathFile;      // получение свойства "полный путь к картинке" - строка
    
    var bytes0 = new propertyBMPfile("d:\\picture\\0.bmp");
    AO.ShowMessage("глубина цвета " + bytes0.nDepthColor+"\n" +
        "ширина растра " + bytes0.nWidth+"\n" +
        "высота растра " + bytes0.nHeigth);
    
    var bytes1 = new propertyBMPfile("d:\\picture\\1.bmp");
    AO.ShowMessage("глубина цвета " + bytes1.nDepthColor+"\n" +
        "ширина растра " + bytes1.nWidth+"\n" +
        "высота растра " + bytes1.nHeigth);

    // !такой картинки в папке нет! Все свойства будут равны 0
    var bytes5 = new propertyBMPfile("d:\\picture\\5.bmp");
    AO.ShowMessage("глубина цвета " + bytes5.nDepthColor+"\n" +
        "ширина растра " + bytes5.nWidth+"\n" +
        "высота растра " + bytes5.nHeigth);
}


Файл класса:

 propertyBMPfile.txt ( 7.85 килобайт ) : 25


Автор: maxdale 19.7.2016, 12:25

есть справочные/обучающие материалы скриптам для Frontol 5 ?

Автор: АТОЛ: Царюков Роман 19.7.2016, 12:35

Цитата(maxdale @ 19.7.2016, 12:25) *
есть справочные/обучающие материалы скриптам для Frontol 5 ?

руководство интегратора

Автор: Волгоград.Инфософт 13.9.2016, 12:06

Функция, возвращающая путь к БД Frontol

Описание:
возвращает строку, которая содержит полный путь к базе данных
путь к базе читается из файла Frontol.ini
если не удалось получить путь, то возвращается пустая сторка


Пример вызова:

cPath = GetPathDBFrontol5(); //функция вернёт: "localhost:E:\BASE\POSBASE\FT_5_TRADE\"

 GetPathDBFrontol5.txt ( 1.51 килобайт ) : 47

Автор: АТОЛ: Царюков Роман 13.9.2016, 12:08

в каком каталоге вы ищите файл Frontol.ini ?
для каких версий фронтол 5 будет работать данный сценарий?

Автор: POUL 13.9.2016, 12:14

del

Автор: Волгоград.Инфософт 13.9.2016, 13:59

Цитата(АТОЛ: Царюков Роман @ 13.9.2016, 13:08) *
в каком каталоге вы ищите файл Frontol.ini ?
для каких версий фронтол 5 будет работать данный сценарий?


Да, я понял вопрос. Представленная версия ищет ini файл в папке bin.
Постараюсь в ближайшие дени выпустить исправленную версию функции.

Автор: Волгоград.Инфософт 13.9.2016, 14:10

Цитата(АТОЛ: Царюков Роман @ 13.9.2016, 13:08) *
для каких версий фронтол 5 будет работать данный сценарий?


Уважаемые разработчики, расширьте пожалуйста класс Frontol свойством currentVersion и currentVariant (вариант поставки Frontol), тогда в скриптах можно будет проверять контекст исполнения и не напороть ошибок из за отличий в версиях.

Автор: АТОЛ: Царюков Роман 13.9.2016, 15:06

Цитата(Волгоград.Инфософт @ 13.9.2016, 14:10) *
Уважаемые разработчики, расширьте пожалуйста класс Frontol свойством currentVersion и currentVariant (вариант поставки Frontol), тогда в скриптах можно будет проверять контекст исполнения и не напороть ошибок из за отличий в версиях.

пока разработчики будут расширять этот функционал - дергайте инфу напрямую из БД

Автор: Волгоград.Инфософт 15.9.2016, 12:16

Функция GetPathDBFrontol5(cPathVariant) - получение пути к БД FronTol5
(это доработанная версия функции из сообщения http://forum.atol.ru/index.php?s=&showtopic=6492&view=findpost&p=21255%38%29

функция GetPathDBFrontol5(cPathVariant) корректно работает для FronTol5 до и после версии 5.9.0

Описание:
Функция GetPathDBFrontol5(cPathVariant) - путь к базе данных Frontol5
возвращает строку, содержащую путь к БД Frontol или пустую строку, в случае ошибки
аргумент функции cPathVariant - строковой литерал со значением:
"OnlyPath" - по умолчанию - функция возвращает только путь к БД (E:\BASE\POSBASE\FT_5_TRADE\)
"HostAndPath" - функция возвращает хост и путь к БД (localhost:E:\BASE\POSBASE\FT_5_TRADE\)

Пример использования:
по умолчанию

Код
cPath = GetPathDBFrontol5(); // возвращает: E:\BASE\POSBASE\FT_5_TRADE\

с аргументом "OnlyPath"
Код
cPath = GetPathDBFrontol5("OnlyPath"); // возвращает: E:\BASE\POSBASE\FT_5_TRADE\

с аргументом "HostAndPath"
Код
cPath = GetPathDBFrontol5("HostAndPath"); // возвращает: localhost:E:\BASE\POSBASE\FT_5_TRADE\


 GetPathDBFrontol5.txt ( 2.48 килобайт ) : 57

Автор: Волгоград.Инфософт 20.9.2016, 11:55

коллеги, оформил полезные функции для Frontol 5 в виде репозитория на GitHub:
https://github.com/infosoft-v/frontol5lib

для каждой функции написана краткая инструкция:
https://github.com/infosoft-v/frontol5lib/wiki

планирую, по мере появления универсальных функций в проектах, пополнять библиотеку.

Коллеги, с удовольствием добавлю ваши полезные универсальные функции для Frontol 5 в библиотеку. Автора и ссылку на источник укажу обязательно.

Автор: POUL 20.9.2016, 12:44

если не сложно, выложите пожалуйста в виде программнго кода, а не в виде ссылок

Автор: Волгоград.Инфософт 20.9.2016, 14:26

коллеги, позволю себе опубликовать тут краткую инструкцию по использованию GitHub и в частности получение текста функций из библиотеки

  1. по ссылке https://github.com/infosoft-v/frontol5lib можно попасть в библиотеку функций, где можно получить текст нужной функции или ознакомиться с документацией по использованию функций
  2. щелкнув на название функции, например getEAN13.js, можно перейти к тексту этой функции. Текст можно скопировать и использовать в своих проектах
  3. щелкнув на слове Wiki можно перейти к описанию функций и примерам использования

Автор: zenik 20.9.2016, 15:56

Цитата(Волгоград.Инфософт @ 20.9.2016, 14:26) *
можно попасть в библиотеку функций, где можно получить текст нужной функции или ознакомиться с документацией по использованию функций

Годнота... Я тоже подумал, что будет "качать". А так супер, жаль что функция пока маловато smile.gif

Автор: Волгоград.Инфософт 21.9.2016, 16:07

добавлена функция формирования строки жирным шрифтом для ККМ

функция getBoldString(cString) - получить строку, которую ККМ выведет жирным (bold) шрифтом

параметры:
cString - строка. Строка текста, которую необходимо преобразовать в в строку, которую ККМ выведет жирным шрифтом

Пример использования: cBoldString = getBoldString("ПРОДАЖА"); // возвращается строка преобразованная в шрифт bold "\tП\tР\tО\tД\tА\tЖ\tА"

эта и другие полезные функции на GitHub https://github.com/infosoft-v/frontol5lib

Автор: Евдокимов Сергей 26.10.2016, 13:01

Сценарий для 4 Фронтола. Он делает запрограммированную скидку в момент, когда продавец выбрал вид оплаты "Банковская карта"
Так же для этого сценарий нужно задать горячую клавишу на f5, которая будет запускать FuncAct этого сценария.

Код
var paymentCode = 2; // Код вида оплаты
var keyMacros = "{F5}"; //Клавиша для запуска макроса

function BeforeAct(AO, RO, E, O, CO) {
    if (E == 6) {
        if (O.Code == paymentCode) {
            runMacros(AO, RO, O);
            AO.Cancel();
        }
    }
}

function AfterAct(AO, RO, E, O, CO) {}

//Сценарий для макроса
function FuncAct(AO, RO, CO) {
    receiptSum = 0;
    for (RO.Pos.Index = 1; RO.Pos.Index <= RO.Pos.Count; RO.Pos.Index++) {
        if (RO.Pos.Storno == 0) {
            //Расчет скидки
            RO.Pos.SetPrice(RO.Pos.Ware.Price - (RO.Pos.Ware.Price / 1.13 * 0.01));
            receiptSum = receiptSum + RO.Pos.SummWD;
        }
    }
    //Добавляем оплату
    RO.AddPayment(paymentCode, receiptSum);

}

function NoAction(AO, RO, POS, CO) {}

//Нажатие кнопки
function runMacros(AO, RO, O) {
    var WShell = new ActiveXObject("WScript.Shell");
    WShell.SendKeys(keyMacros);
}

Автор: Волгоград.Инфософт 20.11.2016, 13:42

пополнил библиотеку полезных функций для FronTol5 двумя модулями:

Код
propertyBMPfile(cFullPathFile) - доступ к различным свойствам BMP файла
userVar - обёртка, для более удобной работы с пользовательскими переменными


библиотека доступна по адресу https://github.com/infosoft-v/frontol5lib

Автор: Калашников Алексей 23.11.2016, 12:19

Функция, которая возвращает строку подключения к базе данных. Удобно использовать при прямых SQL-запросах к базе. Функция незаменима, если кассы подключены к базе по схеме online - одна база на несколько рабочих мест. Читает файл frontol.ini, парсит его и находит путь к базе данных, на основе чего строит правильную строку подключения.
Дополнительно приведён код функции trim() - удаление концевых пробелов - используется как вспомогательная для getDBConnectionString().

Код
/**
* Функция возвращает строку подключения к базе данных. Путь к базе данных читает из файла Frontol.ini, в котором хранятся все штатные настройки Фронтол.
*
*/
function getDBConnectionString() {

    var result = '';

    var CONST_FILEOPEN_FOR_READING = 1;
    var CONST_FILEOPEN_UTF = -1;
    
    // Получаем путь к файлу Frontol.ini.
    var FSO = new ActiveXObject("Scripting.FileSystemObject");
    var path = FSO.GetAbsolutePathName("")+"\\Frontol.ini";    


    var FSO = new ActiveXObject("Scripting.FileSystemObject");
    var File = FSO.GetFile(FSO.GetAbsolutePathName("")+"\\Frontol.ini");
    var TextStream = File.OpenAsTextStream(CONST_FILEOPEN_FOR_READING, CONST_FILEOPEN_UTF);     // Открываем файл только для чтения, в кодировке UTF.

    var curLine = "";
    var mParam = "";
    var pathToDatabase = "";
    while (!TextStream.AtEndOfStream) {

        curLine = TextStream.ReadLine();
        curLine=trim(curLine);  // Удалем концевые пробелы, чтобы не обрабатывать пустые строки.

        if (curLine.length > 0) {
            mParam = curLine.split('=');
            if (mParam.length == 2                      // Если исходную строку удалось разбить по знаку '=' на две подстроки, значит перед нами обычная строка с параметрами.
                && mParam[0].length > 0                 // и строка не начинается со знака '='
                && mParam[0].toLowerCase() == 'path'    // имя параметра может быть написано любыми буквами.
                && mParam[1].length > 0) {              // Если значение параметра не пустое
                    
                    pathToDatabase = trim(mParam[1]);   // удаляем возможные пробелы у параметра, если запись сделана в виде path = localhost:D:\baseAtol\ , то есть имеются пробелы вначале самого параметра.
                    break;
            }

        }
    }
    TextStream.Close();  

    if (pathToDatabase.length > 0) {
        // Формируем строку подключения        
        result = "DRIVER=Firebird/InterBase(r) driver; DBNAME="+pathToDatabase+"MAIN.GDB;UID=ЛОГИН;PWD=ПАРОЛЬ;CHARSET=WIN1251;";

    } else {
        frontol.actions.showError("Ошибка. Невозможно прочитать параметры подключения к базе (код 2). Обратитесь в техническую поддержку.");
        return false;
    }

    return result;
}

/**
* Удаляет концевые пробелы в строке
* @param  {string, any type}   str входная строка
* @return {string}             строка с удалёнными начальными и конечными пробелами (если входной параметр был строкой), пустая строка если входной параметр не был строковым.
*/
function trim(str) {
    if (typeof(str) == "string") {
        return str.replace(/^\s*/,'').replace(/\s*$/,'');       // удаляем начальные, а затем концевые пробелы.
    } else {
        return "";
    }
    
}

Автор: Волгоград.Инфософт 23.11.2016, 18:36

Цитата(Калашников Алексей @ 23.11.2016, 13:19) *
Функция, которая возвращает строку подключения к базе данных.

Алексей, разрешите я добавлю вашу функцию в свою библиотеку полезных функций? Авторство, конечно, будет указано.

Автор: Калашников Алексей 2.12.2016, 9:47

В предыдущем варианте функция неверно определяла путь к файлу Frontol.ini на некоторых кассах, победить пока не удалось. Поэтому рекомендуется использовать этот вариант: путь прописан просто константой.
В коде ниже глобальная переменная PATH_TO_FRONTOL_INI_FILE хранит путь к файлу Frontol.ini. Примеры инициализации:

(для Фронтол 5.3)
var PATH_TO_FRONTOL_INI_FILE = "C:\\Program Files\\ATOL\\Frontol5\\BIN\\Frontol.ini"; // Путь к файлу Frontol.ini

(для Фронтол 5.11, Win PosReady 2009)
var PATH_TO_FRONTOL_INI_FILE = "C:\\Documents and Settings\\All Users\\Application Data\\ATOL\\Frontol5\\Settings\\Frontol.ini"; // Путь к файлу Frontol.ini


Цитата
/**
* Функция возвращает строку подключения к базе данных. Путь к базе данных читает из файла Frontol.ini, в котором хранятся все штатные настройки Фронтол.
*
*/
function getDBConnectionString() {

var result = '';

// ... Определяем константы - режим чтения и кодировка, в которой будет открываться файл Frontol.ini
var CONST_FILEOPEN_FOR_READING = 1; // только для чтения
var CONST_FILEOPEN_UTF = -1; // в кодировке UTF

var FSO = new ActiveXObject("Scripting.FileSystemObject");
var File = FSO.GetFile(PATH_TO_FRONTOL_INI_FILE);
var TextStream = File.OpenAsTextStream(CONST_FILEOPEN_FOR_READING, CONST_FILEOPEN_UTF); // Открываем файл только для чтения, в кодировке UTF.

var curLine = "";
var mParam = "";
var pathToDatabase = "";
while (!TextStream.AtEndOfStream) {

curLine = TextStream.ReadLine();
curLine=trim(curLine); // удалем концевые пробелы, чтобы не обрабатывать пустые строки.

if (curLine.length > 0) {
mParam = curLine.split('=');
if (mParam.length == 2 // Если исходную строку удалось разбить по знаку '=' на две подстроки, значит перед нами обычная строка с параметрами.
&& mParam[0].length > 0 // и строка не начинается со знака '='
&& mParam[0].toLowerCase() == 'path' // имя параметра может быть написано любыми буквами.
&& mParam[1].length > 0) { // Если значение параметра не пустое

pathToDatabase = trim(mParam[1]); // удаляем возможные пробелы у параметра, если запись сделана в виде path = localhost:D:\atol\ , то есть имеются пробелы вначале самого параметра.
break;
}
}
}
TextStream.Close();

if (pathToDatabase.length > 0) {
// Формируем строку подключения
result = "DRIVER=Firebird/InterBase® driver; DBNAME="+pathToDatabase+"MAIN.GDB;UID=SYSDBA;PWD=masterkey;CHARSET=WIN1251;";

} else {
frontol.actions.showError("Ошибка. Невозможно прочитать параметры подключения к базе (код 21). Обратитесь в техническую поддержку.");
return false;
}

return result;
}

/**
* Удаляет концевые пробелы в строке
* @param {string, any type} str входная строка
* @return {string} строка с удалёнными начальными и конечными пробелами (если входной параметр был строкой), пустая строка если входной параметр не был строковым.
*/
function trim(str) {
if (typeof(str) == "string") {
return str.replace(/^\s*/,'').replace(/\s*$/,''); // удаляем начальные, а затем концевые пробелы.
} else {
return "";
}
}


Владимир, можете добавить в свою библиотеку.

Автор: Волгоград.Инфософт 2.12.2016, 10:15

Цитата(Калашников Алексей @ 2.12.2016, 11:47) *
В предыдущем варианте функция неверно определяла путь к файлу Frontol.ini на некоторых кассах, победить пока не удалось. Поэтому рекомендуется использовать этот вариант: путь прописан просто константой.
В коде ниже глобальная переменная PATH_TO_FRONTOL_INI_FILE хранит путь к файлу Frontol.ini. Примеры инициализации:

(для Фронтол 5.3)
var PATH_TO_FRONTOL_INI_FILE = "C:\\Program Files\\ATOL\\Frontol5\\BIN\\Frontol.ini"; // Путь к файлу Frontol.ini

(для Фронтол 5.11, Win PosReady 2009)
var PATH_TO_FRONTOL_INI_FILE = "C:\\Documents and Settings\\All Users\\Application Data\\ATOL\\Frontol5\\Settings\\Frontol.ini"; // Путь к файлу Frontol.ini

Владимир, можете добавить в свою библиотеку.

Алексей, в библиотеке https://github.com/infosoft-v/frontol5lib есть функция https://github.com/infosoft-v/frontol5lib/blob/master/getPathDBFrontol5.js, которая корректно возвращает путь к БД Frontol и для Frontol до версии 5.10 и после этой версии.

Функцию добавлю. Спасибо.

ps. Я Евгений.

Автор: Калашников Алексей 2.12.2016, 15:46

Цитата(Волгоград.Инфософт @ 2.12.2016, 10:15) *
Алексей, в библиотеке https://github.com/infosoft-v/frontol5lib есть функция https://github.com/infosoft-v/frontol5lib/blob/master/getPathDBFrontol5.js, которая корректно возвращает путь к БД Frontol и для Frontol до версии 5.10 и после этой версии.

Функцию добавлю. Спасибо.

ps. Я Евгений.

Отправить ЛС я Вам не могу, придётся писать здесь...

В функции из репозитория https://github.com/infosoft-v/frontol5lib/blob/master/getPathDBFrontol5.js используется метод oFileSystem.GetAbsolutePathName("") чтобы получить папку установки фронтол в ProgramFiles. Именно эта функция в моём коде не работала на некоторых кассах. Вместо пути к "ProgramFiles" возвращала путь к "Document and settings". Она не всегда возвращает путь к папке с установкой Фронтол. И ваш метод точно также не будет корректно работать на некоторых POS-ах, если выполнение кода дойдёт до функции GetAbsolutePathName().

Цитата(Калашников Алексей @ 2.12.2016, 15:36) *
Отправить ЛС я Вам не могу, придётся писать здесь...

В функции из репозитория https://github.com/infosoft-v/frontol5lib/blob/master/getPathDBFrontol5.js используется метод oFileSystem.GetAbsolutePathName("") чтобы получить папку установки фронтол в ProgramFiles. Именно эта функция в моём коде не работала на некоторых кассах. Вместо пути к "ProgramFiles" возвращала путь к "Document and settings". Она не всегда возвращает путь к папке с установкой Фронтол. И ваш метод точно также не будет корректно работать на некоторых POS-ах, если выполнение кода дойдёт до функции GetAbsolutePathName().

Также функция https://github.com/infosoft-v/frontol5lib/blob/master/getPathDBFrontol5.js не учитывает пути вида
"C:\\Documents and Settings\\All Users\\Application Data\\ATOL\\Frontol5\\Settings\\Frontol.ini"
А именно здесь хранится файл Frontol.ini на WinPosReady 2009 для Фронтол 5.11.

Автор: Калашников Алексей 2.12.2016, 15:58

Пока не придумал ничего лучше, чем пробовать перебором.

Код
// Возвращает путь к папке, в которой хранятся файлы с параметрами в текущей версии Фронтол.
// По сути, ищет файл Frontol.ini в папках, в которых разные версии могут хранить этот файл, и
// возвращает путь к папке, в которой файл найден.
function getPathToIniFiles() {

    var PATH_TO_INI_FILES_NEW_VERSION = "C:\\Documents and Settings\\All Users\\Application Data\\ATOL\\Frontol5\\Settings";    // Путь к папке с файлом Frontol.ini в версиях 5.9 и выше.
    var PATH_TO_INI_FILES_OLD_VERSION = "C:\\Program Files\\ATOL\\Frontol5\\BIN";   // Путь к папке, в которой распологался Frontol.ini версии Фронтол 5.3
    var PATH_TO_INI_FILES_NEW_VERSION_WIN7 = "C:\\ProgramData\\ATOL\\Frontol5\\Settings"    // Папка с *.ini файлами на Windows 7 для версий старше Фронтол 5.9

    var fso = new ActiveXObject("Scripting.FileSystemObject");    
    if ( fso.FileExists(PATH_TO_INI_FILES_NEW_VERSION + "\\Frontol.ini") ) {

        return PATH_TO_INI_FILES_NEW_VERSION;

    } else if ( fso.FileExists(PATH_TO_INI_FILES_OLD_VERSION + "\\Frontol.ini") ) {

        return PATH_TO_INI_FILES_OLD_VERSION;

    } else if ( fso.FileExists(PATH_TO_INI_FILES_NEW_VERSION_WIN7 + "\\Frontol.ini") ) {

        return PATH_TO_INI_FILES_NEW_VERSION_WIN7;

    } else {

        frontol.actions.showError("Ошибка (код 23). Невозможно найти файл с настройками. Обратитесь в техподдержку.");
    }    
}

Автор: Волгоград.Инфософт 2.12.2016, 16:34

Цитата(Калашников Алексей @ 2.12.2016, 17:58) *
Пока не придумал ничего лучше, чем пробовать перебором.


Понятно, нужно подумать. Вы правы, в самом деле нужно тестировать не только на Win7, как сделал я, но и на WinXP.

Автор: zenik 28.12.2016, 15:01

Брошу свои 5 копеек. Итак, как мы выяснили, ЕГАИС не очень хорошо относится к продаже 2-х одинаковых марок, а еще хуже он отнесется к моменту: продажа -> возврат -> продажа. Но то что запрещено только словами - никогда не мешает кассиру. Итак - скрипт запрещающий в чек возврата регистрировать акцизную продукцию:

Код
function init()
{

  frontol.addEventListener("addPosition", "BeforeAddPosition", true);

}

function BeforeAddPosition(position) {

  if (frontol.currentDocument.type.code == 2)
    if (position.ware.hasAlcoStamp == 1)
      frontol.actions.showError("Возврат акцизной алкогольной продукции на кассе ЗАПРЕЩЕН!");
  
}

Все просто и банально.
з.ы. FRONTOL 5.x

Автор: николай1974 18.1.2017, 13:49

Контроль акцизок при продаже алкоголя - из учетной программы формируется файл, содержащий акцизные марки на остатке.
При добавлении позиции идет проверка наличия акцизки в файле.
В данном случае работает для документа Продажа, position.ware.type == 1 у нас - это алкоголь.

CODE
frontol.addEventListener("addPosition", "BeforeAddPosition", true);

function BeforeAddPosition(position)
{
doc = frontol.currentDocument;

if (frontol.currentDocument.type.code == 1)
{
if (position.ware.type == 1) {
flag = true;
desc = Open_Text_File('Путь к файлу');
MyResult1 = desc.split(";");
stamp = position.alcoStamp;

for (var index in MyResult1)
{
if (stamp == MyResult1[index])
{flag = false;}
}

if (flag)
{
frontol.actions.showError("Продажа товара запрещена! Отутствует акцизная марка на остатке !");
}
}
}

function Open_Text_File(file_path)
{
var t_file, data_file;
var FSO = new ActiveXObject("Scripting.FileSystemObject")
t_file=FSO.OpenTextFile(file_path, 1, false);
data_file = t_file.ReadAll();
t_file.Close();
return data_file;
}

Автор: Lis_Domino 10.3.2017, 15:48

Николай а для 4-го Фронтола можете написать такую проверку?
Спасибо

Автор: Lis_Domino 13.3.2017, 12:51

Цитата(николай1974 @ 18.1.2017, 14:49) *
Контроль акцизок при продаже алкоголя - из учетной программы формируется файл, содержащий акцизные марки на остатке.
При добавлении позиции идет проверка наличия акцизки в файле.
В данном случае работает для документа Продажа, position.ware.type == 1 у нас - это алкоголь.

CODE
frontol.addEventListener("addPosition", "BeforeAddPosition", true);

function BeforeAddPosition(position)
{
doc = frontol.currentDocument;

if (frontol.currentDocument.type.code == 1)
{
if (position.ware.type == 1) {
flag = true;
desc = Open_Text_File('Путь к файлу');
MyResult1 = desc.split(";");
stamp = position.alcoStamp;

for (var index in MyResult1)
{
if (stamp == MyResult1[index])
{flag = false;}
}

if (flag)
{
frontol.actions.showError("Продажа товара запрещена! Отутствует акцизная марка на остатке !");
}
}
}

function Open_Text_File(file_path)
{
var t_file, data_file;
var FSO = new ActiveXObject("Scripting.FileSystemObject")
t_file=FSO.OpenTextFile(file_path, 1, false);
data_file = t_file.ReadAll();
t_file.Close();
return data_file;
}



неработает


Автор: Lis_Domino 13.3.2017, 15:26

Цитата(Lis_Domino @ 13.3.2017, 13:51) *
неработает


работает но только в таком виде: МИНУС - перебирает ";" - только по одной строке!

function init()
{
frontol.addEventListener("addPosition", "BeforeAddPosition", true);
}

function BeforeAddPosition(position)
{
doc = frontol.currentDocument;
if (frontol.currentDocument.type.code == 1)
{
if (position.ware.type == 1) {
flag = true;
fileName = "c:\\amarka.txt";
desc = Open_Text_File(fileName);
MyResult1 = desc.split(";");
stamp = position.alcoStamp;

for (var index in MyResult1)
{
if (stamp == MyResult1[index])
{flag = false;}
}

if (flag)
{
frontol.actions.showError("Продажа товара запрещена! Отсутствует акцизная марка на остатке !");
}
}
}
}

function Open_Text_File(file_path)
{
var t_file, data_file;
var FSO = new ActiveXObject("Scripting.FileSystemObject")
t_file=FSO.OpenTextFile(file_path, 1, false);
data_file = t_file.ReadAll();
t_file.Close();
return data_file;
}

Автор: ботир 8.4.2017, 21:14

Цитата(POUL @ 21.3.2014, 22:46) *
Во Фронтоле начиная с релиза, по-моему 4.9.7 (могу ошибаться) доступно чтение данных из базы путем SQL запросов без использования внешних провайдеров.
Пример использования:

Код
    QueryCode = "";
    QueryName = "";
    Query = CO.NewDBQuery;
    Query.SQL = "SELECT CODE, NAME FROM SPRT WHERE PARENTID IN (SELECT ID FROM SPRT WHERE ISWARE = 0 AND NAME = "Контрацепция") ORDER BY 1";
    Query.Open();
    if (Query.FieldByName("CODE") != null)
    {
     do
     {
      QueryCode = QueryCode + String (Query.FieldByName("CODE")) + "\n";
      QueryName = QueryName + String (Query.FieldByName("NAME")) + "\n";
      n = Query.RecordCount;
      Query.Next();
     }
     while (n != Query.RecordCount)
     Query.Close();
    }
    if (QueryCode == "" || QueryName == "")
    {
     Query.Close();
     AO.ShowError ("Результат запроса пуст!");
    }
    QueryCode = QueryCode.substring (0, QueryCode.length - 1);
    QueryName = QueryName.substring (0, QueryName.length - 1);

    SelectedSol = Number (AO.SelectString ("Выберите товар:", QueryName, QueryCode));


Повторюсь. Доступно только чтение данных.

Dobriy Vecher Tema ochen staraya no xotelos bi poprosit podskazat kak etot kod pishetsya dlya Frontol 5

Автор: POUL 8.4.2017, 21:39

в 5м фронтоле это недоступно

Автор: POUL 27.6.2017, 9:36

Проверка дня рождения покупателя перед вводом оплаты при продаже алкоголя:

Код
function init()
{
frontol.addEventListener("addPayment","beforeAddPayment",true);  //Сообщение о проверке даты рождения клиента
}

function beforeAddPayment()
{
flag = false;

for (frontol.currentDocument.position.index = 1;
       frontol.currentDocument.position.index <=
       frontol.currentDocument.position.count;
       frontol.currentDocument.position.index ++)
{
  if (frontol.currentDocument.position.type == 1 || frontol.currentDocument.position.type == 1)
   flag = true;
}

if (flag)
{
  now = new Date ();
  day = String (now.getDate ());
  month = String (now.getMonth () + 1);
  year = String (now.getYear () - 18);

  if (day.length == 1)
   day = "0" + day;

  if (month.length == 1)
   month = "0" + month;

  frontol.actions.showMessage("Вы проверили, что дата рождения покупателя раньше, чем: \n" + day + "." + month + "." + year + "?", Button.YesNo + Icon.Question)
}
}

Автор: Сергей Егоричев 4.7.2017, 11:39

Внесу свои 5 копеек

Код
//Возвращает дату в виде строки, с которой можно продавать алкоголь +1 день
function getAdultDate() {
var date = new Date();
var day = date.getDate() + 1;    
var month = date.getMonth() + 1;    
var year = date.getFullYear() - 18;    
var arr = [31,28,31,30,31,30,31,31,30,31,30,31]; //массив с количеством дней в каждом месяце                                                                                                                  
if ( year%4 == 0 && year%4 != 0 )  arr[2] = 29;
if ( day > arr[month-1]) { day = 1; month++; if (month==13) {month = 1; year++; }}

if ( day < 10 ) day = "0" + day;
if ( month < 10 ) month = "0" + month;
var dateText = day + "." + month + "." + year;
return dateText;
}
//--------------------------------------------------------------------------------------------------------------------------

Автор: АТОЛ: Царюков Роман 22.8.2017, 13:29

https://onedrive.live.com/view.aspx?cid=189c614ed00d4c23&id=documents&resid=189C614ED00D4C23%21128&app=OneNote&authkey=!ABE4IM6eOvPtlrQ&&wd=target%28%2F%2FFrontol%204_5.one%7C6d3658de-3113-4b4d-9450-f1ce46152d9a%2FFastReprot.%20%D0%9F%D0%B5%D1%87%D0%B0%D1%82%D1%8C%20%D0%BE%D1%82%D1%87%D0%B5%D1%82%D0%BE%D0%B2%20%D0%BD%D0%B0%20%D0%9A%D0%9A%D0%9C%7Cd2e0a488-2412-4c9f-9015-d05521daf432%2F%29

Автор: Сокол Олег 17.12.2017, 7:54

Цитата(POUL @ 27.6.2017, 9:36) *
Проверка дня рождения покупателя перед вводом оплаты при продаже алкоголя:

Код
function init()
{
frontol.addEventListener("addPayment","beforeAddPayment",true);  //Сообщение о проверке даты рождения клиента
}

function beforeAddPayment()
{
flag = false;

for (frontol.currentDocument.position.index = 1;
       frontol.currentDocument.position.index <=
       frontol.currentDocument.position.count;
       frontol.currentDocument.position.index ++)
{
  if (frontol.currentDocument.position.type == 1 || frontol.currentDocument.position.type == 1)
   flag = true;
}

if (flag)
{
  now = new Date ();
  day = String (now.getDate ());
  month = String (now.getMonth () + 1);
  year = String (now.getYear () - 18);

  if (day.length == 1)
   day = "0" + day;

  if (month.length == 1)
   month = "0" + month;

  frontol.actions.showMessage("Вы проверили, что дата рождения покупателя раньше, чем: \n" + day + "." + month + "." + year + "?", Button.YesNo + Icon.Question)
}
}


frontol.currentDocument.position.ware.type == 1
Должно быть наверное так, иначе ошибка.

Но вот вопрос: как сделать, чтобы ответы влияли на что-то? То есть, при ответе НЕТ, оплата не применялась? Я как ни делаю, у меня при любом DialogResult вылезает frontol.actions.showError

Автор: POUL 17.12.2017, 12:01

в руководстве интегратора есть пример работы с DialogResult

Автор: АТОЛ: Царюков Роман 15.1.2018, 11:53

https://onedrive.live.com/redir?resid=189C614ED00D4C23%21128&authkey=%21ABE4IM6eOvPtlrQ&page=View&wd=target%28%2F%2FFrontol%204_5.one%7C6d3658de-3113-4b4d-9450-f1ce46152d9a%2FFrontol5.%20%D0%A0%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%20%D1%81%20Telegram%7C440d8caf-35dd-4d36-bc23-02505a73dce0%2F%29

Автор: POUL 5.2.2018, 23:34

Запрет продажи алкоголя для Фронтол5
Работает как для групп товаров, так и для вида номенклатуры
Нузно закомментировать ненужное и раскомментировать нужное

Код
function init()
{
frontol.addEventListener("addPosition", "beforeAddPosition", true);
}

function beforeAddPosition (O)
{
now = new Date ();
hour = now.getHours ();

if (hour >= 23 || hour <8)
{
// По группе товара
/*  flag = false;
  for (O.ware.parent.index = 1;
       O.ware.parent.index <=
       O.ware.parent.count;
       O.ware.parent.index ++)
  {
   if (O.ware.parent.code == 2 || O.ware.parent.code == 23411)    // Перечислить группы
   {
    flag = true;
   }
  }

  if (flag)
*/

// По типу номенклатуры
  if (O.ware.type == 1)
  {
   frontol.actions.showError ("Продажа алкоголя запрещена!!!!");
  }
}
}

Автор: POUL 20.2.2018, 22:51

Проверка совершеннолетия покупателя алкоголя

2 варианта.

Код
function init()
{
frontol.addEventListener("closeDocument", "checkBirthDayBeforeCloseDocument", true);
}

function checkBirthDayBeforeCloseDocument()
{
flag = false;
for (frontol.currentDocument.position.index = 1;
      frontol.currentDocument.position.index <=
      frontol.currentDocument.position.count;
      frontol.currentDocument.position.index ++)
{
  for (frontol.currentDocument.position.ware.parent.index = 1;
       frontol.currentDocument.position.ware.parent.index <=
       frontol.currentDocument.position.ware.parent.count;
       frontol.currentDocument.position.ware.parent.index ++)
  {
   if (frontol.currentDocument.position.ware.parent.code == 29504 || frontol.currentDocument.position.ware.parent.code == 32086)
   {
    flag = true;
   }
  }
}

if (flag)
{
  do
  {
   inputError = false;
   inputText = frontol.actions.inputString("Введите дату рождения", "ДД.ММ.ГГГГ", 10);
   defaultText = inputText;
   if (inputText == "")
   {
    inputError = true;
    defaultText = "";
    frontol.actions.showMessage("Вы не ввели значение!", Icon.Exclamation);
   }

   if (inputText == null)
   {
    frontol.actions.cancel ();
   }

   else
   {
    inputText = inputText.replace(/\,/, ".");
    if ((isNaN(inputText.substring (0, 2)) || (Number (inputText.substring (0, 2)) > 31)) || (isNaN(inputText.substring (3, 5)) || (Number (inputText.substring (3, 5)) > 12)) || isNaN(inputText.substring (6)) || inputText.length != 10)
    {
     inputError = true;
     frontol.actions.showMessage("Введены некорректные данные!", Icon.Error);
    }
   }
  }
  while (inputError);

  year = Number (inputText.substring (6)) + 18;
  month = Number (inputText.substring (3, 5)) - 1;
  day = inputText.substring (0, 2);

  birthDay = new Date (year, month, day)

  if (birthDay > new Date ())
   frontol.actions.showError ("Покупателю меньше 18 лет!!!");
}
}




Код
function init()
{
frontol.addEventListener("addPayment","beforeAddPayment",true);  //Сообщение о проверке даты рождения клиента
}

function beforeAddPayment()
{
now = new Date ();
day = String (now.getDate ());
month = String (now.getMonth () + 1);
year = String (now.getYear () - 18);

if (day.length == 1)
  day = "0" + day;

if (month.length == 1)
  month = "0" + month;

frontol.actions.showMessage("Вы проверили, что дата рождения покупателя раньше, чем: \n" + day + "." + month + "." + year + "?", Button.YesNo + Icon.Question)
}

Автор: Анри 20.7.2019, 3:47

Цитата(mozer @ 7.2.2013, 5:20) *
Скрипт организует скидку 20% на каждую 2-ю позицию из классификатора. Скидка применяется к позициям по возрастанию цены, то есть сначала идет скидка на дешёвый товар.
Необходимые условия:
1. Номенклатура дожна принадлежать только 1 классификатору
2. Создать условие по классификатору, в качестве ставки указать ставку которая вычисляет скидку по данному скрипту. Ставка СУММОВАЯ, НЕ ПРОЦЕНТНАЯ
Условий в пункте 2 может быть сколько угодно, тогда скрипт вычислит скидку по каждому классификатору отдельно.
3. Скрипт с моментом действия НЕТ.
Код
function BeforeAct(AO, RO, E, O)
{
}

function AfterAct(AO, RO, E, O)
{
}

function FuncAct(AO, RO)
{
}

//Функция сравнения для сортировки массива позиций
//Умышлено сортируем по возрастанию цены позиции
function Compare(A,B)
{
    if (A.Price < B.Price)
    {
        return -1;
    }
    if (A.Price > B.Price)
    {
        return 1;
    }
    return 0;
}

//Объект позиция. Содержит свойства:
//Идентификатор товара
//Цена товара без скидок
function MyPosition(ID,Price)
{
    this.ID = ID;
    this.Price = Price;
}
//Функция собирает массив позиций
//принадлежащих к одному класификатору.
//Сортирует массив в порядке возрастания цены.
//позиция попадает в массив столько раз, сколько товара взял покупатель
function collectArray(RO,POS)
{
    arrayPosition = new Array;
    //Соберем массив строк принадлежащих одному класификатору
    POS.Classifier.Index = 1;
    for(RO.Pos.Index = 1; RO.Pos.Index <= RO.Pos.Count; RO.Pos.Index++)
    {
        //Если позиция сторнирована или не принадлежит классификаторам, пропустим ее.
        if ((RO.Pos.Storno == 1)||RO.Pos.Classifier.Count == 0)
        {
            continue;
        }
        //Установливаем на первый классификатор
        RO.Pos.Classifier.Index = 1;
        if (RO.Pos.Classifier.Code == POS.Classifier.Code)
        {

            for (var IndexCount = 1; IndexCount <= RO.Pos.Quantity; IndexCount++)
            {
                MyPos = new MyPosition(RO.Pos.PosID,RO.Pos.Price);
                arrayPosition.push(MyPos);
            }
        }
    }
    //Сортируем массив в порядке возрастания цены
    arrayPosition.sort(Compare);
    //обрабатываем массив,для определения скидок по позициям.
    //Найдем количество позиций со скидкой. Каждый второй товар со скидкой.
    CountDiscounWare = arrayPosition.length / 2;
    arrayDiscounPosition = new Array;
    for(var IndexDiscount = 1; IndexDiscount <= CountDiscounWare; IndexDiscount++)
    {
        arrayDiscounPosition.push(arrayPosition.shift());
    }
    return arrayDiscounPosition;

}

//Основная функция ее подставляем в ставку
function MyAction(AO, RO, POS)
{
    var Discount = 0;
    //Если позиция не относится ни к одному класссификатору, тогда и скидки на нее нет.
    if (POS.Classifier.Count == 0)
    {
        return Discount;
    }
    //Сформируем массив скидок
    var arrayDiscounPosition = collectArray(RO,POS);
    //Обрабатываем полученый массив. В массиве может быть не одна текущая позиция, если количество больше 1
    for (var index = 0; index < arrayDiscounPosition.length; index++)
    {
        if ((arrayDiscounPosition[index].ID == POS.PosID))
        {
            Discount = Discount + arrayDiscounPosition[index].Price*0.2;
        }
    }
    return Discount;
}


Здравствуйте!
Подскажите пожалуйста по работе данного скрипта. Как я понимаю, в условие нужно добавить "классификатор товаров" где выбрать нужный классификатор, а в действие "автоматическая скидка из сценария", куда нужно добавить нижнюю часть скрипта под "//Основная функция ее подставляем в ставку", а куда тогда вставлять остальной сценарий? Подскажите пожалуйста, что делаю не так?

Автор: Анри 20.7.2019, 4:46

Или может кто подскажет, как сделать продажу 3го товара из определенного списка товаров, бесплатной? т.е есть определенные товары, если 3й товар из нашего списка попадает в чек, то на самый дешевый из них делается скидка 100%. Наверняка кто то уже делал подобное.

Автор: POUL 21.7.2019, 20:50

создайте свою тему

Автор: Виктор Франко 5.12.2019, 23:20

Цитата(POUL @ 27.6.2017, 10:36) *
Проверка дня рождения покупателя перед вводом оплаты при продаже алкоголя:

Это что-то типа сравнения следующего типа:
true+true = false
false+true = true;
Но уж совсем коротко описал, код намного больше будет, конечно, просто написал в какую сторону смотреть добрый человек. А тут бац и полный рабочий код как раз того, что мне нужно. А я курил через другой форум как это все реализовать.
Цитата(Анри @ 20.7.2019, 5:46) *
Или может кто подскажет, как сделать продажу 3го товара из определенного списка товаров, бесплатной? т.е есть определенные товары, если 3й товар из нашего списка попадает в чек, то на самый дешевый из них делается скидка 100%. Наверняка кто то уже делал подобное.

Что касается вашего вопроса, то с азами здесь можете разобраться https://myht.ru/question/21137140-logicheskoe-dopolnenie-v-javascript Если будут еще вопросы, то можете в личку писать, писать смогу подсказать что к чему, если не будет получаться совсем.

Автор: АТОЛ: Средних Владимир 7.4.2020, 17:28

Как работать с JSON во Фронтоле.
Из-за ограничения используемого движка Microsoft ® JScript, в нём не поддерживается такой замечательный как JSON
https://docs.microsoft.com/en-us/previous-versions/hbxc2t98(v=vs.85)

однако, есть JS-библиотека, которая исправляет этот недостаток: https://github.com/douglascrockford/JSON-js/blob/master/json2.js

Достаточно скопировать её в скрипт Фронтола, и вы получите себе этот объект для работы с JSON

Цитата
// json2.js
// 2017-06-12
// Public Domain.
// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
...... ТУТ МНОГО СТРОК JS-кода!
// If the text is not JSON parseable, then a SyntaxError is thrown.

throw new SyntaxError("JSON.parse");
};
}
}());

function init()
{
var code = '"\u2028\u2029"'
s = JSON.parse(code) // evaluates to "\u2028\u2029" in all engines
var s
s = eval(code)
frontol.actions.showMessage(s)
s = JSON.parse("{\"message\": \"\\u041a\\u0430\\u0440\\u0442\\u0430\\u043d\\u0435\\u043d\\u0430\\u0439\\u0434\\u0435\\u043d\\u0430\"}");
frontol.actions.showMessage(s + " " + s.message);
}

Автор: POUL 8.4.2020, 15:51

видел в некоторых сценариях, как люди как-то иначе обращаются к JSON

Автор: АТОЛ: Царюков Роман 8.4.2020, 16:01

Цитата(POUL @ 8.4.2020, 15:51) *
видел в некоторых сценариях, как люди как-то иначе обращаются к JSON

http://forum.atol.ru/index.php?showtopic=36740&view=findpost&p=268702
http://forum.atol.ru/index.php?showtopic=33946&view=findpost&p=242501


думаешь сильно отличается?

Автор: POUL 6.10.2020, 12:46

Вся эта конструкция валится на релизе 6.8.3
Если в сценарии идет работа с объектом Date
Например:

function beforeOpenDocument ()
{

var now = new Date ();
frontol.actions.showMessage ("1");
var hour = now.getHours ();
var minute = now.getMinutes ();

if ((hour == 22 && minute >= 55) || (hour > 22 && hour < dirol.gif)
frontol.actions.showError ("Рабочий день закончен! Продажи остановлены!");
}

Получаем ошибку добавления объекта.

Автор: АТОЛ: Царюков Роман 6.10.2020, 12:49

http://forum.atol.ru/index.php?showtopic=6492&view=findpost&p=163789

Автор: POUL 6.10.2020, 12:50

Проблему удалось обойти, используя обработчик JSON как функцию, немного ее доработав:

Код
var SCRIPT_JSON = 0;

function get_JSON(){
if (SCRIPT_JSON == 0)
{
  var JSON = {};

  (function () {
    "use strict";

    var rx_one = /^[\],:{}\s]*$/;
    var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
    var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
    var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
    var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;

    function f(n) {
        return n < 10
            ? "0" + n
            : n;
    }

    function this_value() {
        return this.valueOf();
    }

    if (typeof Date.prototype.toJSON !== "function") {

        Date.prototype.toJSON = function () {

            return isFinite(this.valueOf())
                ? this.getUTCFullYear() + "-" +
                        f(this.getUTCMonth() + 1) + "-" +
                        f(this.getUTCDate()) + "T" +
                        f(this.getUTCHours()) + ":" +
                        f(this.getUTCMinutes()) + ":" +
                        f(this.getUTCSeconds()) + "Z"
                : null;
        };

        Boolean.prototype.toJSON = this_value;
        Number.prototype.toJSON = this_value;
        String.prototype.toJSON = this_value;

    }

    var gap;
    var indent;
    var meta;
    var rep;

    function quote(string) {
        rx_escapable.lastIndex = 0;
        return rx_escapable.test(string)
            ? "\"" + string.replace(rx_escapable, function (a) {
                var c = meta[a];
                return typeof c === "string"
                    ? c
                    : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
            }) + "\""
            : "\"" + string + "\"";
    }

    function str(key, holder) {
        var i;
        var k;
        var v;
        var length;
        var mind = gap;
        var partial;
        var value = holder[key];
        if (value && typeof value === "object" &&
                typeof value.toJSON === "function") {
            value = value.toJSON(key);
        }
        if (typeof rep === "function") {
            value = rep.call(holder, key, value);
        }
        switch (typeof value) {
        case "string":
            return quote(value);

        case "number":
            return isFinite(value)
                ? String(value)
                : "null";

        case "boolean":
        case "null":
            return String(value);
        case "object":
            if (!value) {
                return "null";
            }
            gap += indent;
            partial = [];
            if (Object.prototype.toString.apply(value) === "[object Array]") {
                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || "null";
                }
                v = partial.length === 0
                    ? "[]"
                    : gap
                        ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]"
                        : "[" + partial.join(",") + "]";
                gap = mind;
                return v;
            }
            if (rep && typeof rep === "object") {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    if (typeof rep[i] === "string") {
                        k = rep[i];
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (
                                gap
                                    ? ": "
                                    : ":"
                            ) + v);
                        }
                    }
                }
            } else {
                for (k in value) {
                    if (Object.prototype.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (
                                gap
                                    ? ": "
                                    : ":"
                            ) + v);
                        }
                    }
                }
            }
            v = partial.length === 0
                ? "{}"
                : gap
                    ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
                    : "{" + partial.join(",") + "}";
            gap = mind;
            return v;
        }
    }
    if (typeof JSON.stringify !== "function") {
        meta = {
            "\b": "\\b",
            "\t": "\\t",
            "\n": "\\n",
            "\f": "\\f",
            "\r": "\\r",
            "\"": "\\\"",
            "\\": "\\\\"
        };
        JSON.stringify = function (value, replacer, space) {
            var i;
            gap = "";
            indent = "";
            if (typeof space === "number") {
                for (i = 0; i < space; i += 1) {
                    indent += " ";
                }
            } else if (typeof space === "string") {
                indent = space;
            }
            rep = replacer;
            if (replacer && typeof replacer !== "function" &&
                    (typeof replacer !== "object" ||
                    typeof replacer.length !== "number")) {
                throw new Error("JSON.stringify");
            }

            return str("", {"": value});
        };
    }
    if (typeof JSON.parse !== "function") {
        JSON.parse = function (text, reviver) {
            var j;
            function walk(holder, key) {
                var k;
                var v;
                var value = holder[key];
                if (value && typeof value === "object") {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }
            text = String(text);
            rx_dangerous.lastIndex = 0;
            if (rx_dangerous.test(text)) {
                text = text.replace(rx_dangerous, function (a) {
                    return "\\u" +
                            ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }
            if (
                rx_one.test(
                    text
                        .replace(rx_two, "@")
                        .replace(rx_three, "]")
                        .replace(rx_four, "")
                )
            ) {
                j = eval("(" + text + ")");

                return (typeof reviver === "function")
                    ? walk({"": j}, "")
                    : j;
            }
            throw new SyntaxError("JSON.parse");
        };
    }
}());
SCRIPT_JSON = JSON;
}
return SCRIPT_JSON;
}




Далее выдываем методы JSON таким образом:



var responseText = get_JSON().parse (xhr.responseText);

Автор: POUL 6.10.2020, 14:49

Обработчик Base64:

Код
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;
             var f=0;
             e=Base64._utf8_encode(e);
             while(f<e.length){n=e.charCodeAt(f++);
             r=e.charCodeAt(f++);
             i=e.charCodeAt(f++);
             s=n>>2;
             o=(n&3)<<4|r>>4;
             u=(r&15)<<2|i>>6;a=i&63;
             if(isNaN(r)){u=a=64}
             else
             if(isNaN(i))
             {a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e)
             {var t="";
             var n,r,i;
             var s,o,u,a;
             var f=0;
             e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");
             while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));
             o=this._keyStr.indexOf(e.charAt(f++));
             u=this._keyStr.indexOf(e.charAt(f++));
             a=this._keyStr.indexOf(e.charAt(f++));
             n=s<<2|o>>4;r=(o&15)<<4|u>>2;
             i=(u&3)<<6|a;
             t=t+String.fromCharCode(n);
             if(u!=64){t=t+String.fromCharCode(r)}
             if(a!=64){t=t+String.fromCharCode(i)}}
             t=Base64._utf8_decode(t);
             return t},_utf8_encode:function(e)
             {e=e.replace(/\r\n/g,"\n");
             var t="";
             for(var n=0;
                 n<e.length;
                 n++)
             {var r=e.charCodeAt(n);
             if(r<128)
             {t+=String.fromCharCode(r)}
             else
             if(r>127&&r<2048)
             {t+=String.fromCharCode(r>>6|192);
             t+=String.fromCharCode(r&63|128)}
             else{t+=String.fromCharCode(r>>12|224);
             t+=String.fromCharCode(r>>6&63|128);
             t+=String.fromCharCode(r&63|128)}}
             return t},_utf8_decode:function(e){
             var t="";
             var n=0;
             var r=c1=c2=0;
             while(n<e.length)
             {r=e.charCodeAt(n);
             if(r<128)
             {t+=String.fromCharCode(r);
             n++}
             else
             if(r>191&&r<224)
             {c2=e.charCodeAt(n+1);
             t+=String.fromCharCode((r&31)<<6|c2&63);
             n+=2}
             else
             {c2=e.charCodeAt(n+1);
              c3=e.charCodeAt(n+2);
             t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);
             n+=3}}
             return t}}


var encodedString = Base64.encode (clienId + ":" + secretKey);
var decodedString = Base64.decode ('dgYTIEdiuosidsdGkucyesiueFFkdsdhskj');

Автор: zenik 7.6.2021, 15:35

Цитата(POUL @ 6.10.2020, 12:50) *
var responseText = get_JSON().parse (xhr.responseText);


Эта функция позволила создать свою поделку. Итак УТМ 4 может проверять марки "на лету". Идея такая - каждая добавляемая марка проверяется через УТМ, и если не проходит проверку, то в чек не добавляется. Ответ возвращается в JSON, поэтому нужна была функция разбора.

В функции checkStamp меняем адрес УТМ на свой.

Код
var SCRIPT_JSON = 0;

function init() {
  frontol.addEventListener( "addPosition", "beforeAddPosition", true );
  frontol.addEventListener( "changePosition", "beforeChangePosition", true );
}

function checkStamp( stamp ) {

  url = "http://localhost:8080/api/mark/check?code="+stamp;

  try {
    xmlHTTP = new ActiveXObject("Microsoft.xmlHTTP");
    xmlHTTP.Open( "GET", url, 0);
    xmlHTTP.Send();
  } catch(e) {}

  var responseText = get_JSON().parse( xmlHTTP.responseText );

  if ( xmlHTTP.Status != 200 )
    frontol.actions.showError( "УТМ не доступен. Продажа алкогольной продукции не возможна!" );

  if ( responseText.owner != true )
    frontol.actions.showMessage( "Внимание! Данная марка не прошла проверку на УТМ. Продажа не возможна!" );

}

function beforeChangePosition( position ) {
  position.stamp.index = position.stamp.count;
  checkStamp(position.stamp.value);
}

function beforeAddPosition( position ) {
  beforeChangePosition( position );
}

function get_JSON(){
  if (SCRIPT_JSON == 0)
  {
    var JSON = {};

    (function () {
    "use strict";

    var rx_one = /^[\],:{}\s]*$/;
    var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
    var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
    var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
    var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;

    function f(n) {
      return n < 10
        ? "0" + n
        : n;
    }

    function this_value() {
      return this.valueOf();
    }

    if (typeof Date.prototype.toJSON !== "function") {

      Date.prototype.toJSON = function () {

        return isFinite(this.valueOf())
          ? this.getUTCFullYear() + "-" +
              f(this.getUTCMonth() + 1) + "-" +
              f(this.getUTCDate()) + "T" +
              f(this.getUTCHours()) + ":" +
              f(this.getUTCMinutes()) + ":" +
              f(this.getUTCSeconds()) + "Z"
          : null;
      };

      Boolean.prototype.toJSON = this_value;
      Number.prototype.toJSON = this_value;
      String.prototype.toJSON = this_value;

    }

    var gap;
    var indent;
    var meta;
    var rep;

    function quote(string) {
      rx_escapable.lastIndex = 0;
      return rx_escapable.test(string)
        ? "\"" + string.replace(rx_escapable, function (a) {
          var c = meta[a];
          return typeof c === "string"
            ? c
            : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
        }) + "\""
        : "\"" + string + "\"";
    }

    function str(key, holder) {
      var i;
      var k;
      var v;
      var length;
      var mind = gap;
      var partial;
      var value = holder[key];
      if (value && typeof value === "object" &&
          typeof value.toJSON === "function") {
        value = value.toJSON(key);
      }
      if (typeof rep === "function") {
        value = rep.call(holder, key, value);
      }
      switch (typeof value) {
      case "string":
        return quote(value);

      case "number":
        return isFinite(value)
          ? String(value)
          : "null";

      case "boolean":
      case "null":
        return String(value);
      case "object":
        if (!value) {
          return "null";
        }
        gap += indent;
        partial = [];
        if (Object.prototype.toString.apply(value) === "[object Array]") {
          length = value.length;
          for (i = 0; i < length; i += 1) {
            partial[i] = str(i, value) || "null";
          }
          v = partial.length === 0
            ? "[]"
            : gap
              ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]"
              : "[" + partial.join(",") + "]";
          gap = mind;
          return v;
        }
        if (rep && typeof rep === "object") {
          length = rep.length;
          for (i = 0; i < length; i += 1) {
            if (typeof rep[i] === "string") {
              k = rep[i];
              v = str(k, value);
              if (v) {
                partial.push(quote(k) + (
                  gap
                    ? ": "
                    : ":"
                ) + v);
              }
            }
          }
        } else {
          for (k in value) {
            if (Object.prototype.hasOwnProperty.call(value, k)) {
              v = str(k, value);
              if (v) {
                partial.push(quote(k) + (
                  gap
                    ? ": "
                    : ":"
                ) + v);
              }
            }
          }
        }
        v = partial.length === 0
          ? "{}"
          : gap
            ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
            : "{" + partial.join(",") + "}";
        gap = mind;
        return v;
      }
    }
    if (typeof JSON.stringify !== "function") {
      meta = {
        "\b": "\\b",
        "\t": "\\t",
        "\n": "\\n",
        "\f": "\\f",
        "\r": "\\r",
        "\"": "\\\"",
        "\\": "\\\\"
      };
      JSON.stringify = function (value, replacer, space) {
        var i;
        gap = "";
        indent = "";
        if (typeof space === "number") {
          for (i = 0; i < space; i += 1) {
            indent += " ";
          }
        } else if (typeof space === "string") {
          indent = space;
        }
        rep = replacer;
        if (replacer && typeof replacer !== "function" &&
            (typeof replacer !== "object" ||
            typeof replacer.length !== "number")) {
          throw new Error("JSON.stringify");
        }

        return str("", {"": value});
      };
    }
    if (typeof JSON.parse !== "function") {
      JSON.parse = function (text, reviver) {
        var j;
        function walk(holder, key) {
          var k;
          var v;
          var value = holder[key];
          if (value && typeof value === "object") {
            for (k in value) {
              if (Object.prototype.hasOwnProperty.call(value, k)) {
                v = walk(value, k);
                if (v !== undefined) {
                  value[k] = v;
                } else {
                  delete value[k];
                }
              }
            }
          }
          return reviver.call(holder, key, value);
        }
        text = String(text);
        rx_dangerous.lastIndex = 0;
        if (rx_dangerous.test(text)) {
          text = text.replace(rx_dangerous, function (a) {
            return "\\u" +
                ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
          });
        }
        if (
          rx_one.test(
            text
              .replace(rx_two, "@")
              .replace(rx_three, "]")
              .replace(rx_four, "")
          )
        ) {
          j = eval("(" + text + ")");

          return (typeof reviver === "function")
            ? walk({"": j}, "")
            : j;
        }
        throw new SyntaxError("JSON.parse");
      };
    }
  }());
  SCRIPT_JSON = JSON;
  }
  return SCRIPT_JSON;
}


з.ы. Было бы не плохо это интегрировать сразу во фронтол wink.gif

Автор: POUL 7.6.2021, 16:39

во фронтоле вроде это есть

Автор: zenik 7.6.2021, 16:43

Цитата(POUL @ 7.6.2021, 16:39) *
во фронтоле вроде это есть

Он проверяет просто по базе проданных марок... Это не то. Тут гарантированная проверка марки.

Автор: POUL 7.6.2021, 17:00

есть также проверка силами УТМ
но проблема в том, что УТМ проверяет тольк при наличии связи с ЕГАИС
если связи нет, то УТМ возвращает ДОБРО

Автор: zenik 7.6.2021, 17:16

Цитата(POUL @ 7.6.2021, 17:00) *
есть также проверка силами УТМ
но проблема в том, что УТМ проверяет тольк при наличии связи с ЕГАИС
если связи нет, то УТМ возвращает ДОБРО

Нет. УТМ хранит список доступных марок локально и по этому запросу сверяет с локальной базой. В 3.0.8 запрос работал немного по другому и на старые марки всегда возвращал - нет в наличии. В 4.2.0 проверку переделали и этот запрос работает вполне корректно как с новыми так и со старыми марками. Скажу честно, на кассе в бою не проверял, но марки разные подсовывал - было норм. В любом случае получить ошибку на стадии пиканья марок удобнее, нежели после ввода оплаты в чек на сотню марок.
Я буду использовать, но беда в том, что у нас с 7 по 11 запретили продажу алкоголя - я пока вне тестов sad.gif

Автор: zenik 8.6.2021, 10:45

Код
  if ( responseText.owner != true )
    frontol.actions.showMessage( "Внимание! Данная марка не прошла проверку на УТМ. Продажа не возможна!" );

Конечно же должно быть так:
Цитата
if ( responseText.owner != true )
frontol.actions.showError( "Внимание! Данная марка не прошла проверку на УТМ. Продажа не возможна!" );


Автор: zenik 8.6.2021, 14:01

Поправка 1: что бы при ошибке было окно "с кнопкой" и не исчезало при сканировании следующего ШК...

Код
  if ( xmlHTTP.Status != 200 ) {
    frontol.actions.showMessage( "УТМ не доступен. Продажа алкогольной продукции не возможна!", Icon.Error + Button.Ok );
    frontol.actions.cancel();
  }

  if ( responseText.owner != true ) {
    frontol.actions.showMessage( "Внимание! Данная марка не прошла проверку на УТМ. Продажа не возможна!", Icon.Error + Button.Ok );
    frontol.actions.cancel();
  }


Поправка 2: Проверяем марки только у алкоголя естественно smile.gif
Код
function controlPosition( position ) {
  if ( position.ware.type == 1 ) {
    position.stamp.index = position.stamp.count;
    checkStamp(position.stamp.value);
  }
}

function beforeChangePosition( position ) {
  controlPosition( position );
}

function beforeAddPosition( position ) {
  controlPosition( position );
}

Автор: zenik 10.6.2021, 20:01

ЫшО одна поправка:

Код
function controlPosition( position ) {
  if ( position.ware.type == 1 )
    if (position.ware.hasAlcoStamp == 1) {
      position.stamp.index = position.stamp.count;
      checkStamp(position.stamp.value);
    }
}

А то пиво на маркировку пыталсО проверить.
Что могу сказать, в целом скрипт жизнеспособный, проверка марок осуществляется очень быстро. На форуме ФСРАР выяснил, что эта проверка делается сразу на серверах ЕГАИС, а не локально на УТМ. Надо проверить как он отработает в случае отсутствия тырнета...
У нас же обычно как - кассир напикал бутылки и марки, покупатель дал карту, оплата прошла, у покупателя денег сняли, а потом чек в ЕГАИС не прошел, но покупателя это уже не волнует, он взял и пошел.

Автор: zenik 11.6.2021, 18:38

Сегодня, после нескольких дней успешной работы скрипт перестал работать одновременно на 20 кассах blink.gif
Отказала функция JSON:

Код
"Ошибка при выполнении скрипта!Строка 319:         throw new SyntaxError(""JSON.parse"");Исключение брошено и не поймано"

Пришлось что то думать. Решил отказаться от громоздкого парсинга JSON, сделал просто поиск по строке:
Код
  if ( xmlHTTP.responseText.indexOf("true") < 0 ) {
    frontol.actions.showMessage( "Внимание! Данная марка не прошла проверку на УТМ. Продажа не возможна!", Icon.Error + Button.Ok );
    frontol.actions.cancel();
  }

Но ситуация прямо таки очень странная. Скрипт посыпался у всех в одно время.

Автор: gudvin 28.6.2021, 21:53

а можно ли как то реализовать функционал, при считывании QR кода сигарет, расшифровываем его, получаем оттуда МРЦ, и если дата > 01.07.2021 и МРЦ < 108 выводить Ошибку,

frontol.actions.showMessage( "Внимание! Данная пачка сигарет запрещена к продаже", Icon.Error + Button.Ok );

с точек все товары вывезем. но хотелось бы какой то заплатки на этот случай, если вдруг кассир найдет и решит пробить такую пачку сигарет через кассу.

Автор: АТОЛ: Царюков Роман 28.6.2021, 22:00

У вас в DM сигарет имеется дата?

И как бы этот раздел для выкладывания ГОТОВЫХ решений, а не 'мне только спросить'

Автор: gudvin 28.6.2021, 22:21

Цитата(АТОЛ: Царюков Роман @ 28.6.2021, 23:00) *
У вас в DM сигарет имеется дата?

И как бы этот раздел для выкладывания ГОТОВЫХ решений, а не 'мне только спросить'

Роман, зачем дата нужна в DM , достаточно проверить системую.
Прошу простить если не в тот раздел, не нашел просто конкретной темы где можно спросить. Думалось в этой можно спокойно спрашивать.

Автор: АТОЛ: Царюков Роман 28.6.2021, 22:22

В этой теме спокойно можно решения свои готовые выкладывать

Автор: zenik 2.7.2021, 11:57

Сценарий запрещающий добавлять оплаты в "нулевой чек".

Код
function init() {

  frontol.addEventListener( "addPayment", "beforeAddPayment", true );

}

function beforeAddPayment( payment ) {

  if ( frontol.currentDocument.totalSum == 0 ) {
    frontol.actions.showMessage( "Сумма документ равна 0. Добавление оплаты не возможно.", Icon.Error + Button.Ok );
    frontol.actions.cancel();
  }

}

Автор: POUL 28.10.2021, 14:11

В JS нет встроенных функций хеширования, включая MD5, поэтому придется использовать сторонние реализации. Далее приведен пример, функция работает с UTF-8 и кириллическими символами.

Код
var MD5 = function(d) {
    d = unescape(encodeURIComponent(d));
    result = M(V(Y(X(d), 8 * d.length)));
    return result.toLowerCase();
};

function M(d) {
    for (var _, m = "0123456789ABCDEF", f = "", r = 0; r < d.length; r++) _ = d.charCodeAt(r), f += m.charAt(_ >>> 4 & 15) + m.charAt(15 & _);
    return f
}

function X(d) {
    for (var _ = Array(d.length >> 2), m = 0; m < _.length; m++) _[m] = 0;
    for (m = 0; m < 8 * d.length; m += 8) _[m >> 5] |= (255 & d.charCodeAt(m / 8)) << m % 32;
    return _
}

function V(d) {
    for (var _ = "", m = 0; m < 32 * d.length; m += 8) _ += String.fromCharCode(d[m >> 5] >>> m % 32 & 255);
    return _
}

function Y(d, _) {
    d[_ >> 5] |= 128 << _ % 32, d[14 + (_ + 64 >>> 9 << 4)] = _;
    for (var m = 1732584193, f = -271733879, r = -1732584194, i = 271733878, n = 0; n < d.length; n += 16) {
        var h = m,
            t = f,
            g = r,
            e = i;
        f = md5_ii(f = md5_ii(f = md5_ii(f = md5_ii(f = md5_hh(f = md5_hh(f = md5_hh(f = md5_hh(f = md5_gg(f = md5_gg(f = md5_gg(f = md5_gg(f = md5_ff(f = md5_ff(f = md5_ff(f = md5_ff(f, r = md5_ff(r, i = md5_ff(i, m = md5_ff(m, f, r, i, d[n + 0], 7, -680876936), f, r, d[n + 1], 12, -389564586), m, f, d[n + 2], 17, 606105819), i, m, d[n + 3], 22, -1044525330), r = md5_ff(r, i = md5_ff(i, m = md5_ff(m, f, r, i, d[n + 4], 7, -176418897), f, r, d[n + 5], 12, 1200080426), m, f, d[n + 6], 17, -1473231341), i, m, d[n + 7], 22, -45705983), r = md5_ff(r, i = md5_ff(i, m = md5_ff(m, f, r, i, d[n + 8], 7, 1770035416), f, r, d[n + 9], 12, -1958414417), m, f, d[n + 10], 17, -42063), i, m, d[n + 11], 22, -1990404162), r = md5_ff(r, i = md5_ff(i, m = md5_ff(m, f, r, i, d[n + 12], 7, 1804603682), f, r, d[n + 13], 12, -40341101), m, f, d[n + 14], 17, -1502002290), i, m, d[n + 15], 22, 1236535329), r = md5_gg(r, i = md5_gg(i, m = md5_gg(m, f, r, i, d[n + 1], 5, -165796510), f, r, d[n + 6], 9, -1069501632), m, f, d[n + 11], 14, 643717713), i, m, d[n + 0], 20, -373897302), r = md5_gg(r, i = md5_gg(i, m = md5_gg(m, f, r, i, d[n + 5], 5, -701558691), f, r, d[n + 10], 9, 38016083), m, f, d[n + 15], 14, -660478335), i, m, d[n + 4], 20, -405537848), r = md5_gg(r, i = md5_gg(i, m = md5_gg(m, f, r, i, d[n + 9], 5, 568446438), f, r, d[n + 14], 9, -1019803690), m, f, d[n + 3], 14, -187363961), i, m, d[n + 8], 20, 1163531501), r = md5_gg(r, i = md5_gg(i, m = md5_gg(m, f, r, i, d[n + 13], 5, -1444681467), f, r, d[n + 2], 9, -51403784), m, f, d[n + 7], 14, 1735328473), i, m, d[n + 12], 20, -1926607734), r = md5_hh(r, i = md5_hh(i, m = md5_hh(m, f, r, i, d[n + 5], 4, -378558), f, r, d[n + 8], 11, -2022574463), m, f, d[n + 11], 16, 1839030562), i, m, d[n + 14], 23, -35309556), r = md5_hh(r, i = md5_hh(i, m = md5_hh(m, f, r, i, d[n + 1], 4, -1530992060), f, r, d[n + 4], 11, 1272893353), m, f, d[n + 7], 16, -155497632), i, m, d[n + 10], 23, -1094730640), r = md5_hh(r, i = md5_hh(i, m = md5_hh(m, f, r, i, d[n + 13], 4, 681279174), f, r, d[n + 0], 11, -358537222), m, f, d[n + 3], 16, -722521979), i, m, d[n + 6], 23, 76029189), r = md5_hh(r, i = md5_hh(i, m = md5_hh(m, f, r, i, d[n + 9], 4, -640364487), f, r, d[n + 12], 11, -421815835), m, f, d[n + 15], 16, 530742520), i, m, d[n + 2], 23, -995338651), r = md5_ii(r, i = md5_ii(i, m = md5_ii(m, f, r, i, d[n + 0], 6, -198630844), f, r, d[n + 7], 10, 1126891415), m, f, d[n + 14], 15, -1416354905), i, m, d[n + 5], 21, -57434055), r = md5_ii(r, i = md5_ii(i, m = md5_ii(m, f, r, i, d[n + 12], 6, 1700485571), f, r, d[n + 3], 10, -1894986606), m, f, d[n + 10], 15, -1051523), i, m, d[n + 1], 21, -2054922799), r = md5_ii(r, i = md5_ii(i, m = md5_ii(m, f, r, i, d[n + 8], 6, 1873313359), f, r, d[n + 15], 10, -30611744), m, f, d[n + 6], 15, -1560198380), i, m, d[n + 13], 21, 1309151649), r = md5_ii(r, i = md5_ii(i, m = md5_ii(m, f, r, i, d[n + 4], 6, -145523070), f, r, d[n + 11], 10, -1120210379), m, f, d[n + 2], 15, 718787259), i, m, d[n + 9], 21, -343485551), m = safe_add(m, h), f = safe_add(f, t), r = safe_add(r, g), i = safe_add(i, e)
    }
    return Array(m, f, r, i)
}

function md5_cmn(d, _, m, f, r, i) {
    return safe_add(bit_rol(safe_add(safe_add(_, d), safe_add(f, i)), r), m)
}

function md5_ff(d, _, m, f, r, i, n) {
    return md5_cmn(_ & m | ~_ & f, d, _, r, i, n)
}

function md5_gg(d, _, m, f, r, i, n) {
    return md5_cmn(_ & f | m & ~f, d, _, r, i, n)
}

function md5_hh(d, _, m, f, r, i, n) {
    return md5_cmn(_ ^ m ^ f, d, _, r, i, n)
}

function md5_ii(d, _, m, f, r, i, n) {
    return md5_cmn(m ^ (_ | ~f), d, _, r, i, n)
}

function safe_add(d, _) {
    var m = (65535 & d) + (65535 & _);
    return (d >> 16) + (_ >> 16) + (m >> 16) << 16 | 65535 & m
}

function bit_rol(d, _) {
    return d << _ | d >>> 32 - _
}


Пример использования:

var result = MD5('Lorem Ipsum is simply dummy text of the printing');
console.log(result);

var result = MD5('Прародителем текста-рыбы является известный "Lorem Ipsum"');
console.log(result);

Русская версия Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)