/* Server version: WI-V6.3.7.18553 Firebird 2.1 
   SQLDialect: 3. ODS: 11.1. Forced writes: On. Sweep inteval: 20000.
   Page size: 8192. Cache pages: 9500 (76000 Kb). Read-only: False. */

SET SQL DIALECT 3;

ALTER TABLE SYSTEM_CLIENT ADD FISCAL_DEVICE_ERROR NAME_255;

/* Create Procedure... */
SET TERM ^ ;

CREATE PROCEDURE AMOUNT_APPORTION_TO_PRICE$FISC(AMOUNT NUMERIC(15,4),
TOTAL_QTY NUMERIC(15,4),
ROUND_TO NUMERIC(15,4))
 RETURNS(QTY NUMERIC(15,4),
SINGLE_PRICE NUMERIC(15,4))
 AS
 BEGIN SUSPEND; END
^

CREATE PROCEDURE FISCALIZE_DISCOUNTS$FISC(ID_DOC_HEAD INTEGER)
 RETURNS(DISCOUNTS_SUM NUMERIC(15,4))
 AS
 BEGIN SUSPEND; END
^


/* Alter View (Drop, Create)... */
/* Drop altered view: SALE_PAYMENT */
SET TERM ; ^

DROP VIEW SALE_PAYMENT;

/* Create altered view: SALE_PAYMENT */
/* Create view: SALE_PAYMENT */
CREATE VIEW SALE_PAYMENT(
UNIQUE_SALES_NUMBER,
ID_BILL,
ID_OBJECT,
OBJECT_NAME,
SMETKA_DATE,
START_DATE,
END_DATE,
TOTAL_SUM,
PAYMENT_DATE,
USER_NAME,
USER_CODE,
PAYMENT_SUM,
PAYMENT_TYPE,
FISCAL_DEVICE_NUMBER,
ID_CLIENT_POS,
HOTEL_ROOM)
 AS 
select h.UNIQUE_SALES_NUMBER,
       h.ID_SMETKA_HEAD,
       o.ID_OBJECT,
       o.OBJECT_NAME,
       h.SMETKA_DATE,
       h.START_DATE,
       cast(h.STOP_DATETIME as date) as END_DATE,
       h.GRAND_TOTAL_SUM,
       cast(t.TRANS_END as date) as PAY_DATE,
       u.USER_NAME,
       u.CODE as user_code,
       p.PAYMENT_VALUE as pay_sum,
       pt.PAYMENT_NAME as payment_type,
       sfd.FISCAL_DEVICE_ID as fd_number,
       t.ID_CLIENT_POS,
       cast(sf.ROOM_NAME || ' | ' || sf.GUEST_NAME as varchar(140)) as hotel_room
from   SMETKA_HEAD h
       join SMETKA_PAYMENT p on p.ID_SMETKA_HEAD = h.ID_SMETKA_HEAD and p.STATUS = 1
       join NOM_PAYMENT_TYPE pt on pt.ID_PAYMENT_TYPE = p.ID_PAYMENT_TYPE
       join SMETKA_TRANS t on t.ID_SMETKA_TRANS = p.ID_SMETKA_TRANS
       join RIGHT_USER u on u.USER_NAME = t.USER_CREATED
       join NOM_OBJECT o on o.ID_OBJECT = h.ID_OBJECT
       left outer join SMETKA_FRONT sf on sf.ID_SMETKA_FRONT = h.ID_SMETKA_FRONT
       left outer join SMETKA_FISCAL_DATA sfd on sfd.ID_SMETKA_HEAD = h.ID_SMETKA_HEAD
union all
select h.UNIQUE_SALES_NUMBER,
       h.ID_SMETKA_HEAD,
       o.ID_OBJECT,
       o.OBJECT_NAME,
       h.SMETKA_DATE,
       h.START_DATE,
       cast(h.STOP_DATETIME as date) as END_DATE,
       h.GRAND_TOTAL_SUM,
       cast(t.TRANS_END as date) as PAY_DATE,
       u.USER_NAME,
       u.CODE as user_code,
       p.PAYMENT_VALUE as pay_sum,
       pt.PAYMENT_NAME as payment_type,
       sfd.FISCAL_DEVICE_ID as fd_number,
       t.ID_CLIENT_POS,
       cast(sf.ROOM_NAME || ' | ' || sf.GUEST_NAME as varchar(140)) as hotel_room
from   ARCHIVE_HEAD h
       join ARCHIVE_PAYMENT p on p.ID_SMETKA_HEAD = h.ID_SMETKA_HEAD and p.STATUS = 1
       join NOM_PAYMENT_TYPE pt on pt.ID_PAYMENT_TYPE = p.ID_PAYMENT_TYPE
       join ARCHIVE_TRANS t on t.ID_SMETKA_TRANS = p.ID_SMETKA_TRANS
       join RIGHT_USER u on u.USER_NAME = t.USER_CREATED
       join NOM_OBJECT o on o.ID_OBJECT = h.ID_OBJECT
       left outer join SMETKA_FRONT sf on sf.ID_SMETKA_FRONT = h.ID_SMETKA_FRONT
       left outer join SMETKA_FISCAL_DATA sfd on sfd.ID_SMETKA_HEAD = h.ID_SMETKA_HEAD
;

/*  Empty FISCALIZE_PREPARE$FISC for FISCALIZE_PREPARE_LINES$INT(param list change)  */
SET TERM ^ ;

ALTER PROCEDURE FISCALIZE_PREPARE$FISC(ID_DOC_HEAD INTEGER,
DETAIL_LEVEL VARCHAR(20),
ADDITIONAL_PAYMENT_TEXT VARCHAR(30) = null)
 RETURNS(UNIQUE_SALES_NUMBER VARCHAR(30),
FISCALIZATION_DETAILS VARCHAR(255),
VOID_REASON VARCHAR(30),
IS_VOID /* TYPE OF TLOGICAL */ SMALLINT)
 AS
 BEGIN SUSPEND; END
^

/* Alter empty procedure FISCALIZE_PREPARE_LINES$INT with new param-list */
ALTER PROCEDURE FISCALIZE_PREPARE_LINES$INT(ID_DOC_HEAD INTEGER,
DETAIL_LEVEL VARCHAR(20),
ADDITIONAL_PAYMENT_TEXT VARCHAR(30) = null)
 RETURNS(FISC_PRICE NUMERIC(15,4),
FISC_QTY NUMERIC(15,4),
FISC_TEXT VARCHAR(30),
FISC_GRP VARCHAR(10))
 AS
 BEGIN SUSPEND; END
^

/* Alter Procedure... */
/* empty dependent procedure body */
/* Clear: BILL_PAYMENT_ADD for: FISCAL_DEVICE_STATUS_CHECK */
ALTER PROCEDURE BILL_PAYMENT_ADD(ID_CLIENT_POS INTEGER,
ID_END_WORK INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_SMETKA_TRANS INTEGER,
ID_PAYMENT_TYPE INTEGER,
ID_PAYMENT_OVERRULE INTEGER,
PAYMENT_VALUE NUMERIC(15,4),
CHANGE_VALUE NUMERIC(15,4))
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
NEW_ID_SMETKA_PAYMENT INTEGER)
 AS
 BEGIN SUSPEND; END
^

/* empty dependent procedure body */
/* Clear: BILL_POST_ADD for: FISCAL_DEVICE_STATUS_CHECK */
ALTER PROCEDURE BILL_POST_ADD(ID_END_WORK INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_SMETKA_TRANS INTEGER,
ID_MENU_PRICE_LIST INTEGER,
ID_PRICE_LIST_OVERRULE INTEGER,
ID_STOCK INTEGER,
ID_INVENT INTEGER,
ID_VAT INTEGER,
QTY NUMERIC(15,4),
SALE_PRICE NUMERIC(15,4),
LINE_SUM NUMERIC(15,4),
ID_LINE_DISCOUNT INTEGER,
ID_LINE_VOID INTEGER,
EDIT_KIND VARCHAR(10),
ID_MASTER_LINE INTEGER,
LINE_TEXT VARCHAR(2000),
MOD_CNT INTEGER)
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
NEW_ID_MOD_SESSION INTEGER)
 AS
 BEGIN SUSPEND; END
^

/* empty dependent procedure body */
/* Clear: BILL_POST_FINALIZE for: FISCAL_DEVICE_STATUS_CHECK */
ALTER PROCEDURE BILL_POST_FINALIZE(ID_CLIENT_POS INTEGER,
ID_END_WORK INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_SMETKA_TRANS INTEGER,
ID_SMETKA_DISCOUNT INTEGER,
ID_CONTRAGENT INTEGER,
ID_CONTRAGENT_OVERRULE INTEGER,
OPEN_TYPE VARCHAR(10),
CLOSE_TYPE VARCHAR(10),
ID_MANUAL_MENU INTEGER,
ID_SURCHARGE INTEGER,
SURCHARGE_VALUE NUMERIC(15,4),
CNT_LINES INTEGER,
CNT_VOIDS INTEGER,
TOTAL_SUM NUMERIC(15,4),
DISCOUNT_SUM NUMERIC(15,4),
SURCHARGE_SUM NUMERIC(15,4),
GRAND_TOTAL_SUM NUMERIC(15,4),
CUSTOMER_CNT INTEGER,
CARD_CODE VARCHAR(50))
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
VIP_LINES INTEGER)
 AS
 BEGIN SUSPEND; END
^

/* empty dependent procedure body */
/* Clear: UNIQUE_SALES_NUMBER_GEN for: FISCAL_DEVICE_STATUS_CHECK */
ALTER PROCEDURE UNIQUE_SALES_NUMBER_GEN(ID_CLIENT_POS INTEGER)
 RETURNS(UNIQUE_SALES_NUMBER VARCHAR(30))
 AS
 BEGIN SUSPEND; END
^

/* Alter (FISCAL_DEVICE_STATUS_CHECK) */
ALTER PROCEDURE FISCAL_DEVICE_STATUS_CHECK(ID_CLIENT_POS TYPE OF ID_TYPE)
 RETURNS(FISCAL_DEVICE_ID TYPE OF NAME_10)
 AS
declare variable fiscal_device_error varchar(255);
begin
  if (:ID_CLIENT_POS is null) then ID_CLIENT_POS = RDB$GET_CONTEXT('USER_SESSION', 'ID_CLIENT_POS');

  select sc.FISCAL_DEVICE_ID, sc.FISCAL_DEVICE_ERROR
  from   SYSTEM_CLIENT SC
  where  SC.ID_CLIENT_POS = :ID_CLIENT_POS
         and sc.FISCAL_SESSION_ID = current_connection
  into   :FISCAL_DEVICE_ID, :fiscal_device_error;

  if (:FISCAL_DEVICE_ID is null) then exception FISCALIZE_EXCEPTION '-318' || f_character(10) || coalesce(:fiscal_device_error, '');
end
^

/* Alter (FISCAL_SESSION_UPDATE$FISC) */
ALTER PROCEDURE FISCAL_SESSION_UPDATE$FISC(FISCAL_SESSION_ID INTEGER,
FISCAL_DEVICE_ID TYPE OF NAME_10,
FISCAL_DEVICE_ERROR TYPE OF NAME_255)
 AS
begin
  update SYSTEM_CLIENT c
  set    c.FISCAL_DEVICE_ID = :FISCAL_DEVICE_ID,
         c.FISCAL_DEVICE_UPDATED_AT = current_timestamp,
         c.FISCAL_DEVICE_ERROR = :FISCAL_DEVICE_ERROR
  where  C.FISCAL_SESSION_ID = :FISCAL_SESSION_ID
         and ( (c.FISCAL_DEVICE_ID is distinct from :FISCAL_DEVICE_ID)
               or (c.FISCAL_DEVICE_ERROR is distinct from :FISCAL_DEVICE_ERROR)
             );
end
^

/* Alter (FISCALIZE_LINE$FISC) */
ALTER PROCEDURE FISCALIZE_LINE$FISC(ID_DOC_HEAD INTEGER)
 RETURNS(FISC_PRICE NUMERIC(15,4),
FISC_QTY NUMERIC(15,4),
FISC_TEXT VARCHAR(30),
FISC_GRP VARCHAR(10),
TRANSACTION_VALUE NUMERIC(15,4))
 AS
declare variable fld_sep varchar(10);
  declare variable line_sep varchar(10);
  declare variable line varchar(255);
  declare variable end_pos integer;
  declare variable lines_data blob sub_type 1 segment size 100;
  declare variable i integer;
  declare variable status integer;
  declare variable fiscal_device_id varchar(10);
  declare variable storno_fiscal_device_id varchar(10);
