How to check Shipment lines before confirming? - acumatica

I'm trying to cycle though the SOShipLines before confirming to validate the data. I've got the override to work, but can't seem to figure out how I move through the records. The error (which is set to always trip) always returns 0 lines. I'm pretty sure I need to be looking at the shiporder variable but don't know how.
public void ConfirmShipment(SOOrderEntry docgraph, SOShipment shiporder, ConfirmShipmentDelegate baseMethod)
{
int TheCount = 0;
int TheLines = 0;
string TheTest = "";
SOShipLineExt TheSOLineExt = null;
foreach (SOShipLine line in Base.Transactions.Select())
{
TheLines += 1;
TheSOLineExt = PXCache<SOShipLine>.GetExtension<SOShipLineExt>(line);
TheTest += "-" + TheSOLineExt.UsrSpeedyShippedPieces;
if (TheSOLineExt.UsrSpeedyShippedPieces==null)
{
TheCount += 1;
}
//UpdateLineDirect(Base.Caches[typeof(SOLine)], line);
};
//}; if (TheCount > 0 )
throw new PXException("What the hell!!!" + Convert.ToString(TheCount) + "/" + Convert.ToString(TheLines));
baseMethod(docgraph,shiporder);
}
Thanks in advance.
-Travis

It looks like the problem is in an undefined current SOShipment record. Please look at Base.Transactions view - there is Current<SOShipment.shipmentNbr> definition there
public PXSelect<SOShipLine,
Where<SOShipLine.shipmentNbr, Equal<Current<SOShipment.shipmentNbr>>>,
OrderBy<Asc<SOShipLine.shipmentNbr, Asc<SOShipLine.sortOrder>>>> Transactions;
Then look at base ConfirmShipment method and you will see special code which sets current record equal to your SOShipment shiporder parameter
this.Clear();
Document.Current = Document.Search<SOShipment.shipmentNbr>(shiporder.ShipmentNbr);
I suppose that the current SOShipment record is skipping somewhere before the ConfirmShipment method - this why Base.Transactions.Select() returns nothing
There are 2 possible solutions from my standpoint:
Set current SOShipment record equal to your SOShipment shiporder parameter as it is done inside the base ConfirmShipment method
Base.Clear() // maybe this is a good idea to call the Clear method too
Document.Current = Document.Search<SOShipment.shipmentNbr>(shiporder.ShipmentNbr);
Or write your own BQL select with a required parameter instead of current
foreach (SOShipLine line in PXSelect<SOShipLine,
Where<SOShipLine.shipmentNbr, Equal<Required<SOShipment.shipmentNbr>>>,
OrderBy<Asc<SOShipLine.shipmentNbr, Asc<SOShipLine.sortOrder>>>>
.Select(Base, shiporder.ShipmentNbr))
{
// do something here
}

Related

Batch allocation of stock on SOOrder

