Force tax recalculation when TaxZoneID is updated inside event handler - acumatica

I have a graph override for Invoices
public class ARInvoiceEntry_Extension : PXGraphExtension<ARInvoiceEntry>
and in one of the event handlers I am updating TaxZoneID, which works fine. However, the taxes do not get updated or recalculated. I have tried the approach mentioned here
cache.SetValueExt<SOOrder.taxZoneID>(order, branchLoc.VTaxZoneID);
but that doesn't work for me. I have tried it in _FieldUpdating, _FieldUpdated, and even ARInvoice_RowPersisting(PXCache cache, PXRowPersistingEventArgs e, PXRowPersisting InvokeBaseHandler) events. Any ideas on why it doesn't work? The TaxZone and rates are already in the database (we are not using Avatax).
--- edit 1 ---
Here is the code where TaxZoneID is updated
namespace PX.Objects.AR
{
public class ARInvoiceEntry_Extension : PXGraphExtension<ARInvoiceEntry>
{
#region Event Handlers
protected void ARShippingAddress_PostalCode_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e, PXFieldUpdated del)
{
// PXTrace.WriteInformation("ARShippingAddress_PostalCode_FieldUpdated");
ARShippingAddress row = e.Row as ARShippingAddress;
if (row != null) {
if (DoChangeTaxZone(row)) {
var invoice = Base.Document.Current;
if (invoice != null) {
invoice.TaxZoneID = GetTaxZoneId(row);
sender.SetValueExt<ARInvoice.taxZoneID>(invoice, invoice.TaxZoneID);
}
}
}
if (del != null)
{
del(sender, e);
}
}
#endregion
private bool DoChangeTaxZone(ARShippingAddress row)
{
// logic ...
return true;
}
private string GetTaxZoneId(ARShippingAddress row)
{
// logic ...
return "TAX-ZONE-ID";
}
}
}

When you programmatically interact with tax records using the typical methods, the tax total will not refresh properly. The Tax DAC attribute doesn't recalculate the totals by default to improve performance.
To force tax attribute refresh you need to change the tax calc mode.
Tax calc mode NoCalc does not recalculate totals. This is the default mode.
Setting tax calc mode to ManualCalc is necessary to refresh the updated tax.
Code example to update the tax amount field, you can adapt it to update tax zone.
ARInvoiceEntry invoiceMaint = PXGraph.CreateInstance<ARInvoiceEntry>();
TX.TaxAttribute.SetTaxCalc<ARTran.taxCategoryID>(invoiceMaint.Transaction.Cache, null, TX.TaxCalc.ManualCalc);
invoiceMaint.CurrentDocument.Current = invoiceMaint.Document.Search<ARInvoice.refNbr>("AR005452", ARDocType.Invoice).FirstOrDefault();
invoiceMaint.Taxes.Select();
invoiceMaint.Taxes.Current = invoiceMaint.Taxes.Search<ARTaxTran.taxID>("CAGST").FirstOrDefault();
invoiceMaint.Taxes.Cache.SetValueExt<ARTaxTran.curyTaxAmt>(invoiceMaint.Taxes.Current, 3);
invoiceMaint.Taxes.Update(invoiceMaint.Taxes.Current);
invoiceMaint.SelectTimeStamp();
invoiceMaint.Save.Press();

Related

Enable SOLine field after Order Completed