begin
  select f.SMETKA_LINES, h.status, f.FISCAL_DEVICE_ID, f.STORNO_FISCAL_DEVICE_ID
  from   SMETKA_FISCAL_DATA f              
         join SMETKA_HEAD h on h.ID_SMETKA_HEAD = f.ID_SMETKA_HEAD
  where  f.ID_SMETKA_HEAD = :ID_DOC_HEAD
  into   :lines_data, :status, :fiscal_device_id, :storno_fiscal_device_id;
  -- Status && fiscal data compatibility check
  if (:status = 2 and :fiscal_device_id is not null) then
    exception FISCALIZE_EXCEPTION '-315';

  if (:status = -3 and :storno_fiscal_device_id is not null) then
    exception FISCALIZE_EXCEPTION '-314';

  i = 0;
  fld_sep = f_character(28);
  line_sep = f_character(29);
  while (trim(:lines_data) <> '') do begin
    end_pos = position(:line_sep, :lines_data);
    line = substring(:lines_data from 1 for end_pos);
    lines_data = substring(:lines_data from end_pos + 1);
    if (:line <> '') then begin
      FISC_PRICE = substring(:line from 1 for position(:fld_sep, :line)-1);
      line = substring(:line from position(:fld_sep, :line)+1);
      FISC_QTY = substring(:line from 1 for position(:fld_sep, :line)-1);
      line = substring(:line from position(:fld_sep, :line)+1);
      FISC_TEXT = substring(:line from 1 for position(:fld_sep, :line)-1);
      line = substring(:line from position(:fld_sep, :line)+1);
      FISC_GRP = substring(:line from 1 for position(:fld_sep, :line)-1);
      line = substring(:line from position(:fld_sep, :line)+1);
      TRANSACTION_VALUE = substring(:line from 1 for position(:line_sep, :line)-1);
      suspend;
    end
    i= :i + 1;
    if (:i = 4096) then exit; -- Too many cycles, recursion prevents.
  end
end
^

ALTER PROCEDURE FISCALIZE_PREPARE_LINES$INT(ID_DOC_HEAD INTEGER,
DETAIL_LEVEL VARCHAR(20),
ADDITIONAL_PAYMENT_TEXT VARCHAR(30) = null)
 RETURNS(FISC_PRICE NUMERIC(15,4),
FISC_QTY NUMERIC(15,4),
FISC_TEXT VARCHAR(30),
FISC_GRP VARCHAR(10))
 AS
declare variable fisc_status integer;
  declare variable round_to numeric(15,4);
begin
  /*  Description:
  ||   DETAIL_LEVEL = {1 group,  2 detail}
  function fiscalize(const text, taxGroup: string; qty, price, correction: Double): integer;
  function payment(const text, payType: string; summ: Double): integer;
  */
  fisc_price = null;
  fisc_text = null;
  fisc_grp = null;
  round_to = 0.01;

  select max(SP.FISC_STATUS)
  from   SMETKA_PAYMENT SP
         join SMETKA_HEAD SH on SH.ID_SMETKA_HEAD = Sp.ID_SMETKA_HEAD
         join NOM_PAYMENT_TYPE PT on PT.ID_PAYMENT_TYPE = SP.ID_PAYMENT_TYPE
  where  SH.ID_SMETKA_HEAD = :id_doc_head
         and SP.ID_PAYMENT_VOID is null
         and SH.STATUS = 2
  into   :fisc_status;

  if (:fisc_status <> 1) then exit;

  if (:detail_level = 'group') then begin
    execute procedure SYSTEM_GET_TEXT(null, 'FISC_TEXT')
    returning_values  :fisc_text;
    for
      select VG.VAT_CODE, roundto(sum(SL.NETO_SUM), :round_to)
      from   SMETKA_HEAD SH
             join BILL_NETO SL on SL.ID_SMETKA_HEAD = SH.ID_SMETKA_HEAD
             left outer join STOCK S on S.ID_STOCK = SL.ID_STOCK
             left outer join NOM_VAT_GROUP VG on VG.ID_VAT = S.ID_VAT_GROUP
      where  SH.ID_SMETKA_HEAD = :id_doc_head
             and SH.STATUS = 2
             and SL.STATUS > 0
      group by 1
      into   :fisc_grp, :FISC_PRICE
    do begin
      fisc_qty = 1;
      suspend;
    end
  end else begin
    for
      select bl.VAT_CODE, bl.stock_name, sp.SINGLE_PRICE, sp.QTY
      from   (select VG.VAT_CODE, S.STOCK_NAME, roundto(sum(SL.NETO_SUM), :round_to) as amount, sum(SL.QTY) as qty
              from   SMETKA_HEAD SH
                     join BILL_NETO SL on SL.ID_SMETKA_HEAD = SH.ID_SMETKA_HEAD
                     left outer join STOCK S on S.ID_STOCK = SL.ID_STOCK
                     left outer join NOM_VAT_GROUP VG on VG.ID_VAT = S.ID_VAT_GROUP
              where  SL.ID_SMETKA_HEAD = :id_doc_head
                     and SH.STATUS = 2
                     and SL.STATUS > 0
              group by 1, 2, SL.NETO_SUM) bl
             left outer join AMOUNT_APPORTION_TO_PRICE$FISC(bl.amount, bl.qty, :round_to) sp on 1=1
      into   :fisc_grp, :fisc_text, :FISC_PRICE, :FISC_QTY
    do begin
      if (:FISC_PRICE <> 0) then suspend;
    end
  end

  fisc_price = null;
  select SH.SURCHARGE_SUM, D.DISCOUNT_NAME, 'SRV', 1
  from   SMETKA_HEAD SH
         join NOM_DISCOUNT D on D.ID_DISCOUNT = SH.ID_SURCHARGE
  where  SH.ID_SMETKA_HEAD = :id_doc_head
  into   :fisc_price, :fisc_text, :fisc_grp, :FISC_QTY;
  if (:FISC_PRICE <> 0) then suspend;
end
^

/* Alter (FISCALIZE_PREPARE$FISC) */
ALTER PROCEDURE FISCALIZE_PREPARE$FISC(ID_DOC_HEAD INTEGER,
DETAIL_LEVEL VARCHAR(20),
ADDITIONAL_PAYMENT_TEXT VARCHAR(30) = null)
 RETURNS(UNIQUE_SALES_NUMBER VARCHAR(30),
FISCALIZATION_DETAILS VARCHAR(255),
VOID_REASON VARCHAR(30),
IS_VOID TYPE OF TLOGICAL)
 AS
declare variable fld_sep varchar(10);
  declare variable line_sep varchar(10);
  declare variable line varchar(255);
  declare variable lines_data blob sub_type 1 segment size 80;
  declare variable payments_data blob sub_type 1 segment size 80;
  declare variable bill_status integer;
  declare variable min_fisc_status integer;
  declare variable max_fisc_status integer;
begin
  fld_sep = f_character(28);
  line_sep = f_character(29);
  lines_data = '';
  FISCALIZATION_DETAILS = null;  -- Fiscalization API compatibility
  VOID_REASON = null;
  IS_VOID = 0;

  select h.UNIQUE_SALES_NUMBER, h.STATUS
  from   SMETKA_HEAD h
  where  h.ID_SMETKA_HEAD = :ID_DOC_HEAD
  into   :UNIQUE_SALES_NUMBER, :bill_status;

  if (:bill_status <> 2) then exception FISCALIZE_EXCEPTION '-312';

  select max(SP.FISC_STATUS), min(sp.FISC_STATUS)
  from   SMETKA_PAYMENT SP
  where  SP.ID_SMETKA_HEAD = :id_doc_head
         and SP.ID_PAYMENT_VOID is null
  into   :max_fisc_status, :min_fisc_status;

  if (:min_fisc_status <> :max_fisc_status) then exception FISCALIZE_EXCEPTION '-307';
  else if (:max_fisc_status = 0) then exit;  -- Nothing to fiscalize
  else if (:max_fisc_status = 2) then exit;  -- Payments are marked as fiscalized already
  -- !!! Only fisc_status=1 allows to continue with data preparation !!!


  for
    select coalesce(l.FISC_PRICE, 0) || :fld_sep
           || coalesce(l.FISC_QTY, 0) || :fld_sep
           || coalesce(l.FISC_TEXT, '') || :fld_sep
           || coalesce(l.FISC_GRP, '') || :fld_sep
           || '0' || :line_sep  -- removed :TRANSACTION_VALUE
    from   FISCALIZE_PREPARE_LINES$INT (:ID_DOC_HEAD, :DETAIL_LEVEL, :ADDITIONAL_PAYMENT_TEXT) l
    into   :line
  do begin
    lines_data = :lines_data || :line;
  end

  payments_data = '';
  for
    select coalesce(sum(SP.PAYMENT_VALUE), 0) || :fld_sep
           || coalesce(PT.PAYMENT_NAME, '') || :fld_sep
           || coalesce(PT.FISCAL_ID, '') || :line_sep
    from   SMETKA_PAYMENT SP
           join SMETKA_HEAD SH on SH.ID_SMETKA_HEAD = Sp.ID_SMETKA_HEAD
           join NOM_PAYMENT_TYPE PT on PT.ID_PAYMENT_TYPE = SP.ID_PAYMENT_TYPE
    where  SH.ID_SMETKA_HEAD = :id_doc_head
           and SP.ID_PAYMENT_VOID is null
           and SH.STATUS = 2
    group by PT.PAYMENT_NAME, PT.FISCAL_ID
    having sum(SP.PAYMENT_VALUE) > 0
    into   :line
  do begin
    payments_data = :payments_data || :line;
  end

  insert into SMETKA_FISCAL_DATA(ID_SMETKA_HEAD, UNIQUE_SALES_NUMBER, SMETKA_LINES, SMETKA_PAYMENTS)
  values (:ID_DOC_HEAD, :UNIQUE_SALES_NUMBER, :lines_data, :payments_data);
end
^

/* Alter (SYSTEM_LOGON_USER) */
ALTER PROCEDURE SYSTEM_LOGON_USER(ID_CLIENT_POS INTEGER,
USER_PASSWORD VARCHAR(50),
CLIENT_KIND VARCHAR(10),
ID_MULTI_PROFILE INTEGER)
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
ID_LANG_USER INTEGER,
ID_LOGON INTEGER,
USER_NAME VARCHAR(30),
ID_EW_NEXT INTEGER,
FISCAL_SESSION_ID INTEGER)
 AS
declare variable machine_name varchar(30);
  declare variable new_object varchar(30);
  declare variable id_object integer;
  declare variable set_id_object integer;
  declare variable log_logon_enabled integer;
  declare variable id_price_list integer;
  declare variable id_setting_profile integer;
begin
  err_code = 0;
  err_text = '';
  if (current_user = 'SYSDBA') then begin
    err_code = -1;
    err_text = 'Unallowed system user - SYSDBA!'||f_crlf();
    err_text = :err_text||'To fix the problem go to POSitive Admin'||f_crlf()||'Global Settings->System';
    exit;
  end

  if (:client_kind = 'EFFECT_IFC') then exit;
  select RU.USER_NAME
  from   RIGHT_USER RU
  where  RU.USER_PASSWORD = :user_password
         and RU.IS_EXPIRED = 0
  into   :user_name;
  if (:user_name is null) then begin
    err_code = -7;
    exit;
  end
/*
 else if ((:expires_on is not null) and (:expires_on <= current_date)) then err_code = -8;
 GOES TO END WORK
 */
  if (:id_multi_profile = -1) then begin
    select SC.ID_OBJECT, SC.SET_ID_OBJECT
    from   SYSTEM_CLIENT SC
    where  SC.ID_CLIENT_POS = :id_client_pos
    into   :id_object, :set_id_object;
  
    if ((:id_object <> :set_id_object) and (:client_kind = 'POS')) then begin
      err_code = -12;
      select O.OBJECT_NAME
      from   NOM_OBJECT O
      where  O.ID_OBJECT = :id_object
      into   :err_text;
      select O.OBJECT_NAME
      from   NOM_OBJECT O
      where  O.ID_OBJECT = :set_id_object
      into   :new_object;
      err_text = err_text||'->'||:new_object;
      id_object = :set_id_object;
    end
  end else begin
    select SC.ID_SETTING_PROFILE
    from   SYSTEM_CLIENT SC
    where  SC.ID_CLIENT_POS = :id_client_pos
    into   :id_setting_profile;

    select SCM.ID_OBJECT, SCM.ID_PRICE_LIST
    from   SYSTEM_CLIENT_MULTIPROFILE SCM
    where  SCM.ID_MULTI_PROFILE = :id_multi_profile
    into   :id_object, :id_price_list;

    update SETTING_VALUE SV set SV.SETTING_VALUE = :id_price_list
    where  SV.ID_SETTING_PROFILE = :id_setting_profile
           and SV.ID_SETTING = (
             select first 1 S.ID_SETTING
             from   SETTING S
             where  S.SETTING_CODE = 'DEFAULT_PRICE_LIST'
           );
  end

  id_ew_next = null;
  select SO.ID_EW_NEXT
  from   SECURE_OBJECT SO
  where  SO.USER_NAME = :user_name
         and SO.ID_OBJECT = :id_object
  into   :id_ew_next;
  if ((:id_ew_next is null) and (client_kind = 'POS')) then begin
    err_code = -11;
    if (:err_text = '') then
      select O.OBJECT_NAME
      from   NOM_OBJECT O
      where  O.ID_OBJECT = :id_object
      into   :err_text;
    exit;
  end

  if ((:id_object is null) and (client_kind = 'POS')) then begin
    err_code = -10;
    err_text = ' id_client_pos='||coalesce(:id_client_pos, 'null');
    execute procedure SYS_DBG('all SYSTEM_LOGON_USER, err_code='||:err_code||:err_text);
    exit;
  end

  if (:client_kind = 'POS') then begin
    execute procedure SYSTEM_GET_VALUE(:id_client_pos, 'LOG_USER_LOGON')
    returning_values  :log_logon_enabled;

    -- delete potential obsoleted locks
    delete from  SYSTEM_LOCK sl
    where  sl.ID_CLIENT_POS = :ID_CLIENT_POS
           and sl.USER_NAME = :USER_NAME;

  end else begin
    /* ADMIN IS LOGING ON */
    delete from SYSTEM_CLIENT_SECURE SOD
    where  SOD.ID_CLIENT_POS = :id_client_pos;
    insert into SYSTEM_CLIENT_SECURE(ID_CLIENT_POS, ID_OBJECT)
    select :id_client_pos, SO.ID_OBJECT
    from   SECURE_OBJECT SO
    where  SO.USER_NAME = :user_name;
    log_logon_enabled = 1;
  end

  if (:log_logon_enabled = 1) then begin
    select CP.MACHINE
    from   SYSTEM_CLIENT CP
    where  CP.ID_CLIENT_POS = :id_client_pos
    into   :machine_name;

    id_logon = gen_id(GEN_RIGHT_LOGON_LOG_ID, 1 );
    insert into RIGHT_LOGON_LOG(
      ID_LOGON, USER_NAME, LOGON, LOGOFF, IS_CLOSED, MACHINE_NAME, ID_CLIENT_POS
    ) values (
      :id_logon, :user_name, current_timestamp, null, 0, :machine_name, :id_client_pos);
  end

  FISCAL_SESSION_ID = current_connection;

  update SYSTEM_CLIENT SC
  set    SC.ACTIVE_USER = :user_name,
         SC.ID_OBJECT = :id_object,
         sc.FISCAL_SESSION_ID = :FISCAL_SESSION_ID,
         sc.FISCAL_DEVICE_ID = null,
         sc.FISCAL_DEVICE_ERROR = null
  where  SC.ID_CLIENT_POS = :id_client_pos;

  RDB$SET_CONTEXT('USER_SESSION', 'ID_CLIENT_POS', :id_client_pos);

  execute procedure SYSTEM_LOG_ADD('RIGHT_USER', :USER_NAME, 'USER_LOG_IN', f_character(29) || :USER_NAME || f_character(28) || :CLIENT_KIND);
