Acumatica Customize Process Shipments Add branch field from PO Receipts - acumatica

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;
}
}

Related

Difference in behavior between Automation Schedule and Process All action on Processing page

I have customized the GLTran DAC to have a custom field for Tax Zone and it gets populated along with Tax Category and Tax ID if the "Create Tax Transactions" check box is selected. I have also customized the "Release" to populate this Tax Zone over to the TaxTran table.
The Journal Transactions are created by a custom processing page that reads an XML file and create the Journal Transaction document and also releases it.
Everything works fine in my local instance and also on our sandbox instance.
In production, when I process the XML files using the Processing page, everything is fine. But when I create Automation Schedule, the Journal Transactions are created with Tax Zone populated and the document is released without any error. However the Tax Zone is not populated in the TaxTran table!
I don't have the same problem with Automation Schedule in my local instance or Sandbox. All these instances are of same version of Acumatica.
public class GLTranExt : PXCacheExtension<PX.Objects.GL.GLTran>
{
#region UsrTaxZoneID
[PXDBString(10)]
[PXSelector(typeof(Search<TaxZone.taxZoneID>),
typeof(TaxZone.taxZoneID),
typeof(TaxZone.descr))]
[PXUIField(DisplayName = "Tax Zone")]
public virtual string UsrTaxZoneID { get; set; }
public abstract class usrTaxZoneID : IBqlField { }
#endregion
}
public class PostGraph_Extension : PXGraphExtension<PostGraph>
{
public PXSelect<GLTran, Where<GLTran.batchNbr, Equal<Required<GLTran.batchNbr>>, And<GLTran.accountID, Equal<Required<GLTran.accountID>>, And<GLTran.subID, Equal<Required<GLTran.subID>>, And<GLTran.branchID, Equal<Required<GLTran.branchID>>>>>>> GLTransactions;
public delegate void ReleaseBatchProcDelegate(Batch b, bool unholdBatch = false);
[PXOverride]
public void ReleaseBatchProc(Batch b, bool unholdBatch, ReleaseBatchProcDelegate baseMethod)
{
baseMethod(b, unholdBatch = false);
foreach(TaxTran rec in Base.GL_GLTran_Taxes.Select(b.Module, b.BatchNbr))
{
GLTran glTran = GLTransactions.Select(rec.RefNbr, rec.AccountID, rec.SubID, rec.BranchID);
GLTranExt glTranExt = PXCache<GLTran>.GetExtension<GLTranExt>(glTran);
if (glTranExt != null)
rec.TaxZoneID = glTranExt.UsrTaxZoneID;
Base.GL_GLTran_Taxes.Update(rec);
Base.GL_GLTran_Taxes.Cache.Persist(PXDBOperation.Update);
}
}
}
I had the same problem, and in my case it was difference in permissions between account from which Automation Schedule was executed, and processing screen was executed. In order to fix it, I've added those lines:
using (new PXImpersonationContext(PX.Data.Update.PXInstanceHelper.ScopeUser))
{
using (new PXReadBranchRestrictedScope())
{
}
}
first using makes running your code from user name admin, and second using makes your code running with permissions access to necessary branches

How to activate the filtering action for a user field added to a grid

I have a customization to the Release Time Activities screen (EP507020) where I add a user field. This user field will contain the result of fetching the Appointment status from the Appointments screen (FS300200) based on the Appointment ID that I've also added to the Release Time Activities screen grid.
This was done so that the process grid could be filtered for appointment status that were a certain value. The Appointment Status User field I've added contains the same attributes that the Status field contains on the Appointments screen, with the Cache extension looking as follows:
#region UsrApptStatus
public abstract class usrApptStatus : IBqlField
{
}
[PXDBString(1)]
[FSAppointment.status.ListAtrribute]
[PXUIField(DisplayName = "Appt Status",Enabled = false)]
public virtual string UsrApptStatus { get; set; }
#endregion
This works fine when I fetch the status as follows in a Graph extension:
protected virtual void EPActivityApprove_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
var epactivityapprove = (EPActivityApprove)e.Row;
if (epactivityapprove != null)
{
FSxPMTimeActivity rowExt = epactivityapprove.GetExtension<FSxPMTimeActivity>();
if (rowExt != null)
{
var appointmentID = rowExt.AppointmentID;
var fsappt = (FSAppointment)PXSelect<FSAppointment,
Where<FSAppointment.appointmentID, Equal<Required<FSAppointment.appointmentID>>>>.Select(Base, appointmentID);
var epactivityapproveext = PXCache<EPActivityApprove>.GetExtension<EPActivityApproveExt>(epactivityapprove);
epactivityapproveext.UsrApptStatus = fsappt.Status;
}
}
}
The problem is that when I go to filter the Status column, no matter what I choose from the list of options, it clears all rows. I have no idea why this wouldn't work, but I'm sure I'm missing something.
Grid before filtering:
Filter:
Grid after filter:
It seems to me that changing the value in the RowUpdated event is interfering with the filter. If you wrote both extensions, it would help to have both custom fields in the same extension and use the PXFormula attribute to set the value of your status based on the AppointmentID field. This way you wouldn't have to rely on the event:
#region UsrApptStatus
public abstract class usrApptStatus : IBqlField
{
}
[PXDBString(1)]
[FSAppointment.status.ListAtrribute]
[PXFormula(typeof(Selector<appointmentID, FSAppointment.status>))]
[PXUIField(DisplayName = "Appt Status",Enabled = false)]
public virtual string UsrApptStatus { get; set; }
#endregion

