Adding search function to the AlternateID on the Sales Order Line Grid - acumatica

I want to add a pxselector to the AlternateID field on the Sales Order Line grid to search multiple alternate id's for a single item for the customer identified on the SOHeader. I added the following code:
namespace PX.Objects.SO {
public class SOOrderEntry_Extension:PXGraphExtension<SOOrderEntry> {
#region Event Handlers
[PXMergeAttributes(Method = MergeMethod.Merge)]
[PXSelector(typeof(Search<INItemXRef.alternateID,
Where<INItemXRef.inventoryID, Equal<Current<SOLine.inventoryID>>,
And<INItemXRef.bAccountID, Equal<Current<SOOrder.customerID>>>>>),
typeof(INItemXRef.alternateID),
typeof(INItemXRef.inventoryID),
typeof(INItemXRef.bAccountID)
)]
public void SOLine_AlternateID_CacheAttributeCacheAttached() {}
#endregion
}
}
I also deleted the text control from the Transactions grid and re-added it as a selector.
My selector shows up on the AlternateID field on as expected but when the selector is clicked the error "Error #107: View doesn't exist" is displayed.
This was an example Ruslan from Acumatica went over with us at Framework training last week in Ohio, but I obviously missed a step. Any help would be appreciated.

George, you put a wrong name for your event handler and that causes the error. Try replacing your handler with the code snippet below:
[PXMergeAttributes(Method = MergeMethod.Merge)]
[PXSelector(typeof(Search<INItemXRef.alternateID>),
typeof(INItemXRef.alternateID),
typeof(INItemXRef.inventoryID),
typeof(INItemXRef.bAccountID))]
public void SOLine_AlternateID_CacheAttached(PXCache sender)
{
}

Related

Add an Address to the Branch

I am trying to add another address to a Branch in 21.203, similar to the how the DefAddress works. I have extended the Branch DAC and added the field to the extension. I also tried extending the BAccount DAC and table to get this to work but, nothing is working so far. The Address record is being created in the Address table but, the PXDBChildIdentity attribute doesn't seem to be sending back the ID to the Address record. On the screen, when I enter the values for the address, and click Save, all of the values are wiped out and the custom field (UsrCustomAddress) is never populated.
#region UsrCustomAddress
[PXDBInt()]
[PXUIField(DisplayName="Custom Address")]
[PXDBChildIdentity(typeof(Address.addressID))]
public virtual Int32? UsrCustomAddress { get; set; }
public abstract class usrCustomAddress : PX.Data.BQL.BqlInt.Field<usrCustomAddress> { }
#endregion
And, then, I added the View to the BranchMaint extension.
<code>
namespace PX.Objects.CS
{
public class BranchMaint_Extension : PXGraphExtension<BranchMaint>
{
public PXSelect<Address, Where<Address.bAccountID, Equal<Current<BAccount.bAccountID>>,
And<Address.addressID, Equal<Current<BAccountExt.usrCustomAddress>>>>> CustomAddress;
}
}
</code>
Not sure what I'm missing?
TIA!
I handled this by manually calling .Insert() on the specific Data View in the row inserting event and writing it to the field. PXDBChildIdentity then can update the field with the real identity value (on row persist) after the link is established.
protected void _(Events.RowInserting<Branch> e)
{
if (e.Row is null) return;
Address newAddress = CustomAddress.Insert();
BranchExt ext = e.Cache.GetExtension<BranchExt >(e.Row);
ext.UsrCustomAddress = newAddress.AddressID;
}
If anyone knows how to do this in an attribute id love to know. Note, this will only work for newly created records. Any existing records would not have a row inserting event fire and fill your custom field. You need to backfill the data with SQL or just a temporary custom action that you can invoke this code manually.

Acumatica Customize Process Shipments Add branch field from PO Receipts