end
^

/* empty dependent procedure body */
/* Clear: BILL_UNQ_SALES_NUMBER_SET for: UNIQUE_SALES_NUMBER_GEN */
ALTER PROCEDURE BILL_UNQ_SALES_NUMBER_SET(ID_SMETKA_HEAD INTEGER,
ID_CLIENT_POS INTEGER)
 RETURNS(UNIQUE_SALES_NUMBER VARCHAR(30))
 AS
 BEGIN SUSPEND; END
^

/* Alter (UNIQUE_SALES_NUMBER_GEN) */
ALTER PROCEDURE UNIQUE_SALES_NUMBER_GEN(ID_CLIENT_POS INTEGER)
 RETURNS(UNIQUE_SALES_NUMBER VARCHAR(30))
 AS
declare variable user_name varchar(30);
  declare variable num_class varchar(30);
  declare variable fiscal_device_id varchar(10) ;
  declare variable number integer;
  declare variable user_code integer;
begin
  execute procedure FISCAL_DEVICE_STATUS_CHECK(:ID_CLIENT_POS)
  returning_values (:fiscal_device_id);

  execute procedure SYSTEM_GET_VALUE(:ID_CLIENT_POS, 'CURRENT_USER')
  returning_values (:user_name);

  select u.CODE
  from   RIGHT_USER u
  where  u.USER_NAME = :user_name
  into   :user_code;

  if (:user_code is null) then exception MISSING_VALUE '-317';

  NUM_CLASS = 'FISCAL_DEVICE';
  if (not exists (select n.ID_SYSTEM_NOMERATOR
                  from   SYSTEM_NOMERATOR n
                  where  n.NUM_CLASS = :num_class
                         and n.NUM_OBJECT = :FISCAL_DEVICE_ID)) then
    insert into SYSTEM_NOMERATOR (NUM_CLASS, NUM_OBJECT, NUM_NEXT, LAST_DATE)
    values (:num_class, :FISCAL_DEVICE_ID, 1, current_date);

  execute procedure BILL_GET_NUM(:FISCAL_DEVICE_ID, :num_class)
  returning_values (:NUMBER);

  if (:number is null) then exception MISSING_VALUE 'Sales number is empty!';

  UNIQUE_SALES_NUMBER = :FISCAL_DEVICE_ID || '-' || lpad(:user_code, 4, '0') || '-' || lpad(:number, 7, '0');
end
^

/* Restore proc. body: AMOUNT_APPORTION_TO_PRICE$FISC */
ALTER PROCEDURE AMOUNT_APPORTION_TO_PRICE$FISC(AMOUNT NUMERIC(15,4),
TOTAL_QTY NUMERIC(15,4),
ROUND_TO NUMERIC(15,4))
 RETURNS(QTY NUMERIC(15,4),
SINGLE_PRICE NUMERIC(15,4))
 AS
declare variable calc_price numeric(15,4);
  declare variable total_diff numeric(15,4);
  declare variable affected_qty integer;
begin
  calc_price = roundto(cast(:AMOUNT as double precision) / :TOTAL_QTY, :ROUND_TO);
  total_diff = roundto(:AMOUNT - :TOTAL_QTY * :calc_price, :ROUND_TO);
  if (:total_diff <> 0) then begin
    affected_qty = abs(:total_diff)/:ROUND_TO;
    affected_qty = :affected_qty * iif(:TOTAL_QTY > 0 , 1, -1);
    QTY = :TOTAL_QTY - :affected_qty;
    SINGLE_PRICE = :calc_price;
    suspend;

    QTY = :affected_qty;
    SINGLE_PRICE =  :calc_price + :total_diff/:affected_qty;
    suspend;
  end else begin
    QTY = :TOTAL_QTY;
    SINGLE_PRICE = :calc_price;
    suspend;
  end
end
^

/* Restore proc. body: BILL_PAYMENT_ADD */
ALTER PROCEDURE BILL_PAYMENT_ADD(ID_CLIENT_POS INTEGER,
ID_END_WORK INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_SMETKA_TRANS INTEGER,
ID_PAYMENT_TYPE INTEGER,
ID_PAYMENT_OVERRULE INTEGER,
PAYMENT_VALUE NUMERIC(15,4),
CHANGE_VALUE NUMERIC(15,4))
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
NEW_ID_SMETKA_PAYMENT INTEGER)
 AS
declare variable fisc_status integer;
  declare variable fiscal_device_id varchar(10);
begin
  execute procedure FISCAL_DEVICE_STATUS_CHECK(:ID_CLIENT_POS)
  returning_values :fiscal_device_id;

  err_code = 0;
  err_text = '';

  select PT.IS_FISCAL
  from   NOM_PAYMENT_TYPE PT
  where  PT.ID_PAYMENT_TYPE = :id_payment_type
  into   :fisc_status;

  new_id_smetka_payment = gen_id(gen_smetka_payment_id,1);
  insert into SMETKA_PAYMENT (ID_SMETKA_PAYMENT, ID_SMETKA_HEAD, ID_SMETKA_TRANS, ID_PAYMENT_TYPE, ID_PAYMENT_OVERRULE,
    PAYMENT_VALUE, CHANGE_VALUE, STATUS, FISC_STATUS, ID_END_WORK)
  values (:new_id_smetka_payment, :id_smetka_head, :id_smetka_trans, :id_payment_type, :id_payment_overrule,
    :payment_value, :change_value, 1, :fisc_status, :id_end_work);
END
^

/* Restore proc. body: BILL_POST_ADD */
ALTER PROCEDURE BILL_POST_ADD(ID_END_WORK INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_SMETKA_TRANS INTEGER,
ID_MENU_PRICE_LIST INTEGER,
ID_PRICE_LIST_OVERRULE INTEGER,
ID_STOCK INTEGER,
ID_INVENT INTEGER,
ID_VAT INTEGER,
QTY NUMERIC(15,4),
SALE_PRICE NUMERIC(15,4),
LINE_SUM NUMERIC(15,4),
ID_LINE_DISCOUNT INTEGER,
ID_LINE_VOID INTEGER,
EDIT_KIND VARCHAR(10),
ID_MASTER_LINE INTEGER,
LINE_TEXT VARCHAR(2000),
MOD_CNT INTEGER)
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
NEW_ID_MOD_SESSION INTEGER)
 AS
declare variable cost_price numeric(15, 4);
  declare variable status integer;
  declare variable vqty numeric(15, 4);
  declare variable vsl_dbkey char(8);
  declare variable qty_post numeric(15, 4);
  declare variable qty_void numeric(15, 4);
  declare variable id_voided_discount integer;
  declare variable discount_sum numeric(15, 4);
  declare variable void_sum numeric(15, 4);
  declare variable post_status integer;
  declare variable is_service integer;
  declare variable is_use integer;
  declare variable new_line_text integer;
  declare variable tax_percent numeric(15,4);
  declare variable unique_sales_number varchar(30);
  declare variable stock_name varchar(30);
  declare variable object_name varchar(30);
  declare variable smetka_date date;
  declare variable smetka_num integer;
  declare variable fiscal_device_id varchar(10);
  declare variable new_id_line_void integer;
  declare variable id_client_pos integer;
  declare variable id_void_reason integer;
  declare variable id_transfer_source_line integer;