Loading popup value to InventoryID field

with code from Load value from popup to InventoryID field question am able to set the InventoyID from popup to the InventoryID field but the default behavior of Acumatica is missing, like after InventoryID is set from poup, am selecting Item Class then a pop up is firing and asking for confirmation, generally this happen only when we update Item Class for already created Item.
[PXButton]
[PXUIField(DisplayName = "Generate")]
public IEnumerable GenerateInv(PXAdapter adapter)
{
string inventoryCD = "ACUMATICA";
InventoryItem item = PXCache<InventoryItem>.CreateCopy(Base.Item.Current);
OrderedDictionary keys = new OrderedDictionary(StringComparer.OrdinalIgnoreCase)
{
{
typeof(InventoryItem.inventoryCD).Name,
adapter.View.Cache.GetValue(adapter.View.Cache.Current,
typeof(InventoryItem.inventoryCD).Name)
}
};
OrderedDictionary vals = new OrderedDictionary(StringComparer.OrdinalIgnoreCase)
{
{
typeof(InventoryItem.inventoryCD).Name, inventoryCD
}
};
adapter.View.Cache.Update(keys, vals);
if (adapter.Searches != null && adapter.Searches.Length > 0)
{
adapter.Searches[0] = inventoryCD;
}
return adapter.Get();
}
You can try to skip the message window by overriding the Field Verifying on the stock item page for InventoryItem.ItemClassID. You should use some condition to indicate you are running your customer process so the standard message will appear for normal use on the stock items page.
public virtual void InventoryItem_ItemClassID_FieldVerifying(PXCache cache, PXFieldVerifyingEventArgs e, PXFieldVerifying del)
{
try
{
del?.Invoke(cache, e);
}
catch (PXDialogRequiredException)
{
var someSkipMessageWindowCondition = true;
if (someSkipMessageWindowCondition)
{
return;
}
throw;
}
}
Because of the use of the private property doResetDefaultsOnItemClassChange in the Base graph, the process will function as if the message box was answered YES to default the field values from the new item class. If you do not want the values to change using the new class you will need to cancel the field defaultings for the fields found in InventoryItem_ItemClassID_FieldUpdated in the Base graph. This will point out which fields are being updated when doResetDefaultsOnItemClassChange == true.
A better answer to your question would be automatically answering NO which I am unsure how to do. Would be nice to know how do perform that function if someone else knows how to make that call.

How to display product availability in Opportunity Products Grid footer?