I'm trying to figure out if it's possible to add fields to the grid on the Process Shipments screen for PO Receipts when the "Prepare Drop-Ship Invoice" action is selected. Specifically, I want to add the POReceipts.BranchID field.
I've studied the source code, and I see where different select commands are run based on the selected action, but I don't understand what drives the grid to show the different set of columns.
I looked in the screen designer in the customization editor, and I don't see a different view or anything, hopefully I'm just missing something simple.
Thanks
Scott
This is how I solved your mystery. First create a user field for SOShipment.
#region UsrReceiptBranchID
[PXInt]
[PXUIField(DisplayName="Receipt Branch ID")]
[GL.Branch()]
public virtual int? UsrReceiptBranchID { get; set; }
public abstract class usrReceiptBranchID : PX.Data.BQL.BqlInt.Field<usrReceiptBranchID> { }
#endregion
Next is the key part. I extended the data view delegate, and added an extra query to pull the POReceipt branch, when the Filter is Drop-Ship.
public virtual IEnumerable orders()
{
List<SOShipment> records = Base.orders().RowCast<SOShipment>().ToList();
if (Base.Filter.Current.Action.Contains("Drop"))
{
foreach (SOShipment sOShipment in records)
{
POReceipt receipt = PXSelect<POReceipt,
Where<POReceipt.receiptNbr,
Equal<Required<POReceipt.receiptNbr>>>>.Select(Base, sOShipment.ShipmentNbr);
if (receipt != null)
{
SOShipmentExt shipExt = PXCache<SOShipment>.GetExtension<SOShipmentExt>(sOShipment);
shipExt.UsrReceiptBranchID = receipt.BranchID;
}
}
return records;
}
else
{
return records;
}
}

Acumatica Warning Message To Only Affect Shipment Screen SO302000

I'm trying to display a warning every time the ShippedQty field is changed to a value < OrigOrderQty on the "Shipment - SO302000" screen, but I only want the code to be active for that specific screen/form.
I added the code below to extend the SOShipmentEntry graph, which accomplishes my original goal, but the issue is that now the code I added is also being used by the "Create Shipment" action in "Sales Orders - SO301000" screen/form.
Create Shipment Action Discussed
namespace PX.Objects.SO
{
public class SOShipmentEntry_Extension : PXGraphExtension<SOShipmentEntry>
{
#region Event Handlers
protected void SOShipLine_ShippedQty_FieldUpdated(PXCache cache,PXFieldUpdatedEventArgs e)
{
var myrow = (SOShipLine)e.Row;
if (myrow == null) return;
if (myrow.ShippedQty >= myrow.OrigOrderQty)
{
}
else
{
throw new PXSetPropertyException("The difference between the shipped-qty and the ordered-qty will be placed on a back-order", PXErrorLevel.Warning);
}
}
#endregion
}
}
While the warning allows the user to save changes to a shipment on the Shipment Screen/form SO302000 (Because the exception is set up as a warning and not an error), I get the following error when I create a shipment using the "Create Shipment" button on the "Sales Orders - SO301000" screen.
Warning works fine for form-mode
Warning becomes error when processed in background by action button
Any ideas to accomplish this? It is my understanding that if I want to make global changes to a field I must make them in the DAC, but if I want to make changes that only affect screens/forms where a graph is used, then I have to make those changes in the graph code itself. I'm guessing the "Create Shipment" action button in the Sales Orders screen is creating an instance of the graph where I added the code, so I'm wondering what are my options here.
Best regards,
-An Acumatica newbie
If you want your event logic to execute only when CreateShipment is invoked from the Shipment screen you can override the other calls to CreateShipment to dynamically remove your event handler.
The event that invokes CreateShipment action from the SalesOrderEntry graph is Action:
public PXAction<SOOrder> action;
[PXUIField(DisplayName = "Actions", MapEnableRights = PXCacheRights.Select)]
[PXButton]
protected virtual IEnumerable Action(PXAdapter adapter,
[PXInt]
[PXIntList(new int[] { 1, 2, 3, 4, 5 }, new string[] { "Create Shipment", "Apply Assignment Rules", "Create Invoice", "Post Invoice to IN", "Create Purchase Order" })]
int? actionID,
[PXDate]
DateTime? shipDate,
[PXSelector(typeof(INSite.siteCD))]
string siteCD,
[SOOperation.List]
string operation,
[PXString()]
string ActionName
)
In that method it creates a SOShipmentEntry graph to create the shipment. You can override Action and remove your handler from that graph instance:
SOShipmentEntry docgraph = PXGraph.CreateInstance<SOShipmentEntry>();
// >> Remove event handler
SOShipmentEntry_Extension docgraphExt = docgraph.GetExtension<SOShipmentEntry_Extension>();
docgraph.FieldUpdated.RemoveHandler<SOShipLine.shippedQuantity>(docgrapExt.SOShipLine_ShippedQty_FieldUpdated);
// << Remove event handler
docgraph.CreateShipment(order, SiteID, filter.ShipDate, adapter.MassProcess, operation, created);
Note that in order to reference SOShipLine_ShippedQty_FieldUpdated method from another graph you'll have to make it public:
public void SOShipLine_ShippedQty_FieldUpdated(PXCache cache,PXFieldUpdatedEventArgs e)
I have described how to do this in that answer too:
Updating custom field is ending into infinite loop
If you want your event logic to execute only when it is modified in the UI or by web service.
You can use the ExternalCall Boolean property of the PXFieldUpdatedEventArgs parameter.
This property value will be true only when the sender field is modified in the UI or by web service.
Usage example:
protected void SOShipLine_ShippedQty_FieldUpdated(PXCache cache,PXFieldUpdatedEventArgs e)
{
// If ShippedQty was updated in the UI or by a web service call
if (e.ExternalCall)
{
// The logic here won't be executed when CreateShipment is invoked
}
}
ExternalCall Property (PXFieldUpdatedEventArgs)
Gets the value specifying whether the new value of the current DAC field has been changed in the UI or through the Web Service API.

