在客户屏幕中,我根据客户类
配置了一些属性我希望在为此客户筹集资金时将这些属性复制到销售订单中。示例屏幕如下:
销售订单属性网格将是只读的。 我知道如何添加标签项和网格。但我不确定如何为销售订单配置“属性”属性字段。我假设我可以依靠客户的“属性”定义?
我刚刚做了:
public class SOOrderExt : PXCacheExtension<PX.Objects.SO.SOOrder>
{
#region Attributes
[CRAttributesField(typeof (Customer.customerClassID))]
public virtual string[] Attributes { get; set; }
#endregion
}
答案视图:
namespace PX.Objects.SO
{
public class SOOrderEntry_Extension:PXGraphExtension<SOOrderEntry>
{
[PXViewName("Answers")]
public CRAttributeList<Customer>Answers;
}
}
显示客户的属性......太棒了!但是他们需要根据销售订单进行保存。如果在线下,则客户的属性已更改。首次提出订单时,销售订单应复制原始客户的属性。那我该怎么做?谢谢!
答案 0 :(得分:1)
问题是属性的引用是链接到客户而不是您需要的顺序(保存属性)。为此,我们需要在CRAttributeList
类中编写自己的查询/关联调用。我创建了以下继承类,并通过将订单ref noteid与保存的答案相关联,使得属性能够坚持订单。 CRAttributeList
类没有很好地覆盖,因此有很多复制的代码。您可以浏览源代码以查看完整的类并根据需要更新任何内容。我认为这可以简化,但现在它是一个有效的答案。
按照您的方式保持订单dac延期...
public class SOOrderExt : PXCacheExtension<PX.Objects.SO.SOOrder>
{
[CRAttributesField(typeof (Customer.customerClassID))]
public virtual string[] Attributes { get; set; }
}
替换视图以使用新类......
[PXViewName("Answers")]
public SalesCustomerAttributeList Answers;
新课程...... (更改SelectDelegate&amp; base SelectInternal)
public class SalesCustomerAttributeList : CRAttributeList<Customer>
{
public SalesCustomerAttributeList(PXGraph graph) : base(graph)
{
}
//Copy of private method from CRAttributeList
protected string GetClassId(object row)
{
var classIdField = GetClassIdField(row);
if (classIdField == null)
return null;
var entityCache = _Graph.Caches[row.GetType()];
var classIdValue = entityCache.GetValueExt(row, classIdField.Name);
return classIdValue?.ToString()?.Trim();
}
//Copy of private method from CRAttributeList
protected Type GetClassIdField(object row)
{
if (row == null)
return null;
var fieldAttribute =
_Graph.Caches[row.GetType()].GetAttributes(row, null)
.OfType<CRAttributesFieldAttribute>()
.FirstOrDefault();
if (fieldAttribute == null)
return null;
return fieldAttribute.ClassIdField;
}
//Copy of private method from CRAttributeList
protected Type GetEntityTypeFromAttribute(object row)
{
var classIdField = GetClassIdField(row);
if (classIdField == null)
return null;
return classIdField.DeclaringType;
}
//Override to use desired query for sales order and customer/customer class related attributes
protected override IEnumerable SelectDelegate()
{
return this.SelectInternal(
(Customer)_Graph.Caches<Customer>()?.Current,
(SOOrder)_Graph.Caches<SOOrder>()?.Current);
}
/// <summary>
/// Find the customer default value based on the given answer
/// </summary>
protected bool TryGetCustomerAttributeValue(CSAnswers classAnswer, List<CSAnswers> customerAnswers, out string customerDefault)
{
customerDefault = null;
if (classAnswer == null || customerAnswers == null)
{
return false;
}
foreach (var customerAttribute in customerAnswers)
{
if (customerAttribute.AttributeID != classAnswer.AttributeID)
{
continue;
}
customerDefault = customerAttribute.Value;
return true;
}
return false;
}
protected List<CSAnswers> GetCustomerAttributes(Customer customerRow)
{
return PXSelect<CSAnswers, Where<CSAnswers.refNoteID, Equal<Required<CSAnswers.refNoteID>>>>
.Select(_Graph, customerRow.NoteID).FirstTableItems.ToList();
}
//Override to use desired query for sales order and customer/customer class related attributes
protected IEnumerable<CSAnswers> SelectInternal(Customer customerRow, SOOrder orderRow)
{
if (orderRow == null || customerRow == null)
{
yield break;
}
var noteId = GetNoteId(orderRow);
if (!noteId.HasValue)
yield break;
var answerCache = _Graph.Caches[typeof(CSAnswers)];
var orderCache = _Graph.Caches[orderRow.GetType()];
List<CSAnswers> answerList;
var status = orderCache.GetStatus(orderRow);
if (status == PXEntryStatus.Inserted || status == PXEntryStatus.InsertedDeleted)
{
answerList = answerCache.Inserted.Cast<CSAnswers>().Where(x => x.RefNoteID == noteId).ToList();
}
else
{
answerList = PXSelect<CSAnswers, Where<CSAnswers.refNoteID, Equal<Required<CSAnswers.refNoteID>>>>
.Select(_Graph, noteId).FirstTableItems.ToList();
}
var classId = GetClassId(customerRow);
CRAttribute.ClassAttributeList classAttributeList = new CRAttribute.ClassAttributeList();
if (classId != null)
{
classAttributeList = CRAttribute.EntityAttributes(GetEntityTypeFromAttribute(customerRow), classId);
}
//when coming from Import scenarios there might be attributes which don't belong to entity's current attribute class or the entity might not have any attribute class at all
if (_Graph.IsImport && PXView.SortColumns.Any() && PXView.Searches.Any())
{
var columnIndex = Array.FindIndex(PXView.SortColumns,
x => x.Equals(typeof(CSAnswers.attributeID).Name, StringComparison.OrdinalIgnoreCase));
if (columnIndex >= 0 && columnIndex < PXView.Searches.Length)
{
var searchValue = PXView.Searches[columnIndex];
if (searchValue != null)
{
//searchValue can be either AttributeId or Description
var attributeDefinition = CRAttribute.Attributes[searchValue.ToString()] ??
CRAttribute.AttributesByDescr[searchValue.ToString()];
if (attributeDefinition == null)
{
throw new PXSetPropertyException(PX.Objects.CR.Messages.AttributeNotValid);
}
//avoid duplicates
if (classAttributeList[attributeDefinition.ToString()] == null)
{
classAttributeList.Add(new CRAttribute.AttributeExt(attributeDefinition, null, false, true));
}
}
}
}
if (answerList.Count == 0 && classAttributeList.Count == 0)
{
yield break;
}
//attribute identifiers that are contained in CSAnswers cache/table but not in class attribute list
List<string> attributeIdListAnswers =
answerList.Select(x => x.AttributeID)
.Except(classAttributeList.Select(x => x.ID))
.Distinct()
.ToList();
//attribute identifiers that are contained in class attribute list but not in CSAnswers cache/table
List<string> attributeIdListClass =
classAttributeList.Select(x => x.ID)
.Except(answerList.Select(x => x.AttributeID))
.ToList();
//attribute identifiers which belong to both lists
List<string> attributeIdListIntersection =
classAttributeList.Select(x => x.ID)
.Intersect(answerList.Select(x => x.AttributeID))
.Distinct()
.ToList();
var cacheIsDirty = answerCache.IsDirty;
List<CSAnswers> output = new List<CSAnswers>();
//attributes contained only in CSAnswers cache/table should be added "as is"
output.AddRange(answerList.Where(x => attributeIdListAnswers.Contains(x.AttributeID)));
var customerAnswers = GetCustomerAttributes(customerRow);
//attributes contained only in class attribute list should be created and initialized with default value
foreach (var attributeId in attributeIdListClass)
{
var classAttributeDefinition = classAttributeList[attributeId];
if (PXSiteMap.IsPortal && classAttributeDefinition.IsInternal)
continue;
if (!classAttributeDefinition.IsActive)
continue;
CSAnswers answer = (CSAnswers)answerCache.CreateInstance();
answer.AttributeID = classAttributeDefinition.ID;
answer.RefNoteID = noteId;
answer.Value = GetDefaultAnswerValue(classAttributeDefinition);
if (TryGetCustomerAttributeValue(answer, customerAnswers, out var customerValue))
{
answer.Value = customerValue;
}
if (classAttributeDefinition.ControlType == CSAttribute.CheckBox)
{
bool value;
if (bool.TryParse(answer.Value, out value))
answer.Value = Convert.ToInt32(value).ToString(CultureInfo.InvariantCulture);
else if (answer.Value == null)
answer.Value = 0.ToString();
}
answer.IsRequired = classAttributeDefinition.Required;
answer = (CSAnswers)(answerCache.Insert(answer) ?? answerCache.Locate(answer));
output.Add(answer);
}
//attributes belonging to both lists should be selected from CSAnswers cache/table with and additional IsRequired check against class definition
foreach (CSAnswers answer in answerList.Where(x => attributeIdListIntersection.Contains(x.AttributeID)).ToList())
{
var classAttributeDefinition = classAttributeList[answer.AttributeID];
if (PXSiteMap.IsPortal && classAttributeDefinition.IsInternal)
continue;
if (!classAttributeDefinition.IsActive)
continue;
if (answer.Value == null && classAttributeDefinition.ControlType == CSAttribute.CheckBox)
answer.Value = bool.FalseString;
if (answer.IsRequired == null || classAttributeDefinition.Required != answer.IsRequired)
{
answer.IsRequired = classAttributeDefinition.Required;
var fieldState = View.Cache.GetValueExt<CSAnswers.isRequired>(answer) as PXFieldState;
var fieldValue = fieldState != null && ((bool?)fieldState.Value).GetValueOrDefault();
answer.IsRequired = classAttributeDefinition.Required || fieldValue;
}
output.Add(answer);
}
answerCache.IsDirty = cacheIsDirty;
output =
output.OrderBy(
x =>
classAttributeList.Contains(x.AttributeID)
? classAttributeList.IndexOf(x.AttributeID)
: (x.Order ?? 0))
.ThenBy(x => x.AttributeID)
.ToList();
short attributeOrder = 0;
foreach (CSAnswers answer in output)
{
answer.Order = attributeOrder++;
yield return answer;
}
}
}
有关“属性”选项卡的页面条目的示例,您可以查看“客户”页面 - “属性”选项卡。我复制了这个标签,用于测试这个答案。