I am trying to write a process screen to allocate stock on the sales order in FIFO. The Process screen list all the sales order for a period for allocation.
I have gone through the code LSSOLine and not able to figure out the piece of code where allocation is done. anybody knows how to do it?
Update
I have tried the following code and it is working. Is there any better way to do it?
private static void DoStockAllocation(SOLine row, SOOrderEntry grp)
{
try
{
grp.Document.Current = PXSelect<
SOOrder,
Where<SOOrder.orderType, Equal<Required<SOOrder.orderType>>,
And<SOOrder.orderNbr, Equal<Required<SOOrder.orderNbr>>>>>
.Select(grp, row.OrderType, row.OrderNbr);
if (grp.Document.Current != null && grp.Document.Current.Status == SOOrderStatus.Open)
{
grp.Transactions.Current = row;
PXSelectBase<INLocationStatus> cmd = new PXSelectReadonly2<INLocationStatus,
InnerJoin<INLocation, On<INLocation.locationID, Equal<INLocationStatus.locationID>>,
LeftJoin<INSiteStatus, On<INSiteStatus.inventoryID, Equal<INLocationStatus.inventoryID>,
And<INSiteStatus.subItemID, Equal<INLocationStatus.subItemID>,
And<INSiteStatus.siteID, Equal<INLocationStatus.siteID>>>>>>,
Where<INLocationStatus.inventoryID, Equal<Required<INLocationStatus.inventoryID>>,
And<INLocationStatus.subItemID, Equal<Required<INLocationStatus.subItemID>>,
And<INLocationStatus.siteID, Equal<Required<INLocationStatus.siteID>>,
And<INLocation.salesValid, Equal<boolTrue>,
And<INLocation.inclQtyAvail, Equal<boolTrue>,
And<INLocationStatus.qtyOnHand, Greater<decimal0>>>>>>>>(grp);
foreach (PXResult<INLocationStatus, INLocation, INSiteStatus> ln in cmd.Select(row.InventoryID,row.SubItemID,row.SiteID))
{
INLocationStatus locationStatus = ln;
INSiteStatus siteStatus = ln;
SiteStatus accumsiteavail = new SiteStatus();
PXCache<INSiteStatus>.RestoreCopy(accumsiteavail, siteStatus);
accumsiteavail = (SiteStatus)grp.Caches[typeof(SiteStatus)].Insert(accumsiteavail);
decimal? AvailableQty = 0m;
decimal? SiteAvailableQty = locationStatus.QtyHardAvail;//siteStatus.QtyHardAvail + accumsiteavail.QtyHardAvail;
AvailableQty = SiteAvailableQty;
if (AvailableQty <= 0m)
{
continue;
}
if (row.LocationID == null)
{
row.LocationID = locationStatus.LocationID;
grp.Transactions.Update(row);
}
SOLineSplit split = new SOLineSplit();
if ( grp.splits.Select().Count > 0)
{
split = grp.splits.Select(row.OrderType, row.OrderNbr, row.LineNbr);
}
else
{
split = new SOLineSplit();
split = grp.splits.Insert(split);
split.InventoryID = row.InventoryID;
split.SiteID = row.SiteID;
split.OrderType = row.OrderType;
split.OrderNbr = row.OrderNbr;
split.LineNbr = row.LineNbr;
split.UOM = row.UOM;
split = PXCache<SOLineSplit>.CreateCopy(grp.splits.Update(split));
}
//split.LocationID = locationStatus.LocationID;
split.Qty = (AvailableQty < row.OrderQty) ? AvailableQty : row.OrderQty;
split.IsAllocated = true;
grp.splits.Update(split);
break;
}
grp.Save.Press();
}
}
catch(Exception ex)
{
}
}
You will need to reference the combination of LSSOLine and SOLineSplitPlanID on SOLineSplit.PlanID in your process page. Alternatively you might be able to use an instance of SOOrderEntry to do the updates/mark of allocation.
The following have been copied from the SOOrderEntry graph and are the 2 componenetis from what i can tell that drive the allocation logic. From there you just need to mark the split lines that should be allocated and should be good. Or at least a start. The problem you might have is anything that is looking for current SOOrder. You might have to set the current before marking solines splits as allocated. (assuming i under stand your question correctly)
Manage the allocation records...
public LSSOLine lsselect;
Append the use of SOLineSplitPlanID which drives the INItemPlan records...
[PXMergeAttributes(Method = MergeMethod.Append)]
[SOLineSplitPlanID(typeof(SOOrder.noteID), typeof(SOOrder.hold), typeof(SOOrder.orderDate))]
protected virtual void SOLineSplit_PlanID_CacheAttached(PXCache sender)
{
}

FGetting a Form Field from a grid extension

I have added a field to the Prepare Replenishment form that needs to be updated when a user selects a row in the Replenishment Item grid. How do I access the field. I know it is in the INReplenishmentFilterExt but I can't figure out how to get access to the extension.
Edit #1: I am able to get the value of the field but it does not update on the screen when I use cache.SetValue. I am trying to update this filter extension field from inside of the Selected event handler.
protected void INReplenishmentItem_Selected_FieldUpdating(PXCache cache, PXFieldUpdatingEventArgs e)
{
var row = (INReplenishmentItem)e.Row;
if (row == null)
return;
INReplenishmentFilter filter = Base.Filter.Current;
INReplenishmentFilterExt filterExt = PXCache<INReplenishmentFilter>.GetExtension<INReplenishmentFilterExt>(filter);
decimal poAmount = filterExt.UsrPOAmount.HasValue ? filterExt.UsrPOAmount.Value : 0;
decimal lastPrice = pvi.LastPrice.HasValue ? pvi.LastPrice.Value : 0;
decimal newPOAmount = poAmount + lastPrice;
cache.SetValue<INReplenishmentFilterExt.usrPOAmount>(filterExt, newPOAmount);
}
You cannot set the value of the Filter using the Cache of INReplenishmentItem.
I have edited my answer, the code below should work.
//Always use virtual methods for Event Handlers
protected virtual void INReplenishmentItem_Selected_FieldUpdating(PXCache sender, PXFieldUpdatingEventArgs e)
{
//Try not to use vars. Especially when you know the Type of the object
INReplenishmentItem row = e.Row as INReplenishmentItem;
if (row != null)
{
INReplenishmentFilter filter = Base.Filter.Current;
INReplenishmentFilterExt filterExt = PXCache<INReplenishmentFilter>.GetExtension<INReplenishmentFilterExt>(filter);
decimal poAmount = filterExt.UsrPOAmount ?? 0;
decimal lastPrice = pvi.LastPrice ?? 0;//Not sure what pvi is
decimal newPOAmount = poAmount + lastPrice;
//"sender" is the cache that specifically stores the datatype of row
//Therefor you cannot use it to update records of a different datatype
//You also should not pass an Extension into the argument that should be the row object you are trying to update
Base.Filter.Cache.SetValueExt<INReplenishmentFilterExt.usrPOAmount>(filter, newPOAmount);
}
}

