AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Функционал
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 19.08.2009, 21:16   #1  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1296 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
DAX4 Интеркомпани, ошибка разноски внутрихолдинговой накладной
Для информации, может быть кому понадобится.
Совместно с rINT обнаружили ошибку расчета количества при разноске внутрихолдинговой накладной.
При разноске накладной по внутрихолдинговому заказу на продажу автоматически разносится накладная по внутрихолдинговому заказу на покупку. При этом определяется количество, которое нужно разносить.
В классе SalesFormLetter в методе createParmLine есть следующий код:
X++:
[newSalesParmLine.DeliverNow, newSalesParmLine.RemainBefore      , newSalesParmLine.RemainAfter]        = this.qtySales  (_salesLineOrig, this.interCompanyParmLineQty(_salesLineOrig));
[newSalesParmLine.InventNow , newSalesParmLine.RemainBeforeInvent, newSalesParmLine.RemainAfterInvent]  = this.qtyInvent (_salesLineOrig, this.interCompanyParmLineQty(_salesLineOrig));
То есть, и для количества в единицах заказа на продажу и для количества в единицах складского хранения используется один и тот же метод interCompanyParmLineQty. В результате расчет недопоставки / перпоставки идет неверно если складские единицы и единицы в заказе отличаются.
Решение проблемы: для второго вызова interCompanyParmLineQty результат необходимо пересчитать в складские единицы.
За это сообщение автора поблагодарили: kashperuk (5).
Старый 20.08.2009, 10:23   #2  
JeS is offline
JeS
Участник
 
61 / 22 (1) +++
Регистрация: 30.10.2007
Адрес: СПб
Есть такое дело. Тоже исправлял. Вообще в Intercompany я много косяков в свое время нарыл. Такое ощущение, что эту функциональность плохо тестировали.
Старый 21.08.2009, 13:04   #3  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
Raven, а расскажите плз, как именно вы исправляли эту ошибку. Передачей true при втором вызове метода interCompanyParmLineQty?
И, если будет время, было бы здорово привести сценарий, с помощью которого можно воспроизвести это поведение в стандартном функционале.

Спасибо

Последний раз редактировалось kashperuk; 21.08.2009 в 13:07.
За это сообщение автора поблагодарили: Raven Melancholic (4).
Старый 21.08.2009, 15:08   #4  
JeS is offline
JeS
Участник
 
61 / 22 (1) +++
Регистрация: 30.10.2007
Адрес: СПб
Лично я исправлял как Вы сказали:
Цитата:
Передачей true при втором вызове метода interCompanyParmLineQty
Как воспроизвести точно не помню, но вроде так:
создать цепочку заказов (внутрихолд заказ продажи - внутрихолд заказ покупки - исходный заказ) типа прямая поставка. И разнести ее.
За это сообщение автора поблагодарили: Raven Melancholic (2).
Старый 21.08.2009, 15:33   #5  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
Цитата:
Сообщение от JeS Посмотреть сообщение
Лично я исправлял как Вы сказали:

Как воспроизвести точно не помню, но вроде так:
Тогда уже исправили. Спасибо
За это сообщение автора поблагодарили: mazzy (2).
Старый 21.08.2009, 17:17   #6  
JeS is offline
JeS
Участник
 
