I do customization on Release of Check and Payment with the goal:
- When the exchange of currency change, the amount of gain (or loss) will be link to an account
- Gain mean: the additional revenue which come from the rate of currency → link to account 635
- Loss mean: the money we loss because of rate of currency → link to account 515
- This amount of money will be tracked in Project information
For example: I have a Check and Payment Record as following: The cross rate is 26.000 (which is higher than the exchange currency in Bill and Adjustment)
-
Then release this one will generate a GL transaction as following:
Note: there is a row for Account 635 which include information from APBill and APPayment → this is what I customize
-
Post GL Transaction: As a result, there is a Project Transactions generated
-
This cost will be tracked in Project Cost Budget
However, When I turn on Auto Post on Release, There is no Project Transaction being generated. It leads to the cost isn’t statistical in Project cost budget.
![](https://uploads-us-west-2.insided.com/acumatica-en/attachment/1893527f-6c37-470f-b2f8-7afd5c68fe13.png)
Here is the code of my customization
using PX.Common;
using PX.Data;
using PX.Objects.GL;
using System.Collections;
namespace PX.Objects.AP
{
// Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
public class APPaymentEntry_Extension : PXGraphExtension<APPaymentEntry>
{
#region Event Handlers
protected class ProcessBatch : IPrefetchable
{
private Batch _Batch;
public Batch Batch
{
get
{
return _Batch;
}
}
public ProcessBatch(Batch batch)
{
_Batch = batch;
}
void IPrefetchable.Prefetch()
{
}
}
protected class ProcessTran : IPrefetchable
{
private GLTran _Tran;
public GLTran Tran
{
get
{
return _Tran;
}
}
private int? _SubID;
private int? _ProjectID;
private int? _TaskID;
public int? SubID
{
get
{
return _SubID;
}
}
public int? ProjectID
{
get
{
return _ProjectID;
}
}
public int? TaskID
{
get
{
return _TaskID;
}
}
public ProcessTran(GLTran tran, int? subID, int? projectID, int? taskID)
{
_Tran = tran;
_SubID = subID;
_ProjectID = projectID;
_TaskID = taskID;
}
void IPrefetchable.Prefetch()
{
}
}
private void PopulateFromAPTran(PXCache cache, PXRowPersistingEventArgs args, bool storeInSlot = false)
{
var tran = (GLTran)args.Row;
if (tran == null || args.Operation != PXDBOperation.Insert || tran.Module != BatchModule.AP) return;
if (storeInSlot)
{
PXContext.SetSlot<ProcessTran>(new ProcessTran(tran, tran.SubID, tran.ProjectID, tran.TaskID));
}
else
{
ProcessTran processTran = PXContext.GetSlot<ProcessTran>();
if (processTran != null && tran == processTran.Tran)
{
tran.SubID = processTran.SubID;
tran.ProjectID = processTran.ProjectID;
tran.TaskID = processTran.TaskID;
}
}
var account = (Account)PXSelectorAttribute.Select<GLTran.accountID>(cache, tran);
//Account account = PXSelect<Account, Where<Account.accountID, Equal<Required<Account.accountID>>>>.Select(Base, tran.AccountID);
// if account is null or Account isn't 635, 515 -> Skip handling.
if (account == null || (account.AccountClassID != "515" && account.AccountClassID != "635")) return;
var currentSub = (Sub)PXSelectorAttribute.Select<GLTran.subID>(cache, tran);
//Sub currentSub = PXSelect<Sub, Where<Sub.subID, Equal<Required<Sub.subID>>>>.Select(Base, tran.SubID);
if (currentSub == null || currentSub.SubCD != "000000000000") return;
// populate SubID from Bill and Adjustment to GLTran
// find index of GLTran Gain and Lost
var docOrder = 0;
var secondGainAndLostClassID = account.AccountClassID == "515" ? "635" : "515";
Account secondGainAndLostAcc = PXSelect<Account, Where<Account.accountClassID, Equal<Required<Account.accountClassID>>>>.Select(Base, secondGainAndLostClassID);
if (secondGainAndLostAcc != null)
{
PXResultset<GLTran> glTranGainAndLost = PXSelect<GLTran,
Where<GLTran.batchNbr, Equal<Required<GLTran.batchNbr>>,
And<GLTran.lineNbr, NotEqual<Required<GLTran.lineNbr>>, And<Where<GLTran.accountID, Equal<Required<GLTran.accountID>>,
Or<GLTran.accountID, Equal<Required<GLTran.accountID>>>>>>>>
.Select(Base, tran.BatchNbr, tran.LineNbr, account.AccountID, secondGainAndLostAcc.AccountID);
docOrder = glTranGainAndLost.Count;
}
else
{
PXResultset<GLTran> glTranGainAndLost = PXSelect<GLTran,
Where<GLTran.batchNbr, Equal<Required<GLTran.batchNbr>>,
And<GLTran.accountID, Equal<Required<GLTran.accountID>>>>>
.Select(Base, tran.BatchNbr, account.AccountID);
docOrder = glTranGainAndLost.Count;
}
// Get list Bill and Adjustment
PXResultset<APAdjust> documentLines = PXSelect<APAdjust, Where<APAdjust.adjgRefNbr, Equal<Required<APPayment.refNbr>>>>.Select(Base, tran.RefNbr);
if (documentLines.Count <= docOrder) return;
APAdjust docLine = documentLines[docOrder];
//get first row of detail tab
APTran firstLine = PXSelect<APTran, Where<APTran.refNbr, Equal<Required<APTran.refNbr>>>, OrderBy<Asc<APTran.lineNbr>>>.Select(Base, docLine.AdjdRefNbr);
if (firstLine == null) return;
if (!firstLine.SubID.HasValue && !firstLine.ProjectID.HasValue && !firstLine.TaskID.HasValue) return;
if (firstLine.SubID.HasValue)
{
// get sub CD of APTran
var orgSub = (Sub)PXSelectorAttribute.Select<APTran.subID>(cache, firstLine);
//Sub orgSub = PXSelect<Sub, Where<Sub.subID, Equal<Required<Sub.subID>>>>.Select(Base, firstLine.SubID);
if (orgSub != null)
{
// find Sub by SubCD
var newSubCD = orgSub.SubCD.Substring(0, orgSub.SubCD.Length - "CC22".Length) + "CC22";
Sub newSub = PXSelect<Sub, Where<Sub.subCD, Equal<Required<Sub.subCD>>>>.Select(Base, newSubCD);
if (newSub == null)
{
// newSubCD doesn't exist yet. Set SubCD to TranDesc temporary so that it could be created in RowPersisting event handler
Sub sub = new Sub
{
SubCD = newSubCD
};
PXCache<Sub> subCache = new PXCache<Sub>(Base);
sub = subCache.Insert(sub) as Sub;
Base.Caches[typeof(Sub)].PersistInserted(sub);
if (sub != null)
{
tran.SubID = sub.SubID;
tran.OrigSubID = sub.SubID;
}
}
else
{
// newSubCD exist. Add link to Tran
tran.SubID = newSub.SubID;
tran.OrigSubID = newSub.SubID;
}
if (firstLine.ProjectID.HasValue)
{
tran.ProjectID = firstLine.ProjectID;
}
if (firstLine.TaskID.HasValue)
{
tran.TaskID = firstLine.TaskID;
}
}
}
cache.Update(tran);
}
public PXAction<APPayment> release;
[PXUIField(DisplayName = "Release", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)]
[PXProcessButton]
public IEnumerable Release(PXAdapter adapter)
{
PXGraph.InstanceCreated.AddHandler<JournalEntry>((graph) =>
{
//graph.RowInserting.AddHandler<GLTran>((cache, args) =>
//{
// PopulateFromAPTran(cache, args, true);
//});
graph.RowPersisting.AddHandler<GLTran>((cache, args) =>
{
PopulateFromAPTran(cache, args);
});
});
PXGraph.InstanceCreated.AddHandler<JournalEntry>((graph) =>
{
graph.RowInserting.AddHandler<Batch>((cache, args) =>
{
PXContext.SetSlot<ProcessBatch>(null);
});
});
PXGraph.InstanceCreated.AddHandler<JournalEntry>((graph) =>
{
graph.RowPersisted.AddHandler<Batch>((cache, args) =>
{
if (args.TranStatus == PXTranStatus.Completed)
{
PXContext.SetSlot<ProcessBatch>(
new ProcessBatch((Batch)args.Row));
}
});
});
return Base.release.Press(adapter);
}
#endregion
}
}
Thank you in advance,
Khoi