Настройка акций в Кассире — различия между версиями

Материал из wiki.standart-n.ru
Перейти к: навигация, поиск
(Новая страница: «==Начало== В ТМС '''-305 "После добавления позиции в чек"''' создаем скрипт. Начало скрипта буд…»)
 
(Примеры акций)
 
(не показано 25 промежуточных версии 4 участников)
Строка 1: Строка 1:
==Начало==
+
=Настройка ТМС Акции в кассире=
  
В ТМС '''-305 "После добавления позиции в чек"''' создаем скрипт.
+
==Заменить версию Кассира==
Начало скрипта будет таким:
+
Заменить '''zkassa.exe''' на версию '''2.2.1.17''' от апреля 2016 г. или более новую.
  
 +
==Выполняем скрипт==
 
<pre>
 
<pre>
 +
/******************************************************************************/
 +
/***                Generated by IBExpert 18.04.2016 9:25:20                ***/
 +
/******************************************************************************/
 +
 +
/******************************************************************************/
 +
/***      Following SET SQL DIALECT is just for the Database Comparer      ***/
 +
/******************************************************************************/
 +
SET SQL DIALECT 3;
 +
 +
 +
 +
/******************************************************************************/
 +
/***                                Tables                                ***/
 +
/******************************************************************************/
 +
 +
 +
 +
CREATE TABLE DSC_RULES (
 +
    ID          INTEGER NOT NULL,
 +
    DATE_BEG    DM_DATE NOT NULL /* DM_DATE = DATE */,
 +
    DATE_END    DM_DATE NOT NULL /* DM_DATE = DATE */,
 +
    MMBSH1      DM_TEXT /* DM_TEXT = VARCHAR(250) */,
 +
    NDS_O1      DM_ID_NULL /* DM_ID_NULL = BIGINT */,
 +
    SNAME1      DM_TEXT /* DM_TEXT = VARCHAR(250) */,
 +
    SNAME2      DM_TEXT /* DM_TEXT = VARCHAR(250) */,
 +
    SNAME3      DM_TEXT /* DM_TEXT = VARCHAR(250) */,
 +
    SNAME4      DM_TEXT /* DM_TEXT = VARCHAR(250) */,
 +
    SNAME5      DM_TEXT /* DM_TEXT = VARCHAR(250) */,
 +
    PRICE_MIN    DM_DOUBLE /* DM_DOUBLE = DOUBLE PRECISION */,
 +
    PRICE_MAX    DM_DOUBLE /* DM_DOUBLE = DOUBLE PRECISION */,
 +
    QUANT_DIV    DM_ID_NULL /* DM_ID_NULL = BIGINT */,
 +
    QUANT_DSC    DM_ID_NULL /* DM_ID_NULL = BIGINT */,
 +
    SUM_DSC      DM_DOUBLE /* DM_DOUBLE = DOUBLE PRECISION */,
 +
    PERC_DSC    DM_DOUBLE /* DM_DOUBLE = DOUBLE PRECISION */,
 +
    BARCODE_DSC  DM_TEXT /* DM_TEXT = VARCHAR(250) */,
 +
    STATUS      SMALLINT NOT NULL,
 +
    COMMENTS    DM_TEXT /* DM_TEXT = VARCHAR(250) */,
 +
    D$UUID      DM_UUID /* DM_UUID = CHAR(36) NOT NULL */,
 +
    D$SRVUPDDT  DM_DATETIME /* DM_DATETIME = TIMESTAMP */
 +
);
 +
 +
 +
 +
 +
/******************************************************************************/
 +
/***                                Triggers                                ***/
 +
/******************************************************************************/
 +
 +
 +
SET TERM ^ ;
 +
 +
 +
 +
/******************************************************************************/
 +
/***                          Triggers for tables                          ***/
 +
/******************************************************************************/
 +
 +
 +
 +
/* Trigger: DSC_RULES_AD_DISTR */
 +
CREATE OR ALTER TRIGGER DSC_RULES_AD_DISTR FOR DSC_RULES
 +
ACTIVE AFTER DELETE POSITION 0
 +
AS
 +
begin
 +
  update or insert into g$distribute (TABLENAME,UUID,SOPER,FROM_PROFILE_ID) values ('DSC_RULES',old.d$uuid,2,null)
 +
  matching (TABLENAME,UUID);
 +
end
 +
^
 +
 +
 +
/* Trigger: DSC_RULES_BI0 */
 +
CREATE OR ALTER TRIGGER DSC_RULES_BI0 FOR DSC_RULES
 +
ACTIVE BEFORE INSERT POSITION 0
 +
AS
 +
begin
 +
  select max(id)+1 from DSC_RULES into new.id;
 +
  if (new.id is null) then new.id = 1;
 +
end
 +
^
 +
 +
 +
/* Trigger: DSC_RULES_BIU0 */
 +
CREATE OR ALTER TRIGGER DSC_RULES_BIU0 FOR DSC_RULES
 +
ACTIVE BEFORE INSERT OR UPDATE POSITION 0
 +
AS
 +
begin
 +
  new.mmbsh1 = UPPER(new.mmbsh1);
 +
  new.sname1 = UPPER(new.sname1);
 +
  new.sname2 = UPPER(new.sname2);
 +
  new.sname3 = UPPER(new.sname3);
 +
  new.sname4 = UPPER(new.sname4);
 +
  new.sname5 = UPPER(new.sname5);
 +
end
 +
^
 +
 +
 +
/* Trigger: DSC_RULES_BI_DISTR */
 +
CREATE OR ALTER TRIGGER DSC_RULES_BI_DISTR FOR DSC_RULES
 +
ACTIVE BEFORE INSERT POSITION 9999
 +
AS
 +
begin
 +
  if (new.d$uuid is null) then
 +
  begin
 +
    new.d$uuid=UUID_TO_CHAR(GEN_UUID());
 +
    new.d$srvupddt='2000-01-01';
 +
    update or insert into g$distribute (TABLENAME,UUID,SOPER,FROM_PROFILE_ID) values ('DSC_RULES',new.d$uuid,0,null) matching (TABLENAME,UUID);
 +
  end
 +
end
 +
^
 +
 +
 +