In Sales Order documents grid footer. It displays the product's availability.
How to do the same in Opportunity products grid ?
More so, how do you enforce it to be displayed at the footer
instead of a simple grid column ? Is there such attribute ?
Thanks for the replies.
If we compare to sales order, the sales order line gets its value from LSSOLine during Availabilty_FieldSelecting. The wire-up on the page is on the tab via StatusField="Availability". We can do something similar by adding an unbound extension field and then during a field selecting fill in the value. An alternative would be to implement an LSCROpportunityProducts class that is inherits LSSelect similar to LSSoLine (a better preferred solution). To keep this simple and focus on just getting the field to display text, I will use an extension field and a simple field selecting in the extension graph for opportunity.
(1) In a dac extension, create an unbound field (MyAvailability is the example field):
[PXTable(typeof(CROpportunityProducts.cROpportunityID), typeof(CROpportunityProducts.cROpportunityProductID), IsOptional = true)]
[Serializable]
public class CROpportunityProductsMyExtension : PXCacheExtension<CROpportunityProducts>
{
#region MyAvailability
public abstract class myAvailability : PX.Data.IBqlField
{
}
protected string _MyAvailability;
[PXString(IsUnicode = true)]
[PXUIField(DisplayName = "Product Availability", Enabled = false)]
public virtual string MyAvailability
{
get
{
return this._MyAvailability;
}
set
{
this._MyAvailability = value;
}
}
#endregion
}
(2) On the opportunity products tab, wire up the new field as the grid status value by setting property StatusField. The page needs modified to add this value and should look something like this when added (requires screen customization in your project -> actions edit ASPX and locate ProductsGrid to paste in your StatusField and value):
<px:PXGrid ID="ProductsGrid" SkinID="Details" runat="server" Width="100%"
Height="500px" DataSourceID="ds" ActionsPosition="Top" BorderWidth="0px"
SyncPosition="true" StatusField="MyAvailability">
(3) Now in a graph extension populate the field:
Edit: The use of Current<> does not always contain the correct currently highlighted row in the UI. Switched to Required<> based on the PXFieldSelectingEventArgs.Row and the results are correct for multiple rows in the products tab.
public class CROpportunityMaintMyExtension : PXGraphExtension<OpportunityMaint>
{
public virtual void CROpportunityProducts_MyAvailability_FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
var row = (CROpportunityProducts) e.Row;
if (row == null)
{
e.ReturnValue = string.Empty;
return;
}
INLocationStatus locationStatus = PXSelectGroupBy<INLocationStatus,
Where<INLocationStatus.inventoryID, Equal<Required<CROpportunityProducts.inventoryID>>,
And2<Where<INLocationStatus.subItemID, Equal<Required<CROpportunityProducts.subItemID>>,
Or<Not<FeatureInstalled<PX.Objects.CS.FeaturesSet.subItem>>>>,
And<Where<INLocationStatus.siteID, Equal<Required<CROpportunityProducts.siteID>>,
Or<Required<CROpportunityProducts.siteID>, IsNull>>>>>,
Aggregate<Sum<INLocationStatus.qtyOnHand, Sum<INLocationStatus.qtyAvail>>>
>.Select(sender.Graph, row.InventoryID, row.SubItemID, row.SiteID, row.SiteID);
// Need to convert to transaction UOM... (this is always base units)
decimal? qtyOnHand = locationStatus?.QtyOnHand;
decimal? qtyAvail = locationStatus?.QtyAvail;
e.ReturnValue = $"Qty On hand = {qtyOnHand.GetValueOrDefault()} ; Qty Avail = {qtyAvail.GetValueOrDefault()}";
}
}
The results:

Acumatica Scan barcode

In Acumatica project , on Receipt screen have a lookup button "Add Item". When Inventory lookup show dialog ( it's FromDetail template ), i will scan barcode from my bill. But, i must scan second times it to working.
How can i to filter grid details and set checked a row filtered according barcode ? And how can to know row count filtered? . Please help me.
The below figure illustrates.
snip code
Screen Receipt
You will be in a much better position with FieldVerifying handler implemented as follows for the INSiteStatusFilter.BarCode field to split scanned barcode and assign the new values both to BarCode field (though the PXFieldVerifyingEventArgs.New property) and custom unbound FilterQtySelected field. Then inside the FieldUpdated handler you will select all records from the detail grid, that satisfy filter condition and set Qty. Selected according to your unbound FilterQtySelected field:
public class INReceiptEntryExt : PXGraphExtension<INReceiptEntry>
{
public class INSiteStatusFilterExt : PXCacheExtension<INSiteStatusFilter>
{
#region QtySelected
public abstract class filterQtySelected : PX.Data.IBqlField
{
}
[PXQuantity]
public virtual decimal? FilterQtySelected { get; set; }
#endregion
}
public void INSiteStatusFilter_BarCode_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
{
var barCode = (string)e.NewValue;
string[] codes = barCode.Split(';');
decimal qtySelected;
if (codes.Length == 3 && decimal.TryParse(codes[2], out qtySelected))
{
e.NewValue = codes[1];
sender.GetExtension<INSiteStatusFilterExt>(e.Row).FilterQtySelected = qtySelected;
return;
}
sender.GetExtension<INSiteStatusFilterExt>(e.Row).FilterQtySelected = null;
}
public void INSiteStatusFilter_BarCode_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
INSiteStatusFilter filter = e.Row as INSiteStatusFilter;
var filterExt = filter.GetExtension<INSiteStatusFilterExt>();
if (string.IsNullOrEmpty(filter.BarCode) || !filterExt.FilterQtySelected.HasValue) return;
foreach(INSiteStatusSelected record in Base.sitestatus.Select())
{
record.Selected = true;
record.QtySelected = sender.GetExtension<INSiteStatusFilterExt>(e.Row).FilterQtySelected;
Base.sitestatus.Update(record);
}
}
}
You will need to add your custom 2D format support on the existing barcode infrastructure screens via a customization project.

Resources