begin
  execute procedure FISCAL_DEVICE_STATUS_CHECK(null)
  returning_values :fiscal_device_id;

  err_code = 0;
  err_text = '';
  void_sum = 0;
  new_line_text = 0;
  id_transfer_source_line = null;

  select S.COST_PRICE, S.IS_SERVICE, s.IS_USE, s.STOCK_NAME
  from   STOCK S
  where  S.ID_STOCK = :id_stock
  into   :cost_price, :is_service, :is_use, :stock_name;
  if (:is_use = 0 and coalesce(:EDIT_KIND, '') = '') then begin
    ERR_CODE = -311;
    ERR_TEXT = :stock_name;
    exit;
  end
  if (:is_service = 1) then post_status = -3;
  else post_status = 1;

  select tg.TAXES_VALUE
  from   TAX_GROUP TG
  where  TG.ID_TAX_GROUP = :ID_VAT
  into   :tax_percent;

  if (:edit_kind in ('EDIT_VOID', 'EDIT_MOVE', '')) then begin
    if (:id_line_discount is not null) then begin
      select SD.DISCOUNT_SUM
      from   SMETKA_DISCOUNT SD
      where  SD.ID_SMETKA_DISCOUNT = :id_line_discount
      into   :discount_sum;
    end else discount_sum = 0;

    if (:edit_kind in ('EDIT_VOID', 'EDIT_MOVE')) then begin
      select SL.RDB$DB_KEY, SL.ID_LINE_DISCOUNT, SL.QTY, SL.QTY_POST, SL.QTY_VOID,
             SL.ID_MOD_SESSION, s.STOCK_NAME, h.UNIQUE_SALES_NUMBER, o.OBJECT_NAME,
             h.SMETKA_DATE, h.SMETKA_NUM
      from   SMETKA_LINE SL
             join STOCK s on s.ID_STOCK = sl.ID_STOCK
             join SMETKA_HEAD h on h.ID_SMETKA_HEAD = sl.ID_SMETKA_HEAD
             join NOM_OBJECT o on o.ID_OBJECT = h.ID_OBJECT
      where  SL.ID_SMETKA_LINE = :id_master_line
      into   :vsl_dbkey, :id_voided_discount, :vqty, :qty_post, :qty_void,
             :new_id_mod_session, :stock_name, :unique_sales_number, :object_name,
             :smetka_date, :smetka_num;

      if (:vqty < :qty) then begin
        err_code = -1;
        err_text = 'QTY_FOR_VOID='||:qty||', QTY_NALICH='||:vqty||'ID_SMETKA_LINE'||:ID_MASTER_LINE;
        exit;
      end else if (:qty = 0) then status = -10;
      else status = 1;
      if (:edit_kind = 'EDIT_VOID') then begin
        qty_void = (:qty_void + (:vqty - :qty));
        qty_post = :qty_post;

        update SMETKA_LINE SL
        set    SL.ID_LINE_DISCOUNT = :id_line_discount,
               SL.DISCOUNT_SUM = :discount_sum,
               SL.LINE_SUM     = :line_sum,
               SL.STATUS       = :status,
               SL.QTY_VOID     = :qty_void,
               SL.QTY_POST     = :qty_post,
               SL.QTY          = :qty,
               SL.NETO_SUM     = :line_sum
        where  SL.RDB$DB_KEY = :vsl_dbkey;
    
        if (:id_voided_discount is not null) then begin
          delete from SMETKA_DISCOUNT SD
          where SD.ID_SMETKA_DISCOUNT = :id_voided_discount;
        end
  
        execute procedure SYSTEM_LOG_ADD('BILL', :ID_SMETKA_HEAD, 'BILL_LINE_VOID',
                f_character(29) || :stock_name || f_character(28) || :smetka_num || f_character(28) || :object_name || f_character(28)
                || :smetka_date || f_character(28) || float_to_str(:qty_void, 'F;0.0') || f_character(28)
                || float_to_str(:sale_price, 'F;0.00') || f_character(28) || :unique_sales_number);

      end else if (:edit_kind = 'EDIT_MOVE') then begin
        execute procedure BILL_POST_ADD(:ID_END_WORK, :ID_SMETKA_HEAD, :ID_SMETKA_TRANS,
                :ID_MENU_PRICE_LIST, :ID_PRICE_LIST_OVERRULE, :ID_STOCK, :ID_INVENT, :ID_VAT,
                :QTY, :SALE_PRICE, :qty * :SALE_PRICE, :ID_LINE_DISCOUNT, null, 'EDIT_VOID',
                :ID_MASTER_LINE, :LINE_TEXT, 0)
        returning_values (:ERR_CODE, :ERR_TEXT, :NEW_ID_MOD_SESSION);
        if (:ERR_CODE <> 0) then exit;

        id_client_pos = RDB$GET_CONTEXT('USER_SESSION', 'ID_CLIENT_POS');

        execute procedure SYSTEM_GET_VALUE(:id_client_pos, 'TRANSFER_VOID')
        returning_values (:id_void_reason);

        execute procedure BILL_POST_ADD_VOID (RDB$GET_CONTEXT('USER_SESSION', 'ID_CLIENT_POS'),
                :ID_SMETKA_HEAD, :id_void_reason, :ID_PRICE_LIST_OVERRULE)
        returning_values (:ERR_CODE, :ERR_TEXT, :new_id_line_void);
        if (:ERR_CODE <> 0) then exit;

        qty = :vqty - :qty;
        execute procedure BILL_POST_ADD(:ID_END_WORK, :ID_SMETKA_HEAD, :ID_SMETKA_TRANS,
                :ID_MENU_PRICE_LIST, :ID_PRICE_LIST_OVERRULE, :ID_STOCK, :ID_INVENT, :ID_VAT,
                :qty, :SALE_PRICE, :qty*:SALE_PRICE, :ID_LINE_DISCOUNT, :new_id_line_void, 'OLD',
                :ID_MASTER_LINE, '<void>'||:LINE_TEXT, 0)
        returning_values (:ERR_CODE, :ERR_TEXT, :NEW_ID_MOD_SESSION);
        if (:ERR_CODE <> 0) then exit;
      end

      exit;
    end
    if (:id_master_line is not null) then begin
      /* SPLITTING IS GOING ON  */
      select SL.ID_MOD_SESSION, sl.ID_TRANSFER_SOURCE_LINE
      from   SMETKA_LINE SL
      where  SL.ID_SMETKA_LINE = :id_master_line
      into   :new_id_mod_session, :id_transfer_source_line;

      id_transfer_source_line = coalesce(:id_transfer_source_line, :ID_MASTER_LINE);
      id_master_line = null;
    end else begin
      new_line_text = 1;
      new_id_mod_session = gen_id(gen_mod_session_id, 1);
      if (:mod_cnt = 0) then new_id_mod_session = -1*:new_id_mod_session;
    end
    qty_void = 0;
    qty_post = :qty;
    status = 1;
  end else begin
    /* PREPARING DATA FOR VOID LINES */
    qty_void = :qty;
    qty_post = 0;
    id_line_discount = null;
    discount_sum = 0;
    if (:edit_kind = 'CURRENT') then begin
      new_id_mod_session = gen_id(gen_mod_session_id, 1);
      status = -20;
      id_master_line = gen_id(gen_smetka_line_id, 1);
      insert into SMETKA_LINE (ID_SMETKA_LINE, ID_SMETKA_TRANS, ID_SMETKA_HEAD, ID_STOCK, ID_INVENT,
        QTY, QTY_POST, QTY_VOID, COST_PRICE, SALE_PRICE, LINE_SUM, VOID_SUM, ID_MENU_PRICE_LIST,
        ID_VOIDED_LINE, ID_LINE_VOID, ID_LINE_DISCOUNT, POST_STATUS, STATUS, ID_VAT, DISCOUNT_SUM,
        ID_PRICE_LIST_OVERRULE, ID_END_WORK, ID_MOD_SESSION, NETO_SUM, TAX_PERCENT, ID_TRANSFER_SOURCE_LINE)
      values (:id_master_line, :id_smetka_trans, :id_smetka_head, :id_stock, :id_invent,
        0, :qty, :qty, :cost_price, :sale_price, 0, 0, :id_menu_price_list,
        null, null, null, :post_status, :status, :id_vat, 0,
        :id_price_list_overrule, :id_end_work, :new_id_mod_session, 0, :tax_percent, :id_transfer_source_line);

      new_line_text = 1;
    end else if (:edit_kind = 'OLD') then begin
      select SL.ID_MOD_SESSION
      from   SMETKA_LINE SL
      where  SL.ID_SMETKA_LINE = :id_master_line
      into   :new_id_mod_session;
      status = -10;
    end
    qty = 0; /* VOIDING LINES ARE WITH 0 VALID QTY */
    void_sum = :line_sum;
    line_sum = 0;
  end

  if (:new_line_text = 1) then begin
    insert into SMETKA_LINE_TEXT(ID_MOD_SESSION, ID_END_WORK, LINE_TEXT)
    values (:new_id_mod_session, :id_end_work, :line_text);
  end


  insert into SMETKA_LINE (ID_SMETKA_TRANS, ID_SMETKA_HEAD, ID_STOCK, ID_INVENT,
    QTY, QTY_POST, QTY_VOID, COST_PRICE, SALE_PRICE, LINE_SUM, VOID_SUM,
    ID_MENU_PRICE_LIST,
    ID_VOIDED_LINE, ID_LINE_VOID, ID_LINE_DISCOUNT, POST_STATUS, STATUS, ID_VAT,
    DISCOUNT_SUM, ID_PRICE_LIST_OVERRULE, ID_END_WORK,
    ID_MOD_SESSION, NETO_SUM, TAX_PERCENT, ID_TRANSFER_SOURCE_LINE)
  values (:id_smetka_trans, :id_smetka_head, :id_stock, :id_invent,
    :qty, :qty_post, :qty_void, :cost_price, :sale_price, :line_sum, :void_sum,
    :id_menu_price_list,
    :id_master_line, :id_line_void, :id_line_discount, :post_status, :status, :id_vat,
    :discount_sum, :id_price_list_overrule, :id_end_work,
    :new_id_mod_session, :line_sum, :tax_percent, :id_transfer_source_line);

END
^

/* Restore proc. body: BILL_POST_FINALIZE */
ALTER PROCEDURE BILL_POST_FINALIZE(ID_CLIENT_POS INTEGER,
ID_END_WORK INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_SMETKA_TRANS INTEGER,
ID_SMETKA_DISCOUNT INTEGER,
ID_CONTRAGENT INTEGER,
ID_CONTRAGENT_OVERRULE INTEGER,
OPEN_TYPE VARCHAR(10),
CLOSE_TYPE VARCHAR(10),
ID_MANUAL_MENU INTEGER,
ID_SURCHARGE INTEGER,
SURCHARGE_VALUE NUMERIC(15,4),
CNT_LINES INTEGER,
CNT_VOIDS INTEGER,
TOTAL_SUM NUMERIC(15,4),
DISCOUNT_SUM NUMERIC(15,4),
SURCHARGE_SUM NUMERIC(15,4),
GRAND_TOTAL_SUM NUMERIC(15,4),
CUSTOMER_CNT INTEGER,
CARD_CODE VARCHAR(50))
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
VIP_LINES INTEGER)
 AS
declare variable smetka_num integer;
  declare variable status integer;
  declare variable stop_datetime timestamp;
  declare variable old_contragent_overrule integer;
  declare variable old_contragent integer;
  declare variable old_status integer;
  declare variable vip_lines_bill integer;
  declare variable no_use varchar(255) ;
  declare variable paranoid_pay_exist integer;
  declare variable action_text varchar(255);
  declare variable object_name varchar(30);
  declare variable table_no integer;
  declare variable smetka_date date;
  declare variable action_code varchar(50);
  declare variable unique_sales_number varchar(30);
begin
  err_code = 0;
  err_text = '';
  stop_datetime = current_timestamp;
  if (exists(
    select SP.ID_SMETKA_PAYMENT
    from   SMETKA_PAYMENT SP
    where  SP.ID_SMETKA_HEAD = :id_smetka_head
           and SP.PAYMENT_VALUE > 0
           and SP.STATUS > 0
  )) then paranoid_pay_exist = 1;
  else paranoid_pay_exist = 0;

  execute procedure SYS_PARANOID_DEBUG(:paranoid_pay_exist, :id_client_pos,
    :id_end_work, :id_smetka_head, :id_smetka_trans, :open_type, :close_type, 1);

  select SH.ID_CONTRAGENT, SH.SMETKA_NUM,
         SH.ID_CONTRAGENT_OVERRULE, SH.STATUS, o.OBJECT_NAME,
         sh.TABLE_NO, sh.SMETKA_DATE, sh.UNIQUE_SALES_NUMBER
  from   SMETKA_HEAD SH
         join NOM_OBJECT o on o.ID_OBJECT = sh.ID_OBJECT
  where  sh.id_smetka_head = :id_smetka_head
  into   :old_contragent, :smetka_num,
         :old_contragent_overrule, :old_status, :object_name,
         :table_no, :smetka_date, :unique_sales_number;
  if (:old_contragent = :id_contragent) then id_contragent_overrule = :old_contragent_overrule;

  execute procedure BILL_TRANSACTION(:id_smetka_trans, :id_end_work, '', :cnt_voids, :cnt_lines, :id_client_pos, 'STOP')
  returning_values  :err_code, :err_text, :id_smetka_trans;

  delete
  from   SMETKA_LINE SL
  where  SL.ID_SMETKA_HEAD = :id_smetka_head
         and SL.QTY_POST = 0
         and SL.ID_LINE_VOID is null;

  execute procedure BILL_PROBE_VALID(:id_smetka_head)
  returning_values  :VIP_lines_bill;

  if (:open_type = 'CREATE') then begin
    if ((:VIP_lines_bill = 0) and (:close_type = 'CANCEL')) then begin /* WE DO NOT SUPPORT EMPTY BILLS */
      /* 'ORDER' & 'TOTAL' ARE MANAGED IN THE GUI => 'CANCEL' */
      delete from SMETKA_HEAD SH
      where  SH.ID_SMETKA_HEAD = :ID_SMETKA_HEAD;

    end else if ((:VIP_lines_bill = 0) and (:close_type <> 'ORDER')) then close_type = 'OPEN_VOID'; /* BILL MUST BE VOIDED */

  end else if ( :open_type = 'OPEN' ) then begin
    if ((:VIP_lines_bill = 0) and (:close_type in ('TOTAL', 'CANCEL'))) then close_type = 'OPEN_VOID';
  end

  if (:unique_sales_number is null) then begin
    execute procedure BILL_UNQ_SALES_NUMBER_SET(:ID_SMETKA_HEAD, :ID_CLIENT_POS)
    returning_values (:unique_sales_number);
  end

  if (:close_type = 'CANCEL') then begin
    execute procedure SYS_PARANOID_DEBUG(:paranoid_pay_exist, :id_client_pos,
      :id_end_work, :id_smetka_head, :id_smetka_trans, :open_type, :close_type, 2);
    exit;
  end else if (:close_type = 'TOTAL') then begin
    execute procedure FISCAL_DEVICE_STATUS_CHECK(:ID_CLIENT_POS)
    returning_values (:no_use);

    if (:old_status <> 1 /* OPEN (ORDER) */) then begin
      err_code = -153;
      exit;
    end
    status = 2;
  end else if (:close_type = 'ORDER') then begin
    status = 1;
    stop_datetime = null;
  end else if (:close_type in ('TOTAL_VOID', 'OPEN_VOID')) then begin
    execute procedure FISCAL_DEVICE_STATUS_CHECK(:ID_CLIENT_POS)
    returning_values (:no_use);

    if (:close_type = 'TOTAL_VOID') then status = -3;
    else if (:close_type = 'OPEN_VOID') then status = -4;
    /* FINAL PREPARATIOIN FOR VANISHING EVERYTHING */
    surcharge_value = 0;
    id_surcharge = null;
    customer_cnt = 0;
    id_smetka_discount = null;
  end

  update SMETKA_HEAD SH
  set    SH.STATUS = :status,
         SH.SMETKA_NUM = :smetka_num,
         SH.STOP_DATETIME = :stop_datetime,
         SH.TOTAL_SUM = :total_sum,
         SH.DISCOUNT_SUM = :discount_sum,
         SH.SURCHARGE_SUM = :surcharge_sum,
         SH.GRAND_TOTAL_SUM = :grand_total_sum,
         SH.ID_MANUAL_PRICE_LIST = :id_manual_menu,
         SH.ID_SMETKA_DISCOUNT = :id_smetka_discount,
         SH.ID_SURCHARGE = :id_surcharge,
         SH.SURCHARGE_VALUE = :surcharge_value,
         SH.ID_CONTRAGENT = :id_contragent,
         SH.ID_CONTRAGENT_OVERRULE = :id_contragent_overrule,
         SH.CUSTOMER_CNT = :customer_cnt,
         SH.CARD_CODE = :card_code
  where  SH.ID_SMETKA_HEAD = :ID_SMETKA_HEAD;

  if (exists(
    select count(distinct SP.FISC_STATUS)
    from   SMETKA_PAYMENT SP
           join SMETKA_HEAD SH on SH.ID_SMETKA_HEAD = Sp.ID_SMETKA_HEAD
           join NOM_PAYMENT_TYPE PT on PT.ID_PAYMENT_TYPE = SP.ID_PAYMENT_TYPE
    where  SH.ID_SMETKA_HEAD = :id_smetka_head
           and SP.ID_PAYMENT_VOID is null
           and SH.STATUS = 2
    having count(distinct SP.FISC_STATUS) > 1
  )) then begin
    err_code = -307;
    exit;
  end

  execute procedure SYS_PARANOID_DEBUG(:paranoid_pay_exist, :id_client_pos,
    :id_end_work, :id_smetka_head, :id_smetka_trans, :OPEN_TYPE, :close_type, 3);

  if (:close_type in ('TOTAL_VOID', 'OPEN_VOID')) then begin
    action_text = f_character(29) || :smetka_num || f_character(28) || :object_name || f_character(28) || :table_no || f_character(28) || :smetka_date || f_character(28) || coalesce(:unique_sales_number, '');
    if (:close_type = 'OPEN_VOID') then action_code = 'BILL_VOID_OPEN';
    else if (:close_type = 'TOTAL_VOID') then action_code = 'BILL_VOID_CLOSED';

    execute procedure SYSTEM_LOG_ADD('BILL', :ID_SMETKA_HEAD, :action_code, :action_text);
  end

  delete from SMETKA_DISCOUNT SD
  where  SD.ID_SMETKA_HEAD = :id_smetka_head
         and SD.STATUS in (2, 3) /* TOTAL or CONTRAGENT */
         and ((:id_smetka_discount is null) or (SD.ID_SMETKA_DISCOUNT <> :id_smetka_discount));

  /* GENERATE BONS */
  if ((:close_type = 'TOTAL') and (:VIP_lines_bill = 0)) then exit;
  execute procedure BILL_PROBE_VALID(:id_smetka_head, :id_smetka_trans)
  returning_values  :VIP_lines;
  if ((:close_type = 'ORDER') and (:VIP_lines = 0)) then exit;

  execute procedure BILL_POST_FINALIZE_BON(:id_smetka_head, :id_smetka_trans, :id_end_work)
  returning_values  :err_code, :err_text;

  if ((:close_type = 'TOTAL') and (not exists(
    select *
    from   SMETKA_PAYMENT SP
    where  SP.ID_SMETKA_HEAD = :id_smetka_head
           and SP.STATUS > 0
  ))) then begin
    /* BILL WITH 100% DISCOUNT, WE MUST SET SPECIAL PAYMENT TYPE*/
    execute procedure BILL_PAYMENT_ADD(:id_client_pos, :id_end_work, :id_smetka_head, :id_smetka_trans, 0, null, 0, 0)
    returning_values  :err_code, :err_text, :no_use;
  end
