Сводный менеджер: Настройка бух.себестоимости

Материал из wiki.standart-n.ru
Перейти к: навигация, поиск
  • Себестоимость товара это суммовое значение закупочной суммы за вычетом суммы ндс
  • Себестоимость товара по данным стандарта храниться в колонках summa_o, sum_ndso, quant
  • По причине погрешностей округления данная себестоимость не устраивает бухгалтерию
  • Была разработана новая бухгалтерская себестоимость, которая хранится в колонках summa_o_account, sum_ndso_account, quant_account
  • Для решения проблем:
  • 1. При списании последнего остатка товара, списывать остаточную себестоиомость.
  • 2. Идентичное (до копейки (тьин)) отражение отчетов в программе сводный менеджер и 1с бухгалтерия.
  • Расчет бух. себестоимости запускается
  • 1. Ежедневно с назначенным заданием вызывается процедура pr_set_sum_account_gl. Проводит исправление/заполнение себестоимости за последние 15 дней
  • 2. Перед каждой автозагрузкой в 1с бухгалтерию запускается процедура pr_set_sum_account_gl. Проводит исправление/заполнение себестоимости за поледние 15 дней
  • Описание алгоритма PR_SET_SUM_ACCOUNT_GL:
  • первоначально колонки заполняются приблизительно равными стандарту данными, только с учетом округления
  • summa_o_account = round(summa_o,2)
  • sum_ndso_account = round(sum_ndso,2)
  • quant_account = round(quant,3)
  • Если процедура была запущена только на исправление ошибок, то сначала формируем список партий, по которым остатка количества нет, а себестоимость "зависла"
  • и вызываем PR_SET_SUM_ACCOUNT для определенной партии
  • Иначе выполняем PR_SET_SUM_ACCOUNT за весь указанный период
  • Описание алгоритма PR_SET_SUM_ACCOUNT
  • выложено в виде комментариев к процедуре

Файл:Алгоритм расчета себестоимости.zip


SET TERM ^ ;

create or alter procedure PR_SET_SUM_ACCOUNT (
    BEG_DATE DM_DATE,
    END_DATE DM_DATE,
    G$PROFILE_ID DM_ID,
    IN_PART_ID DM_ID_NULL,
    ITER DM_ID_NULL = 0,
    SUSPEND_MODE DM_STATUS = 0)
returns (
    OUT_ID DM_ID_NULL,
    OUT_DOC_ID DM_ID_NULL,
    OUT_DOC_COMMITDATE DM_DATE,
    PROFILE_ID DM_ID_NULL)
as
declare variable QUANT DM_DOUBLE;
declare variable DOC_ID DM_ID;
declare variable CUR_QUANT DM_ACCOUNT_QUANT;
declare variable DD1_ID DM_ID_NULL;
declare variable PRICE_O DM_DOUBLE;
declare variable M_UUID DM_UUID_NULL;
declare variable FOR_PART_ID DM_ID_NULL;
declare variable FOR_PROFILE_ID DM_ID_NULL;
declare variable PART_ID DM_ID_NULL;
declare variable KON_SUMMA_O_ACCOUNT DM_DOUBLE;
declare variable KON_SUM_NDSO_ACCOUNT DM_DOUBLE;
declare variable KON_SUM_NDSO DM_DOUBLE;
declare variable KON_SUMMA_O DM_DOUBLE;
declare variable CUR_SUM_NDSO DM_ACCOUNT;
declare variable TEK_OST DM_DOUBLE;
declare variable TEK_OST_SUM_NDSO DM_ACCOUNT;
declare variable DD_ID DM_ID_NULL;
declare variable TEK_OST_SUMMA_O DM_ACCOUNT;
declare variable CUR_SUMMA_O DM_ACCOUNT;
declare variable DOC_TYPE DM_ID_NULL;
declare variable AGENT_ID DM_ID_NULL;
declare variable BASE_TYPE DM_ID_NULL;
declare variable FOR_S_O_A DM_ACCOUNT;
declare variable FOR_S_N_A DM_ACCOUNT;
declare variable FOR_DOC_ID DM_ID_NULL;
declare variable DOCNUM DM_TEXT;
declare variable FOR_Q_A DM_ACCOUNT_QUANT;
declare variable AVG_QUANT DM_DOUBLE;
declare variable TEK_OST_REAL DM_DOUBLE;
begin
  -- выбираем документы движения за период или по партии
  -- так же расчитывает предыдущий остаток по кол-ву сумме закупа и сумме ндс до момента текущего движения
  for select d.doc_type,d.agent_id,p.price_o,d.vnum,dd.g$profile_id,dd.doc_id,dd.quant,
  dd.id,dd.sum_ndso_account,dd.summa_o_account,dd.part_id,dd.quant_account,
