Acumatica. Customization using sender.SetValueExt - acumatica

I am trying to create customization on Shipment Screen in Acumatica ERP.
I create additional customization to copy the OpenOrderQty to ShippedQty when we select the order number.
Here my code:
public void SOShipLine_RowInserting(PXCache sender, PXRowInsertingEventArgs e, PXRowInserting baseMethod)
{
if (baseMethod != null)
baseMethod(sender, e);
SOShipLine line = e.Row as SOShipLine;
if (line == null) return; // just in case
if (line.ShippedQty == 0)
{
sender.SetValueExt<SOShipLine.ShippedQty>(line, line.OpenOrderQty);
}
}
But, when I publish the customization, there is an error:
\App_RuntimeCode\SOShipmentEntry.cs(85): error CS0118:
'PX.Objects.SO.SOShipLine.ShippedQty' is a 'property' but is used like
a 'type'
Is the problem with the sender.SetValueExt?

You can fix that error by using the 'shippedQty' variable instead of 'ShippedQty'. Notice first letter is different case, lowercase vs uppercase.
Uppercase variable is a C# property and is used when you want to get the value or set the value of the field:
line.ShippedQty = 1M;
var myQuantity = line.ShippedQty;
Lowercase variable is used when you refer to the field as a type. When you provide the generic type (T) of SetValueExt you are not setting or getting the value of T. You are telling SetValueExt that it should use that type for setting the value of the line.ShippedQty field:
SetValueExt<SOShipLine.shippedQty>
When expecting a type (lowercaste) it will often be referred to as a Field parameter in visual studio intellisense:

Related

How do I attach data to custom fields on INTranCost during release of POReceiptEntry?