end
^

/* Restore proc. body: BILL_UNQ_SALES_NUMBER_SET */
ALTER PROCEDURE BILL_UNQ_SALES_NUMBER_SET(ID_SMETKA_HEAD INTEGER,
ID_CLIENT_POS INTEGER)
 RETURNS(UNIQUE_SALES_NUMBER VARCHAR(30))
 AS
declare variable dbkey char(8);
begin
  select h.RDB$DB_KEY, h.UNIQUE_SALES_NUMBER
  from   SMETKA_HEAD h
  where  h.ID_SMETKA_HEAD = :ID_SMETKA_HEAD
  into   :dbkey, :UNIQUE_SALES_NUMBER;

  if ((:unique_sales_number is null) and (:dbkey is not null)) then begin
    if (:ID_CLIENT_POS is null) then ID_CLIENT_POS = RDB$GET_CONTEXT('USER_SESSION', 'ID_CLIENT_POS');

    execute procedure UNIQUE_SALES_NUMBER_GEN(:ID_CLIENT_POS)
    returning_values (:UNIQUE_SALES_NUMBER);

    update SMETKA_HEAD SH
    set    SH.UNIQUE_SALES_NUMBER = :UNIQUE_SALES_NUMBER
    where  SH.rdb$db_key = :dbkey;
  end
end
^

/* Restore proc. body: FISCALIZE_DISCOUNTS$FISC */
ALTER PROCEDURE FISCALIZE_DISCOUNTS$FISC(ID_DOC_HEAD INTEGER)
 RETURNS(DISCOUNTS_SUM NUMERIC(15,4))
 AS
begin
  select h.DISCOUNT_SUM
  from   SMETKA_HEAD h
  where  h.ID_SMETKA_HEAD = :ID_DOC_HEAD
  into   :DISCOUNTS_SUM;
end
^

/* Alter Procedure... */
/* Alter (BILL_PAYMENT_ADD) */
ALTER PROCEDURE BILL_PAYMENT_ADD(ID_CLIENT_POS INTEGER,
ID_END_WORK INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_SMETKA_TRANS INTEGER,
ID_PAYMENT_TYPE INTEGER,
ID_PAYMENT_OVERRULE INTEGER,
PAYMENT_VALUE NUMERIC(15,4),
CHANGE_VALUE NUMERIC(15,4))
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
NEW_ID_SMETKA_PAYMENT INTEGER)
 AS
declare variable fisc_status integer;
  declare variable fiscal_device_id varchar(10);
begin
  execute procedure FISCAL_DEVICE_STATUS_CHECK(:ID_CLIENT_POS)
  returning_values :fiscal_device_id;

  err_code = 0;
  err_text = '';

  select PT.IS_FISCAL
  from   NOM_PAYMENT_TYPE PT
  where  PT.ID_PAYMENT_TYPE = :id_payment_type
  into   :fisc_status;

  new_id_smetka_payment = gen_id(gen_smetka_payment_id,1);
  insert into SMETKA_PAYMENT (ID_SMETKA_PAYMENT, ID_SMETKA_HEAD, ID_SMETKA_TRANS, ID_PAYMENT_TYPE, ID_PAYMENT_OVERRULE,
    PAYMENT_VALUE, CHANGE_VALUE, STATUS, FISC_STATUS, ID_END_WORK)
  values (:new_id_smetka_payment, :id_smetka_head, :id_smetka_trans, :id_payment_type, :id_payment_overrule,
    :payment_value, :change_value, 1, :fisc_status, :id_end_work);
END
^

/* empty dependent procedure body */
/* Clear: BILL_POST_ADD$PDA for: BILL_POST_ADD */
ALTER PROCEDURE BILL_POST_ADD$PDA(ID_CLIENT_POS INTEGER,
ID_END_WORK INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_SMETKA_TRANS INTEGER)
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
TOTAL_SUM NUMERIC(15,4))
 AS
 BEGIN SUSPEND; END
^

/* empty dependent procedure body */
/* Clear: MANAGE_VOID_BILL for: BILL_POST_ADD */
ALTER PROCEDURE MANAGE_VOID_BILL(ID_CLIENT_POS INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_VOID_REASON INTEGER,
ID_VOID_OVERRULE INTEGER)
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
CLOSE_TYPE VARCHAR(10),
VIP_LINES INTEGER,
NEW_ID_SMETKA_TRANS INTEGER)
 AS
 BEGIN SUSPEND; END
^

/* Alter (BILL_POST_ADD) */
ALTER PROCEDURE BILL_POST_ADD(ID_END_WORK INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_SMETKA_TRANS INTEGER,
ID_MENU_PRICE_LIST INTEGER,
ID_PRICE_LIST_OVERRULE INTEGER,
ID_STOCK INTEGER,
ID_INVENT INTEGER,
ID_VAT INTEGER,
QTY NUMERIC(15,4),
SALE_PRICE NUMERIC(15,4),
LINE_SUM NUMERIC(15,4),
ID_LINE_DISCOUNT INTEGER,
ID_LINE_VOID INTEGER,
EDIT_KIND VARCHAR(10),
ID_MASTER_LINE INTEGER,
LINE_TEXT VARCHAR(2000),
MOD_CNT INTEGER)
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
NEW_ID_MOD_SESSION INTEGER)
 AS
declare variable cost_price numeric(15, 4);
  declare variable status integer;
  declare variable vqty numeric(15, 4);
  declare variable vsl_dbkey char(8);
  declare variable qty_post numeric(15, 4);
  declare variable qty_void numeric(15, 4);
  declare variable id_voided_discount integer;
  declare variable discount_sum numeric(15, 4);
  declare variable void_sum numeric(15, 4);
  declare variable post_status integer;
  declare variable is_service integer;
  declare variable is_use integer;
  declare variable new_line_text integer;
  declare variable tax_percent numeric(15,4);
  declare variable unique_sales_number varchar(30);
  declare variable stock_name varchar(30);
  declare variable object_name varchar(30);
  declare variable smetka_date date;
  declare variable smetka_num integer;
  declare variable fiscal_device_id varchar(10);
  declare variable new_id_line_void integer;
  declare variable id_client_pos integer;
  declare variable id_void_reason integer;
  declare variable id_transfer_source_line integer;