/* Trigger: DSC_RULES_BU_DISTR */
 +
CREATE OR ALTER TRIGGER DSC_RULES_BU_DISTR FOR DSC_RULES
 +
ACTIVE BEFORE UPDATE POSITION 0
 +
AS
 +
begin
 +
  if (new.D$SRVUPDDT=old.D$SRVUPDDT) then
 +
    update or insert into g$distribute (TABLENAME,UUID,SOPER,FROM_PROFILE_ID) values ('DSC_RULES',new.d$uuid,1,null)
 +
    matching (TABLENAME,UUID);
 +
end
 +
^
 +
 +
 +
SET TERM ; ^
 +
 +
 +
 +
/******************************************************************************/
 +
/***                              Privileges                              ***/
 +
/******************************************************************************/
 +
 +
 +
SET TERM ^ ;
 +
 +
create or alter procedure PR_GET_DSC_RULES (
 +
    PART_ID_IN DM_ID,
 +
    DOC_ID DM_ID,
 +
    DEL integer = 0)
 +
returns (
 +
    PART_ID_OUT integer,
 +
    DISCOUNT DM_DOUBLE,
 +
    BARCODE_DSC DM_TEXT)
 +
as
 +
declare variable SNAME DM_TEXT;
 +
declare variable PRICE DM_DOUBLE;
 +
declare variable QUANT DM_DOUBLE;
 +
declare variable SNAME1 DM_TEXT;
 +
declare variable SNAME2 DM_TEXT;
 +
declare variable SNAME3 DM_TEXT;
 +
declare variable SNAME4 DM_TEXT;
 +
declare variable SNAME5 DM_TEXT;
 +
declare variable PRICE_MIN DM_DOUBLE;
 +
declare variable PRICE_MAX DM_DOUBLE;
 +
declare variable QUANT_DIV DM_ID;
 +
declare variable QUANT_DSC DM_ID;
 +
declare variable SUM_DSC DM_DOUBLE;
 +
declare variable PERC_DSC DM_DOUBLE;
 +
declare variable MAX_PART_ID DM_ID;
 +
declare variable SUM_QUANT DM_DOUBLE;
 +
declare variable NO_DSC integer;
 +
declare variable CONST_MAX_PRICE integer;
 +
declare variable MMBSH DM_TEXT1024;
 +
declare variable NDS_O DM_ID_NULL;
 +
declare variable MMBSH1 DM_TEXT;
 +
declare variable NDS_O1 DM_ID_NULL;
 +
declare variable BARCODE DM_TEXT;
 +
begin
 +
-------------------- Описание параметров ---------------------------------------------------
 +
--Процедура вызывается в кассире в событиях -305 и -332
 +
--PART_ID_IN - партия, по которой изменили кол-во/добавили/удалили в чеке, и на которую необходимо рассчитать скидку
 +
--DOC_ID - чек
 +
--DEL - признак, что партию удаляют из чека
 +
 +
--PART_ID_OUT - список партий по которым нужно обновить скидку
 +
--DISCOUNT - скидка в рублях
 +
--BARCODE_DSC - штрихкод скидки, который нужно применить (суммовая скидка работает быстрее, лучше использовать её)
 +
 +
------------------- Описание DSC_RULES ------------------------------------------------------
 +
--DATE_BEG и DATE_END - период действия скидки
 +
--MMBSH1 - группа товара
 +
--NDS_O1 - ставка НДС опт (10,18,12 ....)
 +
--SNAME1 ... SNAME5 - слова, которым должна соответствовать позиция для того, чтобы скидка назначилась (все не обязательны)
 +
--PRICE_MIN и PRICE_MAX - ценовой диапазон, которому должна соответствовать позиция, чтобы скидка назначилась (все не обязательны)
 +
--QUANT_DIV и QUANT_DSC, для условий типа: при покупке 4х штук (QUANT_DIV) одна(QUANT_DSC) в подарок
 +
--SUM_DSC - суммовая скидка на позицию в рублях
 +
--PERC_DSC - процентная скидка на позицию в % (например 0.15)
 +
--BARCODE_DSC - штрихкод скидки, который нужно применить (суммовая скидка работает быстрее, лучше использовать её)
 +
