14.08.2012, 10:39 | #1 |
Участник
|
Остатки по номенклатуре в разрезе складов внутри транзакции
Привет всем!
Помогите пожалуйста найти быстрый способ решения следующей задачи: 1) Перебор остатков без привязки к номенклатуре в разрезе складов (или других складских аналитик). Чтобы на выходе я мог группировать по складу, номенклатуре, ячейке и так далее. Фокус в том, что это надо делать внутри транзакции, когда проводки регистрируются, резервируются, в них меняются ячейки, палеты и т.д. В Аксапте 3.0 с этим не было проблем - таблица InventSum всегда содержала актуальную информацию. В Аксапте 2009 таблица InventSum не содержит актуальную информацию до завершения транзакции. Посмотрел класс InventOnHand и манипуляции с таблицей InventSumDelta. Но не понял, как из этого класса взять набор остатков в виде [Склад 1 - остаток 3 штуки, Склад 2 - остаток 5 штук, и т.д.] Но чтобы не изобретать велосипед, может у кого-то уже есть шаблон кода для выполнения этой задачи? На одном проекте видел "велосипед", когда остатки вычислялись хранимой процедурой на SQL, а из Аксапты потом считывались. Можно потратить пару дней и самому разобраться, но если уже у кого-то есть готовый шаблон кода? |
|
14.08.2012, 10:45 | #2 |
Участник
|
Вот наверное ответ на мой вопрос:
dynamicsaxtraining: Get “available physical” values for specific item+dimensions per batch Да, громоздкие получаются конструкции. |
|
14.08.2012, 11:06 | #3 |
Участник
|
вызвать после работы с проводками
X++: Appl.ttsNotifyPreCommit(); |
|
14.08.2012, 12:03 | #4 |
Moderator
|
Цитата:
Вариант из блога dynamicsaxtraning, он - сложный, но единственно применимый в многопользовательской среде. |
|
14.08.2012, 12:30 | #5 |
Участник
|
нормальный совет использовать класс InventOnHand
|
|
|
За это сообщение автора поблагодарили: Logger (3). |
14.08.2012, 12:44 | #6 |
Участник
|
если я правильно понял задачу то примерно такой код
X++: hand = InventDimOnHand::newAvailPhysical('', inventDim, inventDimParm, InventDimOnHandLevel::TotalQty, inventDimParm); iterator = hand.onHandIterator(); while (iterator.more()) { member = iterator.value(); iterator.next(); } |
|
|
За это сообщение автора поблагодарили: Logger (5), Ace of Database (4), FrolovAndy (2). |
14.08.2012, 13:31 | #7 |
Участник
|
Посмотрите реализацию стандартных методов
X++: InventSum::findSum() InventSumDelta::findSumDelta()
__________________
If it ain't broke, take it apart and find out why (с) |
|
14.08.2012, 13:55 | #8 |
Участник
|
поиск остатков с помощью двух таблиц конечно хорош, но вот если нужно, например, разнести в одной транзакции приход и расход, вот тут начинаются сложности
|
|
14.09.2012, 14:22 | #9 |
Участник
|
Omeo, спасибо большое!
Наконец-то научили меня считать остатки самым правильным способом за 8 лет работы X++: static void Job11(Args _args) { InventDimOnHand hand; inventDim inventDim; inventDimParm inventDimParm; InventDimOnHandIterator iterator; InventDimOnHandMember member; ; inventDim.InventLocationId = "Общий"; inventDimParm.initFromInventDim(inventDim); hand = InventDimOnHand::newAvailPhysical('', inventDim, inventDimParm, InventDimOnHandLevel::TotalQty, inventDimParm); iterator = hand.onHandIterator(); while (iterator.more()) { member = iterator.value(); info(strfmt("%1 = %2", member.parmItemId(), member.parmInventQty())); iterator.next(); } } Последний раз редактировалось Ace of Database; 14.09.2012 в 14:25. |
|
|
За это сообщение автора поблагодарили: Logger (3). |
14.09.2012, 14:34 | #10 |
Участник
|
Правда все равно остаются ограничения у такого механизма по сравнению с прямым запросом к InvenSum-InventDim.
В реальной жизни приходится еще делать сортировку\фильтрацию\группировку во всяким дополнительным полям в складах, ячейках, палетах, партиях. Подцеплять побочные таблицы. В следующий раз когда потребуется сложное манипулирование остатками, попробую использовать этот механизм. А пока-что в моем случае остатки достаточно было вычислить всего один раз в разрезе каждой номенклатуры и группы складких аналитик, поэтому транзакционность оказалась не помехой. Последний раз редактировалось Ace of Database; 14.09.2012 в 14:45. |
|
14.09.2012, 15:13 | #11 |
NavAx
|
Цитата:
X++: queryRun = new QueryRun(InventSum::newQuery(null, itemId, inventDimCriteriaLocal, inventDimParmLocal, inventDimParmGroupByLocal)); while (queryRun.next()) { inventSumLocal = queryRun.get(tableNum(InventSum)); inventDimLocal = queryRun.get(tableNum(InventDim)); if (InventUpdateOnhandGlobal::mustAddInventSumDeltaOnhand(itemId)) { select #inventSumFields from inventSumDeltaLocal where inventSumDeltaLocal.ItemId == itemId && inventSumDeltaLocal.IsAggregated == NoYes::No && inventSumDeltaLocal.TTSId == appl.inventUpdateOnhandGlobal().inventUpdateOnhand().tTSId() #InventDimExistsJoin(inventSumDeltaLocal.inventDimId,inventDim2Local,inventDimLocal,inventDimParmLocal); inventSumLocal.addInventSumDelta(inventSumDeltaLocal); } ....... } |
|
|
За это сообщение автора поблагодарили: Ace of Database (2), SRF (1). |
14.09.2012, 15:17 | #12 |
Участник
|
Цитата:
Цитата:
Сообщение от Ace of Database
Правда все равно остаются ограничения у такого механизма по сравнению с прямым запросом к InvenSum-InventDim.
В реальной жизни приходится еще делать сортировку\фильтрацию\группировку во всяким дополнительным полям в складах, ячейках, палетах, партиях. Подцеплять побочные таблицы.
__________________
Sergey Nefedov |
|
17.09.2012, 13:09 | #13 |
Участник
|
Была подобная задача - получить список ячеек хранения на складе с указанием свободного количества по каждой из ячеек
(по какой то отдельное номенклатуре) внутри транзакции. Пвтался использовать метод с использованием класса "inventDimParmOnHandLevel". Остался пример кода. X++: inventDim.InventLocationId = "Склад"; inventDim = inventDim::findDim(inventDim); inventDimParm.InventLocationIdFlag = true; inventDimParmOnHandLevel.ItemIdFlag = true; inventDimParmOnHandLevel.InventLocationIdFlag = true; inventDimParmOnHandLevel.WMSLocationIdFlag = true; hand = InventDimOnHand::newAvailPhysical('29530', inventDim, inventDimParm, InventDimOnHandLevel::DimParm, inventDimParmOnHandLevel); iterator = hand.onHandIterator(); while (iterator.more()) { member = iterator.value(); info(member.parmInventQty()); info(InventDim::find(member.parmInventDimId()).wMSLocationId); iterator.next(); } Может в настройках классов указал что то не правильно ? Так как мне нужно было это использовать при резервировании, то есть довольно активно, такой способ оказался не приемлем. В общем переделал все на прямые запросы к таблицам - InventSum - InventSumDelta (Что то на подобие того как предложил raz). Так все "летает". Только нужно учесть что вариант с циклом по текущим остаткам (inventSum) с прибавлением к нему InventSumDelta по аналитикам группкировки inventSum может дать искаженный результат. Так как в InventSumDelta могут быть остатки по аналитикам, которых нет в inventSum. Правильный результат будет если: 1. Получить остатки в разрезе ячеек по InventSum, например так (для моей задачи): X++: inventDimParm.initFromInventDim(inventDim); inventDimParm.ItemIdFlag = NoYes::Yes; inventDimParm.ClosedQtyFlag = NoYes::Yes; inventDimParmGroupBy.WMSLocationIdFlag = NoYes::Yes; query = inventSum::newQuery(query, itemId, inventDim, inventDimParm, inventDimParmGroupBy); queryRange = query.dataSourceTable(tableNum(inventSum)).addRange(FieldNum(InventSum, ClosedQty)); queryRange.value(queryValue(0)); queryRun = new QueryRun(query); while (queryRun.next()) { inventSum = queryRun.get(tableNum(inventSum)); inventDimLoc = queryRun.get(tableNum(inventDim)); if (inventSum.AvailPhysical != 0) { //Тут текущий остаток в ячейках хранения } } 2. Получить остатки в разрезе ячеек InventSumDelta, например так (для моей задачи) X++: while select sum(AvailPhysical) from inventSumDelta where inventSumDelta.ItemId == itemId && inventSumDelta.IsAggregated == NoYes::No && inventSumDelta.ttsId == appl.inventUpdateOnhandGlobal().inventUpdateOnhand().ttsId() join inventDimLoc group by WMSLocationId where (inventDimLoc.inventDimId == inventSumDelta.InventDimId) && (inventDimLoc.InventLocationId == "Склад") { if (inventSumDelta.AvailPhysical != 0) { //Тут изменнный остаток в ячейках хранения в данной транзакции } } 3. Объеденить два этих набора записей ("сложить" результат). Это кроме того и самый производительный способ - всего два запроса к БД. |
|
|
За это сообщение автора поблагодарили: Logger (5), Ace of Database (4). |
17.09.2012, 13:46 | #14 |
Участник
|
Спасибо большое Raz и SomeOne!
Метод с получением двух query для InventSum и InventSumDelta с последубщими добавлениями в них дополнительных источников данных универсален для всех задач. С последующим суммированием результата через inventSum.addInventSumDelta(inventSumDelta); Я все понял. Осталось заставить себя в следующий раз применить этот способ, а не считать по-старинке прямыми запросами Еще один способ - оптимизировать код таким образом, чтобы алгоритму было без разницы, внутри транзакции идет подсчет остатков или за ее пределами, и в этом случае вынести подсчет остатков за пределы транзакции. Это самый оптимальный вариант, но к сожалению не всегда допустимый. В общем, подсчет остатков - деликатная и объемная тема, достойная диссертации. |
|
10.03.2016, 13:50 | #15 |
Участник
|
Извините, что поднимаю старую тему...
Коллеги, а Вам не кажется, что вот это: Цитата:
Может быть, это исправлено в каком-то SP? |
|
18.05.2017, 13:50 | #16 |
Участник
|
Это не косяк, а так спроектировано. Если сразу добавлять запись (пусть и с пустыми остатками) в InventSum одновременно с созданием InventSumDelta, то можно нарваться на блокировки/конфликт уникального ключа. А InventSumDelta как раз и делали, чтобы этого избежать.
|
|
19.05.2017, 19:37 | #17 |
Участник
|
Подождите, причём тут внесение записи в InventSum? Мне было непонятно, почему InventSumDelta не обрабатывается в классе InventDimOnHand.
|
|
19.05.2017, 21:39 | #18 |
Участник
|
Я отвечал не на первое ваше сообщение, а на вот это:
Цитата:
Цитата:
Так как в InventSumDelta могут быть остатки по аналитикам, которых нет в inventSum.
Может быть, это исправлено в каком-то SP? |
|
02.06.2017, 01:31 | #19 |
Administrator
|
Цитата:
someOne имел в виду, что надо быть аккуратным, когда не используется InventDimOnHand, а остатки выбираются прямыми запросами к InventSum/InventSumDelta. Кстати, в AX 2012 R3 появились View (InventSumAggrDeltaView и InventSumUnionDeltaPhysicalQty), в которых InventSum и InventSumDelta уже связаны. Идея была в том, чтобы читать остатки из этих View, и не заморачиваться с классами InventOnhand. Идея, вероятно, была неплохая, но реализация подкачала: во View забыли добавить TTSId, и теперь любые запросы к ним блокируют и InventSum, и InventSumDelta целиком. Используются эти View в новом Warehouse Management. В блогах и на форумах есть несколько статей, в которых люди пытаются что-то шаманить с индексами на InventSumDelta, чтобы блокировки уменьшить, но корень проблемы в том, что InventSumDelta просто неправильно используется в стандартном коде.
__________________
Not registered yet? Register here! Have comments, questions, suggestions or anything else regarding our web site? Don't hesitate, send them to me |
|
|
За это сообщение автора поблагодарили: Logger (10). |
02.06.2017, 09:01 | #20 |
Участник
|
Цитата:
Цитата:
(у меня в отношении этого были опасения по блокировкам и они подтвердились ) Вы видите способ штатными средствами аксапты (без редактирования вьюхи в SQL) достичь заявленной цели ? Цитата:
Ого ! А как же правильно тогда ? Что там не так ? |
|
Теги |
ax2009, inventsumaggrdeltaview, inventsumdelta, inventsumuniondeltaphysicalqty, как правильно, остатки, транзакции |
|
|