begin
  execute procedure FISCAL_DEVICE_STATUS_CHECK(null)
  returning_values :fiscal_device_id;

  err_code = 0;
  err_text = '';
  void_sum = 0;
  new_line_text = 0;
  id_transfer_source_line = null;

  select S.COST_PRICE, S.IS_SERVICE, s.IS_USE, s.STOCK_NAME
  from   STOCK S
  where  S.ID_STOCK = :id_stock
  into   :cost_price, :is_service, :is_use, :stock_name;
  if (:is_use = 0 and coalesce(:EDIT_KIND, '') = '') then begin
    ERR_CODE = -311;
    ERR_TEXT = :stock_name;
    exit;
  end
  if (:is_service = 1) then post_status = -3;
  else post_status = 1;

  select tg.TAXES_VALUE
  from   TAX_GROUP TG
  where  TG.ID_TAX_GROUP = :ID_VAT
  into   :tax_percent;

  if (:edit_kind in ('EDIT_VOID', 'EDIT_MOVE', '')) then begin
    if (:id_line_discount is not null) then begin
      select SD.DISCOUNT_SUM
      from   SMETKA_DISCOUNT SD
      where  SD.ID_SMETKA_DISCOUNT = :id_line_discount
      into   :discount_sum;
    end else discount_sum = 0;

    if (:edit_kind in ('EDIT_VOID', 'EDIT_MOVE')) then begin
      select SL.RDB$DB_KEY, SL.ID_LINE_DISCOUNT, SL.QTY, SL.QTY_POST, SL.QTY_VOID,
             SL.ID_MOD_SESSION, s.STOCK_NAME, h.UNIQUE_SALES_NUMBER, o.OBJECT_NAME,
             h.SMETKA_DATE, h.SMETKA_NUM
      from   SMETKA_LINE SL
             join STOCK s on s.ID_STOCK = sl.ID_STOCK
             join SMETKA_HEAD h on h.ID_SMETKA_HEAD = sl.ID_SMETKA_HEAD
             join NOM_OBJECT o on o.ID_OBJECT = h.ID_OBJECT
      where  SL.ID_SMETKA_LINE = :id_master_line
      into   :vsl_dbkey, :id_voided_discount, :vqty, :qty_post, :qty_void,
             :new_id_mod_session, :stock_name, :unique_sales_number, :object_name,
             :smetka_date, :smetka_num;

      if (:vqty < :qty) then begin
        err_code = -1;
        err_text = 'QTY_FOR_VOID='||:qty||', QTY_NALICH='||:vqty||'ID_SMETKA_LINE'||:ID_MASTER_LINE;
        exit;
      end else if (:qty = 0) then status = -10;
      else status = 1;
      if (:edit_kind = 'EDIT_VOID') then begin
        qty_void = (:qty_void + (:vqty - :qty));
        qty_post = :qty_post;

        update SMETKA_LINE SL
        set    SL.ID_LINE_DISCOUNT = :id_line_discount,
               SL.DISCOUNT_SUM = :discount_sum,
               SL.LINE_SUM     = :line_sum,
               SL.STATUS       = :status,
               SL.QTY_VOID     = :qty_void,
               SL.QTY_POST     = :qty_post,
               SL.QTY          = :qty,
               SL.NETO_SUM     = :line_sum
        where  SL.RDB$DB_KEY = :vsl_dbkey;
    
        if (:id_voided_discount is not null) then begin
          delete from SMETKA_DISCOUNT SD
          where SD.ID_SMETKA_DISCOUNT = :id_voided_discount;
        end
  
        execute procedure SYSTEM_LOG_ADD('BILL', :ID_SMETKA_HEAD, 'BILL_LINE_VOID',
                f_character(29) || :stock_name || f_character(28) || :smetka_num || f_character(28) || :object_name || f_character(28)
                || :smetka_date || f_character(28) || float_to_str(:qty_void, 'F;0.0') || f_character(28)
                || float_to_str(:sale_price, 'F;0.00') || f_character(28) || :unique_sales_number);

      end else if (:edit_kind = 'EDIT_MOVE') then begin
        execute procedure BILL_POST_ADD(:ID_END_WORK, :ID_SMETKA_HEAD, :ID_SMETKA_TRANS,
                :ID_MENU_PRICE_LIST, :ID_PRICE_LIST_OVERRULE, :ID_STOCK, :ID_INVENT, :ID_VAT,
                :QTY, :SALE_PRICE, :qty * :SALE_PRICE, :ID_LINE_DISCOUNT, null, 'EDIT_VOID',
                :ID_MASTER_LINE, :LINE_TEXT, 0)
        returning_values (:ERR_CODE, :ERR_TEXT, :NEW_ID_MOD_SESSION);
        if (:ERR_CODE <> 0) then exit;

        id_client_pos = RDB$GET_CONTEXT('USER_SESSION', 'ID_CLIENT_POS');

        execute procedure SYSTEM_GET_VALUE(:id_client_pos, 'TRANSFER_VOID')
        returning_values (:id_void_reason);

        execute procedure BILL_POST_ADD_VOID (RDB$GET_CONTEXT('USER_SESSION', 'ID_CLIENT_POS'),
                :ID_SMETKA_HEAD, :id_void_reason, :ID_PRICE_LIST_OVERRULE)
        returning_values (:ERR_CODE, :ERR_TEXT, :new_id_line_void);
        if (:ERR_CODE <> 0) then exit;

        qty = :vqty - :qty;
        execute procedure BILL_POST_ADD(:ID_END_WORK, :ID_SMETKA_HEAD, :ID_SMETKA_TRANS,
                :ID_MENU_PRICE_LIST, :ID_PRICE_LIST_OVERRULE, :ID_STOCK, :ID_INVENT, :ID_VAT,
                :qty, :SALE_PRICE, :qty*:SALE_PRICE, :ID_LINE_DISCOUNT, :new_id_line_void, 'OLD',
                :ID_MASTER_LINE, '<void>'||:LINE_TEXT, 0)
        returning_values (:ERR_CODE, :ERR_TEXT, :NEW_ID_MOD_SESSION);
        if (:ERR_CODE <> 0) then exit;
      end

      exit;
    end
    if (:id_master_line is not null) then begin
      /* SPLITTING IS GOING ON  */
      select SL.ID_MOD_SESSION, sl.ID_TRANSFER_SOURCE_LINE
      from   SMETKA_LINE SL
      where  SL.ID_SMETKA_LINE = :id_master_line
      into   :new_id_mod_session, :id_transfer_source_line;

      id_transfer_source_line = coalesce(:id_transfer_source_line, :ID_MASTER_LINE);
      id_master_line = null;
    end else begin
      new_line_text = 1;
      new_id_mod_session = gen_id(gen_mod_session_id, 1);
      if (:mod_cnt = 0) then new_id_mod_session = -1*:new_id_mod_session;
    end
    qty_void = 0;
    qty_post = :qty;
    status = 1;
  end else begin
    /* PREPARING DATA FOR VOID LINES */
    qty_void = :qty;
    qty_post = 0;
    id_line_discount = null;
    discount_sum = 0;
    if (:edit_kind = 'CURRENT') then begin
      new_id_mod_session = gen_id(gen_mod_session_id, 1);
      status = -20;
      id_master_line = gen_id(gen_smetka_line_id, 1);
      insert into SMETKA_LINE (ID_SMETKA_LINE, ID_SMETKA_TRANS, ID_SMETKA_HEAD, ID_STOCK, ID_INVENT,
        QTY, QTY_POST, QTY_VOID, COST_PRICE, SALE_PRICE, LINE_SUM, VOID_SUM, ID_MENU_PRICE_LIST,
        ID_VOIDED_LINE, ID_LINE_VOID, ID_LINE_DISCOUNT, POST_STATUS, STATUS, ID_VAT, DISCOUNT_SUM,
        ID_PRICE_LIST_OVERRULE, ID_END_WORK, ID_MOD_SESSION, NETO_SUM, TAX_PERCENT, ID_TRANSFER_SOURCE_LINE)
      values (:id_master_line, :id_smetka_trans, :id_smetka_head, :id_stock, :id_invent,
        0, :qty, :qty, :cost_price, :sale_price, 0, 0, :id_menu_price_list,
        null, null, null, :post_status, :status, :id_vat, 0,
        :id_price_list_overrule, :id_end_work, :new_id_mod_session, 0, :tax_percent, :id_transfer_source_line);

      new_line_text = 1;
    end else if (:edit_kind = 'OLD') then begin
      select SL.ID_MOD_SESSION
      from   SMETKA_LINE SL
      where  SL.ID_SMETKA_LINE = :id_master_line
      into   :new_id_mod_session;
      status = -10;
    end
    qty = 0; /* VOIDING LINES ARE WITH 0 VALID QTY */
    void_sum = :line_sum;
    line_sum = 0;
  end

  if (:new_line_text = 1) then begin
    insert into SMETKA_LINE_TEXT(ID_MOD_SESSION, ID_END_WORK, LINE_TEXT)
    values (:new_id_mod_session, :id_end_work, :line_text);
  end


  insert into SMETKA_LINE (ID_SMETKA_TRANS, ID_SMETKA_HEAD, ID_STOCK, ID_INVENT,
    QTY, QTY_POST, QTY_VOID, COST_PRICE, SALE_PRICE, LINE_SUM, VOID_SUM,
    ID_MENU_PRICE_LIST,
    ID_VOIDED_LINE, ID_LINE_VOID, ID_LINE_DISCOUNT, POST_STATUS, STATUS, ID_VAT,
    DISCOUNT_SUM, ID_PRICE_LIST_OVERRULE, ID_END_WORK,
    ID_MOD_SESSION, NETO_SUM, TAX_PERCENT, ID_TRANSFER_SOURCE_LINE)
  values (:id_smetka_trans, :id_smetka_head, :id_stock, :id_invent,
    :qty, :qty_post, :qty_void, :cost_price, :sale_price, :line_sum, :void_sum,
    :id_menu_price_list,
    :id_master_line, :id_line_void, :id_line_discount, :post_status, :status, :id_vat,
    :discount_sum, :id_price_list_overrule, :id_end_work,
    :new_id_mod_session, :line_sum, :tax_percent, :id_transfer_source_line);

END
^

/* empty dependent procedure body */
/* Clear: BILL_POST_FINALIZE$PDA for: BILL_POST_ADD$PDA */
ALTER PROCEDURE BILL_POST_FINALIZE$PDA(ID_CLIENT_POS INTEGER,
ID_SMETKA_HEAD INTEGER,
TABLE_NO INTEGER,
ID_MANUAL_MENU INTEGER,
ID_END_WORK INTEGER,
CUSTOMER_CNT INTEGER)
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255))
 AS
 BEGIN SUSPEND; END
^

/* Alter (BILL_POST_ADD$PDA) */
ALTER PROCEDURE BILL_POST_ADD$PDA(ID_CLIENT_POS INTEGER,
ID_END_WORK INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_SMETKA_TRANS INTEGER)
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
TOTAL_SUM NUMERIC(15,4))
 AS
declare variable dbg varchar(2000);

  declare variable post_price numeric(15,4);
  declare variable stock_name varchar(50);
  declare variable pbp_dbkey char(8);
  declare variable base_dbkey char(8);
  declare variable int_mod_cnt integer;
  declare variable int_base_price numeric (15,4);
  declare variable int_line_text varchar(2000);

  declare variable id_menu_price_list integer;
  declare variable id_stock integer;
  declare variable post_qty numeric(15,4);
  declare variable base_tot_price numeric(15,4);
  declare variable mod_price numeric(15,4);

  declare variable active_user varchar(50);
  declare variable id_invent integer;
  declare variable is_modifier integer;
  declare variable id_vat integer;
  declare variable line_sum numeric(15,4);
  declare variable line_text varchar(2000);
  declare variable mod_cnt integer;
  declare variable new_id_mod_session integer;
begin
  /*  Description:
  ||
  */
  err_code = 0;
  err_text = '';
  execute procedure SYSTEM_GET_VALUE(:id_client_pos, 'CURRENT_USER')
  returning_values  :active_user;


  base_dbkey = null;
  for
    select PBP.RDB$DB_KEY, PBP.POST_PRICE, S.IS_MODIFIER, F_FIRSTUPPERCASE(S.STOCK_NAME)
    from   PDA_BILL_POST PBP
           join STOCK S on S.ID_STOCK = PBP.ID_STOCK
    where  PBP.ID_SESSION = current_connection
    order by PBP.ID_PDA_POST
    into   :pbp_dbkey, :post_price, :is_modifier, :stock_name
  do begin
    if (:is_modifier = 1) then begin
      update PDA_BILL_POST PBPU
      set    PBPU.INT_BASE_PRICE = :post_price,
             PBPU.INT_MOD_CNT = 0,
             PBPU.INT_LINE_TEXT = ''
      where  PBPU.RDB$DB_KEY = :pbp_dbkey;
      int_base_price = :int_base_price + :post_price;
      int_mod_cnt = :int_mod_cnt + 1;
      int_line_text = :int_line_text || F_CRLF() || '    -' || :stock_name;
    end else if (:base_dbkey is not null) then begin
      update PDA_BILL_POST PBPU
      set    PBPU.INT_BASE_PRICE = :int_base_price,
             PBPU.INT_MOD_CNT = :int_mod_cnt,
             PBPU.INT_LINE_TEXT = :int_line_text
      where  PBPU.RDB$DB_KEY = :base_dbkey;
      base_dbkey = null;
    end
    if (:base_dbkey is null) then begin
      base_dbkey = :pbp_dbkey;
      int_base_price = :post_price;
      int_mod_cnt = 0;
      int_line_text = :stock_name;
    end
  end
  if (:base_dbkey is not null) then begin
    update PDA_BILL_POST PBPU
    set    PBPU.INT_BASE_PRICE = :int_base_price,
           PBPU.INT_MOD_CNT = :int_mod_cnt,
           PBPU.INT_LINE_TEXT = :int_line_text
    where  PBPU.RDB$DB_KEY = :base_dbkey;
  end
  new_id_mod_session = null;
  total_sum = 0;
  for
    select PBP.ID_MENU_PRICE_LIST, PBP.ID_STOCK, PBP.POST_QTY, PBP.INT_BASE_PRICE, PBP.POST_PRICE,
           PBP.INT_MOD_CNT, PBP.INT_LINE_TEXT, S.ID_VAT_GROUP, S.IS_MODIFIER
    from   PDA_BILL_POST PBP
           join STOCK S on S.ID_STOCK = PBP.ID_STOCK
    where  PBP.ID_SESSION = current_connection
    order by PBP.ID_PDA_POST
    into   :id_menu_price_list, :id_stock, :post_qty, :base_tot_price, :mod_price,
           :mod_cnt, :line_text, :id_vat, :is_modifier
  do begin
    select ML.ID_INVENT
    from   MENU_PRICE_LIST MPL
           join MENU_LINE ML on ML.ID_MENU_HEAD = MPL.ID_MENU_HEAD
    where  MPL.ID_MENU_PRICE_LIST = :id_menu_price_list
           and ML.ID_STOCK = :id_stock
    into   :id_invent;

    if (:is_modifier = 0) then begin
      line_sum = roundto(:post_qty * :base_tot_price, 0.01);
      execute procedure BILL_POST_ADD(:id_end_work, :id_smetka_head, :id_smetka_trans, :id_menu_price_list,
              null, :id_stock, :id_invent, :id_vat, :post_qty, :base_tot_price, :line_sum, null, null, '',
              null, :line_text, :mod_cnt)
      returning_values  :err_code, :err_text, :new_id_mod_session;
      if (:err_code < 0) then exit;
    end else begin
      line_sum = 0;

      insert into SMETKA_MODIFFIER (ID_END_WORK, ID_MOD_SESSION, ID_STOCK, SALE_PRICE, NETO_SUM, SURCHARGE_SUM, DISCOUNT_SUM)
      values (:id_end_work, :new_id_mod_session, :id_stock, :mod_price, null, 0, 0);
    end
    total_sum = :total_sum + :line_sum;
  end

  delete from PDA_BILL_POST PBP where PBP.ID_SESSION = current_connection;
end
^

/* Alter (BILL_POST_FINALIZE) */
ALTER PROCEDURE BILL_POST_FINALIZE(ID_CLIENT_POS INTEGER,
ID_END_WORK INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_SMETKA_TRANS INTEGER,
ID_SMETKA_DISCOUNT INTEGER,
ID_CONTRAGENT INTEGER,
ID_CONTRAGENT_OVERRULE INTEGER,
OPEN_TYPE VARCHAR(10),
CLOSE_TYPE VARCHAR(10),
ID_MANUAL_MENU INTEGER,
ID_SURCHARGE INTEGER,
SURCHARGE_VALUE NUMERIC(15,4),
CNT_LINES INTEGER,
CNT_VOIDS INTEGER,
TOTAL_SUM NUMERIC(15,4),
DISCOUNT_SUM NUMERIC(15,4),
SURCHARGE_SUM NUMERIC(15,4),
GRAND_TOTAL_SUM NUMERIC(15,4),
CUSTOMER_CNT INTEGER,
CARD_CODE VARCHAR(50))
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
VIP_LINES INTEGER)
 AS
declare variable smetka_num integer;
  declare variable status integer;
  declare variable stop_datetime timestamp;
  declare variable old_contragent_overrule integer;
  declare variable old_contragent integer;
  declare variable old_status integer;
  declare variable vip_lines_bill integer;
  declare variable no_use varchar(255) ;
  declare variable paranoid_pay_exist integer;
  declare variable action_text varchar(255);
  declare variable object_name varchar(30);
  declare variable table_no integer;
  declare variable smetka_date date;
  declare variable action_code varchar(50);
  declare variable unique_sales_number varchar(30);