--STATUS, 1 - правило активно, 0 - неактивно
 +
 +
  CONST_MAX_PRICE = 99999;
 +
  DISCOUNT = 0;
 +
  PART_ID_OUT = PART_ID_IN;
 +
 +
  select UPPER(sname), price, abs(quant), coalesce(nds,0), UPPER(mmbsh) from vw_doc_detail_active where doc_id = :doc_id and part_id = :part_id_in into :sname, :price, :quant, :nds_o, :mmbsh;
 +
 +
  for select MMBSH1, NDS_O1, SNAME1,SNAME2,SNAME3,SNAME4,SNAME5,PRICE_MIN,PRICE_MAX,QUANT_DIV,QUANT_DSC,SUM_DSC,PERC_DSC,BARCODE_DSC
 +
        from DSC_RULES dr where status = 1 and dr.date_beg <= current_date and dr.date_end >= current_date
 +
      into :MMBSH1, :NDS_O1, :SNAME1,:SNAME2,:SNAME3,:SNAME4,:SNAME5,:PRICE_MIN,:PRICE_MAX,:QUANT_DIV,:QUANT_DSC,:SUM_DSC,:PERC_DSC,:BARCODE do
 +
  begin
 +
    NO_DSC = 0;
 +
 +
    if (:MMBSH1 is null) then MMBSH1 = '';
 +
    if (:SNAME1 is null) then SNAME1 = '';
 +
    if (:SNAME2 is null) then SNAME2 = '';
 +
    if (:SNAME3 is null) then SNAME3 = '';
 +
    if (:SNAME4 is null) then SNAME4 = '';
 +
    if (:SNAME5 is null) then SNAME5 = '';
 +
 +
    --Проверка на группу
 +
    if (NO_DSC = 0) then
 +
    if ( Not(MMBSH containing MMBSH1) ) Then
 +
    NO_DSC = 1;
 +
 +
    --Проверка на НДС
 +
    if (NO_DSC = 0) then
 +
    if((:NDS_O1 is not null) and (:NDS_O1 <> :NDS_O)) Then
 +
    NO_DSC = 1;
 +
 +
    --Проверка на наименование
 +
    if (NO_DSC = 0) then
 +
    if (Not((sname containing SNAME1) and (sname containing SNAME2) and (sname containing SNAME3) and (sname containing SNAME4) and (sname containing SNAME5))) Then
 +
    NO_DSC = 1;
 +
 +
    --Проверка на цену
 +
    if (NO_DSC = 0) then
 +
    if ( (price < coalesce(PRICE_MIN,0)) or
 +
        (price > coalesce(PRICE_MAX,CONST_MAX_PRICE)) ) Then
 +
    NO_DSC = 1;
 +
 +
    --Проверка на кратность
 +
    if (NO_DSC = 0) then
 +
    if (QUANT_DIV is not null) then
 +
    begin
 +
      --Определяем партию на которую будем назначать скидку
 +
      select max(part_id), abs(sum(quant)), min(price) from vw_doc_detail_active da where doc_id = :doc_id
 +
                                                                                          and UPPER(da.sname) containing :SNAME1
 +
                                                                                          and UPPER(da.sname) containing :SNAME2
 +
                                                                                          and UPPER(da.sname) containing :SNAME3
 +
                                                                                          and UPPER(da.sname) containing :SNAME4
 +
                                                                                          and UPPER(da.sname) containing :SNAME5
 +
                                                                      and ( ((:DEL = 1) and (part_id <> :PART_ID_IN)) or (:DEL = 0) )
 +
                                                                      and (price >= coalesce(:PRICE_MIN,0)) and (price <= coalesce(:PRICE_MAX,:CONST_MAX_PRICE))
 +
                                                                      and UPPER(da.MMBSH) containing :MMBSH1
 +
                                                                      and ( (:NDS_O1 is null) or ((:NDS_O1 is not null) and (:NDS_O1 = NDS)) )
 +
      into :max_part_id, :sum_quant, :price;
 +
        --Скидка назначается только на партию с максимальным ID и если условие кратности соблюдается
 +
        --По всем остальным партиям скидку сбрасываем
 +
        for select part_id from vw_doc_detail_active da where doc_id = :doc_id and UPPER(da.sname) containing :SNAME1
 +
                                                                              and UPPER(da.sname) containing :SNAME2
 +
                                                                              and UPPER(da.sname) containing :SNAME3
 +
                                                                              and UPPER(da.sname) containing :SNAME4
 +
                                                                              and UPPER(da.sname) containing :SNAME5
 +
                                                            and ( ((:DEL = 1) and (part_id <> :PART_ID_IN)) or (:DEL = 0) )
 +
                                                            and (price >= coalesce(:PRICE_MIN,0)) and (price <= coalesce(:PRICE_MAX,:CONST_MAX_PRICE))
 +
                                                            and UPPER(da.MMBSH) containing :MMBSH1
 +
                                                            and ( (:NDS_O1 is null) or ((:NDS_O1 is not null) and (:NDS_O1 = NDS)) )
 +
        into :PART_ID_OUT do
 +
        Begin
 +
          if ((max_part_id = PART_ID_OUT) and (Trunc(sum_quant/QUANT_DIV) > 0)) then
 +
            begin
 +
              if (QUANT_DSC is not null) then DISCOUNT = QUANT_DSC*:price*Trunc(sum_quant/QUANT_DIV);
 +
              if (SUM_DSC is not null) then DISCOUNT = SUM_DSC*Trunc(sum_quant/QUANT_DIV);
 +
              if (PERC_DSC is not null) then DISCOUNT = :quant*:price*PERC_DSC;
 +
              if (BARCODE is not null) then BARCODE_DSC = :BARCODE;
 +
            end
 +
          else
 +
            Begin
 +
              DISCOUNT = 0;
 +
              BARCODE_DSC = Null;
 +
            end
 +
          suspend;
 +
        End
 +
      exit;
 +
    End
 +
 +
    --Суммовая скидка
 +
    if ( (NO_DSC = 0) and (DEL = 0) and ((SUM_DSC is not null) or (BARCODE is not null)) ) then
 +
    Begin
 +
      DISCOUNT =  :quant*SUM_DSC;
 +
      BARCODE_DSC = BARCODE;
 +
      suspend;
 +
      exit;
 +
    End
 +
 +
    --Процентная скидка
 +
    if ( (NO_DSC = 0) and (DEL = 0) and ((PERC_DSC is not null) or (BARCODE is not null)) ) then
 +
    Begin
 +
      DISCOUNT =  :quant*:price*PERC_DSC;
 +
      BARCODE_DSC = BARCODE;
 +
      suspend;
 +
      exit;
 +
    End
 +
 +
  End
 +
end^
 +
 +
SET TERM ; ^
 +
 +
/* Following GRANT statetements are generated automatically */
 +
 +
GRANT SELECT ON VW_DOC_DETAIL_ACTIVE TO PROCEDURE PR_GET_DSC_RULES;
 +
GRANT SELECT ON DSC_RULES TO PROCEDURE PR_GET_DSC_RULES;
 +
 +
/* Existing privileges on this procedure */
 +
 +
GRANT EXECUTE ON PROCEDURE PR_GET_DSC_RULES TO SYSDBA;
 +
</PRE>
 +
 +
==Создаем в кассире ТМС==
 +
<PRE>
 
uses
 
uses
 
   Graphics, Controls, Forms, Dialogs, StdCtrls,
 
   Graphics, Controls, Forms, Dialogs, StdCtrls,
Строка 10: Строка 321:
 
   ScriptRes, Barcode,ZKassa, StrUtils, Windows, Classes,
 
   ScriptRes, Barcode,ZKassa, StrUtils, Windows, Classes,
 
   IBDataBase, SysUtils, DateUtils, chequelist;
 
   IBDataBase, SysUtils, DateUtils, chequelist;
 +
 +
Procedure  RecalcDSC(del:integer)
 
var iq: tibquery;
 
var iq: tibquery;
    dsc, part_id: integer;
 
    discount: float;
 
 
begin
 
begin
 
  Try
 
  Try
Строка 19: Строка 330:
 
   iq.transaction.starttransaction;
 
   iq.transaction.starttransaction;
 
   iq.active := False;
 
   iq.active := False;