(select sum(dd1.quant_account) from doc_detail dd1 where dd1.part_id=dd.part_id and
( (dd1.doc_commitdate<=dd.doc_commitdate) or (dd1.doc_commitdate=dd.doc_commitdate and dd1.id<=dd.id))
and dd1.g$profile_id=dd.g$profile_id),
(select sum(dd1.quant) from doc_detail dd1 where dd1.part_id=dd.part_id and
( (dd1.doc_commitdate<=dd.doc_commitdate) or (dd1.doc_commitdate=dd.doc_commitdate and dd1.id<=dd.id))
and dd1.g$profile_id=dd.g$profile_id),
(select sum(dd1.sum_ndso_account) from doc_detail dd1 where dd1.part_id=dd.part_id and
( (dd1.doc_commitdate<=dd.doc_commitdate) or (dd1.doc_commitdate=dd.doc_commitdate and dd1.id<=dd.id))
and dd1.g$profile_id=dd.g$profile_id),
(select sum(dd1.summa_o_account) from doc_detail dd1 where dd1.part_id=dd.part_id and
( (dd1.doc_commitdate<=dd.doc_commitdate)  or (dd1.doc_commitdate=dd.doc_commitdate and dd1.id<=dd.id))
and dd1.g$profile_id=dd.g$profile_id),
(select avg(-dd1.quant_account) from doc_detail dd1 where
dd1.part_id=dd.part_id and dd1.quant<0 and dd1.g$profile_id=dd.g$profile_id),
coalesce(dt.base_type,0)
 from doc_detail dd
 left join docs d on d.id=dd.doc_id and d.g$profile_id=dd.g$profile_id
 left join doc_types dt on dt.id=d.doc_type
 left join parts p on p.id=dd.part_id and p.g$profile_id=dd.g$profile_id where
 ((doc_commitdate between :beg_date and :end_date) or (part_id=:in_part_id) )
 and dd.g$profile_id=:g$profile_id
 into
 :DOC_TYPE,:agent_id,:price_o,:DOCNUM,:PROFILE_ID,:doc_id,:quant,:dd_id,:cur_sum_ndso,
 :cur_summa_o,:part_id,:cur_quant,:tek_ost,:TEK_OST_REAL,:tek_ost_sum_ndso,:tek_ost_summa_o,:avg_quant,:base_type do
 --ДАННЫЕ СТАНДАРТА
 --quant - количество в детализации
 --TEK_OST_REAL - остаток по количеству до детализации

 --БУХГАЛТЕРСКИЕ ДАННЫЕ
 --cur_sum_ndso - сумма НДС в детализации
 --cur_summa_o - сумма закупочная в детализации
 --cur_quant - количество в детализации
 --tek_ost - остаток по количеству до детализации
 --tek_ost_sum_ndso - остаток суммы НДС до детализации
 --tek_ost_summa_o- остаток суммы закупочной до детализации

 --ПРОЧИЕ
 --dd_id - идентификатор детализации
 --avg_quant - средний размер кол-ва списания

 begin
    if ( (doc_type not in (1) ) or (doc_type is null)) then --and (BASE_TYPE<>3)
    begin
    --Если эта детализации соответвует последней продаже по данной партии,
    --то пытаемся в детализации в колонках бухгалтерских данных проставить
    --остаточное значение кол-ва, суммы закупочной и НДС
    if (abs(:tek_ost) >0 and  abs(:tek_ost_real)<0.0001) then --abs(:tek_ost)<:avg_quant/10
    begin
      update doc_detail set quant_account=:cur_quant-:tek_ost,
      dcard='1' ||'-'||:G$PROFILE_ID ||'-'|| :IN_PART_ID||'-'||:ITER||'-'||:SUSPEND_MODE||'part_id '||:part_id
       where id=:dd_id and g$profile_id=:profile_id and
       (quant_account<>:cur_quant-:tek_ost ) RETURNING new.id,new.doc_id,new.doc_commitdate into
        :OUT_ID, :OUT_DOC_ID,:OUT_DOC_COMMITDATE ;
       if (:SUSPEND_MODE=1) then suspend;
   end

    if ((abs(:tek_ost)<0.1 or (abs(:tek_ost_real)<0.01)) and
    ((abs(:tek_ost_sum_ndso) > 0.001) or (abs(:tek_ost_summa_o) between 0.001 and 10) ) and
    ((:base_type<>3 or (:quant<0)) )) then
    --Если до детализации остаток партии по количеству уже меньше 0.01,
    --но сумма бухгалтерской себестоимости еще осталась на остатках,
    --то нужно эту остаточную себестоимость списать в текущем движении
    -- Условие :base_type<>3 or (:quant<0) показывает, что в корр-ках и переоценках правим,
    --только строку списания товара
    begin
      select sum(summa_o),sum(summa_o_account),sum(sum_ndso),sum(sum_ndso_account) from doc_detail where part_id=:part_id and
       g$profile_id=:profile_id into :kon_summa_o,:kon_summa_o_account,:kon_sum_ndso,:kon_sum_ndso_account;
      if  ((abs(KON_Summa_o_account)>=0.001 ) or (abs(kon_sum_ndso_account)>=0.001 ) ) then     --(abs(KON_SUM_NDSO)>=0.001 or (abs(KON_Summa_o)>=0.001 ) or
      --исправление проводим только если за весь период движения по партии
      --сохранился не нулевой остаток бух.себестоимости
      update doc_detail set sum_ndso_account=:cur_sum_ndso-:tek_ost_sum_ndso,
      summa_o_account=:cur_summa_o-:tek_ost_summa_o, dcard='2' ||'-'||:G$PROFILE_ID ||'-'|| :IN_PART_ID||'-'||:ITER||'-'||:SUSPEND_MODE||'part_id '||:part_id
       where id=:dd_id and g$profile_id=:profile_id and
       (sum_ndso_account<>:cur_sum_ndso-:tek_ost_sum_ndso or (summa_o_account<>:cur_summa_o-:tek_ost_summa_o) )
       RETURNING new.id,new.doc_id,new.doc_commitdate into
        :OUT_ID, :OUT_DOC_ID,:OUT_DOC_COMMITDATE ;
       if (:SUSPEND_MODE=1) then suspend; --and doc_commitdate>='01.06.2017'
    end
    end
    else
    --Действия при приходе
    begin
      for select id from doc_detail where id=:dd_id and g$profile_id=:profile_id and
       (abs(summa_o_account-round(summa_o, 2))>0.1 or (abs(sum_ndso_account-round(sum_ndso, 2))>0.1) ) into :DD1_ID do
       --при наличии большой разницы в приходах от поставщика (более 0.1 тенге)
       -- в закупочноу сумме или сумме НДС
       --возвращаем значения Стандарта
       begin
          update doc_detail set summa_o_account=round(summa_o, 2),sum_ndso_account=round(sum_ndso, 2),
           dcard='3' ||'-'||:G$PROFILE_ID ||'-'|| :IN_PART_ID||'-'||:ITER||'-'||:SUSPEND_MODE||'part_id '||:part_id
           where id=:DD1_ID and g$profile_id=:profile_id
           RETURNING new.id,new.doc_id,new.doc_commitdate into
        :OUT_ID, :OUT_DOC_ID,:OUT_DOC_COMMITDATE ;
       if (:SUSPEND_MODE=1) then suspend;
          --Если исправление в приходе прошло, то необходимо пересчиать/проверить
          --бух.себестоимость для всех движений по данной партии
          for select OUT_ID,OUT_DOC_ID,OUT_DOC_COMMITDATE from PR_SET_SUM_ACCOUNT(null,null,:profile_id,:part_id,:ITER+1,:suspend_mode)into
          :OUT_ID,:OUT_DOC_ID,:OUT_DOC_COMMITDATE do
             if (:suspend_mode=1) then suspend;
       end
       -- в случае если это не первый приход партии и остаток на момент второго прихода
       --был почти 0, но имел зависшую себестоимость, то нужно это исправить в текущей детализации
       if (abs(:tek_ost)<0.01 and ((abs(:tek_ost_sum_ndso) > 0.001) or (abs(:tek_ost_summa_o) between 0.001 and 10) )) then
       for select first 1 id from  doc_detail where g$profile_id=:g$profile_id  and part_id=:part_id and quant<0 order by id desc into :dd1_id do
       begin

         update doc_detail set sum_ndso_account=sum_ndso_account-:tek_ost_sum_ndso,
         summa_o_account=summa_o_account-:tek_ost_summa_o , dcard='4' ||'-'||:G$PROFILE_ID ||'-'|| :IN_PART_ID||'-'||:ITER||'-'||:SUSPEND_MODE||'part_id '||:part_id
         where id=:dd1_id and g$profile_id=:profile_id RETURNING new.id,new.doc_id,new.doc_commitdate into
        :OUT_ID, :OUT_DOC_ID,:OUT_DOC_COMMITDATE ;
       if (:SUSPEND_MODE=1) then suspend;
       end
    end
    --Отдельно рассмотрим переоценки и корр-ки
    if (doc_type=8 or doc_type=7) then
    begin
      if (quant<0) then
      begin
        select summa_o_account,sum_ndso_account,quant_account from doc_detail dd where
        dd.g$profile_id=:g$profile_id and dd.id=:dd_id into  :FOR_S_O_A,:FOR_S_N_A,:for_q_a;
        for select id from parts where MOTHERPART_ID=:part_id and doc_id=:doc_id and g$profile_id=:g$profile_id and abs(price_o-:price_o)<0.01 into :for_part_id do
          --принудительно переписываем бух. себестоимость детализации оприходования
          --соответвующую текущей детализации списания на значения идентичные текущей детализации
          -- а процедура PR_SET_SUM_ACCOUNT_REC так же дополнительно запустит проверку/заполнение
          -- бух.себестоимости по партии оприходования в случае исправления сумм
          for select OUT_ID,OUT_DOC_ID,OUT_DOC_COMMITDATE
           from PR_SET_SUM_ACCOUNT_REC(:for_part_id,:profile_id,:FOR_S_N_A,:FOR_S_O_A,:for_q_a,:doc_id,:ITER,:suspend_mode) into
           :OUT_ID,:OUT_DOC_ID,:OUT_DOC_COMMITDATE do
             if (:suspend_mode=1) then suspend;
      end
    end
    --Отдельно рассмотрим расход пермещением
    if (doc_type=6) then
       begin
         if (exists (select id from doc_detail where id=:dd_id and g$profile_id=:profile_id and
         (summa_o_account<>round(summa_o, 2) or (sum_ndso_account<>round(sum_ndso, 2))) )) then
         --проверяем только те детализации, которые мы в текущей процедуре исправляли
         begin
         select summa_o_account,sum_ndso_account,quant_account from doc_detail dd
         where dd.g$profile_id=:g$profile_id and dd.id=:dd_id into  :FOR_S_O_A,:FOR_S_N_A,:for_q_a;
         select d$uuid from parts where id=:part_id and g$profile_id=:profile_id into :m_uuid;
         if ((select coalesce(email,'') from agents where id=:agent_id)<>'') then
         begin
           select first 1 cast(email as dm_id_null) from agents where id=:agent_id into :for_profile_id;
           for select first 1 p.id,p.g$profile_id,p.doc_id from parts p left join docs d on d.id=p.doc_id
           and p.g$profile_id=d.g$profile_id where p.motherpart_uuid=:m_uuid and p.g$profile_id=:for_profile_id
            and p.d$uuid<>:m_uuid  and abs(price_o-:price_o)<0.01  order by abs(:quant-p.quant) into :for_part_id,:for_profile_id,:for_doc_id do
            --находим партию их прихода пермещения с другого профиля,
            --соответвующую партии из текущего расхода пермещением
            --и запускаем процедуру PR_SET_SUM_ACCOUNT_REC исправления бух.себестоимости
            for select OUT_ID,OUT_DOC_ID,OUT_DOC_COMMITDATE
             from PR_SET_SUM_ACCOUNT_REC(:for_part_id,:for_profile_id,:FOR_S_N_A,:FOR_S_O_A,:for_q_a,:for_doc_id,:ITER)
            into
           :OUT_ID,:OUT_DOC_ID,:OUT_DOC_COMMITDATE do
             if (:suspend_mode=1) then suspend;
          end
         end
       end
  end

end^

SET TERM ; ^

/* Following GRANT statetements are generated automatically */

GRANT SELECT,UPDATE ON DOC_DETAIL TO PROCEDURE PR_SET_SUM_ACCOUNT;
GRANT SELECT ON DOCS TO PROCEDURE PR_SET_SUM_ACCOUNT;
GRANT SELECT ON DOC_TYPES TO PROCEDURE PR_SET_SUM_ACCOUNT;
GRANT SELECT ON PARTS TO PROCEDURE PR_SET_SUM_ACCOUNT;
GRANT EXECUTE ON PROCEDURE PR_SET_SUM_ACCOUNT TO PROCEDURE PR_SET_SUM_ACCOUNT;
GRANT EXECUTE ON PROCEDURE PR_SET_SUM_ACCOUNT_REC TO PROCEDURE PR_SET_SUM_ACCOUNT;
GRANT SELECT ON AGENTS TO PROCEDURE PR_SET_SUM_ACCOUNT;

/* Existing privileges on this procedure */

GRANT EXECUTE ON PROCEDURE PR_SET_SUM_ACCOUNT TO PROCEDURE PR_SET_SUM_ACCOUNT;
GRANT EXECUTE ON PROCEDURE PR_SET_SUM_ACCOUNT TO PROCEDURE PR_SET_SUM_ACCOUNT_REC;
GRANT EXECUTE ON PROCEDURE PR_SET_SUM_ACCOUNT TO PROCEDURE PR_SET_WAREBASE_DATE;
GRANT EXECUTE ON PROCEDURE PR_SET_SUM_ACCOUNT TO SYSDBA;