begin
  err_code = 0;
  err_text = '';
  stop_datetime = current_timestamp;
  if (exists(
    select SP.ID_SMETKA_PAYMENT
    from   SMETKA_PAYMENT SP
    where  SP.ID_SMETKA_HEAD = :id_smetka_head
           and SP.PAYMENT_VALUE > 0
           and SP.STATUS > 0
  )) then paranoid_pay_exist = 1;
  else paranoid_pay_exist = 0;

  execute procedure SYS_PARANOID_DEBUG(:paranoid_pay_exist, :id_client_pos,
    :id_end_work, :id_smetka_head, :id_smetka_trans, :open_type, :close_type, 1);

  select SH.ID_CONTRAGENT, SH.SMETKA_NUM,
         SH.ID_CONTRAGENT_OVERRULE, SH.STATUS, o.OBJECT_NAME,
         sh.TABLE_NO, sh.SMETKA_DATE, sh.UNIQUE_SALES_NUMBER
  from   SMETKA_HEAD SH
         join NOM_OBJECT o on o.ID_OBJECT = sh.ID_OBJECT
  where  sh.id_smetka_head = :id_smetka_head
  into   :old_contragent, :smetka_num,
         :old_contragent_overrule, :old_status, :object_name,
         :table_no, :smetka_date, :unique_sales_number;
  if (:old_contragent = :id_contragent) then id_contragent_overrule = :old_contragent_overrule;

  execute procedure BILL_TRANSACTION(:id_smetka_trans, :id_end_work, '', :cnt_voids, :cnt_lines, :id_client_pos, 'STOP')
  returning_values  :err_code, :err_text, :id_smetka_trans;

  delete
  from   SMETKA_LINE SL
  where  SL.ID_SMETKA_HEAD = :id_smetka_head
         and SL.QTY_POST = 0
         and SL.ID_LINE_VOID is null;

  execute procedure BILL_PROBE_VALID(:id_smetka_head)
  returning_values  :VIP_lines_bill;

  if (:open_type = 'CREATE') then begin
    if ((:VIP_lines_bill = 0) and (:close_type = 'CANCEL')) then begin /* WE DO NOT SUPPORT EMPTY BILLS */
      /* 'ORDER' & 'TOTAL' ARE MANAGED IN THE GUI => 'CANCEL' */
      delete from SMETKA_HEAD SH
      where  SH.ID_SMETKA_HEAD = :ID_SMETKA_HEAD;

    end else if ((:VIP_lines_bill = 0) and (:close_type <> 'ORDER')) then close_type = 'OPEN_VOID'; /* BILL MUST BE VOIDED */

  end else if ( :open_type = 'OPEN' ) then begin
    if ((:VIP_lines_bill = 0) and (:close_type in ('TOTAL', 'CANCEL'))) then close_type = 'OPEN_VOID';
  end

  if (:unique_sales_number is null) then begin
    execute procedure BILL_UNQ_SALES_NUMBER_SET(:ID_SMETKA_HEAD, :ID_CLIENT_POS)
    returning_values (:unique_sales_number);
  end

  if (:close_type = 'CANCEL') then begin
    execute procedure SYS_PARANOID_DEBUG(:paranoid_pay_exist, :id_client_pos,
      :id_end_work, :id_smetka_head, :id_smetka_trans, :open_type, :close_type, 2);
    exit;
  end else if (:close_type = 'TOTAL') then begin
    execute procedure FISCAL_DEVICE_STATUS_CHECK(:ID_CLIENT_POS)
    returning_values (:no_use);

    if (:old_status <> 1 /* OPEN (ORDER) */) then begin
      err_code = -153;
      exit;
    end
    status = 2;
  end else if (:close_type = 'ORDER') then begin
    status = 1;
    stop_datetime = null;
  end else if (:close_type in ('TOTAL_VOID', 'OPEN_VOID')) then begin
    execute procedure FISCAL_DEVICE_STATUS_CHECK(:ID_CLIENT_POS)
    returning_values (:no_use);

    if (:close_type = 'TOTAL_VOID') then status = -3;
    else if (:close_type = 'OPEN_VOID') then status = -4;
    /* FINAL PREPARATIOIN FOR VANISHING EVERYTHING */
    surcharge_value = 0;
    id_surcharge = null;
    customer_cnt = 0;
    id_smetka_discount = null;
  end

  update SMETKA_HEAD SH
  set    SH.STATUS = :status,
         SH.SMETKA_NUM = :smetka_num,
         SH.STOP_DATETIME = :stop_datetime,
         SH.TOTAL_SUM = :total_sum,
         SH.DISCOUNT_SUM = :discount_sum,
         SH.SURCHARGE_SUM = :surcharge_sum,
         SH.GRAND_TOTAL_SUM = :grand_total_sum,
         SH.ID_MANUAL_PRICE_LIST = :id_manual_menu,
         SH.ID_SMETKA_DISCOUNT = :id_smetka_discount,
         SH.ID_SURCHARGE = :id_surcharge,
         SH.SURCHARGE_VALUE = :surcharge_value,
         SH.ID_CONTRAGENT = :id_contragent,
         SH.ID_CONTRAGENT_OVERRULE = :id_contragent_overrule,
         SH.CUSTOMER_CNT = :customer_cnt,
         SH.CARD_CODE = :card_code
  where  SH.ID_SMETKA_HEAD = :ID_SMETKA_HEAD;

  if (exists(
    select count(distinct SP.FISC_STATUS)
    from   SMETKA_PAYMENT SP
           join SMETKA_HEAD SH on SH.ID_SMETKA_HEAD = Sp.ID_SMETKA_HEAD
           join NOM_PAYMENT_TYPE PT on PT.ID_PAYMENT_TYPE = SP.ID_PAYMENT_TYPE
    where  SH.ID_SMETKA_HEAD = :id_smetka_head
           and SP.ID_PAYMENT_VOID is null
           and SH.STATUS = 2
    having count(distinct SP.FISC_STATUS) > 1
  )) then begin
    err_code = -307;
    exit;
  end

  execute procedure SYS_PARANOID_DEBUG(:paranoid_pay_exist, :id_client_pos,
    :id_end_work, :id_smetka_head, :id_smetka_trans, :OPEN_TYPE, :close_type, 3);

  if (:close_type in ('TOTAL_VOID', 'OPEN_VOID')) then begin
    action_text = f_character(29) || :smetka_num || f_character(28) || :object_name || f_character(28) || :table_no || f_character(28) || :smetka_date || f_character(28) || coalesce(:unique_sales_number, '');
    if (:close_type = 'OPEN_VOID') then action_code = 'BILL_VOID_OPEN';
    else if (:close_type = 'TOTAL_VOID') then action_code = 'BILL_VOID_CLOSED';

    execute procedure SYSTEM_LOG_ADD('BILL', :ID_SMETKA_HEAD, :action_code, :action_text);
  end

  delete from SMETKA_DISCOUNT SD
  where  SD.ID_SMETKA_HEAD = :id_smetka_head
         and SD.STATUS in (2, 3) /* TOTAL or CONTRAGENT */
         and ((:id_smetka_discount is null) or (SD.ID_SMETKA_DISCOUNT <> :id_smetka_discount));

  /* GENERATE BONS */
  if ((:close_type = 'TOTAL') and (:VIP_lines_bill = 0)) then exit;
  execute procedure BILL_PROBE_VALID(:id_smetka_head, :id_smetka_trans)
  returning_values  :VIP_lines;
  if ((:close_type = 'ORDER') and (:VIP_lines = 0)) then exit;

  execute procedure BILL_POST_FINALIZE_BON(:id_smetka_head, :id_smetka_trans, :id_end_work)
  returning_values  :err_code, :err_text;

  if ((:close_type = 'TOTAL') and (not exists(
    select *
    from   SMETKA_PAYMENT SP
    where  SP.ID_SMETKA_HEAD = :id_smetka_head
           and SP.STATUS > 0
  ))) then begin
    /* BILL WITH 100% DISCOUNT, WE MUST SET SPECIAL PAYMENT TYPE*/
    execute procedure BILL_PAYMENT_ADD(:id_client_pos, :id_end_work, :id_smetka_head, :id_smetka_trans, 0, null, 0, 0)
    returning_values  :err_code, :err_text, :no_use;
  end
end
^

/* Alter (BILL_POST_FINALIZE$PDA) */
ALTER PROCEDURE BILL_POST_FINALIZE$PDA(ID_CLIENT_POS INTEGER,
ID_SMETKA_HEAD INTEGER,
TABLE_NO INTEGER,
ID_MANUAL_MENU INTEGER,
ID_END_WORK INTEGER,
CUSTOMER_CNT INTEGER)
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255))
 AS
declare variable new_lines_sum numeric(15,4);
  declare variable new_id_smetka_trans integer;
  declare variable id_smetka_discount integer;
  declare variable id_contragent_ integer;
  declare variable card_code_ varchar(50);

  declare variable id_surcharge_ integer;
  declare variable surcharge_value numeric(15,4);
  declare variable is_percent_surcharge integer;
  declare variable cnt_lines integer;
  declare variable cnt_voids integer;
  declare variable total_sum numeric(15,4);
  declare variable discount_sum numeric(15,4);
  declare variable surcharge_sum numeric(15,4);
  declare variable grand_total_sum numeric(15,4);
  declare variable vip_lines integer;

  declare variable id_discount integer;
  declare variable is_percent_discount integer;
  declare variable discount_base numeric(15,4);
  declare variable discount_value numeric(15,4);

  declare variable open_type varchar(20);
  declare variable close_type varchar(20);
  declare variable discount_status integer;
  declare variable id_discount_overrrule integer;
  declare variable smetka_num varchar(20);
  declare variable no_use varchar(100);
begin
  err_code = 0;
  err_text = '';
  if (:id_smetka_head > 0) then open_type = 'OPEN';
  else open_type = 'CREATE';

  execute procedure BILL_OPEN(:id_client_pos, :id_smetka_head, :table_no, :customer_cnt, 'OPEN', :id_end_work)
  returning_values  :err_code, :err_text, :id_smetka_head, :new_id_smetka_trans,
                    :no_use, :no_use, :no_use, :id_contragent_,
                    :no_use, :id_smetka_discount, :id_surcharge_, :no_use,
                    :surcharge_value, :is_percent_surcharge, :id_discount, :is_percent_discount,
                    :no_use, :discount_status, :discount_value, :no_use,
                    :no_use, :no_use, :card_code_;
  if (:err_code < 0) then exit;

  execute procedure BILL_POST_ADD$PDA(:id_client_pos, :id_end_work, :id_smetka_head, :new_id_smetka_trans)
  returning_values  :err_code, :err_text, :new_lines_sum;
  if (:err_code < 0) then exit;

  select sum(iif(SL.STATUS < 0, 0, SL.LINE_SUM)),
         sum(iif((SL.STATUS < 0) or (SL.ID_LINE_DISCOUNT is not null), 0, SL.LINE_SUM)),
         count(SL.ID_SMETKA_LINE),
         count(iif(SL.STATUS < 0, SL.ID_SMETKA_LINE, null))
  from   SMETKA_LINE SL
  where  SL.ID_SMETKA_HEAD = :id_smetka_head
  into   :total_sum, :discount_base, :cnt_lines, :cnt_voids;

  if (:id_discount is not null) then begin
    if (:is_percent_discount = 1) then begin
      discount_sum = roundto((:discount_value/100.00) * :discount_base, 0.01);
    end else discount_sum = :discount_value;
    if (:id_smetka_discount is not null) then begin
      select SD.ID_RIGHT_OVERRULE
      from   SMETKA_DISCOUNT SD
      where  SD.ID_SMETKA_DISCOUNT = :id_smetka_discount
      into   :id_discount_overrrule;
    end else id_discount_overrrule = null;
    execute procedure BILL_POST_ADD_DISC(:id_end_work, :id_smetka_head, :id_discount, :discount_status,
            :discount_base, :discount_value, :discount_sum, :id_discount_overrrule)
    returning_values  :err_code, :err_text, :id_smetka_discount;
    if (:err_code < 0) then exit;
  end else discount_sum = 0;

  if (:id_surcharge_ is not null) then begin
    if (:is_percent_surcharge = 1) then begin
      surcharge_sum = roundto((:surcharge_value/100.00) * (:total_sum - :discount_sum), 0.01);
    end else surcharge_sum = :surcharge_value;
  end else surcharge_sum = 0;

  grand_total_sum = :total_sum - :discount_sum + :surcharge_sum;
  if (:open_type = 'CREATE' and :cnt_lines = 0) then close_type = 'CANCEL';
  else close_type = 'ORDER';
  execute procedure BILL_POST_FINALIZE(:id_client_pos, :id_end_work, :id_smetka_head, :new_id_smetka_trans,
          :id_smetka_discount, :id_contragent_, null, :open_type,
          :close_type, :id_manual_menu, :id_surcharge_, :surcharge_value, :cnt_lines,
          :cnt_voids, :total_sum, :discount_sum, :surcharge_sum, :grand_total_sum,
          :customer_cnt, :card_code_)
  returning_values  :err_code, :err_text, :vip_lines;
  if (:err_code < 0) then exit;

  execute procedure BILL_POST_FINALIZE_NUM(:id_client_pos, :id_smetka_head, :new_id_smetka_trans, :close_type, null)
  returning_values  :err_code, :err_text, :smetka_num;
  if (:err_code < 0) then exit;

  execute procedure BILL_POST_PRINT$PDA(:id_client_pos, :id_smetka_head, :new_id_smetka_trans, :close_type, :vip_lines)
  returning_values  :err_code, :err_text;