I need to attach custom data into new fields added to INTranCost when the PO Receipt occurs.
Following the breadcrumbs, it seems that POReceiptEntry -> Release Action eventually calls INDocumentRelease.ReleaseDoc that eventually creates INTranCost. I tried extending both POReceiptEntry and INDocumentRelease to add an event for INTranCost_RowInserted to publish a PXTrace message, but the trace doesn't appear, telling me that I'm not hitting the event that I expected. (Which explains why the real business logic I need included didn't fire.)
protected virtual void _(Events.RowInserted<INTranCost> e)
{
PXTrace.WriteInformation("This is it!");
}
Of course, I want to put real code in this spot, but I am just trying to make sure I'm hitting the event properly. This works on pretty much everything else I've done, including attaching similar data to INTranExt fields. I cannot get it to work for INTranCost so that I can add to INTranCostExt. At this point, I can't determine if it is location (which graph extension) or a special methodology required for this special case.
I also tried overriding events and putting a breakpoint on the code, but it's like I'm not even on the same process. (Yes, I checked that I am connected to the right Acumatica instance and that I have no errors.)
What event in which graph is required to capture the creation in INTranCost for a PO Receipt to update custom fields in INTranCostExt?
Using Request Profiler, I was able to determine that I was close but not deep enough. While the INTranCost object to insert was built in INDocumentRelease FILE, the actual insert was processed in INReleaseProcess graph in that same file.
I only need to execute this "push" from the data captured on the POLine when the INTranCost record is created, and LineNbr is a key field and therefore never updated after it is set. I need to be sure that I have enough data to make the connection back, and the primary key links me back to the INTran easily. That subsequently gets back to the POReceiptLine to the POLine where the data is maintained that needs the "current value" to be captured when the transaction is posted. Since I need to update the DAC Extension, I need to use an event that will allow an existing DAC.Update to apply my values. Therefore, I added an event handler on INTranCost_LineNbr_FieldUpdated since that value should not be "updated" after it is set initially.
Code that accomplished the task:
public class INReleaseProcess_Extension : PXGraphExtension<INReleaseProcess>
{
public override void Initialize()
{
base.Initialize();
}
protected virtual void _(Events.FieldUpdated<INTranCost.lineNbr> e)
{
INTranCost row = (INTranCost) e.Row;
INTran tran = PXSelect<INTran,
Where<INTran.docType, Equal<Required<INTran.docType>>,
And<INTran.refNbr, Equal<Required<INTran.refNbr>>,
And<INTran.lineNbr, Equal<Required<INTran.lineNbr>>
>>>>
.SelectSingleBound(Base, null, row.DocType, row.RefNbr, (int?) e.NewValue);
if (tran?.POReceiptType != null && tran?.POReceiptNbr != null)
{
PXResultset<POReceiptLine> Results = PXSelectJoin<POReceiptLine,
InnerJoin<POLine, On<POLine.orderType, Equal<POReceiptLine.pOType>,
And<POLine.orderNbr, Equal<POReceiptLine.pONbr>,
And<POLine.lineNbr, Equal<POReceiptLine.pOLineNbr>>>>,
InnerJoin<POOrder, On<POOrder.orderType, Equal<POLine.orderType>,
And<POOrder.orderNbr, Equal<POLine.orderNbr>>>>>,
Where<POReceiptLine.receiptType, Equal<Required<POReceiptLine.receiptType>>,
And<POReceiptLine.receiptNbr, Equal<Required<POReceiptLine.receiptNbr>>,
And<POReceiptLine.lineNbr, Equal<Required<POReceiptLine.lineNbr>>>>>>.
SelectSingleBound(Base, null, tran.POReceiptType, tran.POReceiptNbr, tran.POReceiptLineNbr);
if (Results != null)
{
foreach (PXResult<POReceiptLine, POLine, POOrder> result in Results)
{
POReceiptLine receipt = result;
POLine line = result;
POOrder order = result;
POLineExt pOLineExt = PXCache<POLine>.GetExtension<POLineExt>(line);
INTranCostExt iNTranCostExt = PXCache<INTranCost>.GetExtension<INTranCostExt>(row);
if (pOLineExt != null && iNTranCostExt != null)
{
Base.Caches[typeof(INTranCost)].SetValueExt<INTranCostExt.usrField>(row, pOLineExt.UsrField);
}
}
}
}
}
}

PXSubordinateSelector Using the Where

I need information on how to use the PXSubordinateSelector attribute with the Where type that you can allegedly set on the attribute. Does anybody know how to use this attribute?
Specifically, I need to filter the selector by a custom field in the EPCompanyTree table if possible. Not sure what tables this selector attribute usese. It seems to be tucked into the Acumatica black box. Something like this:
[PXSubordinateSelector(typeof(Where<EPCompanyTree.customField, Equal<{somevalue}>>))]
I've tried setting the Where to an arbitrary filter on the EPCompanyTree.sortorder field but, I'm getting an "is not bound" error when clicking on the lookup.
TIA!
The reason for this error is the defined Search in the GetCommand method of the PXSubordinateSelectorAttribute. Below is the disassembled code of that method:
private static Type GetCommand(Type where)
{
Type whereType = typeof(Where<CREmployee.userID, Equal<Current<AccessInfo.userID>>, Or<EPCompanyTreeMember.workGroupID, Owned<Current<AccessInfo.userID>>>>);
if (where != null)
{
whereType = BqlCommand.Compose(new Type[]
{
typeof(Where2<, >),
typeof(Where<CREmployee.userID, Equal<Current<AccessInfo.userID>>, Or<EPCompanyTreeMember.workGroupID, Owned<Current<AccessInfo.userID>>>>),
typeof(And<>),
where
});
}
return BqlCommand.Compose(new Type[]
{
typeof(Search5<, , , >),
typeof(CREmployee.bAccountID),
typeof(LeftJoin<EPCompanyTreeMember, On<EPCompanyTreeMember.userID, Equal<CREmployee.userID>>>),
whereType,
typeof(Aggregate<GroupBy<CREmployee.acctCD>>)
});
}
As you can see from code the Search is being organized on CREmployee with left joined EPCompanyTreeMember, meanwhile, your code is trying to add a condition on EPCompanyTree field which is not participating in the Search.

How to restrict/write a validation code just for impex?

I want to know if it is a way to trigger my Validation Code just for impex. What I mean is that my Code should validate a new Product created through Impex (not through backoffice). Here is my Code:
#Override
public void onValidate(final Object o, final InterceptorContext ctx) throws InterceptorException
if (o instanceof ProductModel)
{
final ProductModel product = (ProductModel) o;
if (ctx.isNew(product))
{
final String manufacturerName = enumerationService.getEnumerationName(product.getManufacturerName()); // if ManufacturerName is Null enumerationService throw "Parameter plainEnum must not be null!"
final String code = product.getCode().toString();
final String manufacturerProductId = product.getManufacturerProductId();
final int a = productLookupService.getProduct(code, manufacturerName, manufacturerProductId);
switch (a)
{
case 1:
throw new InterceptorException("Product already in ProductLookup");
case 2:
throw new InterceptorException(
"There are more than one product with the same " + code + " number in ProductLookup !");
default:
}
}
}
My Problem is that in BackOffice when I create a new Product I don´t have manufacturerName and manufacturerProductId fields.
Thanks! and sorry if I said something wrong, I am new in this :)
You said that "In BackOffice when i create a new Product i don´t have manufacturerName and manufacturerProductId fields".
This can also be the case for Impex. Most probably the impex you are using is specifying those two attributes now and that is why there is no problem.
If you want, you can make this two attributes mandatory and then nobody will be able to create a product without specifying the manufacturerName ad the manufacturerProductId.
I also believe that the backOffice will update to also include these two attributes during creation since they are mandatory.
You can specify that an attribute is mandatory in your {extensionName}-items.xml(under your type definition) using the optional flag like below:
<attribute qualifier="manufacturerProductId" type="String">
<persistence type="property"/>
<modifiers optional="false"/>
</attribute>
If these two attributes are not mandatory, you have to consider the case when a product will be created without having them(like your current backOffice situation).
However your Interceptor should take into consideration both cases (when you have these attributes specified during creation and when you don't)
Because of this you can edit your code to verify whether this two attributes are null or not before using them. You can add this right before trying to get the manufacturerName:
if (product.getManufacturerName() == null || product.getManufacturerProductId() == null) {
return;
}

Welding a ContentPart having a ContentField

I'm trying to Weld my custom ContentPart SitesPart containing a ContentField of type TaxonomyField but it is not working for me. When i attach this part from UI it works perfectly fine and i see the TaxonomyField in edit mode as well as in display mode.
Following is the Activating method of my ContentHandler.
protected override void Activating(ActivatingContentContext context)
{
if (context.ContentType == "Page")
{
context.Builder.Weld<SitesPart>();
}
}
I tried to go deep into the Weld function and found out that it is not able to find correct typePartDefinition. It goes inside the condition if (typePartDefinition == null) which creates an empty typePartDefinition with no existing ContentFields etc.
// obtain the type definition for the part
var typePartDefinition = _definition.Parts.FirstOrDefault(p => p.PartDefinition.Name == partName);
if (typePartDefinition == null) {
// If the content item's type definition does not define the part; use an empty type definition.
typePartDefinition =
new ContentTypePartDefinition(
new ContentPartDefinition(partName),
new SettingsDictionary());
}
I would be highly thankful for any guidance.
Oh, you are totally right, the part is welded but if there are some content fields, they are not welded. The ContentItemBuilder try to retrieve the part definition through the content type definition on which we want to add the part. So, because it's not possible, a new content part is created but with an empty collection of ContentPartFieldDefinition...
I think that the ContentItemBuilder would need to inject in its constructor and use a ContentPartDefinition or more generally an IContentDefinitionManager... But, for a quick workaround I've tried the following that works
In ContentItemBuilder.cs, replace this
public ContentItemBuilder Weld<TPart>()...
With
public ContentItemBuilder Weld<TPart>(ContentPartDefinition contentPartDefinition = null)...
And this
new ContentPartDefinition(partName),
With
contentPartDefinition ?? new ContentPartDefinition(partName),
And in you part handler, inject an IContentDefinitionManager and use this
protected override void Activating(ActivatingContentContext context) {
if (context.ContentType == "TypeTest") {
var contentPartDefinition = _contentDefinitionManager.GetPartDefinition(typeof(FruitPart).Name);
context.Builder.Weld<FruitPart>(contentPartDefinition);
}
}
Best
To attach a content part to a content type on the fly, you can use this in your handler
Filters.Add(new ActivatingFilter<YourContentPart>("YourContentType"));
There are many examples in the source code
Best

CRM2011 How to get object type code by entity name in Javascript?

I have a lookup field in the form, before select the related entity for the lookup, I check some conditions, if not pass, I overwrite the lookup onclick event to alert user; else, I need to overwrite the onclick event to show the lookup window to allow user to select entity.
So I need the object type code of this lookup, but before select there is no value then I can't get the object type code by use this code: var objecttypecode = Xrm.Page.getAttribute("field id").getValue()[0].type;
How to get object type code by entity name?
I find out the way:
function GetObjectTypeCode(entityName) {
try {
var lookupService = new RemoteCommand("LookupService", "RetrieveTypeCode");
lookupService.SetParameter("entityName", entityName);
var result = lookupService.Execute();
if (result.Success && typeof result.ReturnValue == "number") {
return result.ReturnValue;
}
else {
return null;
}
}
catch (ex) {
throw ex;
}
}
One of the supported way of doing this is using the Metadata service and then retrieving the Object Type Code or etc (entity Type Code).

Resources