Marking when a SO is printed - Acumatica

I'm interested in hooking into the print report action on a Sales Order to mark the SO "Traveler Printed" when someone has printed that particular report. Suggestions for how to accomplish this? I know it's done on the PO but I'm struggling to parse out the where and how of it.
In Customization Project Editor Code section, create a graph extension for SOOrderEntry.
Customization Project Editor has an Override Method feature that is handy for generating the event handler prototype:
You can then edit the generated stub definition like this:
namespace PX.Objects.SO
{
public class SOOrderEntry_Extension:PXGraphExtension<SOOrderEntry>
{
#region Event Handlers
public delegate IEnumerable ReportDelegate(PXAdapter adapter, String reportID);
[PXOverride]
public IEnumerable Report(PXAdapter adapter, String reportID, ReportDelegate baseMethod)
{
if (reportID == "SO641010")
{
PXTrace.WriteInformation("I'm doing my things here, after report action has been invoked, just before report is actually launched.");
}
return baseMethod(adapter,reportID);
}
#endregion
}
}

Unable to create Sales Order via API with items from Inactive Vendor

When attempting to create a Sales Order via an API call, if the Vendor marked as Default for any item on the order has status Inactive an error is returned with the message: "PX.Data.PXException: The vendor status is 'Inactive'"
However, when creating the Sales Order through the standard Screen there is no issue with ordering items with Inactive Default Vendors.
We want to keep the Vendors marked as Inactive but need to create the Sales Orders for the items like the Screen allows. How can this be done?
I'm assuming the error is coming from SOLine.VendorID from Brendan comment.
Here are the steps to debug that problem and fix it using FieldVerifying event.
Add SOLine.VendorID field on SalesOrder screen:
Reproduce the error in SalesOrder using the field you added:
Check trace error, it indicates the error is coming from a PXRestrictor attribute:
Check VendorID field, it has a PXRestrictor and the VendorIsInStatus error message you receive:
PXRestrictor attribute validation can be cancelled using the FieldVerifying event, add that handler to SalesOrder for SOLine.VendorID:
public class SOOrderEntry_Extension:PXGraphExtension<SOOrderEntry>
{
protected void SOLine_VendorID_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
{
e.Cancel = true;
}
}
Test SalesOrder again, if it works you can remove the VendorID/VendorName you added to the grid to debug:
From Brendan's comment I went with modifying the PXRestrictor attribute of the SOLine.VendorID to allow Inactive status as well:
public class SOLineExt : PXCacheExtension<PX.Objects.SO.SOLine> {
[PXMergeAttributes(Method = MergeMethod.Merge)]
[PXRestrictor(typeof(Where<Vendor.status, IsNull,
Or<Vendor.status, Equal<BAccount.status.active>,
Or<Vendor.status, Equal<BAccount.status.inactive>,
Or<Vendor.status, Equal<BAccount.status.oneTime>>>>>), PX.Objects.AP.Messages.VendorIsInStatus, typeof(Vendor.status))]
public virtual Int32? VendorID { get; set; }
}

Resources