Call action within another graph, how to pass the adapter

My goal is, from a given screen :
- Add lines to the Adjustments tab of the payment & application graph
- Release
I tried to do this :
override public void createLettering(List<ARRegister> lines)
{
string refNbr = "";
foreach (ARRegister line in lines)
{
if (line.DocType == "PMT") refNbr = line.RefNbr;
}
// Get the paymententry graph, and add the invoice
ARPaymentEntry graphPmt = PXGraph.CreateInstance<ARPaymentEntry>();
ARPayment pmt = PXSelect<ARPayment, Where<ARPayment.refNbr, Equal<Required<ARPayment.refNbr>>,
And<ARPayment.docType, Equal<Required<ARPayment.docType>>>>>
.Select(this,refNbr, "PMT");
graphPmt.Document.Current = pmt;
if (pmt == null) throw new PXException(Constantes.errNotFound);
//pmt.CuryOrigDocAmt = 0m;
//graphPmt.Document.Update(pmt);
ARAdjust adj = new ARAdjust();
foreach(ARRegister line in lines)
{
if (line.DocType == "INV")
{
adj = new ARAdjust();
adj.AdjdDocType = line.DocType;
adj.AdjdRefNbr = line.RefNbr;
graphPmt.Adjustments.Insert(adj);
}
}
PXAdapter adapter = new PXAdapter(new PXView(graphPmt,true, graphPmt.Document.View.BqlSelect));
graphPmt.Persist();
graphPmt.Release(adapter);
}
My problem is I think my adapter gets every single ARPayment in it and thus tries to release them all. (The output of this function is : long processing time and then tells me 'PaymentMethod can't be null', but the paymentMethod of my graphPmt.Document is not null when I check in debug).
so How do I pass a correct PXAdapter to the Release(PXAdapter adapter) method of the PaymentEntry graph, from another custom graph of mine ?
I would think you should be able to call the action such as...
graphPmt.release.Press();
I have not tested this but I recall doing something like this for other actions in the past.

Change String length or decimal precision of field attribute dynamically

I'm trying to use setup data from one table to allow me to format fields on the fly / dynamically. I know I can change field names and visibility based on the PXUIFieldAttribute class, but changing the precision or string length is a bit trickier, obviously. From the research I've done, I've come up with the following example code that seems like it should work - but I get the error:
"Unable to cast object of type 'PX.Data.PXUIFieldAttribute' to type 'PX.Data.PXDBDecimalAttribute'.
I don't see why this is occurring...
protected virtual void xTACOpenSourceDetail_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
var osd = (PXCache)sender;
foreach (PXDBDecimalAttribute attribute in this.Caches<xTACOpenSourceDetail>().GetAttributes("Number1"))
{
PXDBDecimalAttribute someAttribute = attribute as PXDBDecimalAttribute;
if (someAttribute != null)
{
someAttribute.DBProperties._precision = 4;
}
}
}
I just tried the below code in sales order screen and it seems working!
var props = typeof(SOOrder).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(PXDecimalAttribute)));
foreach (System.Reflection.PropertyInfo item in props)
{
PXDecimalAttribute.SetPrecision(this.Base.Caches[typeof(SOOrder)], item.Name, 1);
}
You might need to change this to match your DAC.

Using label control inside foreach loop to display multiple value in a single label

//program.cs
foreach (Mutant mutant in mutants)
{
label12.Text=mutant.displayInfo();
}
//Mutant.cs
public abstract int dangerQuotient();
public String displayInfo()
{
return(codename + " " + dangerQuotient().ToString());
}
//physicalMutant.cs
public override int dangerQuotient()
{
return level * IQ * strength;
}
//elementMutant.cs
public override int dangerQuotient()
{
return level * region;
}
Mutant is my base class and I have two derived classes physicalMutant and elementMutant. At final I'm getting the output for elementMutant class alone in label12. How to get output of physicalMutant too in the same label.
By using message box control inside foreach loop i can get values from the displayQuotient() in both derived classes but by using label i can able to display only the last iteration value.How to overcome this problem?
I am really not sure what you are wanting, but if it is to add all of your Mutants dangerQuotient to your label you will need to change your foreach statement to something like this. You are currently replacing the label's contents every iteration of your foreach loop.
label12.Text = ""; // or just delete the text in the designer
foreach(Mutant mutant in mutants)
{
label12.Text += mutant.displayInfo() + "\n"; // This will create a seperate line for each displayInfo
// label12.Text += mutant.displayInfo(); //This will add them on the same line
}

Resources