#foreign-keys #acumatica
#внешние ключи #acumatica
Вопрос:
Мой ЦАП и таблицы определены следующим образом Структура данных
Родительский файл AC (FormView) Дочерний файл AC1 (TAb1 / Grid) Дочерний файл AC2 (Tab2 / Grid)
в ChildDAc2, ChildDAc1ID должен отображаться как селектор, как это можно сделать? Мы сталкиваемся с проблемой, если данные не сохранены для Parent / ChildDAc1, то они недоступны для поиска ChildDAc2
Обновление — Бизнес-сценарий — Рабочий элемент имеет несколько задач и несколько шагов для выполнения рабочего элемента. теперь WORKITEMDAC является родительским TASKDAC, а STEPDAC является дочерним элементом родительского WORKITEMDAC. Хорошо, пока все в порядке и обычно.. Теперь предполагается, что каждый шаг связан с задачей родительского рабочего элемента. ИТАК, в таблице ШАГОВ требуется селектор для выбора ЗАДАЧИ. Здесь у меня есть правильные отношения между родителями и дочерними элементами, с этим нет проблем, у меня есть только одна проблема, то есть я могу выбрать только ту задачу, которая уже была сохранена с родительским WORKITEM, несохраненные задачи не отображаются в селекторе. Итак, мой вопрос был в том, есть ли у нас какой-либо способ получить задачу в запросе PXSelector, который еще не сохранен? Следующий селектор используется в STEPDAC в столбце TaskId — [PXSelector(typeof(SearchИдентификатор задачи, где<TASKDAC.TaskId, равен<Current>>>), typeof(TASKDAC.taskCD), SubstituteKey = typeof(TASKDAC.taskCD))]
Примечание — столбец TaskId в TASKDAC является столбцом идентификатора, и этот DAC имеет WorkItemID, определенный как родительский.
Комментарии:
1. Любые обновления по моему запросу…
Ответ №1:
Обновить
Спасибо за обновление с более подробной информацией. Оставляя исходный ответ ниже для тех, кому это может понадобиться в отношении родительского / дочернего элемента.
Я никогда не видел селектор, определенный со ссылкой на Current<> и без текущего WHAT . Честно говоря, я удивлен, что он скомпилирован. «Где поле = текущее поле просмотра» указывает селектору ограничить результаты только значениями, связанными с текущим значением любого указанного вами поля, и оно извлекается из «ViewName.Текущая» запись в графике. Ваше предложение where не указывает, какой «текущий» должен соответствовать. Поскольку вы направляете селектор на поиск, я подозреваю, что это приводит к тому, что селектор не может найти соответствующую запись и прерывает сохранение результата для этого поля.
Похоже, что TASKDAC — это запись типа «основные данные», определяющая все возможные задачи, которые необходимо выполнить. Следовательно, TaskId не является ключевым полем в STEPDAC, а скорее ключевым полем в TASKDAC. Если вы хотите выбрать «любую» задачу, вам вообще не нужно предложение where.
[PXSelector(
typeof(TASKDAC.TaskID),
typeof(TASKDAC.taskCD),
SubstituteKey = typeof(TASKDAC.taskCD)
)]
Давайте на мгновение предположим, что вы назначаете WORKITEMDAC.WorkOrderType, который используется для фильтрации допустимых задач по одному и тому же полю. (Только задачи восстановления могут быть назначены типу рабочего порядка ремонта, сборка — типу рабочего порядка сборки и т.д.) В этом случае вы бы использовали указанный вами синтаксис, но вы бы указали, гдеТип рабочего порядка, равныйWorkOrderType>>. Если вместо этого задача ограничена чем-то в STEPDAC, просто замените WORKITEMDAC полем STEPDAC в примере ниже.
[PXSelector(
typeof(Search<TASKDAC.TaskID,
Where<TASKDAC.WorkOrderType, Equal<Current<WORKITEMDAC.WorkOrderType>>>>),
typeof(TASKDAC.taskCD),
SubstituteKey = typeof(TASKDAC.taskCD)
)]
Пожалуйста, обновите свой PXSelector и сообщите, устраняет ли это проблему или какую ошибку вы получите дальше.
Оригинальный ответ
С таким небольшим ответом на ваш вопрос природа вашего вопроса очень неясна. Похоже, вы пытаетесь сделать что-то, что, возможно, должно использовать другой подход. Определение селектора не должно влиять на сохранение поля, но структура отношения родитель-потомок имеет решающее значение для правильного определения. Таким образом, кажется очень странным, что у вас будет дочерний идентификатор, который отличается от родительского, и что он будет использоваться в селекторе.
Как правило, дочерний ЦАП разделяет идентификатор с родительским ЦАП. Мы часто определяем поле LineNbr в дочернем элементе, которому присваивается значение с помощью PXLineNbrAttribute, и ключевыми полями будут ID LineNbr для идентификации конкретной дочерней записи. Мы также устанавливаем SyncPosition = true в документе ASPX в таблице, чтобы пользовательский интерфейс оставался полностью синхронизированным с любой записью, выбранной пользователем в таблице. Отношения между родителями и дочерними элементами могут быть последовательными, например, с помощью SOOrder -> SOLine -> SOLineSplit, но, в конечном счете, поле идентификатора дочернего элемента не является селектором… идентификатор родительского элемента равен.
Подумайте о селекторе как о средстве поиска основных данных. Основные данные могут быть связаны с другими данными, но в большинстве случаев у них не будет родительского элемента. Есть исключения, но давайте не будем усложнять. У заказа на поставку, например, не было бы родительского элемента, а ключевое поле было бы общим полем выбора. Однако, поскольку дочерний элемент всегда должен быть связан со своим родителем и не может изменить своего родителя, идентификатор дочернего поля не будет селектором. Другие таблицы в Acumatica будут ссылаться на некоторое поле или комбинацию полей, чтобы связать их с данными дочернего DAC.
Из учебных руководств T210 я многое убрал, чтобы показать именно этот момент.
RSSVRepairPrice — Родительский
using System;
using PX.Data;
namespace PhoneRepairShop
{
[PXCacheName("Repair Price")]
public class RSSVRepairPrice : IBqlTable
{
#region PriceID
[PXDBIdentity]
public virtual int? PriceID { get; set; }
public abstract class priceID : PX.Data.BQL.BqlInt.Field<priceID> { }
#endregion
#region ServiceID
[PXDBInt(IsKey = true)]
[PXDefault]
[PXUIField(DisplayName = "Service", Required = true)]
[PXSelector(
typeof(Search<RSSVRepairService.serviceID>),
typeof(RSSVRepairService.serviceCD),
typeof(RSSVRepairService.description),
DescriptionField = typeof(RSSVRepairService.description),
SelectorMode = PXSelectorMode.DisplayModeText)]
public virtual int? ServiceID { get; set; }
public abstract class serviceID : PX.Data.BQL.BqlInt.Field<serviceID> { }
#endregion
#region RepairItemLineCntr
[PXDBInt()]
[PXDefault(0)]
public virtual Int32? RepairItemLineCntr { get; set; }
public abstract class repairItemLineCntr : PX.Data.BQL.BqlInt.Field<repairItemLineCntr> { }
#endregion
}
}
RSSVRepairItem — Дочерний элемент
using System;
using PX.Data;
using PX.Objects.IN;
using PX.Data.BQL.Fluent;
namespace PhoneRepairShop
{
[PXCacheName("Repair Item")]
public class RSSVRepairItem : IBqlTable
{
#region ServiceID
[PXDBInt(IsKey = true)]
[PXDBDefault(typeof(RSSVRepairPrice.serviceID))]
[PXParent(
typeof(SelectFrom<RSSVRepairPrice>.
Where<RSSVRepairPrice.serviceID.IsEqual<
RSSVRepairItem.serviceID.FromCurrent>>
))]
public virtual int? ServiceID { get; set; }
public abstract class serviceID : PX.Data.BQL.BqlInt.Field<serviceID> { }
#endregion
#region LineNbr
[PXDBInt(IsKey = true)]
[PXLineNbr(typeof(RSSVRepairPrice.repairItemLineCntr))]
[PXUIField(DisplayName = "Line Nbr.", Visible = false)]
public virtual int? LineNbr { get; set; }
public abstract class lineNbr : PX.Data.BQL.BqlInt.Field<lineNbr> { }
#endregion
}
}
В родительском файле обратите внимание на использование атрибута PXDBIdentityAttribute, который указывает, что идентификатор записи устанавливается базой данных в PriceID . Поле «ID», используемое дочерним элементом, берется из поля serviceId, которое определено как селектор для другой основной таблицы. Использование IsKey = true в PXDBIntAttribute сообщает Acumatica, что она может найти уникальную запись по комбинации всех полей, отмеченных IsKey = true. (Обратите внимание, что фактическое учебное руководство включает другое поле как часть ключа, но я упростил приведенный здесь пример.) Также обратите внимание на использование поля PDXBInt, значение которого по умолчанию равно 0, для использования в качестве счетчика строк. (RepairItemLineCtr)
Теперь, в дочернем элементе, обратите внимание, что ключевое поле родительского элемента снова определено в DAC, но без селектора. Атрибут PXParentAttribute определяет обратную связь с родительским ЦАП, и значение поля по умолчанию вводится из этого родительского. Это то, что приводит к автоматическому сохранению значения в записи. Он также связывает отношения, так что удаление родительского элемента также приведет к удалению дочернего элемента. Обратите внимание на использование PXLineNbrAttribute для автоматического увеличения поля родительского ЦАП и установки значения в поле LineNbr. Именно комбинация этих 2 полей указывает вам на точную запись.
Бывают случаи, когда Acumatica использует поле NoteID (уникальное значение GUID) для идентификации записи дочернего ЦАП, но это немного сложнее. Вы можете видеть это во взаимосвязи INItemPlan (Спрос) с заказами на поставку.
Если у вас возникли проблемы с отношениями родитель-потомок (Master-Detail), я бы настоятельно рекомендовал ознакомиться с учебным курсом T210.
Комментарии:
1. Обновил мой вопрос для большей ясности
2. ПРИВЕТ, мозг, я обновил свой пост для деталей. Основная проблема связана с получением несохраненных данных в поиске (PSSelector) для родственных объектов
3. Я обновил свой ответ вскоре после того, как вы обновили свой вопрос. Вы исправили Current<>, у которого не было ссылки на текущее поле? Пожалуйста, опубликуйте обновленный код, если вам нужна дополнительная помощь.
4. Привет, Брайан, это была опечатка. Да, pxselector имеет ссылку на текущий, но
5. Был тип0 — [PXSelector(typeof(Search Идентификатор задачи, где Идентификатор рабочего элемента, равный<текущему<идентификатору рабочего элемента>>>>), typeof(TAskDAC. TaskCD), SubstituteKey = typeof(TaskDAC. TaskCD ))] Да, сценарий отличается от обычного, когда на данные одной вкладки нужно ссылаться в другой вкладке.. Проблема, не удается получить несохраненные данные
Ответ №2:
Для ссылки на несохраненные данные используйте IsDirty =true в PXSelector. также для сохранения значения ключа идентификации несохраненных данных в указанном месте используйте [PXDBDefault()]