I need to enable the Salesperson ID and Commissionable fields of Sales Order Lines for Sales Orders in the Completed state.
I referenced the question here about enabling fields in the SOOrder header: How to enable CustomerOrderNbr field in Sales Order screen?
I added the two fields to the Automation Steps for the SO Complete step
And added customization code:
public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
public void SOOrderLine_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
SOOrderLine line = e.Row as SOOrderLine;
if (line == null) return;
PXUIFieldAttribute.SetEnabled<SOOrderLine.salesPersonID>(sender, line, true);
PXUIFieldAttribute.SetEnabled<SOOrderLine.commissionable>(sender, line, true);
}
}
However, the fields are still disabled. Is there something I'm missing?
I have a similar requirement with one of my clients. You're on the right track with automation steps, but you need something else to enable editing. Here are the two event handlers we use:
protected void SOOrder_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
// Make the promised on ship date field editable even after the order has been completed.
// This code is not enough to make the feature work - automation steps need to be modified for SO Completed and SO Invoiced to ensure the
// caches are not disabled.
sender.AllowUpdate = true;
Base.Transactions.Cache.AllowUpdate = true;
}
protected void SOLine_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
if (Base.Document.Current != null)
{
//Automation steps were modified to keep the transactions grid enabled for the completed status; we are manually disabling it here but leaving the promised on ship date field editable.
if(Base.Document.Current.Status == SOOrderStatus.Completed)
PXUIFieldAttribute.SetEnabled(sender, e.Row, false);
PXUIFieldAttribute.SetEnabled<SOLineExt.usrPromisedShipOnDate>(sender, e.Row, true);
PXUIFieldAttribute.SetEnabled<SOLineExt.usrLateReasonCode>(sender, e.Row, true);
}
}
To finish out the solution to this, in this case I found it was not necessary to Enable the full Sales Order Line via Automation Steps and then disable it via SOLine_RowSelect. It was, however, necessary to add the Sales Order > Order Nbr field to the automation steps (to make the document Save available after changing the Sales Order line). And strangely it was also necessary for us to give this Customization Project a higher Level than the others implementing it after other customizations that may have made changes to the same screen or objects.
public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
protected void SOOrder_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
sender.AllowUpdate = true;
Base.Transactions.Cache.AllowUpdate = true;
}
protected void SOLine_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
if (Base.Document.Current != null)
{
PXUIFieldAttribute.SetEnabled<SOLine.salesPersonID>(sender, e.Row, true);
PXUIFieldAttribute.SetEnabled<SOLine.commissionable>(sender, e.Row, true);
}
}
}

Acumatica Unbound User Fields on SOOrder

I have a user field on an SOOrder DAC extension, which is a sum of some of the lines on the document (based on a field in the SOLine extension). When I add a new line, the total is updating properly. However, when I load the document for the first time, the screen is showing 0.00. I created an SOOrderEntry extension and I put code into the SOLine_RowSelecting event handler. When I load the document, it steps into the code and it looks like it is setting the fields properly, but they don't show on the screen. The same method is called from the SOLine_CuryLineAmt_FieldUpdated, and that works just fine. Here is the code I'm using:
public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
{
//Used to prevent recursive calls in RowSelecting
bool _isCalculating = false;
protected virtual void SOLine_RowSelecting(PXCache cache, PXRowSelectingEventArgs e)
{
var row = e.Row as SOLine;
if (row == null) return;
using (new PXConnectionScope())
{
if (!_isCalculating)
CalcTotals();
}
}
protected virtual void SOLine_CuryLineAmt_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
if (!_isCalculating)
CalcTotals();
}
public void CalcTotals()
{
SOOrder order = Base.CurrentDocument.Select();
if (order == null) return;
_isCalculating = true;
var orderExt = order.GetExtension<SOOrderExt>();
orderExt.UsrMyCustomField = 0m;
//Get totals
foreach (SOLine lineSum in Base.Transactions.Select())
{
var lineSumExt = lineSum.GetExtension<SOLineExt>();
if (lineSumExt.UsrMyCondition)
orderExt.UsrMyCustomField += lineSum.CuryLineAmt;
}
_isCalculating = false;
}
}
RowSelected is called on each callback to select the data. There's no need to re-calculate on FieldUpdated event too because RowSelected will be called when updating records. Therefore consider removing SOLine_CuryLineAmt_FieldUpdated
You have the RowSelected event declared for SOLine DAC. The event then selects all SOLine to compute the totals. This amount to when selecting one of the Detail compute the total of all Detail, that smells lack a recursive pattern. Therefore consider declaring RowSelected on the Master document which is SOOrder in this case and remove all the workarounds you have to break recursion.
There's no null check in computations. Acumatica DAC fields are nullable. With your code you can end up in situation where you add null to a number which would results in type violation at runtime. Therefore consider checking if CuryLineAmt is null before using it's value to compute the total.
You are accumulating the total in the UsrMyCustomField DAC field
using the += addition assignment operator. It works but I would
advise against that. The DAC fields aren't meant as register for
computations or temporary value place-holder. Therefore consider
accumulating the total in a local variable and assign only the final
computed value to the DAC field.
Code to compute a total with all these points considered:
public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
{
public void SOOrder_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
SOOrder order = e.Row as SOOrder;
if (order != null)
{
SOOrderExt orderExt = order.GetExtension<SOOrderExt>();
if (orderExt != null)
{
decimal total = 0M;
foreach (SOLine line in Base.Transactions.Select())
{
total += line.CuryLineAmt.HasValue ? line.CuryLineAmt.Value : 0M;
}
orderExt.UsrMyCustomField = total;
}
}
}
}

How to enable CustomerOrderNbr field in Sales Order screen?