</pre>
 
  
==Примеры акций==
+
  iq.sql.text := 'select * from PR_GET_DSC_RULES('+IntToStr(ChequeList.ActivePID)+','+IntToStr(ChequeList.Active.ID)+','+IntToStr(del)+')';
Далее пишем скрипты акций.
+
  iq.active := True;
 +
  while not (iq.Eof) do
 +
  begin
 +
      ChequeList.Active.P_Index:= ChequeList.Active.IndexByPartID(iq.FieldByName('part_id_out').AsInteger);
 +
      if (iq.FieldByName('BARCODE_DSC').AsString = '') Then
 +
        ChequeList.Active.P_SetDiscount(-1*iq.FieldByName('DISCOUNT').AsFloat)
 +
      else
 +
        Barcode.ApplyDiscount(iq.FieldByName('BARCODE_DSC').AsString, iq.FieldByName('part_id_out').AsInteger);
 +
      iq.next;
 +
  End;
  
===Пример №1===
+
finally
* скидка 20% на группу "Акция"
+
    iq.active := False;
* акция действует с 31.07.2015 по 23.08.2015
+
    iq.transaction.free;
 +
    iq.free;
 +
end;
  
<pre>
+
end;
  if ((date() >= StrToDate('31.07.2015')) and (date() <= StrToDate('23.08.2015'))) Then
+
</PRE>
  Begin
+
  part_id := ChequeList.ActivePID;
+
  //запросик к базе
+
  iq.sql.text := 'select abs(price) as price, abs(quant) as quant from vw_doc_detail_active where UPPER(mmbsh) containing ''АКЦИЯ'' and doc_id='+inttostr(ChequeList.Active.ID)+' and part_id = '+inttostr(part_id) ;
+
  iq.active := True;
+
  //забираем данные
+
  if not(iq.Eof) then
+
    begin
+
      dsc := -0.20;
+
      ChequeList.Active.P_Index:= ChequeList.Active.IndexByPartID(part_id);
+
      ChequeList.Active.P_SetDiscount(abs(iq.FieldByName('price').AsFloat*iq.FieldByName('quant').AsFloat)*dsc);
+
    end;                 
+
  end;
+
</pre>
+
  
 +
'''Вызываем RecalcDSC в ТМС'''
 +
'''-305 как  RecalcDSC(0); '''
 +
'''-332 как  RecalcDSC(1); '''
 +
Скрипт должен выглядеть примерно так:
 +
<PRE>
 +
uses
 +
  p10112u1;
 +
begin
 +
  RecalcDSC(0);
 +
end;
 +
</PRE>
  
===Пример №2===
+
==Пример №2 (старый мех-м)==
* скидка 10% на группу "Акция"
+
* акция действует всегда пока включен переключатель в параметрах системы
* акция действует с 24.08.2015 по 24.09.2015
+
<PRE>
 +
