i am doing some customization is sales totals class for getting the summary of the sales order.
successfully iam getting the total summary of the sales order in the Summary fact box.
but i am trying to confirm the sales order, getting the debugger error.
i am trying to debug the class but no clue.
*This post is locked for comments
Hi Srinivas,
Please provide some more details for the problem. Because when this issue occurred to me, I sorted out that I have a non existing field which is pointing to another table.
It would be helpful if you send me the .xpo of your customization.
[address removed by moderator]
Thanks & Regards,
Gaurav Singhal
Hi Gaurav,
I am Checking all relations in the tables related to sales totals summary part.
Again same error
Hi Srinivas,
I stated this error above in previous answer. This is caused when you have a
one or many non working relationships between tables.
Use debugger to trace the mistake you have done. This will resolve the issue.
Hi Gaurav Singhal,
i am trying to run the incremental CIL it is through error in the salesTotals_ParmTans methods.
Error : Object Reference is not set to the instance of the object.
"display height" means..
Hi Srinivas,
Answer for first Question
Yes you can create a Form Part for SalesTableListPage.
Answer for second Question
I saw your code, for creating summary fact box you have to create a separate form for summary and its related tables which you have already created correctly.
Your code seems correct to me, as this is exactly same as that in PurchTableListPage summary factbox.
Try this, add your summary form to "Parts" and then put a breakpoint in Summary form, you will get to know the "FieldId" for which you are getting debugger error. Also check the relations between tables, this also causes Debugger Error (usually "display height").
Hi Gaurav,
Please find below what i did the customization.
I am created the two tables called salestotalssummary and SalesEncumbranceSummary with some fields related to summary of the sales order.
then i add two table in the form called salestotalssummarypart
in salestotalssummary datasource override the method execute query
public void executeQuery()
{
SalesTable salesTableParent;
salesTableParent = element.args().record() as SalesTable;
if(salesTableParent.RecId)
{
SalesTotalsSummaryController::updateTotals(salesTableParent, tableNum(SalesTotalsSummary));
super();
}
}
then i created new class called slaestotalssummarycontroller.
add the methods and code like this.
//class declaration
/// <summary>
/// This class is responsible for calculating and storing sales order totals and encumbrance summary amounts
/// in <c>SalesTotalsSummary</c> and <c>SalesEncumbranceSummary</c> tables respectively. These amounts are displayed on
/// Sales order details and list pages in factboxes.
/// </summary>
class SalesTotalsSummaryController
{
SalesId salesId;
boolean invalidateTotalsRecord;
}
//checkFields
/// <summary>
/// Checks if the fields that affect totals amounts have been updated and sets the result in a class level variable.
/// </summary>
/// <param name="_common">
/// Buffer for the record being updated
/// </param>
/// <remarks>
/// This class handles table record updates in two steps. First <c>checkFields()</c> is called from table <c>update()</c> method before the <c>super()</c> call
/// to check if any of the fields that affects sales order total amounts has been modified and a class level flag is set to indicate so.
/// Calling <c>checkFields()</c> before <c>super()</c> is needed becuase the <c>super()</c> call resets the original buffer. After the <c>super()</c> is called and
/// the updates have been written to the table a call to <c>onUpdate()</c> is made to write the class level varaible indicating the result from
/// <c>checkFields()</c> to <c>SalesTotalsSummary</c> table.
/// </remarks>
public void checkFields(Common _common)
{
SalesTable salesTable;
SalesLine salesLine;
MarkupTrans markupTrans;
if(!_common || !SalesParameters::find().AutomaticFactBoxUpdateOnSO_PSN)
{
return;
}
switch(_common.TableId)
{
case tableNum(SalesTable):
salesTable = _common as SalesTable;
invalidateTotalsRecord = invalidateTotalsRecord || (salesTable.orig().CurrencyCode != salesTable.CurrencyCode);
invalidateTotalsRecord = invalidateTotalsRecord || (salesTable.orig().InclTax != salesTable.InclTax);
if(TaxParameters::find().JournalInclTax)
{
invalidateTotalsRecord = invalidateTotalsRecord || (salesTable.orig().CashDisc != salesTable.CashDisc);
}
break;
case tableNum(SalesLine):
salesLine = _common as SalesLine;
invalidateTotalsRecord = invalidateTotalsRecord || (salesLine.orig().LineAmount != salesLine.LineAmount);
invalidateTotalsRecord = invalidateTotalsRecord || (salesLine.orig().SalesQty != salesLine.SalesQty);
invalidateTotalsRecord = invalidateTotalsRecord || (salesLine.orig().SalesPrice != salesLine.SalesPrice);
break;
case tableNum(MarkupTrans):
markupTrans = _common as MarkupTrans;
invalidateTotalsRecord = invalidateTotalsRecord || (markupTrans.orig().MarkupCategory != markupTrans.MarkupCategory);
invalidateTotalsRecord = invalidateTotalsRecord || (markupTrans.orig().Value != markupTrans.Value);
invalidateTotalsRecord = invalidateTotalsRecord || (markupTrans.orig().CurrencyCode != markupTrans.CurrencyCode);
break;
}
}
private void new(SalesId _salesId)
{
salesId = _salesId;
}
/// <summary>
/// This method will invalidate the totals record if it was marked to be invalidated from the checkFields() and
/// then notifies the calling datasource about the invalid totals record.
/// This method gets called after the update() is called on the table.
/// </summary>
/// <param name="_common">
/// Table buffer to notify about the invalidation of the record
/// </param>
public void onUpdate(Common _common)
{
SalesTable salesTable;
SalesLine salesLine;
if(!_common || !SalesParameters::find().AutomaticFactBoxUpdateOnSO_PSN)
{
return;
}
if(invalidateTotalsRecord)
{
SalesTotalsSummaryController::setValidFlag(salesId, tableNum(SalesTotalsSummary), false);
switch(_common.TableId)
{
case tableNum(SalesTable):
salesTable = _common as SalesTable;
SalesTotalsSummaryController::updateTotals(salesTable, tableNum(SalesTotalsSummary));
break;
case tableNum(SalesLine):
salesLine = _common as SalesLine;
SalesTotalsSummaryController::updateTotals(SalesTable::find(salesLine.SalesId), tableNum(SalesTotalsSummary));
break;
default:
break;
}
}
}
/// <summary>
/// Calculates or gets the totals for the sales order and inserts the data in the <c>SalesTotalsSummary</c> table.
/// </summary>
/// <param name="_salesTable">
/// <c>SalesTable</c> record buffer for which to update the totals.
/// </param>
/// <param name="_salesTotalsSummary">
/// <c>SalesTotalsSummary</c> record buffer that needs to be updated; optional.
/// </param>
/// <param name="_salesTotals">
/// <c>SalesTotals</c> object that carries the totals values calculated; optional.
/// </param>
/// <returns>
/// true - indiating that the <c>SalesTotalsSummary</c> record was updated.
/// </returns>
public server static boolean calculateAndUpdateTotalsSummary(SalesTable _salesTable, SalesTotalsSummary _salesTotalsSummary = null, SalesTotals _salesTotals = null)
{
ttsbegin;
if(!_salesTotals)
{
_salesTotals = SalesTotals::newSalesTable(_salesTable, SalesUpdate::All);
_salesTotals.calc();
}
if (!_salesTotalsSummary || !_salesTotalsSummary.selectForUpdate())
{
_salesTotalsSummary = SalesTotalsSummary::findBySalesId(_salesTable.SalesId,true);
}
_salesTotalsSummary.SalesId = _salesTable.SalesId;
_salesTotalsSummary.CurrencyCode = _salesTotals.salesCurrency();
_salesTotalsSummary.NetLines = _salesTotals.salesBalance();
_salesTotalsSummary.LineDiscount = _salesTotals.salesLineDisc();
_salesTotalsSummary.Discounts = _salesTotals.salesEndDisc();
_salesTotalsSummary.SalesTax = _salesTotals.salesTaxTotal();
_salesTotalsSummary.MiscCharges = _salesTotals.salesMarkup();
_salesTotalsSummary.RoundOff = _salesTotals.salesRoundOff();
_salesTotalsSummary.UseTax = _salesTotals.salesUseTax();
_salesTotalsSummary.OtherCharges = _salesTotals.salesOtherMiscCharges();
_salesTotalsSummary.TotalAmount = _salesTotals.salesTotalAmount();
_salesTotalsSummary.CashDiscount = _salesTotals.salesCashDiscAmount();
_salesTotalsSummary.IsValid = true;
if(!_salesTotalsSummary)
{
_salesTotalsSummary.insert();
}
else
{
_salesTotalsSummary.update();
}
ttscommit;
return true;
}
/// <summary>
/// Creates and returns an object of <c>SalesTotalsSummaryController</c> for the given sales order id.
/// </summary>
/// <param name="_salesId">
/// Sales order Id
/// </param>
/// <returns>
/// Object of <c>SalesTotalsSummaryController</c>
/// </returns>
public static SalesTotalsSummaryController construct(SalesId _salesId)
{
SalesTotalsSummaryController salesTotalsSummaryController = new SalesTotalsSummaryController(_salesId);
return salesTotalsSummaryController;
}
/// <summary>
/// Sets the <c>isValid</c> field of the <c>SalesEncumbranceSummary</c> table to false so
/// that the next time the <c>updateTotals</c> method is called the table will be updated.
/// </summary>
/// <param name="_salesId">
/// The <c>SalesId</c> value of the record to invalidate.
/// </param>
public static void invalidateEncumbranceSummaryRecord(SalesId _slaesId)
{
SalesTotalsSummaryController::setValidFlag(_slaesId, tableNum(SalesEncumbranceSummary), false);
}
/// <summary>
/// Checks the table buffer to see from which table record was deleted,
/// If a record is deleted from a table which affects totals then the totals record is invalidated.
/// </summary>
/// <param name="_common">
/// Buffer for delted record
/// </param>
public server static void onDelete(Common _common)
{
SalesTable salesTable;
SalesLine salesLine;
MarkupTrans markupTrans;
SalesTotalsSummary salesTotalsSummary;
if (!_common || !SalesParameters::find().AutomaticFactBoxUpdateOnSO_PSN)
{
return;
}
switch(_common.TableId)
{
case tableNum(SalesLine):
salesLine = _common as SalesLine;
salesTotalsSummary = SalesTotalsSummary::findBySalesId(salesLine.SalesId, true);
SalesTotalsSummaryController::setValidFlag(salesLine.SalesId, tableNum(SalesTotalsSummary), false, salesTotalsSummary);
SalesTotalsSummaryController::updateTotals(SalesTable::find(salesLine.SalesId), tableNum(SalesTotalsSummary), salesTotalsSummary);
break;
case tableNum(MarkupTrans):
markupTrans = _common as MarkupTrans;
salesTable = SalesTable::findRecId(markupTrans.TransRecId);
SalesTotalsSummaryController::setValidFlag(salesTable.SalesId, tableNum(SalesTotalsSummary), false);
break;
}
}
/// <summary>
/// Checks the table buffer to see in which table record was inserted.
/// If a record is inserted in SalesTable then records in totals tables are created,
/// otherwise records are invalidated if the insertion is in some other table that affects totals
/// </summary>
/// <param name="_common">
/// Buffer for new record
/// </param>
public server static void onInsert(Common _common)
{
SalesTable salesTable;
SalesLine salesLine;
MarkupTrans markupTrans;
SalesTotalsSummary salesTotalsSummary;
if (!_common || !SalesParameters::find().AutomaticFactBoxUpdateOnSO_PSN)
{
return;
}
switch(_common.TableId)
{
case tableNum(SalesTable):
salesTable = _common as SalesTable;
SalesTotalsSummary::createRecord(salesTable.SalesId, salesTable.CurrencyCode);
SalesEncumbranceSummary::createRecord(salesTable.SalesId, salesTable.CurrencyCode);
break;
case tableNum(SalesLine):
salesLine = _common as SalesLine;
SalesTotalsSummary = SalesTotalsSummary::findBySalesId(salesLine.SalesId, true);
SalesTotalsSummaryController::setValidFlag(salesLine.SalesId, tableNum(SalesTotalsSummary), false, salesTotalsSummary);
SalesTotalsSummaryController::updateTotals(SalesTable::find(salesLine.SalesId), tableNum(SalesTotalsSummary), salesTotalsSummary);
break;
case tableNum(MarkupTrans):
markupTrans = _common as MarkupTrans;
salesTable = SalesTable::findRecId(markupTrans.TransRecId);
SalesTotalsSummaryController::setValidFlag(salesTable.SalesId, tableNum(SalesTotalsSummary), false);
break;
}
}
/// <summary>
/// Invalidates the <c>SalesEncumbranceSummary</c> record if some document related to sales orders is posted.
/// </summary>
/// <param name="_postedJournalSet">
/// A <c>Set</c> object containing posted journal entries
/// </param>
public server static void onPostingSuccess(Set _postedJournalSet)
{
SetEnumerator enumerator;
Common commonBuffer;
CustInvoiceJour custInvoiceJourLocal;
CustConfirmJour custConfirmJourLocal;
if (!SalesParameters::find().AutomaticFactBoxUpdateOnSO_PSN)
{
return;
}
if (_postedJournalSet)
{
enumerator = _postedJournalSet.getEnumerator();
while (enumerator.moveNext())
{
commonBuffer = enumerator.current();
switch (commonBuffer.TableId)
{
case tableNum(CustConfirmJour):
custConfirmJourLocal = commonBuffer as CustConfirmJour;
SalesTotalsSummaryController::populateEncumbranceSummaryValues(custConfirmJourLocal.SalesId);
break;
case tableNum(CustInvoiceJour):
custInvoiceJourLocal = commonBuffer as CustInvoiceJour;
SalesTotalsSummaryController::populateEncumbranceSummaryValues(custInvoiceJourLocal.SalesId);
break;
}
}
}
}
/// <summary>
/// Populates the <c>SalesEncumbranceSummary</c> record with encumbrance and relieving amounts for the sales order.
/// </summary>
/// <param name="_salesId">
/// Sales order id for which to populate the record.
/// </param>
private server static void populateEncumbranceSummaryValues(SalesId _salesId)
{
SalesTable salesTable;
if (_salesId && SalesParameters::find().AutomaticFactBoxUpdateOnSO_PSN)
{
salesTable = SalesTable::find(_salesId);
SalesTotalsSummaryController::updateTotals(salesTable, tableNum(SalesEncumbranceSummary));
SalesTotalsSummaryController::setValidFlag(salesTable.SalesId, tableNum(SalesEncumbranceSummary), false);
}
}
/// <summary>
/// Sets the summary record's IsValid flag only if the new value is different from the one already stored.
/// </summary>
/// <param name="_salesId">
/// Sales order id for which to update the record
/// </param>
/// <param name="_tableId">
/// Table id of the summary table
/// </param>
/// <param name="_isValid">
/// Value for the IsValid flag
/// </param>
/// <param name="_salesSummary">
/// SalesTotalsSummary or SalesEncumbranceSummary table whose record needs to be updated
/// </param>
private server static void setValidFlag(SalesId _salesId, tableId _tableId, boolean _isValid = true, Common _salesSummary = null)
{
SalesTotalsSummary salesTotalsSummary;
SalesEncumbranceSummary salesEncumbranceSummary;
switch(_tableId)
{
case tableNum(SalesTotalsSummary):
salesTotalsSummary = _salesSummary as SalesTotalsSummary;
if (!salesTotalsSummary ||!salesTotalsSummary.selectForUpdate())
{
salesTotalsSummary = SalesTotalsSummary::findBySalesId(_salesid, true);
}
if(salesTotalsSummary && salesTotalsSummary.IsValid!=_isValid)
{
salesTotalsSummary.IsValid = _isValid;
ttsbegin;
salesTotalsSummary.update();
ttscommit;
}
break;
case tableNum(SalesEncumbranceSummary):
salesEncumbranceSummary = _salesSummary as SalesEncumbranceSummary;
if (!salesEncumbranceSummary ||!salesEncumbranceSummary.selectForUpdate())
{
salesEncumbranceSummary = SalesEncumbranceSummary::findBySalesId(_salesId, true);
}
if(salesEncumbranceSummary && salesEncumbranceSummary.IsValid!=_isValid)
{
salesEncumbranceSummary.IsValid = _isValid;
ttsbegin;
salesEncumbranceSummary.update();
ttscommit;
}
break;
}
}
/// <summary>
/// Updates the totals or encumbrance amounts for the sales order, if invalid, based on the table id passed.
/// </summary>
/// <param name="_salesTable">
/// <c>SalesTable</c> record buffer for which to update the totals
/// </param>
/// <param name="_tableId">
/// Table Id of the table whose record needs to be updated
/// </param>
/// <param name="_salesSummary">
/// SalesTotalsSummary or SalesEncumbranceSummary table whose record needs to be updated
/// </param>
/// <returns>
/// true if a summary record was updated, false otherwise
/// </returns>
public server static boolean updateTotals(SalesTable _salesTable, tableId _tableId, Common _salesSummary = null)
{
SalesTotalsSummary salesTotalsSummary;
AccountingDistributionOrderSummaryCalc accountingDistributionOrderSummaryCalc;
SalesEncumbranceSummary salesEncumbranceSummary;
boolean recordUpdated = false;
if (!SalesParameters::find().AutomaticFactBoxUpdateOnSO_PSN)
{
return recordUpdated;
}
switch(_tableId)
{
case tableNum(SalesTotalsSummary):
SalesTotalsSummary = _salesSummary as SalesTotalsSummary;
recordUpdated = SalesTotalsSummaryController::calculateAndUpdateTotalsSummary(_salesTable, salesTotalsSummary);
break;
case tableNum(SalesEncumbranceSummary):
salesEncumbranceSummary = _salesSummary as SalesEncumbranceSummary;
if (!salesEncumbranceSummary ||!salesEncumbranceSummary.selectForUpdate())
{
salesEncumbranceSummary = SalesEncumbranceSummary::findBySalesId(_salesTable.PurchId, true);
}
if(!salesEncumbranceSummary)
{
salesEncumbranceSummary = SalesEncumbranceSummary::createRecord(_salesTable.SalesId, _salesTable.CurrencyCode);
}
if(!salesEncumbranceSummary.IsValid)
{
accountingDistributionOrderSummaryCalc = new AccountingDistributionOrderSummaryCalc();
accountingDistributionOrderSummaryCalc.populateEncumbranceSummaryAmount(_salesTable.SalesId);
salesEncumbranceSummary.LastCalculatedOn = DateTimeUtil::getSystemDateTime();
salesEncumbranceSummary.CurrencyCode = _salesTable.CurrencyCode;
salesEncumbranceSummary.EncumberedAmount = accountingDistributionOrderSummaryCalc.parmTotalEncumbered();
salesEncumbranceSummary.RelievedAmount = accountingDistributionOrderSummaryCalc.parmTotalRelieved();
salesEncumbranceSummary.IsValid = true;
ttsbegin;
salesEncumbranceSummary.update();
ttscommit;
recordUpdated = true;
}
break;
}
return recordUpdated;
}
Hi Gaurav Singhal,
Thanks for your reply.
I have some questions for sales order.
U r created the summary fact box for sales order same as purchase order?
It is Possible to create the Summary part for sales order?
Hi Srinivas,
I have faced this debugger issue before. I think your code is somehow disturbing the "AmountCur" field. If you can share the code here then I think we can sort out the issue you are facing.
Regards,
Gaurav Singhal
Stay up to date on forum activity by subscribing. You can also customize your in-app and email Notification settings across all subscriptions.
André Arnaud de Cal... 291,113 Super User 2024 Season 2
Martin Dráb 229,918 Most Valuable Professional
nmaenpaa 101,156