/// <summary>
/// Helper class to reverse project invoice voucher transactions and match those, which relates to customer
/// </summary>
class VKProjectInvoicePostingHelper
{
LedgerJournalTrans paymLedgerJournalTrans;
/// <summary>
/// Entry point to execute reversal and matching process
/// </summary>
/// <param name = "_projProposalJour">ProjProposalJour record</param>
public void reverseTranasctions(ProjProposalJour _projProposalJour)
{
setPrefix("Invoice reversal process");
if (_projProposalJour && _projProposalJour.ProjInvoiceId)
{
ProjInvoiceJour projInvoiceJour = ProjInvoiceJour::find(_projProposalJour.ProjInvoiceId, _projProposalJour.InvoiceDate);
LedgerJournalTable ledgerJournalTable = this.createJournal(projInvoiceJour);
if (ledgerJournalTable)
{
info(strFmt("Reversing general journal %1", ledgerJournalTable.JournalNum));
this.postGeneralJournal(ledgerJournalTable);
this.settleTransactions(projInvoiceJour, paymledgerJournalTrans);
}
}
}
/// <summary>
/// Creates general journal based on project invoice voucher transactions
/// </summary>
/// <param name = "_projInvoiceJour">ProjInvoiceJour record</param>
/// <returns>LedgerJournalTable record</returns>
public LedgerJournalTable createJournal(ProjInvoiceJour _projInvoiceJour)
{
GeneralJournalEntry generalJournalEntry;
GeneralJournalAccountEntry generalJournalAccountEntry;
LedgerJournalTable ledgerJournalTable;
LedgerJournalTrans ledgerJournalTrans;
while select generalJournalEntry
where generalJournalEntry.SubledgerVoucher == _projInvoiceJour.LedgerVoucher
join generalJournalAccountEntry
where generalJournalAccountEntry.GeneralJournalEntry == generalJournalEntry.RecId
{
if (!ledgerJournalTable)
{
ledgerJournalTable = this.createJournalHeader(_projInvoiceJour);
}
ledgerJournalTrans.clear();
ledgerJournalTrans.JournalNum = ledgerJournalTable.JournalNum;
ledgerJournalTrans.TransDate = _projInvoiceJour.InvoiceDate;
if (LedgerPostingType::CustBalance == generalJournalAccountEntry.PostingType)
{
ledgerJournalTrans.AccountType = LedgerJournalACType::Cust;
ledgerJournalTrans.LedgerDimension = LedgerDynamicAccountHelper::getDynamicAccountFromAccountNumber(_projInvoiceJour.InvoiceAccount, ledgerJournalTrans.AccountType);
ledgerJournalTrans.DefaultDimension = LedgerDimensionFacade::getDefaultDimensionFromLedgerDimension(generalJournalAccountEntry.LedgerDimension);
}
else
{
ledgerJournalTrans.AccountType = LedgerJournalACType::Ledger;
ledgerJournalTrans.LedgerDimension = generalJournalAccountEntry.LedgerDimension;
}
if (LedgerPostingType::Tax == generalJournalAccountEntry.PostingType)
{
TaxTrans taxTrans;
select firstonly taxTrans
where taxTrans.Voucher == _projInvoiceJour.LedgerVoucher
&&taxTrans.TransDate == generalJournalEntry.AccountingDate
&&taxTrans.SourceCurrencyCode == generalJournalAccountEntry.TransactionCurrencyCode
&&taxTrans.SourceTaxAmountCur == generalJournalAccountEntry.TransactionCurrencyAmount;
if (taxTrans)
{
ledgerJournalTrans.TaxGroup = taxTrans.TaxGroup;
ledgerJournalTrans.TaxItemGroup = taxTrans.TaxItemGroup;
ledgerJournalTrans.TaxCode = taxTrans.TaxCode;
}
}
//ledgerJournalTrans.OffsetAccountType = ledgerJournalTransOrig.OffsetAccountType;
//ledgerJournalTrans.OffsetLedgerDimension = ledgerJournalTransOrig.OffsetLedgerDimension;
//ledgerJournalTrans.OffsetDefaultDimension = ledgerJournalTransOrig.OffsetDefaultDimension;
ledgerJournalTrans.CurrencyCode = _projInvoiceJour.CurrencyId;
if (generalJournalAccountEntry.IsCredit)
{
ledgerJournalTrans.AmountCurDebit = abs(generalJournalAccountEntry.AccountingCurrencyAmount);
}
else
{
ledgerJournalTrans.AmountCurCredit = abs(generalJournalAccountEntry.AccountingCurrencyAmount);
}
//addition fields
//ledgerJournalTrans.Approver = HcmWorker::userId2Worker(curuserid());
//ledgerJournalTrans.Approved = NoYes::Yes;
ledgerJournalTrans.Txt = strFmt("Reverse invoice %1", _projInvoiceJour.ProjInvoiceId);
ledgerJournalTrans.SkipBlockedForManualEntryCheck = true;
ledgerJournalTrans.defaultRow();
ledgerJournalTrans.insert();
if (LedgerPostingType::CustBalance == generalJournalAccountEntry.PostingType)
{
paymledgerJournalTrans.data(ledgerJournalTrans);
}
}
return ledgerJournalTable;
}
/// <summary>
/// Creates general journal header
/// </summary>
/// <param name = "_projInvoiceJour">ProjInvoiceJour record</param>
/// <returns>LedgerJournalTable record</returns>
public LedgerJournalTable createJournalHeader(ProjInvoiceJour _projInvoiceJour)
{
ProjParameters projParameters = ProjParameters::find();
LedgerJournalTable ledgerJournalTable;
if (!projParameters.VKJournalName)
{
throw Error("Please specify project sales invoice reversal general journal name");
}
ledgerJournalTable.clear();
ledgerJournalTable.initValue();
ledgerJournalTable.JournalNum = JournalTableData::newTable(ledgerJournalTable).nextJournalId();
ledgerJournalTable.initFromLedgerJournalName(projParameters.VKJournalName);
// journal description, should be after initFromLedgerJournalName
ledgerJournalTable.Name = strFmt("Reverse invoice %1", _projInvoiceJour.ProjInvoiceId);
ledgerJournalTable.CurrencyCode = _projInvoiceJour.CurrencyId;
ledgerJournalTable.insert();
return ledgerJournalTable;
}
/// <summary>
/// Posts general journal
/// </summary>
/// <param name = "_ledgerJournalTable">LedgerJournalTable record</param>
void postGeneralJournal(LedgerJournalTable _ledgerJournalTable)
{
LedgerJournalCheckPost ledgerJournalCheckPost;
ledgerJournalCheckPost = ledgerJournalCheckPost::newLedgerJournalTable(_ledgerJournalTable, NoYes::Yes);
ledgerJournalCheckPost.runOperation();
//ledgerJournalCheckPost.run();
}
/// <summary>
/// Settles customer transactions
/// </summary>
/// <param name = "_projInvoiceJour">ProjInvoiceJour record</param>
/// <param name = "_paymLedgerJournalTrans">LedgerJournalTrans record</param>
void settleTransactions(ProjInvoiceJour _projInvoiceJour, LedgerJournalTrans _paymLedgerJournalTrans)
{
CustTable custTable = CustTable::find(_projInvoiceJour.InvoiceAccount);
// find related transaciton
LedgerJournalTrans paymLedgerJournalTransLoc;
CustTransOpen invCustTransOpen, paymCustTransOpen;
CustTrans invCustTrans, paymCustTrans;
// Given the payment LedgerJournalTrans, find the related CustTrans which has CustTransOpen
select firstonly paymLedgerJournalTransLoc
where paymLedgerJournalTransLoc.RecId == _paymLedgerJournalTrans.RecId
join paymCustTrans
where paymCustTrans.Voucher == paymLedgerJournalTransLoc.Voucher
&& paymCustTrans.AccountNum == custTable.AccountNum
&& paymCustTrans.RecId == paymLedgerJournalTransLoc.CustTransId
&& paymCustTrans.TransType == LedgerTransType::GeneralJournal
join paymCustTransOpen
where paymCustTransOpen.RefRecId == paymCustTrans.RecId;
// Find the related invoice to be settled against the payment journal in the query above
select firstonly invCustTrans
where invCustTrans.AccountNum == custTable.AccountNum
&& invCustTrans.Voucher == _projInvoiceJour.LedgerVoucher
&& invCustTrans.TransType == LedgerTransType::Project
join invCustTransOpen
where invCustTransOpen.RefRecId == invCustTrans.RecId;
// Settlement
SpecTransExecutionContext specTransExecutionContext = SpecTransExecutionContext::newFromSource(custTable);
//SpecTransManager specTransManager = SpecTransManager::construct(specTransExecutionContext.parmSpecContext());
SpecTransManager specTransManager = SpecTransManager::newFromSpec(specTransExecutionContext.parmSpecContext());
Amount settleAmount = invCustTransOpen.AmountCur;
//Prevent over settle
if(settleAmount > (-paymCustTransOpen.AmountCur))
{
settleAmount = (-paymCustTransOpen.AmountCur);
}
//Payment
specTransManager.insert(paymCustTransOpen.DataAreaId,
paymCustTransOpen.TableId,
paymCustTransOpen.RecId,
-settleAmount,
paymCustTrans.CurrencyCode);
//Invoice
specTransManager.insert(invCustTransOpen.DataAreaId,
invCustTransOpen.TableId,
invCustTransOpen.RecId,
settleAmount,
invCustTrans.CurrencyCode);
//Settle
if(CustTrans::settleTransaction(specTransExecutionContext, CustTransSettleTransactionParameters::construct()))
{
info(strFmt("Settlement completed for Invoice %1, Journal reversal payment %2, Amount %3", _projInvoiceJour.ProjInvoiceId, _paymLedgerJournalTrans.JournalNum, settleAmount));
}
else
{
throw Error("Settlement failed");
}
}
}
internal final class VKTest
{
/// <summary>
/// Class entry point. The system will call this method when a designated menu
/// is selected or when execution starts and this class is set as the startup class.
/// </summary>
/// <param name = "_args">The specified arguments.</param>
public static void main(Args _args)
{
VKProjectInvoicePostingHelper projectInvoicePostingHelper = new VKProjectInvoicePostingHelper();
ProjProposalJour projProposalJour = ProjProposalJour::find('VK-000301');
ttsbegin;
projectInvoicePostingHelper.reverseTranasctions(projProposalJour);
ttscommit;
info('done');
}
}
If you found value in what I share, I've set up a Buy Me a Coffee page as a way to show your support.
Buy Me a CoffeeNo comments. Be the first one to comment on this post.
DaxOnline.org is free platform that allows you to quickly store and reuse snippets, notes, articles related to Dynamics 365 FO.
Authors are allowed to set their own "buy me a coffee" link.
Join us.