INSERT INTO PARAMS ( PARENT_ID, PARAM_ID, PARAM_CAPTION, PARAM_TYPE, PARAM_VALUE, AUDIT_ID, IMAGEINDEX,
 +
STATUS, INSERTDT, SORTING, PARAM_TYPE_DATA, PACKET) VALUES ( -30, 'AKCCII_TOGGLE', 'Включение акций', 14, '0', 3229, NULL, 0, '19-DEC-2015 10:45:34.828', NULL, 'FIXEDITEMS SHOWONLYVALUES RETURNNAME
 +
0=выключено
 +
1=включено');
 +
</PRE>
 +
* если переключатель включен - в кассире при продаже активируется скидка = разнице между заполненными колонками price и ещё одной ценовой колонкой, ниже использована PRICE_R
  
   //Акция с 24.08.2015 по 24.09.2015 на товары группы Акция скидка
+
<PRE>
   //заявка в УР 483165
+
uses
   //if ((date() >= StrToDate('24.08.2015')) and (date() <= StrToDate('24.09.2015'))) Then  //заявка в УР 491368
+
   Graphics, Controls, Forms, Dialogs, StdCtrls,
   Begin
+
   ComCtrls, ExtCtrls, ibquery, DB, ChequeList, FR,
  part_id := ChequeList.ActivePID;
+
   ScriptRes, Barcode,ZKassa, StrUtils, Windows, Classes,
  //запросик к базе
+
   IBDataBase, SysUtils, DateUtils, chequelist;
  iq.sql.text := 'select abs(price) as price, abs(quant) as quant from vw_doc_detail_active where UPPER(mmbsh) containing ''АКЦИЯ'' and doc_id='+inttostr(ChequeList.Active.ID)+' and part_id = '+inttostr(part_id) ;
+
var iq: tibquery;
  iq.active := True;
+
    dsc, part_id: integer;
  //забираем данные
+
    discount: float;
   if not(iq.Eof) then
+
begin
    begin
+
Try
      dsc := -0.10;
+
   iq := tibquery.create(nil);
      ChequeList.Active.P_Index:= ChequeList.Active.IndexByPartID(part_id);
+
  iq.transaction := CreateRT(CurrDB);
      ChequeList.Active.P_SetDiscount(abs(iq.FieldByName('price').AsFloat*iq.FieldByName('quant').AsFloat)*dsc);
+
  iq.transaction.starttransaction;
    end;
+
   iq.active := False;
   end;
+
  
==Конец==
+
  iq.sql.text := 'select wb.part_id,wb.PRICE_R,(select p.param_value from params p where p.param_id = ''AKCCII_TOGGLE'') from  vw_warebase wb  where wb.part_id = '+
Весь сркипт заканчивается так:
+
  IntToStr(ChequeList.ActivePID);
  
<pre>
+
  iq.active := True;
 +
  if not(iq.Eof) then
 +
    if ( not iq.FieldByName('param_value').AsInteger=0 )then
 +
      if ( not iq.FieldByName('PRICE_R').isNull )then
 +
        if (iq.FieldByName('PRICE_R').AsFloat>0) then begin
 +
            ChequeList.Active.P_Index := ChequeList.Active.IndexByPartID(iq.FieldByName('part_id').AsInteger);
 +
            temp := -(ChequeList.Active.P_Price - iq.FieldByName('PRICE_R').AsFloat);
 +
            ChequeList.Active.P_SetDiscount(temp);
 +
        end;
 
   finally
 
   finally
 
   iq.active := False;
 
   iq.active := False;
Строка 77: Строка 408:
 
   end;
 
   end;
  
end;
+
</PRE>
</pre>
+
 
 +
=Отправка ТМС по синхронизации=
 +
 
 +
==Выгружаем тмс из базы из blob поля в файл==
 +
Тмс можно найти спомощью запроса:
 +
select * from groups g where g.id=-305
 +
 
 +
Затем сохраняем на жесткий диск тмс из поля '''data'''.
 +
 
 +
==Загружаем ТМС в серверную базу==
 +
* В таблице '''g$profiles''' выбираем blob поле, которое будем использовать для отправки, например '''TMP_BLOB1'''.
 +
* Убеждаемся, что данная таблица синхронизируется в двухстороннем направлении.
 +
* Загружаем в это поле например первому профилю наш ТМС скрипт.
 +
* Ждем когда это обновление придет всем по синхронизации.
 +
 
 +
==Применяем всем ТМС==
 +
По '''g$tasks''' отправляем запрос нужным профилям:
 +
update groups gp set gp.data = (select g.tmp_blob1 from G$PROFILES g where g.id = 1) where gp.id = -305;
 +
 
 +
=Примеры акций=
 +
1. Акция на апрель, Эссенциале №30 - скидка 150 руб за каждую упаковку
 +
2. Акция до конца мая, на Магне №50 (с ценой от 500 до 1000 руб) - при покупке 4х упаковок 1 в подарок;
 +
3. Акция на Ферверкс, при покупке 2х упаковок 1 в подарок;
 +
4. Аналогично Ферверксу, только на Маалокс;
 +
5 и 6. На зодак №7 и №28 скидка 15%;
 +
7. Акция на Эмолиум, при покупке 2х упаковок скидка 350 руб.;
 +
8. Аналогично, на Фитолизин - 100 руб.;
 +
9. На все ЖНВЛС скидка 5%;
 +
10. На все товары с НДС опт. 10%, - скидка 10%;
 +
11. На все товары с НДС опт. 18%, - скидка 15%;
 +
 
 +
[[Файл:Примеры_акций.png|Окно программы]]
 +
 
 +
* '''DATE_BEG''' и '''DATE_END''' - период действия скидки
 +
* '''MMBSH1''' - группа товара
 +
* '''NDS_O1''' - ставка НДС опт (10,18,12 ....)
 +
* '''SNAME1''' ... '''SNAME5''' - слова, которым должна соответствовать позиция для того, чтобы скидка назначилась (все не обязательны)
 +
* '''PRICE_MIN''' и '''PRICE_MAX''' - ценовой диапазон, которому должна соответствовать позиция, чтобы скидка назначилась (все не обязательны)
 +
* '''QUANT_DIV''' и '''QUANT_DSC''', для условий типа: при покупке 4х штук (QUANT_DIV) одна(QUANT_DSC) в подарок
 +
* '''SUM_DSC''' - суммовая скидка на позицию в рублях
 +
* '''PERC_DSC''' - процентная скидка на позицию в % (например 0.15)
 +
* '''STATUS''', 1 - правило активно, 0 - неактивно
 +
 
 +
 
 +
[[Problems:Настройка системы скидок для сети|'''ПОСМОТРЕТЬ ЕЩЕ ПРИМЕРЫ''']]
 +
 
 +
 
 +
=Видео=
 +
* [[Файл:Ролик по акциям2.mp4]]

Текущая версия на 16:58, 14 сентября 2018

Настройка ТМС Акции в кассире

Заменить версию Кассира

Заменить zkassa.exe на версию 2.2.1.17 от апреля 2016 г. или более новую.

Выполняем скрипт

/******************************************************************************/
/***                Generated by IBExpert 18.04.2016 9:25:20                ***/
/******************************************************************************/

/******************************************************************************/
/***      Following SET SQL DIALECT is just for the Database Comparer       ***/
/******************************************************************************/
SET SQL DIALECT 3;



/******************************************************************************/
/***                                 Tables                                 ***/
/******************************************************************************/



CREATE TABLE DSC_RULES (
    ID           INTEGER NOT NULL,
    DATE_BEG     DM_DATE NOT NULL /* DM_DATE = DATE */,
    DATE_END     DM_DATE NOT NULL /* DM_DATE = DATE */,
    MMBSH1       DM_TEXT /* DM_TEXT = VARCHAR(250) */,
    NDS_O1       DM_ID_NULL /* DM_ID_NULL = BIGINT */,
    SNAME1       DM_TEXT /* DM_TEXT = VARCHAR(250) */,
    SNAME2       DM_TEXT /* DM_TEXT = VARCHAR(250) */,
    SNAME3       DM_TEXT /* DM_TEXT = VARCHAR(250) */,
    SNAME4       DM_TEXT /* DM_TEXT = VARCHAR(250) */,
    SNAME5       DM_TEXT /* DM_TEXT = VARCHAR(250) */,
    PRICE_MIN    DM_DOUBLE /* DM_DOUBLE = DOUBLE PRECISION */,
    PRICE_MAX    DM_DOUBLE /* DM_DOUBLE = DOUBLE PRECISION */,
    QUANT_DIV    DM_ID_NULL /* DM_ID_NULL = BIGINT */,
    QUANT_DSC    DM_ID_NULL /* DM_ID_NULL = BIGINT */,
    SUM_DSC      DM_DOUBLE /* DM_DOUBLE = DOUBLE PRECISION */,
    PERC_DSC     DM_DOUBLE /* DM_DOUBLE = DOUBLE PRECISION */,
    BARCODE_DSC  DM_TEXT /* DM_TEXT = VARCHAR(250) */,
    STATUS       SMALLINT NOT NULL,
    COMMENTS     DM_TEXT /* DM_TEXT = VARCHAR(250) */,
    D$UUID       DM_UUID /* DM_UUID = CHAR(36) NOT NULL */,
    D$SRVUPDDT   DM_DATETIME /* DM_DATETIME = TIMESTAMP */
);




/******************************************************************************/
/***                                Triggers                                ***/
/******************************************************************************/


SET TERM ^ ;



/******************************************************************************/
/***                          Triggers for tables                           ***/
/******************************************************************************/



/* Trigger: DSC_RULES_AD_DISTR */
CREATE OR ALTER TRIGGER DSC_RULES_AD_DISTR FOR DSC_RULES
ACTIVE AFTER DELETE POSITION 0
AS
begin
  update or insert into g$distribute (TABLENAME,UUID,SOPER,FROM_PROFILE_ID) values ('DSC_RULES',old.d$uuid,2,null)
  matching (TABLENAME,UUID);
end
^


/* Trigger: DSC_RULES_BI0 */
CREATE OR ALTER TRIGGER DSC_RULES_BI0 FOR DSC_RULES
ACTIVE BEFORE INSERT POSITION 0
AS
begin
  select max(id)+1 from DSC_RULES into new.id;
  if (new.id is null) then new.id = 1;
end
^


/* Trigger: DSC_RULES_BIU0 */
CREATE OR ALTER TRIGGER DSC_RULES_BIU0 FOR DSC_RULES
ACTIVE BEFORE INSERT OR UPDATE POSITION 0
AS
begin
  new.mmbsh1 = UPPER(new.mmbsh1);
  new.sname1 = UPPER(new.sname1);
  new.sname2 = UPPER(new.sname2);
  new.sname3 = UPPER(new.sname3);
  new.sname4 = UPPER(new.sname4);
  new.sname5 = UPPER(new.sname5);
end
^


/* Trigger: DSC_RULES_BI_DISTR */
CREATE OR ALTER TRIGGER DSC_RULES_BI_DISTR FOR DSC_RULES
ACTIVE BEFORE INSERT POSITION 9999
AS
begin
  if (new.d$uuid is null) then
  begin
    new.d$uuid=UUID_TO_CHAR(GEN_UUID());
    new.d$srvupddt='2000-01-01';
    update or insert into g$distribute (TABLENAME,UUID,SOPER,FROM_PROFILE_ID) values ('DSC_RULES',new.d$uuid,0,null) matching (TABLENAME,UUID);
  end
end
^


/* Trigger: DSC_RULES_BU_DISTR */
CREATE OR ALTER TRIGGER DSC_RULES_BU_DISTR FOR DSC_RULES
ACTIVE BEFORE UPDATE POSITION 0
AS
begin
  if (new.D$SRVUPDDT=old.D$SRVUPDDT) then
    update or insert into g$distribute (TABLENAME,UUID,SOPER,FROM_PROFILE_ID) values ('DSC_RULES',new.d$uuid,1,null)
    matching (TABLENAME,UUID);
end
^


SET TERM ; ^



/******************************************************************************/
/***                               Privileges                               ***/
/******************************************************************************/


SET TERM ^ ;

create or alter procedure PR_GET_DSC_RULES (
    PART_ID_IN DM_ID,
    DOC_ID DM_ID,
    DEL integer = 0)
returns (
    PART_ID_OUT integer,
    DISCOUNT DM_DOUBLE,
    BARCODE_DSC DM_TEXT)
as
declare variable SNAME DM_TEXT;
declare variable PRICE DM_DOUBLE;
declare variable QUANT DM_DOUBLE;
declare variable SNAME1 DM_TEXT;
declare variable SNAME2 DM_TEXT;
declare variable SNAME3 DM_TEXT;
declare variable SNAME4 DM_TEXT;
declare variable SNAME5 DM_TEXT;
declare variable PRICE_MIN DM_DOUBLE;
declare variable PRICE_MAX DM_DOUBLE;
declare variable QUANT_DIV DM_ID;
declare variable QUANT_DSC DM_ID;
declare variable SUM_DSC DM_DOUBLE;
declare variable PERC_DSC DM_DOUBLE;
declare variable MAX_PART_ID DM_ID;
declare variable SUM_QUANT DM_DOUBLE;
declare variable NO_DSC integer;
declare variable CONST_MAX_PRICE integer;
declare variable MMBSH DM_TEXT1024;
declare variable NDS_O DM_ID_NULL;
declare variable MMBSH1 DM_TEXT;
declare variable NDS_O1 DM_ID_NULL;
declare variable BARCODE DM_TEXT;
begin
-------------------- Описание параметров ---------------------------------------------------
--Процедура вызывается в кассире в событиях -305 и -332
--PART_ID_IN - партия, по которой изменили кол-во/добавили/удалили в чеке, и на которую необходимо рассчитать скидку
--DOC_ID - чек
--DEL - признак, что партию удаляют из чека

--PART_ID_OUT - список партий по которым нужно обновить скидку
--DISCOUNT - скидка в рублях
--BARCODE_DSC - штрихкод скидки, который нужно применить (суммовая скидка работает быстрее, лучше использовать её)

------------------- Описание DSC_RULES ------------------------------------------------------
--DATE_BEG и DATE_END - период действия скидки
--MMBSH1 - группа товара
--NDS_O1 - ставка НДС опт (10,18,12 ....)
--SNAME1 ... SNAME5 - слова, которым должна соответствовать позиция для того, чтобы скидка назначилась (все не обязательны)
--PRICE_MIN и PRICE_MAX - ценовой диапазон, которому должна соответствовать позиция, чтобы скидка назначилась (все не обязательны)
--QUANT_DIV и QUANT_DSC, для условий типа: при покупке 4х штук (QUANT_DIV) одна(QUANT_DSC) в подарок
--SUM_DSC - суммовая скидка на позицию в рублях
--PERC_DSC - процентная скидка на позицию в % (например 0.15)
--BARCODE_DSC - штрихкод скидки, который нужно применить (суммовая скидка работает быстрее, лучше использовать её)
--STATUS, 1 - правило активно, 0 - неактивно

  CONST_MAX_PRICE = 99999;
  DISCOUNT = 0;
  PART_ID_OUT = PART_ID_IN;

  select UPPER(sname), price, abs(quant), coalesce(nds,0), UPPER(mmbsh) from vw_doc_detail_active where doc_id = :doc_id and part_id = :part_id_in into :sname, :price, :quant, :nds_o, :mmbsh;

  for select MMBSH1, NDS_O1, SNAME1,SNAME2,SNAME3,SNAME4,SNAME5,PRICE_MIN,PRICE_MAX,QUANT_DIV,QUANT_DSC,SUM_DSC,PERC_DSC,BARCODE_DSC
        from DSC_RULES dr where status = 1 and dr.date_beg <= current_date and dr.date_end >= current_date
      into :MMBSH1, :NDS_O1, :SNAME1,:SNAME2,:SNAME3,:SNAME4,:SNAME5,:PRICE_MIN,:PRICE_MAX,:QUANT_DIV,:QUANT_DSC,:SUM_DSC,:PERC_DSC,:BARCODE do
  begin 
    NO_DSC = 0;

    if (:MMBSH1 is null) then MMBSH1 = '';
    if (:SNAME1 is null) then SNAME1 = '';
    if (:SNAME2 is null) then SNAME2 = '';
    if (:SNAME3 is null) then SNAME3 = '';
    if (:SNAME4 is null) then SNAME4 = '';
    if (:SNAME5 is null) then SNAME5 = '';

    --Проверка на группу
    if (NO_DSC = 0) then
    if ( Not(MMBSH containing MMBSH1) ) Then
     NO_DSC = 1;

    --Проверка на НДС
    if (NO_DSC = 0) then
    if((:NDS_O1 is not null) and (:NDS_O1 <> :NDS_O)) Then
     NO_DSC = 1;

    --Проверка на наименование
    if (NO_DSC = 0) then
    if (Not((sname containing SNAME1) and (sname containing SNAME2) and (sname containing SNAME3) and (sname containing SNAME4) and (sname containing SNAME5))) Then
     NO_DSC = 1;

    --Проверка на цену
    if (NO_DSC = 0) then
    if ( (price < coalesce(PRICE_MIN,0)) or
         (price > coalesce(PRICE_MAX,CONST_MAX_PRICE)) ) Then
     NO_DSC = 1;

    --Проверка на кратность
    if (NO_DSC = 0) then
    if (QUANT_DIV is not null) then
    begin
      --Определяем партию на которую будем назначать скидку
      select max(part_id), abs(sum(quant)), min(price) from vw_doc_detail_active da where doc_id = :doc_id
                                                                                          and UPPER(da.sname) containing :SNAME1
                                                                                          and UPPER(da.sname) containing :SNAME2
                                                                                          and UPPER(da.sname) containing :SNAME3
                                                                                          and UPPER(da.sname) containing :SNAME4
                                                                                          and UPPER(da.sname) containing :SNAME5
                                                                      and ( ((:DEL = 1) and (part_id <> :PART_ID_IN)) or (:DEL = 0) )
                                                                      and (price >= coalesce(:PRICE_MIN,0)) and (price <= coalesce(:PRICE_MAX,:CONST_MAX_PRICE))
                                                                      and UPPER(da.MMBSH) containing :MMBSH1
                                                                      and ( (:NDS_O1 is null) or ((:NDS_O1 is not null) and (:NDS_O1 = NDS)) )
      into :max_part_id, :sum_quant, :price;
        --Скидка назначается только на партию с максимальным ID и если условие кратности соблюдается
        --По всем остальным партиям скидку сбрасываем
        for select part_id from vw_doc_detail_active da where doc_id = :doc_id and UPPER(da.sname) containing :SNAME1
                                                                               and UPPER(da.sname) containing :SNAME2
                                                                               and UPPER(da.sname) containing :SNAME3
                                                                               and UPPER(da.sname) containing :SNAME4
                                                                               and UPPER(da.sname) containing :SNAME5
                                                             and ( ((:DEL = 1) and (part_id <> :PART_ID_IN)) or (:DEL = 0) )
                                                             and (price >= coalesce(:PRICE_MIN,0)) and (price <= coalesce(:PRICE_MAX,:CONST_MAX_PRICE))
                                                             and UPPER(da.MMBSH) containing :MMBSH1
                                                             and ( (:NDS_O1 is null) or ((:NDS_O1 is not null) and (:NDS_O1 = NDS)) )
        into :PART_ID_OUT do
        Begin
          if ((max_part_id = PART_ID_OUT) and (Trunc(sum_quant/QUANT_DIV) > 0)) then
            begin
              if (QUANT_DSC is not null) then DISCOUNT = QUANT_DSC*:price*Trunc(sum_quant/QUANT_DIV);
              if (SUM_DSC is not null) then DISCOUNT = SUM_DSC*Trunc(sum_quant/QUANT_DIV);
              if (PERC_DSC is not null) then DISCOUNT = :quant*:price*PERC_DSC;
              if (BARCODE is not null) then BARCODE_DSC = :BARCODE;
            end
          else
            Begin
              DISCOUNT = 0;
              BARCODE_DSC = Null;
            end
          suspend;
        End
      exit;
    End

    --Суммовая скидка
    if ( (NO_DSC = 0) and (DEL = 0) and ((SUM_DSC is not null) or (BARCODE is not null)) ) then
    Begin
      DISCOUNT =  :quant*SUM_DSC;
      BARCODE_DSC = BARCODE;
      suspend;
      exit;
    End

    --Процентная скидка
    if ( (NO_DSC = 0) and (DEL = 0) and ((PERC_DSC is not null) or (BARCODE is not null)) ) then
    Begin
      DISCOUNT =  :quant*:price*PERC_DSC;
      BARCODE_DSC = BARCODE;
      suspend;
      exit;
    End

  End
end^

SET TERM ; ^

/* Following GRANT statetements are generated automatically */

GRANT SELECT ON VW_DOC_DETAIL_ACTIVE TO PROCEDURE PR_GET_DSC_RULES;
GRANT SELECT ON DSC_RULES TO PROCEDURE PR_GET_DSC_RULES;

/* Existing privileges on this procedure */

GRANT EXECUTE ON PROCEDURE PR_GET_DSC_RULES TO SYSDBA;

Создаем в кассире ТМС

uses
  Graphics, Controls, Forms, Dialogs, StdCtrls,
  ComCtrls, ExtCtrls, ibquery, DB, ChequeList, FR,
  ScriptRes, Barcode,ZKassa, StrUtils, Windows, Classes,
  IBDataBase, SysUtils, DateUtils, chequelist;

Procedure  RecalcDSC(del:integer)
var iq: tibquery;
begin
 Try
   iq := tibquery.create(nil);
   iq.transaction := CreateRT(CurrDB);
   iq.transaction.starttransaction;
   iq.active := False;

   iq.sql.text := 'select * from PR_GET_DSC_RULES('+IntToStr(ChequeList.ActivePID)+','+IntToStr(ChequeList.Active.ID)+','+IntToStr(del)+')';
   iq.active := True;
   while not (iq.Eof) do
   begin
      ChequeList.Active.P_Index:= ChequeList.Active.IndexByPartID(iq.FieldByName('part_id_out').AsInteger);
      if (iq.FieldByName('BARCODE_DSC').AsString = '') Then
        ChequeList.Active.P_SetDiscount(-1*iq.FieldByName('DISCOUNT').AsFloat)
      else
        Barcode.ApplyDiscount(iq.FieldByName('BARCODE_DSC').AsString, iq.FieldByName('part_id_out').AsInteger);
      iq.next;
   End;

 finally
    iq.active := False;
    iq.transaction.free;
    iq.free;
 end;

end;

Вызываем RecalcDSC в ТМС

-305 как  RecalcDSC(0); 
-332 как  RecalcDSC(1); 

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

uses
  p10112u1;
begin
  RecalcDSC(0);
end;

Пример №2 (старый мех-м)

  • акция действует всегда пока включен переключатель в параметрах системы
INSERT INTO PARAMS ( PARENT_ID, PARAM_ID, PARAM_CAPTION, PARAM_TYPE, PARAM_VALUE, AUDIT_ID, IMAGEINDEX,
STATUS, INSERTDT, SORTING, PARAM_TYPE_DATA, PACKET) VALUES ( -30, 'AKCCII_TOGGLE', 'Включение акций', 14, '0', 3229, NULL, 0, '19-DEC-2015 10:45:34.828', NULL, 'FIXEDITEMS SHOWONLYVALUES RETURNNAME
0=выключено
1=включено');
  • если переключатель включен - в кассире при продаже активируется скидка = разнице между заполненными колонками price и ещё одной ценовой колонкой, ниже использована PRICE_R
uses
  Graphics, Controls, Forms, Dialogs, StdCtrls,
  ComCtrls, ExtCtrls, ibquery, DB, ChequeList, FR,
  ScriptRes, Barcode,ZKassa, StrUtils, Windows, Classes,
  IBDataBase, SysUtils, DateUtils, chequelist;
var iq: tibquery;
    dsc, part_id: integer;
    discount: float;
begin
 Try
   iq := tibquery.create(nil);
   iq.transaction := CreateRT(CurrDB);
   iq.transaction.starttransaction;
   iq.active := False;

   iq.sql.text := 'select wb.part_id,wb.PRICE_R,(select p.param_value from params p where p.param_id = ''AKCCII_TOGGLE'') from  vw_warebase wb  where wb.part_id = '+
   IntToStr(ChequeList.ActivePID);

   iq.active := True;
   if not(iq.Eof) then
     if ( not iq.FieldByName('param_value').AsInteger=0 )then
       if ( not iq.FieldByName('PRICE_R').isNull )then
         if (iq.FieldByName('PRICE_R').AsFloat>0) then begin
            ChequeList.Active.P_Index := ChequeList.Active.IndexByPartID(iq.FieldByName('part_id').AsInteger);
            temp := -(ChequeList.Active.P_Price - iq.FieldByName('PRICE_R').AsFloat);
            ChequeList.Active.P_SetDiscount(temp);
         end;
  finally
   iq.active := False;
   iq.transaction.free;
   iq.free;
  end;

Отправка ТМС по синхронизации

Выгружаем тмс из базы из blob поля в файл

Тмс можно найти спомощью запроса:

select * from groups g where g.id=-305

Затем сохраняем на жесткий диск тмс из поля data.

Загружаем ТМС в серверную базу

  • В таблице g$profiles выбираем blob поле, которое будем использовать для отправки, например TMP_BLOB1.
  • Убеждаемся, что данная таблица синхронизируется в двухстороннем направлении.
  • Загружаем в это поле например первому профилю наш ТМС скрипт.
  • Ждем когда это обновление придет всем по синхронизации.

Применяем всем ТМС

По g$tasks отправляем запрос нужным профилям:

update groups gp set gp.data = (select g.tmp_blob1 from G$PROFILES g where g.id = 1) where gp.id = -305;

Примеры акций

1. Акция на апрель, Эссенциале №30 - скидка 150 руб за каждую упаковку
2. Акция до конца мая, на Магне №50 (с ценой от 500 до 1000 руб) - при покупке 4х упаковок 1 в подарок;
3. Акция на Ферверкс, при покупке 2х упаковок 1 в подарок;
4. Аналогично Ферверксу, только на Маалокс;
5 и 6. На зодак №7 и №28 скидка 15%;
7. Акция на Эмолиум, при покупке 2х упаковок скидка 350 руб.;
8. Аналогично, на Фитолизин - 100 руб.;
9. На все ЖНВЛС скидка 5%;
10. На все товары с НДС опт. 10%, - скидка 10%;
11. На все товары с НДС опт. 18%, - скидка 15%;

Окно программы

  • DATE_BEG и DATE_END - период действия скидки
  • MMBSH1 - группа товара
  • NDS_O1 - ставка НДС опт (10,18,12 ....)
  • SNAME1 ... SNAME5 - слова, которым должна соответствовать позиция для того, чтобы скидка назначилась (все не обязательны)
  • PRICE_MIN и PRICE_MAX - ценовой диапазон, которому должна соответствовать позиция, чтобы скидка назначилась (все не обязательны)
  • QUANT_DIV и QUANT_DSC, для условий типа: при покупке 4х штук (QUANT_DIV) одна(QUANT_DSC) в подарок
  • SUM_DSC - суммовая скидка на позицию в рублях
  • PERC_DSC - процентная скидка на позицию в % (например 0.15)
  • STATUS, 1 - правило активно, 0 - неактивно


ПОСМОТРЕТЬ ЕЩЕ ПРИМЕРЫ


Видео