end
^

/* empty dependent procedure body */
/* Clear: EAI_FRONT for: BILL_UNQ_SALES_NUMBER_SET */
ALTER PROCEDURE EAI_FRONT(ID_END_WORK INTEGER,
HOTEL VARCHAR(50),
ID_SMETKA_HEAD INTEGER,
ID_EAI_MAP INTEGER,
EXTERNAL_IFC VARCHAR(100),
GUEST_BOARD VARCHAR(30) = null)
 RETURNS(TAG_VALUE VARCHAR(30),
TAG_NAME VARCHAR(50),
SUM_VALUE NUMERIC(15,4),
ALL_INCLUSIVE_PAYMENT_TYPE INTEGER,
UNIQUE_SALES_NUMBER VARCHAR(30))
 AS
 BEGIN SUSPEND; END
^

/* Alter (BILL_UNQ_SALES_NUMBER_SET) */
ALTER PROCEDURE BILL_UNQ_SALES_NUMBER_SET(ID_SMETKA_HEAD INTEGER,
ID_CLIENT_POS INTEGER)
 RETURNS(UNIQUE_SALES_NUMBER VARCHAR(30))
 AS
declare variable dbkey char(8);
begin
  select h.RDB$DB_KEY, h.UNIQUE_SALES_NUMBER
  from   SMETKA_HEAD h
  where  h.ID_SMETKA_HEAD = :ID_SMETKA_HEAD
  into   :dbkey, :UNIQUE_SALES_NUMBER;

  if ((:unique_sales_number is null) and (:dbkey is not null)) then begin
    if (:ID_CLIENT_POS is null) then ID_CLIENT_POS = RDB$GET_CONTEXT('USER_SESSION', 'ID_CLIENT_POS');

    execute procedure UNIQUE_SALES_NUMBER_GEN(:ID_CLIENT_POS)
    returning_values (:UNIQUE_SALES_NUMBER);

    update SMETKA_HEAD SH
    set    SH.UNIQUE_SALES_NUMBER = :UNIQUE_SALES_NUMBER
    where  SH.rdb$db_key = :dbkey;
  end
end
^

/* Alter (EAI_FRONT) */
ALTER PROCEDURE EAI_FRONT(ID_END_WORK INTEGER,
HOTEL VARCHAR(50),
ID_SMETKA_HEAD INTEGER,
ID_EAI_MAP INTEGER,
EXTERNAL_IFC VARCHAR(100),
GUEST_BOARD VARCHAR(30) = null)
 RETURNS(TAG_VALUE VARCHAR(30),
TAG_NAME VARCHAR(50),
SUM_VALUE NUMERIC(15,4),
ALL_INCLUSIVE_PAYMENT_TYPE INTEGER,
UNIQUE_SALES_NUMBER VARCHAR(30))
 AS
declare variable id_map_tag integer;
  declare variable id_object integer;
  declare variable id_eai_map_allinclusive integer;
  declare variable id_stock_group integer;
  declare variable has_payable smallint;
  declare variable transfer_value numeric(15,4);
  declare variable group_name varchar(30);
  declare variable smetka_line_ids varchar(5000);
  declare variable id_log integer;
begin
  /*  Description:
  ||
  */

  select SH.ID_OBJECT, sh.UNIQUE_SALES_NUMBER
  from   SMETKA_HEAD SH
  where  SH.ID_SMETKA_HEAD = :id_smetka_head
  into   :id_object, :UNIQUE_SALES_NUMBER;

  if ((:UNIQUE_SALES_NUMBER is null) and (:ID_SMETKA_HEAD is not null)) then begin
    -- preliminary UNIQUE_SALES_NUMBER sets
    execute procedure BILL_UNQ_SALES_NUMBER_SET(:ID_SMETKA_HEAD, null)
    returning_values (:UNIQUE_SALES_NUMBER);
  end

  all_inclusive_payment_type = null;
  select first 1 A.ID_PAYMENT_TYPE, A.ID_EAI_MAP_ALLINCLUSIVE
  from   EAI_MAP_ALLINCLUSIVE A
  where  A.HOTEL = :hotel
         and A.ID_OBJECT = :id_object
         and ((coalesce(a.COMPATIBLE_GUEST_BOARD, '') = '')
              or (a.COMPATIBLE_GUEST_BOARD = coalesce(:GUEST_BOARD, '')))
  order by nullif(a.COMPATIBLE_GUEST_BOARD, '') asc nulls last
  into   :ALL_INCLUSIVE_PAYMENT_TYPE, :id_eai_map_allinclusive;

  has_payable = 0;
  for
    select IFS.ID_MAP_TAG, IFS.ID_STOCK_GROUP, sum(IFS.SUM_VALUE), list(ifs.smetka_line_ids)
    from   EAI_FRONT_SUB(:id_end_work, :id_smetka_head, :id_eai_map, :external_ifc, :id_eai_map_allinclusive) IFS
    group by 1, 2
    into   :id_map_tag, :id_stock_group, :transfer_value, :smetka_line_ids
  do begin
    select MT.TAG_NAME, MT.TAG_VALUE
    from   EAI_MAP_TAG MT
    where  MT.ID_MAP_TAG = :id_map_tag
    into   :tag_name, :tag_value;

    select sg.STOCK_GROUP
    from   STOCK_GROUP SG
    where  sg.ID_STOCK_GROUP = :id_stock_group
    into   :group_name;

    insert into EAI_P2F_TRANSFER_LOG (ID_SMETKA_HEAD, ID_SMETKA_PAYMENT, ID_END_WORK, EXTERNAL_IFC,
           TAG_NAME, TAG_VALUE, STOCK_GROUP, TRANSFER_AMOUNT)
    values (:ID_SMETKA_HEAD, null, :ID_END_WORK, :EXTERNAL_IFC,
           :TAG_NAME, :TAG_VALUE, :group_name, :transfer_value)
    returning ID_EAI_P2F_TRANSFER_LOG into :id_log;

    insert into EAI_P2F_TRANSFER_LOG_LINES(ID_EAI_P2F_TRANSFER_LOG, ID_SMETKA_LINE)
    select :id_log, p.ITEM
    from   SYSTEM_STRING_PARSER(:smetka_line_ids, ',') p;

    has_payable = 1;
  end

  tag_value = null;
  tag_name = null;
  sum_value = null;
  if (:has_payable = 1) then begin
    for
      select tl.TAG_NAME, tl.TAG_VALUE, sum(tl.TRANSFER_AMOUNT)
      from   EAI_P2F_TRANSFER_LOG tl
      where  tl.EXTERNAL_IFC is not distinct from :EXTERNAL_IFC
             and ( (tl.ID_SMETKA_HEAD = nullif(:ID_SMETKA_HEAD, -1) and tl.ID_SMETKA_PAYMENT is null)
                  or (tl.ID_END_WORK = nullif(:ID_END_WORK, -1))
                 )
      group by 1, 2
      into   :TAG_NAME, :TAG_VALUE, :SUM_VALUE
    do begin
      suspend;
    end
  end else begin
    suspend;
  end
/*  end*/
end
^

/* Alter (MANAGE_VOID_BILL) */
ALTER PROCEDURE MANAGE_VOID_BILL(ID_CLIENT_POS INTEGER,
ID_SMETKA_HEAD INTEGER,
ID_VOID_REASON INTEGER,
ID_VOID_OVERRULE INTEGER)
 RETURNS(ERR_CODE INTEGER,
ERR_TEXT VARCHAR(255),
CLOSE_TYPE VARCHAR(10),
VIP_LINES INTEGER,
NEW_ID_SMETKA_TRANS INTEGER)
 AS
declare variable cnt_voids integer;
  declare variable id_smetka_void integer;
  declare variable active_user varchar(50);
  declare variable right_required varchar(20);
  declare variable smetka_status integer;
  declare variable id_smetka_line integer;
  declare variable id_menu_price_lis integer;
  declare variable id_invent integer;
  declare variable id_stock integer;
  declare variable id_vat integer;
  declare variable qty numeric(15, 4);
  declare variable sale_price numeric(15, 4);
  declare variable line_sum numeric(15, 4);
  declare variable id_contragent integer;
  declare variable id_current_ew integer;
  declare variable no_use integer;
  declare variable card_code varchar(50);
begin
  err_code = 0;
  err_text = '';
  select SH.STATUS, SH.ID_CONTRAGENT, SH.CARD_CODE
  from   SMETKA_HEAD SH
  where  SH.ID_SMETKA_HEAD = :id_smetka_head
  into   :smetka_status, :id_contragent, :card_code;

  if (:smetka_status is null) then err_code = -158;
  else if (:smetka_status not in (1, 2)) then err_code = -155;
  if (:err_code < 0) then exit;

  if (:smetka_status = 2) then begin
    right_required = 'BILL_VOID_TOTAL';
    close_type = 'TOTAL_VOID';
  end else if (:smetka_status = 1) then begin
    right_required = 'BILL_VOID_OPEN';
    close_type = 'OPEN_VOID';
  end

  execute procedure SYSTEM_CHECK_RIGHT(:id_client_pos, :right_required)
  returning_values  :err_code, :err_text;
  if (:err_code < 0) then exit;

  execute procedure MANAGE_COMMON(:id_client_pos, null, :id_smetka_head, :close_type,
    :id_void_reason, :id_void_overrule, '', 'LOG')
  returning_values  :err_code, :err_text, :id_smetka_void;
  if (:err_code < 0) then exit;

  execute procedure SYSTEM_GET_VALUE(:id_client_pos, 'CURRENT_USER')
  returning_values  :active_user;

  execute procedure SYSTEM_GET_VALUE(:id_client_pos, 'CURRENT_EW')
  returning_values  :id_current_ew;

  execute procedure BILL_TRANSACTION(null, :id_current_ew, :active_user, 0, 0, :id_client_pos, 'START')
  returning_values  :err_code, :err_text, :new_id_smetka_trans;
  if (:err_code < 0) then exit;

  /* 1. PAY_VOID  */
  if (:close_type = 'TOTAL_VOID') then begin
    update SMETKA_HEAD SH
    set    SH.STATUS = 1,
           SH.STOP_DATETIME = null
    where  SH.ID_SMETKA_HEAD = :id_smetka_head;

    execute procedure BILL_PAYMENT_VOID(:id_client_pos, :id_smetka_head, :new_id_smetka_trans,
      :id_void_reason, :id_void_overrule)
    returning_values  :err_code, :err_text;
    if (:err_code < 0) then exit;
  end

  /* 2. VOID_LINES */
  cnt_voids = 0;
  for
    select SL.ID_SMETKA_LINE, SL.ID_MENU_PRICE_LIST, SL.ID_INVENT, SL.ID_STOCK, SL.ID_VAT,
           SL.QTY, SL.SALE_PRICE, SL.LINE_SUM
    from   SMETKA_LINE SL
    where  SL.ID_SMETKA_HEAD = :id_smetka_head
           and SL.STATUS > 0
    into   :id_smetka_line, :id_menu_price_lis, :id_invent, :id_stock, :id_vat,
           :qty, :sale_price, :line_sum
  do begin
    execute procedure BILL_POST_ADD(:id_current_ew, :id_smetka_head, :new_id_smetka_trans, :id_menu_price_lis, null,
      :id_stock, id_invent, :id_vat, :qty, :sale_price, :line_sum, null, :id_smetka_void,
      'OLD', :id_smetka_line, null, null)
    returning_values  :err_code, :err_text, :no_use;
    if (:err_code < 0) then exit;

    execute procedure BILL_POST_ADD(:id_current_ew, :id_smetka_head, :new_id_smetka_trans, :id_menu_price_lis, null,
      :id_stock, id_invent, :id_vat, 0, :sale_price, 0, null, null,
      'EDIT_VOID', :id_smetka_line, null, null)
    returning_values  :err_code, :err_text, :no_use;
    if (:err_code < 0) then exit;
    cnt_voids = :cnt_voids + 1;
  end

  /* 3. BILL_FINALIZE */
  execute procedure BILL_POST_FINALIZE(:id_client_pos, :id_current_ew, :id_smetka_head, :new_id_smetka_trans, null,
   :id_contragent, null, 'OPEN', :close_type, null, null, 0, :cnt_voids, :cnt_voids, 0, 0, 0, 0, 0, :card_code)
  returning_values :err_code, err_text, :VIP_lines;
end
^

SET TERM ; ^

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN ID_CLIENT_POS POSITION 1;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN ID_OBJECT POSITION 2;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN ID_SETTING_PROFILE POSITION 3;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN CLIENT_NAME POSITION 4;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN MACHINE POSITION 5;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN CLIENT_KIND POSITION 6;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN ACTIVE_USER POSITION 7;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN SET_ID_OBJECT POSITION 8;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN IS_USE POSITION 9;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN CREATED_AT POSITION 10;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN UPDATED_AT POSITION 11;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN ARCHIVED_AT POSITION 12;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN FISCAL_DEVICE_ID POSITION 13;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN FISCAL_DEVICE_UPDATED_AT POSITION 14;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN FISCAL_SESSION_ID POSITION 15;

ALTER TABLE SYSTEM_CLIENT ALTER COLUMN FISCAL_DEVICE_ERROR POSITION 16;