61 / 22 (1) +++
Регистрация: 30.10.2007
Адрес: СПб
Ну тогда, позвольте я еще про одну ошибку изложу. Скажу сразу, что толком не помню как ее воспроизвести (давненько это было). Кроется она в классе IntercompanyTransferInventDim метод transfer:
X++:
...
while (qr.next())
{
     fromInventTrans = qr.get(tablenum(InventTrans));
     fromInventDim   = qr.get(tablenum(InventDim));
     ...
     fromQty = -fromInventTrans.Qty;
     if (inventDimParm.InventLocationIdFlag && fromInventDim.InventLocationId)
     {
        convInventLocation = new TradeInterCompanyConv();
        salesInventLocationId = fromInventDim.InventLocationId;
        convInventLocation.axInventLocationId(fromValueMap, fromInventDim.InventLocationId);
     }
     ...
     changecompany(_toDataAreaId)
     {
          toInventTrans = null;
          
          select forceplaceholders sum(Qty) from toInventTrans
                           where toInventTrans.InventTransId == _toInventTransId
                           &&   (toInventTrans.StatusReceipt <= StatusReceipt::Registered
                              || toInventTrans.InterCompanyInventDimTransferred == true)
                           &&    toInventTrans.StatusIssue   == StatusIssue::None
         #inventDimJoin(toInventTrans.InventDimId,toInventDim,fromInventDim,inventDimParm);

         fromQty -= toInventTrans.Qty;
        ....
Обратите внимание на третий параметр в макросе #inventDimJoin, и что этот запрос исполняется уже в другой компании. В случае, если стоит настройка, при которой происходит синхронизация складов между компаниями через внешние коды, то последний запрос будет всегда возвращать ноль. То есть лечить надо примерно так: сначала сконвертировать значение fromInventDim.InventLocationId, и только потом использовать его в запросе.

P.S.: надеюсь понятно пояснил, как-то сумбурно получилось, млин
Старый 21.08.2009, 17:20   #7  
JeS is offline
JeS
Участник
 
61 / 22 (1) +++
Регистрация: 30.10.2007
Адрес: СПб
Забыл добавить: DAX 4.0 SP2
Старый 21.08.2009, 20:53   #8  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1296 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Ошибка проявляется в именно так, как описал JeS.
Ой, насчет:
Цитата:
Кроется она в классе IntercompanyTransferInventDim метод transfer
К сожалению это не одна ошибка в этом методе (даже если отбросить тот факт, что про ГТД в этом методе ничего нет).
Он неправильно отрабатывает в случаях, когда были кредит-ноты по строке, лечилось добавлением в выборки (те, которые зависят от того, какие аналитики включены) условий (то, что выделено комментариями):
Код:
select forceplaceholders sum(Qty) from fromInventTrans
     where fromInventTrans.InventTransId == _fromInventTransId
          &&    fromInventTrans.StatusIssue   <= _statusIssue
          &&    fromInventTrans.StatusReceipt == StatusReceipt::None
// ААК: МФД40_09_01_0013_001 11.07.2009 [Сторнирование и копирование документов] -->
          &&    fromInventTrans.InvoiceReturned        == NoYes::No
          &&    fromInventTrans.PackingSlipReturned    == NoYes::No
// ААК: МФД40_09_01_0013_001 11.07.2009 [Сторнирование и копирование документов] <--
          join InventLocationId, InventBatchId, InventSerialId, InventGtdId_RU  from fromInventDim
               group by InventLocationId, InventBatchId, InventSerialId, InventGtdId_RU
               where fromInventDim.InventDimId == fromInventTrans.InventDimId;
Одна трудно уловимая ошибка связана со вторым проходом метода, то есть тогда, когда вызван с параметром _registerReceipt равном true. При определении того, какие проводки связанной закупки уже отработаны, метод ориентируется на поле InterCompanyInventDimTransferred. В отработанных проводках он проставляет в это поле true и больше к ним не обращается. Но, когда вызывается регистрация InventTransWMS_Register, то естественно, процедура регистрации ничего не знает про это поле и регистрирует любую запись в InventTrans (естественно, с учетом аналитик), если их по лоту несколько. В результате получаем ситуацию, что признак обработки поставили в одной записи, а зарегистрированы другие. Соответственно на следующем проходе выборка:
X++:
select forupdate toInventTrans
     index hint TransIdIdx
     where toInventTrans.InventTransId                    == _toInventTransId
          &&    toInventTrans.StatusReceipt                    == StatusReceipt::Ordered
          &&    toInventTrans.StatusIssue                      == StatusIssue::None
          &&    toInventTrans.InterCompanyInventDimTransferred == false;
может ничего не найти.
Для воспроизведения этой ошибки нужно чтобы проводки по лоту были расщеплены как со стороны заказа на продажу, так и со стороны заказа на покупку. Причем, иногда везет и комплектуются те записи, по которым установили флаг, но везение не всегда случается. Эту проблему решили, но грубо (стояли отгрузки, поэтому было не до изящности):
вместо
X++:
inventTransUpd.InterCompanyInventDimTransferred = true;
поставили:
X++:
//lex 24.04.2009 при регистрации могла выбраться еще необработанная проводка и на пересечении...-->
if (!_registerReceipt)
{
      inventTransUpd.InterCompanyInventDimTransferred = true;
}
//lex 24.04.2009 при регистрации могла выбраться еще необработанная проводка и на пересечении...<--
А саму установку флага делаем после регистрации:
X++:
//lex 24.04.2009 при регистрации могла выбраться еще необработанная проводка и на пересечении...-->
update_recordset refInventTrans
     setting InterCompanyInventDimTransferred = true
     where refInventTrans.InventTransId      == inventTransUpd.InventTransId
          && refInventTrans.StatusReceipt == StatusReceipt::Registered
          && !refInventTrans.InterCompanyInventDimTransferred;
//lex 24.04.2009 при регистрации могла выбраться еще необработанная проводка и на пересечении...<--
За это сообщение автора поблагодарили: JeS (1).
Старый 21.08.2009, 21:18   #9  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1296 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Еще один недостаток этого метода. Бывает, что в компании, в которой производится продажа конечному клиенту, требуются данные не только номера партии, а и то, что есть в таблице партий (например, дата производства, если существует контроль и предоставление клиенту информации о сроках годности), тоже может требоваться по серийным номерам и, естественно, нужна страна по ГТД. Однако, в методе создаются записи только с номером:
X++:
if (!InventBatch::exist(toInventTrans.ItemId,inventDimUpd.InventBatchId))
{
     inventBatch.clear();
     inventBatch.ItemId          = toInventTrans.ItemId;
     inventBatch.InventBatchId   = inventDimUpd.InventBatchId;
     inventBatch.insert();
}
Пришлось допиливать: в исходной компании запоминать данные из соотвествующих таблиц:
X++:
while (fromInventTrans)
{
     // ААК: МФД40_08_01_0003 19.03.2009 [Планирование сделок] -->
     inventBatchFrom     = null;
     if (fromInventDim.inventBatchId)
     {
          inventBatchFrom     = InventBatch::find(fromInventDim.inventBatchId, itemIdFrom);
     }
     ...
А создание в компании-продавце добавлять:
X++:
if (!InventBatch::exist(toInventTrans.ItemId,inventDimUpd.InventBatchId))
{
     inventBatch.clear();
     inventBatch.ItemId          = toInventTrans.ItemId;
     inventBatch.InventBatchId   = inventDimUpd.InventBatchId;
     // ААК: МФД40_08_01_0003 19.03.2009 [Планирование сделок] -->
     inventBatch.initFromInventBatch_OVK(inventBatchFrom);
     // ААК: МФД40_08_01_0003 19.03.2009 [Планирование сделок] <--
     inventBatch.insert();
}
В общем, вещи достаточно нужные, но почему-то нереализованные. А рука локализаторов вообще не касалась этого механизма. Помимо того, что в описываемом методе пришлось добавлять синхронизацию по ГТД, еще самостоятельно реализовывали настройки синхронизации росийских форматов адресов и использование этих настроек в методах InterCompanyMirror классов SalesTableType, SalesLineType и их же по закупкам, включая классы Ax*
Старый 21.08.2009, 21:30   #10  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1296 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Цитата:
Сообщение от kashperuk Посмотреть сообщение
Raven, а расскажите плз, как именно вы исправляли эту ошибку. Передачей true при втором вызове метода interCompanyParmLineQty?
Нет, Иван, это вариант не для всех ситуаций. Как бы мы не написали метод interCompanyParmLineQty, он работает по данным компании-продавца и даст данные по единицам складского учета компании-продавца. Однако, складские единицы могут различаться в компаниях. Например, наш случай: в закупочной компании складские единицы тонны, а в компании-покупатели килограммы. Если единицы продажи/покупки механизм интеркомпани заставляет делать одинаковыми (какой-бы метод согласования в настройках конечных точкек мы не выбрали, пересчета на стыке компаний не будет), то складские единицы могут различаться. Если метод interCompanyParmLineQty вернет мне тонны, а у меня в покупающей компании складскими единицами являются килограммы, то проблема остается.
Поэтому лучше получить данные методом interCompanyParmLineQty для единиц закупки/продажи и приводить к складским уже в компании-покупателе.
Старый 24.08.2009, 10:11   #11  
JeS is offline
JeS
Участник
 
61 / 22 (1) +++
Регистрация: 30.10.2007
Адрес: СПб
В предыдущем сообщении, где приводил пример кода, я допустил ошибку. Исправляюсь. Код в базовой функциональности должен выглядеть так:
X++:
while (fromInventTrans)
{
     ...
     fromQty = -fromInventTrans.Qty;
     if (inventDimParm.InventLocationIdFlag && fromInventDim.InventLocationId)
     {
        convInventLocation = new TradeInterCompanyConv();
        salesInventLocationId = fromInventDim.InventLocationId;
        convInventLocation.axInventLocationId(fromValueMap, fromInventDim.InventLocationId);
     }
     ...
     changecompany(_toDataAreaId)
     {
          toInventTrans = null;
          
          select forceplaceholders sum(Qty) from toInventTrans
                           where toInventTrans.InventTransId == _toInventTransId
                           &&   (toInventTrans.StatusReceipt <= StatusReceipt::Registered
                              || toInventTrans.InterCompanyInventDimTransferred == true)
                           &&    toInventTrans.StatusIssue   == StatusIssue::None
         #inventDimJoin(toInventTrans.InventDimId,toInventDim,fromInventDim,inventDimParm);

         fromQty -= toInventTrans.Qty;
        ....
Но, вобщем, сути дела это не меняет, но сбить столку кого-нибудь может.
Кстати непонятно зачем нужно было писать такую "этажерку" в этом методе до вышеописанного цикла:
X++:
if (inventDimParm.InventLocationIdFlag && inventDimParm.InventBatchIdFlag && inventDimParm.InventSerialIdFlag)
            {
                select forceplaceholders sum(Qty) from fromInventTrans
                       where fromInventTrans.InventTransId == _fromInventTransId
                       &&    fromInventTrans.StatusIssue   <= _statusIssue
                       &&    fromInventTrans.StatusReceipt == StatusReceipt::None
                join InventLocationId, InventBatchId, InventSerialId from fromInventDim
                     group by InventLocationId, InventBatchId, InventSerialId
                     where fromInventDim.InventDimId == fromInventTrans.InventDimId;
            }
            else if (inventDimParm.InventLocationIdFlag && inventDimParm.InventBatchIdFlag)
            {
                select forceplaceholders sum(Qty) from fromInventTrans
                       where fromInventTrans.InventTransId == _fromInventTransId
                       &&    fromInventTrans.StatusIssue   <= _statusIssue
                       &&    fromInventTrans.StatusReceipt == StatusReceipt::None
                join InventLocationId, InventBatchId from fromInventDim
                     group by InventLocationId, InventBatchId
                     where fromInventDim.InventDimId == fromInventTrans.InventDimId;
            }
            else if (inventDimParm.InventLocationIdFlag && inventDimParm.InventSerialIdFlag)
            {
            ...
и так далее, перебирая всевозможные комбинации аналитик. Почему нельзя было воспользоваться QueryBuild-классами? Может я чего-то не понимаю. Просто если вдруг понадобится добавить скажем еще 2 аналитики, то становится дурно (одну то точно добавить хочется - ГТД).
Старый 24.08.2009, 10:20   #12  
JeS is offline
JeS
Участник
 
61 / 22 (1) +++
Регистрация: 30.10.2007
Адрес: СПб
to Raven
Цитата:
К сожалению это не одна ошибка в этом методе
За это спасибо
Цитата:
даже если отбросить тот факт, что про ГТД в этом методе ничего нет
локализация вообще как-то стороной обошла Intercompany. Помимо ГТД добавлю, что и счета-фактуры не синхронизируются, что вобщем-то тоже напрашивается. Более того, если будешь разносить сразу фактуру во внутрихолдинговом заказе на продажу (в цепочке типа прямой поставки) - то просто выкинет ошибку.
Старый 29.08.2009, 21:08   #13  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1296 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Еще наткнулись. Ка-то странно работает кэш (я про это уже писал, но тогда было только тестирование и как-то не зациклились на этой теме).
У нас в разных компаниях номенклатура ведет себя по разному (например, в производственной это спецификация с номенклатурной группой "Готовая продукция", в торговых домах это номенклатура с номенклатурной группой Товар, ну и т.п.). Поэтому справочник не общий, а в каждой компании свой (есть доработка по вводу и синхронизации определенных номенклатур, но суть не в этом). Так вот, простой код джоба:
X++:
itemId = 'ВентиляторWRW50/40';
intentTable = InventTable::find(itemId);
changeCompany('TRD')
{
   inventTableChg = null;
   select firstOnly inventTableChg where inventTableChg == itemId;
   ...
}
Думаете, что после переключения мы нашли номенклатуру в компании TRD? Сильно ошибаетесь. Возвращена номенклатура компании, в которой запустили джоб! Если в компании запуска не было поиска по этому номенклатурному номеру, то все нормально.
То же происходит если в разных компаниях номера складских лотов совпадают. Если после переключения компании искать складские операции по номеру лота, совпадающему с тем, что был выполнен ранее, то вернется ранее найденный лот.
Помогает включения в код запрет кэша:
X++:
itemId = 'ВентиляторWRW50/40';
intentTable = InventTable::find(itemId);
changeCompany('TRD')
{
   inventTableChg = null;
   inventTableChg.disableCash(true);
   select firstOnly inventTableChg where inventTableChg == itemId;
   ...
}
Если по номенклатуре это можно понять (есть кэширование), то по InventTrans что-то невнятное: у этой таблице CashLookup стоит NONE.
Такое впечатление, то кэш игнорирует компании. вобщем-то можно было бы отключить кэш DAX для всех таблиц (кэш MS SQL хорошо справляется со своей работой), но InventTrans не кэшируется DAX!
Так же понятно, что из-за этой проблемы следует стараться иметь для одних и тех же таблиц в разных компаниях разные уникальные идентификаторы. Но, в нашем случае, это возможно для InventTrans, но не интересно для InvntTable. Можно всегда добавлять disableCach, но в наших разработках мы так и делаем, но есть стандартный код!
PS: кстати, без использования disableCash Trasert MS SQL показывает, что к базе данных было только оно обращение, то есть цепочка: поиск в одной компании, changeCompany, поиск в другой компании того же значения обрабатывает DAx независимо от типа кэширования.

Последний раз редактировалось Raven Melancholic; 29.08.2009 в 21:16.
За это сообщение автора поблагодарили: JeS (1), Kabardian (3).
Старый 29.08.2009, 21:30   #14  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1296 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Еще один момент. Правда это не ошибка, но непонятка.
В SalesLine и PurchLine для механизма Интеркомпани есть поле InterCompanyInventTransId по которому связаны строки заказа на покупки и заказа на продажу Интеркомпани. Но в классах, наследниках от TradeInterCompany поиск связанной строки почему-то происходит не по этим лотам, а по номерам строк.
Я, конечно, понимаю, что индекс, включающий номер строки является кластерным и когда требуется получить все данные строки он работает быстро (тем более, что в интерфейсе поле LineNum не представлено, а в методах InteCompanyMirror оно синхронизируется). Но, все-таки, во всех остальных местах связь идет по полю InterCompanyInventTransId, к тому же, индекс, включающий LineNumне уникальный, что позволяет при работе не из интерфейса, а из кода дублировать номера строк (кстати, в стандартном приложении бывает, что номер строки несколько раз бывает нулевым, правда это относится не к заказам на покупку-продажу, а складским журналам и журналам ГК).
Почему для классов, наследников TradeInterCompany сделано исключение из общего правила?
Теги
ax4.0, intercompany, ошибка, интеркомпани

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Отмена разноски отборочной накладной Iskorka DAX: Функционал 7 03.07.2008 18:23
ошибка при обработке накладной по заказу kashperuk DAX: Функционал 9 18.09.2006 10:40
Настройка разноски Накладной в Заказах vesna DAX: Функционал 19 18.11.2005 16:11
разноска счета на оплату после разноски накладной OlegKocherga DAX: Функционал 14 12.03.2004 17:48
Русская локализация Axapta 3 ? SlavaK DAX: Администрирование 59 01.07.2003 22:38

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 12:44.