Denis Fedotenko: Себестоимость и закрытие склада
Статья известного специалиста о закрытии склада в Dynamics AX
1.1 О чем все это?
По моему опыту общения с консультантами и разработчиками, внедряющими DAX, вопросы,связанные с закрытием склада и расчетом себестоимости, стали одними из самых больных на внедрениях. Люди относятся к закрытию склада как к черному ящику, который в зависимости от фазы луны выдает самые разнообразные результаты и отрабатывает за самое разное время. На мой взгляд – это вызвано тем, что информация по процедуре расчета себестоимости разбросана по разным местам документации, кроме того некоторые тонкости этой процедуры вообще нигде не описаны и их приходится изучать методом проб и ошибок или изучая исходные тексты процедуры закрытия склада.
В данной статье я попытался дать достаточно подробное объяснение того, как работает данная процедура и разобрать некоторые другие темы связанные с себестоимостью. Часть из приведенной информации собрана из различной документации по DAX, часть получена на основании опыта работы с Axapta 3.0 и разбора ее исходных текстов. На версии 4.0 я всю собранную информацию не перепроверял, но судя по анализу исходных кодов закрытия склада новой версии – серьезных изменений в алгоритме не произошло.
Документ рассчитан на достаточно опытных консультантов и архитекторов DAX, уже знакомых с основными идеями модуля торговли, уже потренировавшимися несколько раз в процедуре закрытия склада и накопивших вопросы по данной тематике.
1.2 Зачем вообще нужно закрытие склада?
Основной задачей процедуры закрытия склада является вычисление истинной себестоимости списания номенклатуры. DAX работает с себестоимостью следующим образом: В момент выполнения операции списания, система не пытается точно сосчитать себестоимость списания по данному складскому движению в соответствии с настройками складской модели. Вместо этого, система списывает некоторую оценочную себестоимость, в целом – более или менее похожую на истинную себестоимость. (Подробнее – в разделе "Оценочная себестоимость.”) Далее – при выполнении закрытия склада рассчитывается истинное значение себестоимости в соответствии с настройками складской модели. После выполнения расчета истинной себестоимости система рассчитывает коррекцию –разницу между оценочной себестоимостью и истинной. Эта коррекция записывается в таблицу складских проводок и разносится в ГК.Почему так сделано ? В общем случае, в момент выполнения списания, система может не содержать всех данных, необходимых для расчета истинной себестоимости. Например , при работе в режиме отрицательного склада, мы можем списывать номенклатуру до того как ее приход проведен по системе; при использовании модели "средняя за период» невозможно посчитать себестоимость до того момента, как период будет закончен и все приходы будут проведены по системе и так далее; по модели LIFO мы аналогичным образом не можем посчитать себестоимость расхода до тех пор пока у нас не будет проведен последний приход за период и т.д.
Время от времени, начинающие консультанты на внедрении, приходили ко мне с предложением как-то подправить аксапту, чтобы бухгалтера могли руками вводить в документы списания мгновенную себестоимость и в дальнейшем при закрытии склада, система не трогала такие складские проводки и не пыталась рассчитывать по ним истинную себестоимость. Так вот – подобный подход СИЛЬНЕЙШИМ образом противоречит идеологии метода расчета себестоимости, используемой в DAX. Истинная себестоимость списания ВСЕГДА должна рассчитываться системы на основании сопоставления приходных и расходных складских проводок.
После того как система посчитала истинную себестоимость за период, выполнение любых складских движений за этот период запрещается. Это вполне логично – поскольку вставка новых приходов или расходов задним числом, может привести к нарушению логики уже созданных сопоставлений приходов с расходами и появлению некорректной истинной себестоимости. Если нам нужно просто предварительно посчитать себестоимость списания для текущего состояния складских проводок, без блокировки дальнейших складских движений за период, достаточно вместо процедуры закрытия склада запустить процедуру пересчета.
Итак – закрытие склада состоит из следующих этапов:
- Сопоставление приходных и расходный складских проводок. Расчет первичной себестоимости списания по расходным проводкам.
- Прогон коррекции себестоимости по графу движения складских запасов
- Разноска в ГК.
Рассмотрим каждый из этих этапов подробнее.
1.3 Сопоставление приходных и расходных проводок
Расчет истинной себестоимости списания в Аксапте всегда производится на основании сопоставления расходной складской проводки с приходной. При этом сопоставление происходит для проводок, с совпадающим кодом номенклатуры и аналитиками финансового склада.
Для модели FIFO или LIFO, правильность такого подхода вполне очевидна: Например если мы купили 2 штуки по 10 рублей и 5 штук по 14 рублей, то для того чтобы посчитать себестоимость списания 3 штук, надо сопоставить их с первым приходом общей суммой 20 рублей и одной штукой из второго прихода – суммой 14 рублей. Таким образом, в нашем примере, себестоимость списания по расходной проводке на 3 штуки составит 34 рубля. В дальнейшем ,я буду оперировать понятиями количества и суммы сопоставления. При этом сумма сопоставления рассчитывается как себестоимость приходной складской проводки на дату закрытия склада помноженная на отношения сопоставленного количества к количеству в исходной проводке. Естественно – сумма сопоставления округляется в соответствии с настройками округления в справочнике валют.
Как сопоставляются приходы и расходы по модели FIFO– интуитивно понятно, поэтому я не буду об этом подробно рассказывать. Единственное что стоит отметить - система не проверяет соответствие порядка приходов и расходов. Скажем – если мы провели по системе закупку изделия 15ым числом, а списание 5ым (предположим, что у нас никогда не было других движений по этому изделию). В этом случае – эти два прихода будут молча сопоставлены по модели FIFO, что вообще говоря – противоречит здравому смыслу. Подробнее о причинах появления отрицательного остатка и негативных последствиях этого явления я расскажу в разделах “Почему возникает отрицательный складской остаток?” и "Прогон себестоимости”
Модели LIFO или LIFO на дату в DAX было бы правильнее назвать LILO (Last In – Last out). Работают они как и FIFO , но только с точностью до наоборот. Система бежит по списку несопоставленных расходов, начиная с самого последнего и сопоставляет с каждым из них самый последний несопоставленный приход. “LIFO на дату” отличается от LIFO тем, что с расходом может сопоставляться только тот приход, который уже был на складе в момент выполнения расхода. Существование двух этих методов вызвано следующим соображением: Представим себе ситуацию, при которой у нас товар закупался 1ым,5ым и 31ым числами, а продавался, допустим, 10 и 18ым числами. С точки зрения строгой бухгалтерской идеи LIFO, у нас гарантировано должен быть списан товар, закупленный 31 числа (Поскольку по LIFO последние приходы списываются в первую очередь). С другой стороны – получается, что у нас отгрузка от 18 числа сопоставляется с приходом от 31ого – что выглядит как-то уж очень не интуитивно. Вот разработчики системы и реализовали эти два подхода, чтобы удовлетворить обе точки зрения. Первый из них – более правилен с бухгалтерской точки зрения , второй – с обывательской.
Теперь рассмотрим сопоставление в режиме "Средняя” и “Средняя на дату”. В давние былинные времена – до выхода версии Axapta 3.0 sp2 (В локализованной версии – этот service pack ,был выпущен под номером 3), в этом режиме КАЖДЫЙ расход периода сопоставлялся с КАЖДЫМ несопоставленным приходом (в случае Средней на дату – с каждым несопоставленным приходом, с датой меньшей или равной дате расхода). Получалось ,что если скажем у нас на начало месяца на складе уже лежали два несопоставленных прихода и еще 6 приходов пришло за месяц, то каждый из расходов (а их у нас, допустим было 40 штук) сопоставлялся с каждым из этих 8 приходов. В результате – у нас в таблице сопоставлений образовывалось 8*40*2 записей (на два результат умножен из за некоторых особенностей архитектуры сопоставления при которой на каждое сопоставление рождается две записи). Нетрудно сделать вывод, что такой рост таблицы не способствовал быстрому выполнению процедуры закрытия. Поэтому в Axapta 3.0Sp2 был реализован некий компромиссный вариант расчета средней себестоимости. Был введен порог для количества или суммы для каждого сопоставления. Грубо говоря - система, при сопоставлении каждого из расхода с приходами, начинает процесс с тех из несопоставленных приходов, у которых больше несопоставленное количество и стремиться сопоставить каждый раз не менее порогового количества. Если по данному приходу или расходу несопоставленным осталось меньше порогового количества – система сопоставляет столько, сколько осталось – независимо от порога. Пороговое количество может быть задано несколькими способами:
- Можно указать минимальный процент сопоставляемого количества в параметрах закрытия. В этом случае – система будет стремиться сопоставлять в одном сопоставлении не менее чем N% данного прихода.
- В поле “минимальное среднее количество” номенклатурной карточке
- В поле "Минимальное среднее количество” параметров модуля управления запасами. Данный параметр работает только для тех номенклатур, у которых не указанное значение по пункту 2.
Наконец – существует еще один способ косвенно ограничить сопоставляемое количество: Указав при закрытии склада минимальную сумму сопоставления, можно заставить систему стремиться не создавать сопоставлений с суммой сопоставления (то есть – долей себестоимости прихода, сопоставленной с расходом в данном сопоставлении) меньше заданного порогового значения.
Таким образом – в обычных ситуациях (закупочная цена не очень сильно меняется за период, закупки происходят значительно реже, чем продажи), алгоритм расчета средней будет давать очень незначительные отклонения от истинной средней стоимости, рассчитанной, грубо говоря, с помощью Excel, делением себестоимости всех несопоставленных приходов на количество. Но если представить себе ситуацию при которой мы обычно закупаем за период 2-3 партии по тысяче штук, продаем несколько тысяч партий по 1-2 штуки, но почему-то в данном конкретном периоде мы внезапно закупили дополнительно одну партию из 5 штук по цене в два раза выше обычной, то есть большие шансы что те несколько расходов , которые будут сопоставлены с этой странной партией будут иметь завышенную себестоимость списания.
Особо отмечу, что при расчете себестоимости для сопоставления используется сальдо приходной проводки на дату закрытия. То есть – если мы закупили товар 15 числом, потом 25 числом доначислили накладные расходы на закупку, то при закрытии склада 20ым числом для расчета суммы сопоставлении будет использоваться себестоимость из исходной закупки – без суммы накладных расходов.
1.4 Прогон себестоимости
Итак – сопоставление и приходных и расходных проводок закончен. Истинная себестоимость посчитана. Зачем нужен какой-то дополнительный этап?
Представим себе, следующую ситуацию:
- 1 января закупили номенклатуру на сумму 2000 рублей
- 5 числа – перенесли ее на соседний склад. Очевидно, что мгновенная себестоимость расхода, а вместе с ней и себестоимость прихода на соседний склад составит 2000 рублей
- 10 числа – продали номенклатуру с соседнего склада. Мгновенная себестоимость списания опять-таки составит 2000 рублей
- 20 числа – на первоначальную закупку доначислили расходы за транспортировку в сумме 400 рублей
- Модель FIFO
Если мы закрываем склад 30ым числом, то система у нас сопоставит расход от 5 числа с приходом от 1ого и посчитает истинную себестоимость списания по складскому переносу, равную 2400 рублям. Естественно – возникает задача как-то добиться того, чтобы себестоимость прихода на соседний склад и себестоимость списания с такового по продаже также была скорректирована до 2400 рублей.
Эта задача решается следующим образом:
При любом обновлении расходной складской проводки, система проверяет – нет ли для данного списания, связанного с ним прихода? (Связанные приходы существуют для складских проводок списания, порожденных из журнала переноса;журнала карантинного заказа; заказа на перемещение; производственного заказа; журнала спецификаций. Кроме того – связанный приход существует для тех складских проводок прихода, у которых заполнен номер возвращаемого лота. ) Если данный расход действительно имеет связанный приход, то сумма коррекции записывается в таблицу “Коррекции уровня лота” (inventCostListTrans), вместе с номером лота связанного прихода и некоторой другой служебной информацией.
Далее на стадии прогона себестоимости, система считывает из этой таблицы информацию о корректируемых приходных проводках, проводит коррекцию себестоимости по этим приходным проводкам, а затем корректирует и себестоимости списания в расходных складских проводках, сопоставленных с корректируемой приходной проводкой. Если скорректированные подобным образом расходные проводки, в свою очередь также имеют связанные приходные проводки, то процесс прогона себестоимости повторяется.
Таким образом – процесс прогона себестоимости по сути, является итерационным. Фактически – система через таблицу складских сопоставлений строит граф движения материалов от их начального оприходования до окончательного списания, проходя при этом через все цепочки переноса и сборки номенклатуры.
Как определить число итераций прогона себестоимости, необходимое для полного закрытия склада ? Вопрос сложный. В теории – число итераций прогона себестоимости должно равняться максимальному числу переносов данной номенклатуры между разными складскими аналитиками через журнал переноса, плюс уровень вложенности спецификаций, плюс число переносов номенклатуры через журнал карантинного заказа и тп. В реальности – на число итерации кардинальным образом влияет наличие циклов в графе прогона себестоимости. Если их нет – закрытие отрабатывает за 5-7-10-15 итераций, если они есть, то время, требуемое для полной прогонки себестоимости становится труднопредсказуемым.
Почему в графе прогона себестоимости образуются циклы:
Представим себе следующую ситуацию
1 числа закупили 1 штуку номенклатуры по цене 200 рублей (зак1)
20 числа закупили еще 4 штуки - по цене 250 рублей (зак2)
Потом – задним числом провели 5ым числом перенос 2 штук со склада 1 на склад 2. (пер1)
Потом - задним числом перенесли 6ым числом эти две штуки обратно (пер2)
25 числа – все продали (зкз1)
Ну и наконец – 27ым числом доначислили 20 рублей накладных расходов на первую закупку.
Закрываем склад 31 числом по модели FIFO. Аналитика финансового склада - склад
До закрытия склада имеем следующую картину себестоимостей и количеств в проводках:
Документ |
Дата |
Количество |
Себестоимость |
Склад |
Зак1 |
01.01.2007 |
1 |
270 |
1 |
Пер1 |
05.01.2007 |
-2 |
-480 |
1 |
Пер1 |
05.01.2007 |
2 |
480 |
2 |
Пер2 |
06.01.2007 |
-2 |
-480 |
2 |
Пер2 |
06.01.2007 |
2 |
480 |
1 |
Зак2 |
20.01.2007 |
4 |
1000 |
1 |
Зкз1 |
25.01.2007 |
-5 |
-1200 |
1 |
По итогам сопоставления по модели FIFO имеем следующую картину
ИТОГИ
Обратим внимание на то, что у нас в грАфе прогона себестоимости образовался цикл: списание по первому переносу сопоставилось с приходом по второму переносу. А приход по второму переносу, в свою очередь зависит от списания по первому переносу. В поле "Коррекция” для приходов и расходов будет указана сумма коррекции себестоимости, накопленная на данной итерации закрытия склада. Посмотрим - к чему это приведет. Далее – каждая таблица будет соответствовать одной итерации прогона себестоимости
ИТЕРАЦИИ 1 – ИТЕРАЦИЯ 6
Ну и так далее. Если бы у нас не было ситуации с переносом между складами при отсутствии товара на складе, то закрытие склада отработало бы за две итерации. В нашем же примере – на закрытие потребуется порядка 15-20 итераций. Кроме того – можно спрогнозировать, что в итоге у нас себестоимость списания по заказу так и не дотянет до правильного значения в 1270 рублей, при этом дельта будет списана с проводок по переносу в виде округления.
Таким образом, если возникла ситуация при которой в какой-то точке периода, за который мы закрываем склад, возникла ситуация отрицательного складского остатка, то это может привести к чрезмерно большому числу итераций по закрытию склада и недостаточной точности результата. Подробнее про причины этого явления написано в разделе "Почему возникает отрицательный складской остаток?”
Кроме того – стоит помнить, что при использовании модели средней себестоимости за период или на дату, у нас, в общем случае, КАЖДАЯ приходная проводка, сопоставляется с КАЖДОЙ расходной, соответственно - циклы в графе прогона себестоимости образуются даже если складской остаток не падал ниже ноля в течении закрываемого периода.
Поскольку ситуация, при которой закрытие склада длится неопределенно долгое время, случается, к сожалению не так уж редко, в процедуре закрытия склада имеются два параметра, которые позволяют явно или неявно ограничивать число итераций прогона себестоимости.
- В параметрах закрытия склада имеется поле "максимальная пропускная способность”. Именно в этом поле и указывается максимальное число итераций, которое система может выполнить на стадии прогонки себестоимости. Если при выполнении процедуры прогонки себестоимости добрались до итерации, указанной в этом поле, то при выполнении этой итерации, коррекция себестоимости (пришедшая с предыдущей итерации в таблице InventCostListTrans) по данной приходной проводке не переносится на сопоставленный с данным приходом расход, а списывается с прихода на счет прибылей и убытков.
- Также в параметрах закрытия склада имеется поле "Минимальная коррекция пропускной способности”. Если значение коррекции себестоимости, пришедшая с предыдущей итерации в таблице inventCostListTrans, меньше этого значения, то эта коррекция не переносится связанный с данным приходом расход, а просто списывается с прихода на счет прибылей и убытков. Хочу подчеркнуть, что этот параметр работает для КАЖДОЙ строки таблицы inventCostListTrans. То есть – если у нас в inventCostListTrans имеется три коррекции на 2,10 и 80 копеек, значение параметра "минимальная коррекция пропускной способности” установлена в 5 копеек, то списание в прибыли и убытки будет выполнено только для первой из коррекций. По остальным коррекциям итерации прогонки будут продолжаться.
Подробнее об списании коррекций – в разделе "О списании ошибок округления и корректировок при прогонке себестоимости.”
1.5 Разноска в ГК
Прежде всего, хочу отметить, что разноска в ГК выполняется только в том случае, если в параметрах закрытия склада была установлена галка "Обновить главную книгу». Смысла не устанавливать эту галку я не вижу, поскольку начиная еще с версии Axapta 2.5Sp1Hf1, разноска стала выполняться достаточно быстро по сравнению со всеми остальными этапами закрытия склада и отключение этой галки не приводит к серьезному повышению быстродействия этой процедуры.
На предыдущих этапах закрытия склада была заполнена таблица складских сопоставлений (InventSettlement) в которой помимо собственно сопоставленных количеств, сопоставленных сумм и сумм коррекций находится также информация, необходимая для создания бухгалтерских проводок по выполненной коррекции – счет, коррсчет, аналитика, разноска для счета и коррсчета. Поэтому процедура разноски закрытия склада работает достаточно просто. Она группирует суммы коррекции в разрезе этих бухгалтерских параметров, а потом делает соответствующие проводки в ГК. Поэтому – нет особой необходимости в процедуре, которая бы позволяла выверять проводки в ГК по закрытию склада со складскими проводками. Вся информация по счетам аналитикам и тп находится в таблице складских сопоставлений. Если у вас при закрытии склада сгенерировались проводки в ГК с какими-то неожиданными аналитиками или счетами – ищите эти счета и аналитики в таблице сопоставлений. Ну а из этой таблицы уже можно перейти к исходным складским проводкам и попытаться понять – на основании каких операций были сгенерированы эти странные проводки.
Кроме того – на разноску влияет параметр процедуры закрытия склада, называющийся спецификация. В оригинальной (нелокализованной) версии DAX, этот параметр позволял процедуре разноски группировать проводки не только по бухгалтерским параметрам, но и по коду номенклатуры, по которой была выполнена коррекция или коду ее номенклатурной группы. То есть – даже если операции с разными номенклатурами породили коррекции с одинаковыми аналитиками, счетами и корсчетами, в ГК эти коррекции попадали двумя отдельными проводками. К сожалению – этот режим оказался несовместимым с механизмом корреспонденции счетов, используемым в российской версии DAX. Поэтому – если режим корреспонденции счетов включен, то группировка проводок по номенклатуре или номенклатурным группам невозможна. У меня этот факт не смущает, , поскольку, как я уже сказал, выверять проводки в ГК по процедуре закрытия гораздо удобнее и легче с помощью таблицы складских сопоставлений (inventSettlement).
1.6 О параллельном закрытии склада с нескольких рабочих мест.
Наиболее значительным изменением в процедуре закрытия склада со времен выхода первой локализованной версии 2.1 в 2000ом году, стала реализация параллельного закрытия склада с нескольких рабочих станций впервые выпущенная в составе международного Service Pack 2 для Axapta 3.0. На мой взгляд - если до выхода этого пакета обновления низкая производительность процедуры закрытия склада иногда становилась неразрешимой проблемой, то с его выходом – большая часть проблем производительности была снята.
Итак – рассмотрим процедуру закрытия склада в многопользовательском режиме поподробнее:
Того клиента DAX, на котором была первоначально запущена процедура закрытия склада мы в дальнейшем будем называть мастер-клиентом. Всех остальных клиентов – клиентами – помощниками. Напомню, что запуск клиента-помошника выполняется нажатием кнопки Рассчет->Помошь в расчете. При этом в списке закрытий склада должно быть выбрано незаконченное закрытие склада.
В момент запуска процедуры закрытия склада на мастер-клиенте, он записывает параметры закрытия склада в таблицу “Закрытие запасов” (InventClosing) для того, чтобы эту информацию могли бы потом использовать клиенты-помощники. После этого мастер-клиент пишет в таблицу “Список рассчета”(inventCostList) коды той номенклатуры, по которой имеются незакрытые операции. Далее – и мастер-клиент и клиенты-помошники начинают сопоставлять открытые складские проводки по каждой из номенклатур, попавших в эту таблицу. Данный процесс считается нулевой итерацией закрытия склада. Как я уже написал в разделе "Прогон себестоимости”, во время сопоставления, система порождает записи в таблице InventCostListTrans, которые в дальнейшем будут использоваться для итераций прогона себестоимости. Надо сказать, что если в таблицу inventCostListTrans попала хотя бы одна запись для некоторой номенклатуре, то запись об этой номенклатуре попадает также и в таблицу InventCostList. Это необходимо для координации совместной работы нескольких клиентов на стадии прогона себестоимости.
На стадии прогона себестоимости, система действует по похожему алгоритму. Каждый клиент бежит по таблице InventCostList и по каждой из попавших в эту таблицу номенклатур обрабатывает коррекции себестоимости (прогоняет их по графу сопоставлений), попавшие в таблицу inventCostListTrans на прошлой итерации закрытия склада.
Разноска закрытия склада в ГК выполняется только с мастер-клиента.
Если во время выполнения закрытия склада нажать кнопку Рассчет->Останов расчета, то закрытие склада будет приостановлено. После этого можно будет повторно нажать кнопку Рассчет->Помощь в закрытии склада и закрытие склада будет продолжена. В этом случае – мастер-клиентом станет тот клиент, который первый присоединится к незавершенной процедуре закрытия склада.
Если вы используете при параллельном закрытии склада больше 2-3 клиентов, то стоит контролировать загрузку сервера БД и AOS. После того как одна из них добралась процентов до 60-70, подключать к закрытию новых клиентов уже не имеет смысла. Кроме того, опыт показывает, что хотя классы закрытия склада выполняются на клиенте, с одной рабочей станции можно запустить 2 или даже 3 клиента, поскольку часть времени они не создают нагрузку на процессор, поскольку ждут завершения операции на AOS или БД. Хочу также заметить, что на стадии сопоставления система создает значительно бОльшую нагрузку на сервер БД чем на стадии прогонки себестоимости. Поэтому – до момента начала прогонки себестоимости не стоит подключать к процедуре закрытия склада слишком большое количество клиентов.
Хотя авторы системы многопользовательского закрытия склада предназначали таблицы inventCostList и inventCostListTrans исключительно как некий внутрисистемный инструмент для взаимодействия между несколькими клиентами, в качестве побочного эффекта – эти таблицы являются прекрасным инструментом для мониторинга процесса закрытия склада. Поэтому прежде чем приступить к обсуждению того, как этот процесс мониторить – давайте посмотрим структуру данных таблиц:
InventCostList
Поле |
Описание |
ItemId |
Код номенклатуры по которой требуется выполнять закрытие |
Voucher |
Номер документа ГК – используется для связи с таблицей закрытия (inventClosing) |
CostNum,NumOfIteration |
Поля с обратными по отношению к друг другу значениями: NumOfIteration – номер текущей итерации прогонки себестоимости; CostNum – обратное значение. Грубо говоря – максимальное число итераций минус номер текущей итерации. |
InventCostListTrans
Поле |
Описание |
ItemId |
Код номенклатуры по которой требуется прогонка себестоимости |
InventTransId |
Номер приходного лота, на который требуется перенести коррекцию на очередной итерации прогонки себестоимости |
InventTransIdReturn |
Номер возвращаемого лота. Используется в тех случаях,если при возврате заказа (точнее – вообще складского списания) был указан номер возвращаемого лота (то есть – исходного лота по которому было выполнено списание). Для повышения производительности закрытия склада этот случай обрабатывается специальным образом: Вместо того чтобы брать сумму коррекции на данной итерации из поля adjustment, система просто рассчитывает себестоимость исходной проводки списания и приравнивает к ней себестоимость возврата |
VoucherPhysical |
Используется только для возвратов и карантинного заказа. Нужен для корректной связи с исходной проводкой, из которой был порожден данный возврат или карантинный заказ. |
Voucher |
Номер документа ГК – используется для связи с таблицей закрытия (inventClosing) |
NumOfIteration |
Номер итерации прогонки себестоимости. Нужен для того чтобы координировать действия клиентов и какой-нибудь клиент не начал обрабатывать итерацию N+1, пока все остальные трудятся еще над итерацией N |
Adjustment |
Собственно – сумма коррекции, которую нужно провести на по данной складской проводке на данной итерации. |
Считается, что стадия сопоставления приходов и расходов соответствует номеру итерации ноль. На этой итерации в таблице inventCostListTrans записей нет.
1.7 Как мониторить процесс закрытия склада?
Самый правильный способ мониторить закрытие склада –сразу после того как это закрытие было запущено, открыть в соседнем окне еще одного клиента DAX и залезть с помощью браузера таблиц в таблицу inventCostListTrans
Если отсортировать записи в этой таблице по номеру итерации, то сразу можно увидеть - какая итерация у вас сейчас идет. Как правило, при сколько-нибудь серьезной логистике, меньше чем итераций за 5-7 склад просто не закрывается. Если у вас склад закрывается за меньшее время, то скорее всего проблемы производительности этой процедуры вас просто не волнуют и вы можете пропустить остаток этого раздела. В течении этих самых первых 5-7 итераций, количество записей в таблице inventCostListTrans резко падает (обычно – с нескольких тысяч до пары сотен или даже нескольких десятков). После того, как эти итерации прошли – есть смысл начинать пристально следить за содержимым этой таблицы. Допустим – вы заметили пару лотов, по которым коррекция себестоимости в поле adjustment с каждой итерацией падает как-то уж слишком медленно. Скажем – если коррекция равняется 600 рублям и с каждой итерацией она уменьшается всего на 10 копеек – самое время попытаться понять – не случилось ли по данной номенклатуре какой-то беды с данными. Скорее всего – причина такого медленного прогона себестоимости состоит в том, что у вас остаток по данной номенклатуре в какой-то точке закрываемого периода падал ниже ноля, в результате чего в грАфе прогона себестоимости образовался цикл. Что делать в подобной ситуации ?
Если вы перфекционист или сумма прогоняемой коррекции как-то уж слишком высока (скажем – 6000 при месячном объеме продаж в 6000000), то нужно записать номера проблемных лотов на бумажку, после чего остановить процедуру закрытия склада, отменить незавершенное закрытие склада и начать разбираться с проблемными лотами. Если вы нашли ситуации отрицательного остатка, то нужно их аккуратно отсторнировать, примаркировать (или указать номер возвращаемого лота)неправильное списание к его сторно и попробовать повторить попытку закрытия склада. (Про маркирование и номера возвращаемых лотов подробнее написано в разделе "О возвратах”
Если же вы обнаружили что сумма прогоняемой коррекции небольшая по сравнению с оборотами за период (допустим 500 при обороте в 50000000, то можно просто подкорректировать (в браузере таблиц) поле MinTransferValue в таблице InventClosing. Это то самое поле, в котором хранится максимальная коррекция пропускной способности, заданная вами в начале процедуры закрытия склада. Если вы укажете там значение, большее чем максимальная из оставшихся в прогоне коррекций, то но очередной итерации прогонки себестоимости, зависшие коррекции просто будут списаны на счета прибылей и убытков. На реальный финансовый результат фирмы это не повлияет, но может слегка подпортить картину прибыльности (или убыточности) отдельных продаж.
Вот почему так важно, чтобы оценочная себестоимость списания не слишком сильно отличалась от истинной себестоимости. Ведь если разница была слишком большой, то в случае образования циклов в графе прогонки себестоимости, каждые несколько дополнительных рублей разницы могут обойтись вам в несколько минут дополнительного времени закрытия.
Кроме того, могу посоветовать разработать какой-нибудь отчет, который пробегал бы по открытым проводкам и выдавал сообщение о переходе остатка через ноль. Если вы будете запускать подобный отчет перед каждым закрытием, то у вас появится возможность привести данные в порядок, без необходимости ждать пока закрытие доберется до 20ой итерации.
В заключение хочу отметить, что существуют определенные ситуации, в которых переход остатков через ноль вызван не ошибками в вводе данных, а объективной спецификой бизнеса. В таком случае – единственный способ бороться с производительностью закрытия склада - это корректировка поля MinTransferValue, о которой я писал чуть выше. Конечно – в этом случае мы не сможем получить корректную себестоимость списания по каждой складской проводке, но по большому счету, в такой ситуации эта себестоимость и не будет иметь особого экономического смысла.
1.8 О пересчете склада
. В DAX имеется не только процедура закрытия склада, которая расчитывает окончательную себестоимость списания по каждой складской проводке за период (блокируя при этом дальнейшие складские движения в этом периоде), но и процедура пересчета склада. Эта процедура отличается от закрытия склада следующим образом:
- Сопоставление приходов и расходов строится “в памяти”, то есть – данные о сопоставлении приходов и расходов не записываются в таблицу складских сопоставлений.
- В случае необходимости корректировки себестоимости по расходной или приходной проводке, запись в inventSettlement создается, но в ней не заполняются сопоставленные количества и суммы. Происходит заполнение только суммы коррекции.
- Как следствие первых двух пунктов –перед КАЖДОЙ итерацией прогонки себестоимости происходит операция сопоставления "в памяти” приходных и расходных проводок. Это логично – ведь если мы не пишем в таблицы БД данные о сопоставлении, то перед каждой прогонкой себестоимости нам нужно заново создать в памяти данные о сопоставлении, чтобы понять – с каким расходом сопоставлен корректируемый на данной итерации приход.
- Можно проводить пересчет склада по отдельной номенклатуре или номенклатурной группе. Нужно только помнить о том, что если данная номенклатура списывалась по журналу спецификаций или журналу производства, то коррекция может затронуть и ту номенклатуру, в производство которой была списана данная номенклатура.
- В отличие от закрытия склада – система не списывает при пересчете склада погрешности округления.
Если мы хорошенько подумаем о смысле 3 пункта, то можно сделать вывод о том, что пересчет склада будет почти гарантировано работать медленнее, чем закрытие. Ведь перед каждой итерацией прогонки себестоимости, мы вынуждены заново проходить стадию сопоставления приходных и расходных проводок. Мы конечно сэкономим немного времени за счет того что мы не тратим на каждой итерации время на запись данных по сопоставлению в таблицу БД, но скорее всего – сэкономленное время будет с лихвой перекрыто дополнительным временем, нужным на повторяющееся сопоставление.
1.9 Партионная себестоимость
Нигде в Dynamics AX нет специальной настройки для включения режима партионной себестоимости. Тем не менее – если в настройках группы складских аналитик поставить галку "Финансовый склад" для складских аналитик "Номер партии" или "Серийный номер", то при закрытии склада по ЛЮБОЙ модели мы и получим режим партионной себестоимости, поскольку сопоставление приходов и расходов будет идти только в рамках одного номера партии или серийного номера. Разумнее всего, для той номенклатуры, у которой включен партионный учет, ставить модель закрытия склада по FIFO, поскольку с технической точки зрения она быстрее всего работает.
1.10 Один лот – одна себестоимость
Архитектура модуля расчета себестоимости в DAX неявным образом предполагает что элементарной единицей списания или приходования себестоимости является один складской лот. То есть – даже если по одному лоту у нас имеется несколько строк в таблице складских проводок, при расчете с себестоимости система все равно будет считать все себестоимость по лоту в целом.
С первого взгляда – подход логичный, но он содержит в себе некую ловушку, в которую регулярно попадают начинающие внедренцы. Допустим – мы включили по одной из номенклатур партионный учет (включаем нужную галку в настройках групп складской аналитики) и у нас имеется две партии товара по 10 рублей (2шт) и 15 рублей (3шт).
Далее, создаем журнал переноса на 5 штук. Как известно, если мы не указали в настройках групп складской аналитики номер партии, как первичную аналитику, то мы можем не вводить номер партии в строки журнала переноса. При выполнении резервирование по такому журналу, система автоматически разобьет приходные и расходные складские проводки, подставив в каждую из них нужный номер партии. Это удобно для пользователей, поскольку позволяет им не задумываться над подбором номера партии, если в данной ситуации нас не волнует то, какие именно партии будут списаны.
Только вот после разноски такого журнала, мы обнаружим, что во всех приходных проводках стоит одинаковая цена - 13 рублей. В момент разноски журнала, система использует себестоимость списания ЛОТА с первого склада в качестве себестоимости прихода на второй склад. Себестоимость списания лота с первого склада – 65 рублей. Соответственно – при расчете себестоимостей прихода по двум складским проводкам данного лота будет использоваться эта сумма, которая будет распределена между приходными складскими проводками пропорционально их количествам. Закрытие склада не исправит ситуацию, поскольку коррекция себестоимости в таблице InventCostListTrans также привязана к номеру лота, соответственно – на стадии закрытия коррекция будет распределена по приходным проводкам пропорционально их количествам. Вот именно в такой ситуации и возникают жалобы на тему "Аксапта испортила мою себестоимость".
Когда я первый раз столкнулся с подобной ситуацией, я с наскока пытался решить ее, переделав процедуру разноски журнала переноса и закрытия склада. Только вот оказалось, что не очень понятно – как решать эту задачу в общем случае. Например – мы ведь можем по переносу списывать несколько партий и создавать из них одну. Как мы вообще должны обрабатывать ситуацию, при которой у нас в результате переноса меняется не только аналитика физического склада (например - склад или номер ячейки), но и аналитика финансового склада?
Возникновение подобной ситуации, говорит о попытке решить бизнес-проблему айтишным путем. Ведь если вдуматься – ситуация абсурдная: С одной стороны - бухгалтерия пытается вести партионный учет. С другой стороны – бизнес-процессы организованы так, что логистики не знают или не хотят знать – какой номер партии им подставлять и пытаются добиться, чтобы эти номера система подставляла автоматически. В такой ситуации, существует два варианта решения проблемы:
- Можно настаивать на том, чтобы пользователи вводили номера партий в ручную. В такой ситуации спонсор проекта со стороны заказчика должен жестко разъяснить пользователям – почему так важна партионная себестоимость.
- Можно настаивать на том, чтобы бухгалтера перешли на использование модели FIFO. Если уж все так хотят чтобы система автоматически подставляла номера партий – так пусть и подставляет. Если аналитика "Номер партии" не будет указана как аналитика финансового склада, то после закрытия склада мы и получим информацию, связывающую расходы с номерами партий в приходах. Только в данной ситуации у нас в складской аналитике расходных проводок не будет номеров партий; Нам придется собирать эту информацию из таблицы складских сопоставлений, связывающей расходы с приходами.
1.11 Складские сопоставления и как с ними работать.
В данном разделе пойдет речь о таблице складских сопоставлений (inventSettlement). Строго говоря – использование термина "сопоставление” (Settlement) в названии этой таблицы, поскольку, эта таблица несет двоякий смысл: она может описывать и сопоставление прихода с расходом, и коррекцию себестоимости. Хотя для расходных складских проводок коррекция себестоимости ВСЕГДА порождается на основании сопоставления, для приходных складских проводок коррекция себестоимости происходит совершенно независимо от сопоставления с расходными. Кроме того – если коррекция себестоимости расхода делается на основании пересчета, а не закрытия склада, то коррекция себестоимости будет помещена в эту таблицу без данных о сопоставленном количестве. Давайте внимательнее рассмотрим список полей этой таблицы:
Поле |
Описание |
TransRecId |
Ссылка на складскую проводку к которой относится данная запись |
InventTransId |
Номер лота, по которому прошла коррекция/сопоставление. |
Itemid |
Код номенклатуры по которой прошла коррекция/сопоставление |
ItemGroupId |
Номенклатурная группа номенклатуры по которой прошла коррекция/сопоставление |
TransDate |
Дата коррекции/сопоставления |
Voucher |
Документ ГК, по которому прошла коррекция/сопоставления |
SettleTransId |
Идентификатор коррекции сопоставления. По большому счету – некий внутренний счетчик. Поле очень любопытно в том случае, если данная запись была порождена закрытием склада. В этом случае- записи в этой таблице, относящиеся к расходной и приходной проводке будут иметь одинаковое значение этого поля. |
QtySettled |
Сопоставленное количество. В том случае если данная запись порождена корректировкой прихода или корректировкой расхода, порожденной пересчетом (не закрытием) склада, то данное поле будет пустым. |
CostAmountSettled |
Сопоставленная сумма. В том случае если данная запись порождена корректировкой прихода или корректировкой расхода, порожденной пересчетом (не закрытием) склада, то данное поле будет пустым. |
CostAmountAdjustment |
Сумма коррекции. Если в результате сопоставления при закрытии склада коррекция себестоимости расхода отсутствовала – поле нулевое. Если запись связанна с приходом и порождена закрытием склада – поле нулевое. |
BalanceSheetAccount |
Складской счет, на который будет разнесена данная коррекция |
OperationsAccount |
Коррсчет, на который будет разнесена данная коррекция. Обычно – счет затрат (operations) |
BalaceSheetPosting |
Тип разноски в ГК, который будет использоваться при разноске на складской счет |
OperationsPosting |
Тип разноски в ГК, который будет использоваться при разноске на корсчет. |
Dimension |
Финансовая аналитика которая будет использоваться при разноске в ГК |
Canceled |
Признак отмены данного сопоставления. Если вы отменяете закрытие или пересчет склада - у исходного складского сопоставления в это поле ставится true. Фактически – это признак сторнирования сопоставления |
SettleModel |
Принцип сопоставления. В первом приближении – соответствует складской модели. |
Posted |
Признак того, что информация по данной строке была обработана модулем разноски в ГК |
SettleType |
Тип сопоставления. |
При выполнении закрытии склада информация о сопоставлении заносится в эту таблицу следующим образом: и для сопоставляемого расхода и для сопоставляемого прихода создается запись в таблице складских сопоставлений, при этом поле SettleTransId у этих двух записей будет одинаковым.
В случае коррекции себестоимости прихода (например – при разноске накладных расходов по накладной или переоценке складских запасов) – создается одна запись. Аналогичным образом – одна запись создается и при коррекции себестоимости расходных проводок (при пересчете склада).
Поля, связанные с разноской в ГК заполняются информацией из таблицы складской разноски (InventTransPosting), связанной с корректируемой (сопоставляемой) складской проводкой. Если запись в этой таблице по каким-то причинам отсутствует – в качестве складского счета берется счет складского прихода или расхода из таблицы настроек складской разноски по данной номенклатуре, в качестве корсчета – счет прибылей и убытков из той же таблицы. Отмечу что в саму таблицу inventTransPosting информация попадает из финансовой аналитики исходного документа и остается в данной таблице даже если этот документ удален.
Надо сказать – что далеко не всегда коррекция себестоимости приводит к необходимости делать проводки в ГК. Скажем – при коррекции себестоимости прихода или расхода по журналу переноса никакой проводки в ГК делать не надо – поскольку по исходному журналу – такой проводки тоже не было. Этот момент отслеживается с помощью поля isPosted в таблице InventTransPosting. Если данное поле не было установлено в true, то счета и корсчета в inventSettlement не заполняются и подсистема разноски сопоставлений в ГК эти коррекции/сопоставления игнорирует. Так что если вы занимаетесь выверкой проводок в ГК по закрытию с себестоимостью, то следует игнорировать записи с пустыми счетами и корсчетами. Наконец – хочу уточнить что поле posted в таблице inventSettlement не имеет никакого отношения к факту разноски или неразноски данного сопоставления(коррекции) в ГК. Это поле заполняется в момент обработки сопоставления классом разноски сопоставлений в ГК и говорит только о том, что эта запись была обработана этим классом.
1.12 Немного о складских проводках
Имеет смысл рассказать о тех полях в складских проводках, которые относятся к себестоимости и закрытию склада.
Поле |
Описание |
CostAmountPosted |
Для приходных складских проводок – себестоимость прихода (в случае закупки – без учета накладных расходов). Для расходных проводок – оценочная себестоимость списания |
CostAmountAdjustment |
Сумма корректировки себестоимости. ВСЕГДА должна равняться сумме поля costAmountAdjustment складских сопоставлений, связанных с этой проводкой. |
CostAmountSettled |
Сума сопоставленной по закрытию склада себестоимости. У закрытых складских проводок – равняется сумме costAmountPosted и costAmountAdjustment |
Qty |
Количество по складскому документу. Всегда считается в тех единицах измерения, которые были указаны как единица измерения склада |
qtySettled |
Сопоставленное по закрытию склада количество |
costAmountPhysical |
Себестоимость по физическим операциям (попросту говоря – по отборочным накладным). Для проводок списания – почти всегда равняется оценочной себестоимости. Для приходных проводок – себестоимости из приходного документа. Вообще – использовать значения из этого поля для каких-то отчетов – дурной тон. Оно предназначено чтобы ПРИБЛИЗИТЕЛЬНО (плюс-минус трамвайная остановка) оценить складские движения по которым не было еще проведено реальное финансовое складское движение. |
CostAmountStd |
Стандартная себестоимость, для данной номенклатуры. Если для данной номенклатуры не включен в группе складских моделей флажок стандартной себестоимости – поле нулевое. |
CostAmountOperations |
Для расходов – не заполняется. Для приходов – почти всегда равняется costAmountPosted с обратным знаком. Единственное исключение – закупка номенклатуры, у которой выключен в группе складских моделей флажок "Разносить финансовые операции”. В этом случае – в costAmountPosted пишется ноль, а в costAmountOperations – закупочная себстоимость. В общем случае – в этом поле хранится сумма по корсчету складской операции. |
ValueOpen |
Признак того – закрыта данная складская проводка или не закрыта. Закрыта может быть только финансовая складская проводка. Проводка помечается как закрытая только в том случае, если значение costAmountSettled равняется истинной себестоимости (сумме полей costAmountPosted и costAmountAdjustment) и разница между qty и qtySettled меньше чем 0.00000001 |
DateClosed |
В момент когда проводка помечается как закрытая, система автоматически заполняет это поле датой последнего складского сопоставления, связанного с данной складской проводкой. |
1.13 О возвратах
В системе существует два принципиально разных механизма создания возвратов.
- Для возврата списаний, используется механизм под названием "номер возвращаемого лота”. Этот номер можно указывать, например, в строках заказов или складских проводок. Затем – этот номер возвращаемого лота копируется в складскую проводку по данному возврату (в поле inventTransIdReturn). В момент закрытия склада, наткнувшись на проводку прихода, у которой это поле заполнено, система на итерации прогона себестоимости корректирует себестоимость данного прихода до себестоимости возвращаемого расхода (естественно – с обратным знаком).
- Для возврата приходов используется механизм маркировки складских проводок. Именно об этом механизме я и расскажу поподробнее в оставшейся части раздела.
Итак – система позволяет примаркировать приходные и расходные проводки друг к другу. При этом, в связанные приходные и расходные проводки в поле inventrefTransId записывается ссылка на связанную проводку. При закрытии склада или пересчете, подобные примаркированные проводки не попадают в стандартные процедуры сопоставления приходных и расходных проводок и прогонки себестоимости. Вместо этого, подобные проводки сопоставляются непосредственно друг с другом независимо от используемой складской модели. Как следствие этого – себестоимость списания расходной проводки становится гарантировано равной себестоимости приходной проводки. Следует отметить, что и при маркировании, и при обработке примаркированных проводок в момент закрытия или пересчета склада, система проверяет совпадение складской аналитики финансового склада. То есть – если у вас, например, номер партии является аналитикой финансового склада, то примаркировать проводки с разными номерами партий вам не удастся. Даже если каким-то образом (например – модификацией полей в inventTrans напрямую) подобные проводки будут примаркированы друг к другу, то в момент закрытия/пересчета склада, система отменит подобные маркировки, очистив поле inventRefTransId в складских проводках.
Маркировать проводки можно как до их финансовой разноски, так и после. Нельзя маркировать открытую складскую проводку к закрытой, поскольку в момент закрытия/пересчета склада такие проводки не будут сопоставлены и смысл в маркировании теряется.
К сожалению, кроме расчета себестоимости, маркировка проводок используется в DAX и для других целей:
- При резервировании в заказанных, маркировка позволяет связать данный резерв в заказанных с конкретной приходной проводкой в статусе "Заказано”. В дальнейшем – при складском или финансовом оприходовании этой складской проводки, именно примаркированная к ней расходная проводка будет переведена в статус "Зарезервировано”. Таким образом – мы можем привязать наш резерв в заказанных к конкретному приходу.
- При работе модуля сводного планирования, маркировка используется для организации связи данных покрытия чистых потребностей со складскими проводками. Грубо говоря – если две складских проводки примаркированы друг к другу, то запись о данном приходе в таблице чистых потребностей (reqTrans) будет рассматриваться как покрывающая для записи по данному расходу. Аналогично – если мы будем создавать перенос или закупку на основании созданных при сводном планировании планового переноса или закупку, то проводки по данной закупке/переносу будут примаркированы к той расходной проводке, на основании которой появилась данная потребность.
Следует помнить о наличии данных механизмов, поскольку может возникнуть ситуация, когда последствия их работы (примаркированные проводки) фактически выпадут из стандартной процедуры расчета себестоимости о складской модели. При этом – фактически система начнет работать в режиме партионного учета, поскольку себестоимость конкретного расхода будет однозначно определятся себестоимостью конкретного расхода. (Вместо усреднения или расчета по модели FIFO например).
1.14 О списании ошибок округления и корректировок при прогонке себестоимости.
Представим себе следующую ситуацию: Допустим – мы закупили 3 единицы товара на общую сумму 10 рублей. Затем – каждую из этих единиц мы продали (отдельными заказами). Предположим также, что мы этот товар не переносили между складами и вообще никаких операций по нему кроме этой закупки и трех продаж, не выполняли. После закрытия склада – себестоимость списания по каждому из этих заказов будет равняться 33 рубля и 3 копейки. По приходу у нас возникнет интересная картина. Сопоставленное количество – будет равняться 3 (то есть – в общем-то, все списали и приходную проводку пора помечать как закрытую). В то же время – сопоставленная сумма будет равняться 9 рублям и 99 копейкам (то есть – приходную проводку нельзя помечать как закрытую, поскольку на проводке зависло сальдо в одну копейку). В подобной ситуации, система просто переоценит приходную проводку на одну копейку в корреспонденции с корсчетом прихода. (Следует отметить – что для закупок, корсчетом прихода является счет потребления по закупке из настроек складских разносок). То есть – после закрытия склада – у нас в приходной проводке в поле costAmountPosted будет указана исходная сумма (10 рублей), а в costAmountAdjustment - -1 копейка, при этом сама приходная проводка будет помечена как закрытая.
В том случае, если у нас происходит списание прогоняемой коррекции себестоимости (либо потому что она меньше заданного при закрытии склада порога точности, либо потому что у нас исчерпался счетчик итераций), система поступает аналогичным образом.
Допустим – у нас с предыдущей итерации прогона себестоимости на данный приход по журналу спецификации пришла коррекция в 3 копейки, которая меньше чем наш порог точности закрытия (Параметр закрытия – "Минимальная коррекция пропускной способности”. Система сгенерирует сопоставление, корректирующее себестоимость прихода по журналу спецификации в корреспонденции с корсчетом прихода на 3 копейки, а затем сразу же уценит этот приход на три копейки в корреспонденции со счетом прибылей и убытков.
1.15 Стандартная себестоимость.
Существует мнение, что закрытие склада игнорирует те номенклатуры, у которых в складской модели включен режим стандартной себестоимости. Это не так. Более того – если у нас за последнее время менялась стандартная себестоимость по данному изделию, то закрытие склада, например, по средней, усреднит себестоимость старых и новых приходов (сделанных по разным стандартным себестоимостям). Поэтому – очень рекомендуется (не с точки зрения целостности системы, а с точки зрения методики учета), при любом изменении стандартной стоимости изделия, проводить переоценку запасов таким образом, чтобы себестоимость запасов после переоценки равнялась их количеству, помноженному на новую стандартную стоимость.
Кроме того, хочу уточнить, что когда система списывает при приходе разницу между закупочной стоимостью и стандартной, она выполняет эту операцию как стандартную процедуру переоценки приходной складской проводки в корреспонденции со счетом отклонений. При этом – в costAmountPosted попадает закупочная стоимость, в costAmountAdjustment – сумма отклонения, ну и в costAmountStd – стандартная себестоимость (которая будет равнятся сумме закупочной себестоимости и отклонения).
1.16 Закрытие склада по услугам.
Надо заранее сказать, что настройка работы с услугами в Dynamics AX имеет некоторую неоднозначность.
- Существует тип номенклатуры "Услуга”. Для номенклатур данного типа система не ведет записей в таблице запасов в наличии (inventSum), по ним нельзя выполнять операцию резервирования, в общем – система в целом не позволяет делать те операции, которые нельзя выполнять по тем сущностям, которые нельзя положить на склад.
- Существует галка "разносить финансовые операции” в настройках складских моделей. Если эта галка выключена (а для услуг она в 99% случаев отключается), то при приходе данной номенклатуры по закупке, ее себестоимость относится на счета затрат. Во всех остальных вариантах приходов – никаких проводок по номенклатуре с данной складской моделью вообще не делается.
Соответственно – особого смысла закрытие склада для услуг никогда не имело. Собственно – закрытие склада и необходимо для того, чтобы мы могли посчитать точную себестоимость списания в затраты тех активов, которые мы когда-то отнесли на инвентарные счета. Услуги мы и так сразу списали на затратные счета. Возникает законный вопрос – зачем тратить время на складские сопоставления, если себестоимость по ним мы считать не будем ? В версии 4.0 (по крайней мере – в русской версии 4.0) закрытие склада по услугам было отключено. То есть – при выполнении закрытия склада – для всех номенклатур типа услуга сопоставленное количество автоматически приравнивается общему, а в таблицу складских сопоставлений пишется складское сопоставление с пустыми финансовыми суммами и моделью сопоставления "Складское сопоставление номенклатуры по услуге”.
Нужно помнить об этой особенности и если у вас на проекте использовалась номенклатура типа услуга с ВКЛЮЧЕННОЙ галкой "Разносить финансовые операции”, то нужно эту номенклатуру переделать из типа “услуга” в тип “Номенклатура". Впрочем – на реальных внедрениях, я случая подобной настройки не видел.
1.17 Почему возникает отрицательный складской остаток?
Наверное все знают, что в Dynamics AX существуют настройки складской модели, разрешающие списание товара, до его приходования на склад. (“Отрицательный физический склад” и “отрицательный финансовый склад”). Хотя время от времени этот режим включают осознанно (например – у складской модели для услуг), но в большинстве случаев – этот режим сознательно выключен. Поэтому – начинающие консультанты очень часто недоумевают, обнаружив, что при выключенном режиме отрицательного склада, складской остаток, тем не менее, опускался ниже ноля на какой-то момент времени.
Это вызвано тем, что в момент выполнения списания (физического или финансового) система контролирует ТЕКУЩИЙ складской остаток. Поэтому – если товар пришел только вчера, а списание мы выполняем более ранней датой, система разрешает это сделать.
Почему так сделано ? Я, на одном из своих проектов, переделывал проверку количества при списаниях. Мне пришлось написать код, который проверяет остаток на складе (по финансовым складским операциям) на дату списания, а затем пробегает по ВСЕМ списаниям и приходам, произошедшим ПОСЛЕ выполненного списания. В том случае, если списание проходило текущей датой, система выполняла операцию чуть медленнее, чем до этой переделки. В то же время, если операция выполнялась, скажем, датой трехнедельной давности – задержка была гораздо более ощутимой – где-то процентов на 40-50, по сравнению со стандартной проверкой. Кроме того – в такой ситуации проверка остатков за период порождала достаточно много блокировок. Когда же я попытался реализовать подобный режим и для физических складских операций, я столкнулся с еще большей проблемой. Не понятно, что в этом случае делать с резервами. Может возникнуть ситуация, при которой менеджер по продажам СЕГОДНЯ поставил резерв под заказ, а система ему не дает проводить отгрузку по заказу задним числом, потому что на тот момент – на складе такого количества товара не было. В общем – на этом проекте я сделал контроль остатка по финансовым операциям и оставил старую проверку для физических операций. Поскольку списания задним числом на этом проекте происходило достаточно редко – проблем подобный подход не вызвал. Но в общем случае полную проверку остатка в реальном времени не сделать. Правильнее будет периодически проверять наличие отрицательного остатка на дату с помощью какого-нибудь отчета.
1.18 Оценочная себестоимость.
В общем случае – оценочная себестоимость рассчитывается как “мгновенная средняя» то есть текущий остаток в валюте для данной номенклатуры в разрезе аналитик финансового склада, деленный на текущее количество данной номенклатуры на складе в разрезе тех же аналитик. При этом – в расчет берутся только те количества и суммы, которые были проведены по накладной (но не по отборочной накладной).
В целом - вопрос точности расчета системой не является особо критичным. Чем меньше разница между истинной и оценочной себестоимостями – тем быстрее работает закрытие склада. Если у нас на складе лежит номенклатура, закупленная по цене 400, 600 и 700 рублей, то даже если мы каким-то образом разочек списали одну штуку по мгновенной себестоимости 1200 рублей, ничего страшного не произойдет. В худшем случае – у нас чуть дольше будет работать процедура закрытия склада. В тоже время , если у нас в такой ситуации списалось по оценочной стоимости в 100000000 рублей – то на время закрытия склада это может повлиять достаточно сильно и негативно. Кроме того,надо помнить, что если мы слишком много номенклатуры спишем по какой-то уж совсем бредовой оценочной себестоимости , то у нас может возникнуть ситуация при которой у нас на складе осталось 3 штуки изделий по себестоимости минус 600 рублей. Вылечить подобную ситуацию можно только закрытием или пересчетом склада.
Наконец, надо разобрать несколько специальных случаев. В том случае, если в настройках группы складских моделей установлена галка "Включать физическую себестоимость», то в расчет мгновенной средней включаются суммы и количества, проведенные не только по обычным накладным, но и по отборочным. Если у нас между оформлением отборочной накладной и обычной накладной имеется большой временной лаг, то включение этого режима позволяет несколько увеличить точность расчета мгновенной средней.
В том случае если при расчете мгновенной средней, выяснилось что на складе нет данной номенклатуры и мы не можем вообще посчитать среднюю мгновенную себестоимость ,то система пытается использовать вместо мгновенной средней, то значение стандартной цены из номенклатурной карточки (то есть – тоже самое поле что и для стандартной себестоимости).
Отдельным случаем является работа в режиме стандартной себестоимости. В этом случае – вместо вычисления мгновенной средней, используется стандартная себестоимость, указанная в номенклатурном справочнике в поле "Цена» группы полей "Затраты». (В версии 3.0 эта группа полей называлась "Склад»). В принципе – эту цену можно настраивать не только в разрезе номенклатуры, но и для сочетания номенклатурных аналитик по данной номенклатуре. Но это – тема для отдельного обсуждения.
В том случае, если система рассчитывает себестоимость списания для возврата, то для расчета себестоимости списания используется себестоимость того прихода, по которому происходит возврат. При этом для определения ситуации возврата используется либо механизм маркировки проводок, либо указание возвращаемого лота.
1.19 "Странные проводки" по возврату закупок.
Часто приходится слышать жалобы начинающих консультантов, на "странные проводки" по возврату закупки. Скажем что-то типа "Почему по закупке у нас прошли проводки Д 10 К 60 1400руб, а по возврату Д 10 К 60 -1400руб; Д10 К10 -200руб?. Откуда вторая проводка?".
Вот причина этой ситуации: В проводки по поставщику мы должны поставить сумму возврата (1400рублей). В тоже время, при отсутствии маркирования, и оценочная и истинная себестоимость списания у нас может отличаться от этих 1400 рублей (В нашем случае – оценочная себестоимость – 1200 рублей). Вот DAX и пытается побороть эту проблему следующим способом:
- Со складского счета на счет задолженности списывается сумма возврата (количество, помноженное на цену)
- Складской счет дооценивается (или уценивается) на сумму разницы между мгновенной себестоимостью и суммой возврата, в корреспонденции со счетом потребления из настроек складских разносок. По какой-то непонятной причине, принято в это поле настройки разноски прописывать обычный складской счет, тот же самый что и в настройке счета прихода. Поэтому и появляется странная проводка Д 10 К 10.
Правильнее было бы прописывать в качестве счета потребления прописывать какой-нибудь рабочий счет типа 76.xxx. Тогда разноска возврата имела бы более понятный вид:
Д10 К60 -1400
Д76 К10 -200
В конце периода, после закрытия склада, у нас на счете 76.xxx накопился бы финансовый результат по нашим возвратам. Например – если мы регулярно ухитрялись возвращать поставщику залежалый товар выше себестоимости – у нас бы там накопилось кредитовое сальдо, в обратной ситуации – кредитовое. В конце периода этот счет я бы закрывал на прибыли и убытки, показывая корректировку финансовых результатов периода за счет возвратов поставщику.
Более того, если мы после разноски возвратов смогли примаркировать их к исходным закупкам (то есть – все возвраты прошли в том же периоде что и закупки), то после закрытия склада, у нас вообще не будет сальдо на этом счете, поскольку проведенные по закрытию склада коррекции отреверсируют все проводки со складского счета на счет потребления.