In Sales Order screen, I'm trying to enable the CustomerOrderNbr field if the status is completed
protected void SOOrder_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
SOOrder doc = e.Row as SOOrder;
if (doc == null)
{
return;
}
if (doc.Completed == true )
{
PXUIFieldAttribute.SetEnabled(cache, doc , true);
PXUIFieldAttribute.SetEnabled<SOOrder.customerOrderNbr>(cache, doc, true);
}
}
however, it remains disabled and not doing what it's supposed to do. So what am I doing wrong ? Am I on the right event to override at all ?
Or is the screen really locked in once the Sales Order is Completed ?
Thanks for any answers.
Since Sales Orders screen is heavily driven by Automation Steps, in addition to extended RowSelected handler for the SOOrder DAC, it's an absolute must to modify automation steps for Completed orders that disable entire SOOrder :
In addition to the automation step change shown above, you should keep SOOrder_RowSelected handler:
public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
{
public void SOOrder_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
SOOrder order = e.Row as SOOrder;
if (order == null) return;
if (order.Completed == true)
{
PXUIFieldAttribute.SetEnabled<SOOrder.customerOrderNbr>(sender, order, true);
}
}
}
With those 2 changes in place, Customer Order will stay enabled for SO Orders with Completed status:

Disable an entire Row in AR Invoice Lines (AEF)

I know it is possible to enable/disable particular fields progrmmatically. Is it also possible to disable editing of the entire row? I am referring to the Transactions (ARTran) in the Invoice Entry screen. I would want to disable changing any values in the line on particular conditions.
PXUIFieldAttribute.SetEnabled method has an overload that works on all fields of a row.
public class SOInvoiceEntry_Extension : PXGraphExtension<SOInvoiceEntry>
{
protected virtual void ARTran_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
ARTran row = e.Row as ARTran;
if (row == null)
{
return;
}
bool myCondition = false;
PXUIFieldAttribute.SetEnabled(Base.Caches[typeof(ARTran)], row, myCondition);
}
}

Make Salesperson ID a Required field on SOLine

I need to make Salesperson ID on SOLine as a required field. But as Transfer orders do not have Salesperson, hence it should only validate when I create orders other than Transfer orders.
I tried with below code but it seems it is not working. Might be it is overrided with some existing code. Let me know if anyone has any suggestions.
public PXSetup<SOOrderTypeOperation,
Where<SOOrderTypeOperation.orderType, Equal<Optional<SOOrderType.orderType>>,
And<SOOrderTypeOperation.operation, Equal<Optional<SOOrderType.defaultOperation>>>>> sooperation;
protected bool IsTransferOrder
{
get
{
return (sooperation.Current.INDocType == INTranType.Transfer);
}
}
protected virtual void SOLine_RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
{
var row = (SOLine)e.Row;
if (row == null) return;
PXDefaultAttribute.SetPersistingCheck<SOLine.salesPersonID>(sender, row, IsTransferOrder ? PXPersistingCheck.Nothing : PXPersistingCheck.Null);
}
I usually thrown an appropriate exception in Row Persisting when the condition exists.
Here is an example from SOShipmentEntry checking for transfer and checking the null value of a field:
protected virtual void SOShipment_RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
{
SOShipment doc = (SOShipment)e.Row;
if (doc.ShipmentType == SOShipmentType.Transfer && doc.DestinationSiteID == null)
{
throw new PXRowPersistingException(typeof(SOOrder.destinationSiteID).Name, null, ErrorMessages.FieldIsEmpty, typeof(SOOrder.destinationSiteID).Name);
}
}
I have also called RaiseExceptionHandling similar to this example within RowPersisting
// sender = PXCache
if (row.OrderQty == Decimal.Zero)
sender.RaiseExceptionHandling<POLine.orderQty>(row, row.OrderQty, new PXSetPropertyException(Messages.POLineQuantityMustBeGreaterThanZero, PXErrorLevel.Error));
Both examples should stop the page from the save. calling the Raise Exception handling should point out the field with the Red X which is the better approach and easier for the user to find the field in question.
For your example:
protected virtual void SOLine_RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
{
SOLine row = (SOLine)e.Row;
if (row == null)
{
return;
}
if (!IsTransferOrder && row.SalesPersonID == null)
{
sender.RaiseExceptionHandling<SOLine.salesPersonID>(row, row.SalesPersonID, new PXSetPropertyException(ErrorMessages.FieldIsEmpty, PXErrorLevel.Error));
}
}

Resources