Here is the scenario I am trying to accomplish:
When an AP invoice is released using the APRelease - ReleaseInvoice process, I want to check each line of the APTran table for the given invoice. If the line item has an AllocationIDUsed code in the UsrAllocationIDUsed field, I want to delete that line and create new ones based on the setup of the custom allocation ID setup.
The APTran table is not part of the signature of the ReleaseInvoice process so I don’t have access to it via a cache.
My approach is to pass the “doc” into a function and do the allocation there.
In order to make updates to the APTran table, I need a graph (I think).
In my AllocateLine function, do I create a generic graph for each call to the function? I am pretty sure I can accomplish my goal doing it that way, but I think I might have a performance issue if the invoice has 100 APTran lines and every one of them is allocated. Yes...my customer releases HUNDREDS of AP Invoices every day and each invoice might have 50 to 100 lines.
This is my code so far.
public delegate List<APRegister> ReleaseInvoiceDelegate(JournalEntry je, ref APRegister doc, PXResult<APInvoice, CurrencyInfo, Terms, Vendor> res, bool isPrebooking, out List<INRegister> inDocs);
sPXOverride]
public virtual List<APRegister> ReleaseInvoice(JournalEntry je, ref APRegister doc, PXResult<APInvoice, CurrencyInfo, Terms, Vendor> res, bool isPrebooking, out List<INRegister> inDocs, ReleaseInvoiceDelegate baseMethod)
{
if (doc.Released != true && doc.DocType == "INV" && (!isPrebooking || doc.Prebooked != true))
{
doc.DocDesc = "JOE ####";
AllocateLine(doc);
}
return baseMethod(je, ref doc, res, isPrebooking, out inDocs);
}
protected void AllocateLine(APRegister doc)
{
foreach (APTran apTran in SelectFrom<APTran>
.Where<APTran.refNbr.IsEqual<@P.AsString>>
.View.Select(Base, doc.RefNbr, 0))
{
if (apTran.CuryLineAmt > 0)
{
//if line has allocation code on it
// 1) delete original line
// 2) add in new lines that are allocated
APTranExt itemExt = PXCache<APTran>.GetExtension<APTranExt>(apTran);
if (itemExt.UsrAllocationIDUsed == null) continue;
// allocate line
}
}
At the // allocate line comment is where I would do the allocation.
The issue is how to make changes to the DB so that prior to calling the base method, the APTran table has been updated.
I have a custom Action on the Bills and Adjustments screen which works fine if you allocate the lines as they are entered. Due to the number of lines to be allocated, this is not a feasible approach since the AP invoices are uploaded and released automatically.
This is the code for my Action WHICH WORKS GREAT! But this action has reference to the cache on the invoice since it is part of the graph. I am showing this code as maybe I can leverage it somehow.
public PXAction<APTran> AllocateLine;
aPXButton(CommitChanges = true)]
=PXUIField(DisplayName = "Allocate Line", Enabled = false, Visible = false)]
protected void allocateLine()
{
APTran currentItem = this.Base.Transactions.Current;
if (currentItem == null) return;
if (currentItem.CuryLineAmt == 0) return;
APTranExt itemExt = PXCache<APTran>.GetExtension<APTranExt>(currentItem);
if (itemExt.UsrAllocationID == null)
{
throw new Exception("You need to select an Allocation Template");
}
int itemCounter = 0;
int itemCount = 0;
decimal? balanceToAllocate = currentItem.CuryLineAmt;
decimal? balanceToAllocateDiscount = currentItem.CuryDiscAmt;
foreach (var detail in SelectFrom<ICAllocationAccounts>.InnerJoin<ICAllocationCode>.
On<ICAllocationCode.allocationID.IsEqual<ICAllocationAccounts.allocationID>>.
Where<ICAllocationCode.allocationID.IsEqual<@P.AsInt>>.
View.Select(Base, itemExt.UsrAllocationID))
{
itemCount++;
}
this.Base.Transactions.Delete(currentItem);
ICAllocationCode code = SelectFrom<ICAllocationCode>.
Where<ICAllocationCode.allocationID.IsEqual<@P.AsInt>>.
View.Select(Base, itemExt.UsrAllocationID);
foreach (ICAllocationAccounts detail in SelectFrom<ICAllocationAccounts>.InnerJoin<ICAllocationCode>.
On<ICAllocationCode.allocationID.IsEqual<ICAllocationAccounts.allocationID>>.
Where<ICAllocationCode.allocationID.IsEqual<@P.AsInt>>.
View.Select(Base, itemExt.UsrAllocationID))
{
itemCounter++;
APTran newRow = (APTran)this.Base.Transactions.Cache.CreateCopy(currentItem);
newRow.LineNbr = null;
newRow.TranID = null;
newRow.NoteID = null;
if (itemCount == itemCounter)
{
newRow.CuryLineAmt = balanceToAllocate;
if (currentItem.CuryDiscAmt != null && currentItem.CuryDiscAmt > 0)
{
newRow.CuryDiscAmt = balanceToAllocateDiscount;
newRow.CuryTranAmt = newRow.CuryLineAmt - newRow.CuryDiscAmt;
}
else
{
newRow.CuryTranAmt = balanceToAllocate;
}
}
else
{
newRow.CuryLineAmt = Math.Round((decimal)((detail.Percent / 100) * currentItem.CuryLineAmt), 2);
balanceToAllocate -= (decimal)newRow.CuryLineAmt;
if (currentItem.CuryDiscAmt != null && currentItem.CuryDiscAmt > 0)
{
newRow.CuryDiscAmt = Math.Round((decimal)((detail.Percent / 100) * currentItem.CuryDiscAmt), 2);
balanceToAllocateDiscount -= (decimal)newRow.CuryDiscAmt;
newRow.CuryTranAmt = newRow.CuryLineAmt - newRow.CuryDiscAmt;
}
else
{
newRow.CuryTranAmt = newRow.CuryLineAmt;
}
}
if (currentItem.Qty != null && currentItem.Qty > 0)
{
newRow.Qty = Math.Round((decimal)((decimal)(detail.Percent / 100) * currentItem.Qty), 2);
}
if (code.DescriptionSource == DescriptionSourceType.FromDocument)
{
newRow.TranDesc = currentItem.TranDesc;
}
else
{
newRow.TranDesc = code.TranDescr;
}
newRow.BranchID = detail.BranchID;
if(newRow.InventoryID == null)
{
newRow.SubID = detail.AccountSubID;
}
else
{
newRow.TranDesc = currentItem.TranDesc;
}
APTranExt itemExt2 = PXCache<APTran>.GetExtension<APTranExt>(newRow);
itemExt2.UsrAllocationIDUsed = itemExt.UsrAllocationID;
itemExt2.UsrAllocationID = null;
this.Base.Transactions.Cache.Update(newRow);
//this.Base.Transactions.Cache.Insert(newRow);
}
}
If there is some way to call my Action from the ReleaseInvoice delegate that might save a lot of code. I don’t know if it is possible to call an action from the Bills and Adjustments graph.