I am trying to set a custom field I created in the INTranSplit table. I fetch the target record, set the custom field, update the view holding the records for the page, and perform a PressSave(). At some point during the save, the code sets the INTranSplit.TranDate to null and throws an exception.
Here is my code:
public PXAction<EMPWorkOrder> addSerialNbr;
[PXUIField(DisplayName = "Add Serial Number")]
protected void AddSerialNbr()
{
AvailableSNFilter serialNbrRow = AvailableSNs.Current;
if (serialNbrRow.AvailableSNs != null)
{
//Gets record from DAC created from a SQL View
GetAvailableSerialNumbers targetSN = (GetAvailableSerialNumbers)PXSelectorAttribute.Select<AvailableSNFilter.availableSNs>(AvailableSNs.Cache, serialNbrRow);
//Fetches the INTranSplit record
INTranSplit tran = PXSelect<INTranSplit, Where<INTranSplit.refNbr, Equal<Required<GetAvailableSerialNumbers.refNbr>>, And<INTranSplit.lotSerialNbr,
Equal<Required<GetAvailableSerialNumbers.lotSerialNbr>>, And<INTranSplit.inventoryID, Equal<Required<GetAvailableSerialNumbers.inventoryID>>
,And<INTranSplit.lotSerialNbr, NotEqual<Required<INTranSplit.lotSerialNbr>>>>>>>
.Select(this, targetSN.RefNbr, targetSN.LotSerialNbr, targetSN.InventoryID, string.Empty);//, targetSN.SplitLineNbr);
INTranSplitExt tranExt = PXCache<INTranSplit>.GetExtension<INTranSplitExt>(tran);
//Sets the custom field
tranExt.UsrWOID = CurrentDocument.Current.Id;
WorkOrderSerialNumbers.Update(tran);
serialNbrRow.AvailableSNs = string.Empty;
AvailableSNs.Update(serialNbrRow);
this.Actions.PressSave();
}
else
{
Document.Ask("AvailableSN Null", MessageButtons.OK);
}
}
The error I get is this:
Error #14: Updating 'IN Transaction Split' record raised at least one error. Please review the errors.
Error: 'Transaction Date' cannot be empty.
I stepped through the process and found that the TranDate is not null until after the code enters the PressSave() function.
Is there any way to solve this issue?
Make sure you are including the parent views and setting current for INRegister and INTran as TranDate in the INTranSplit is based on INRegister.
Related
I have a similar type of issue as in
Acumatica refer custom field to another custom field on different screen
except that I am using custom source fields.
I created and added 2 fields to the SO Line to capture EDI data needed for invoicing. I created 2 new fields on the invoice line with the same name (different data class of course) on the SO Invoice form. Below is the code for 1 field on each of the forms:
SO301000 (Sales Orders):
[PXDBString(3)]
[PXUIField(DisplayName="Cust.Invoice Line Nbr.")]
[PXFormula(typeof(Selector<SOLineExt.usrCInvLine, ARTranExt.usrCInvLine>))]
SO303000 (Invoices):
[PXDBString(3)]
[PXUIField(DisplayName="Cust.Invoice Line Nbr.")]
It compiles but the data is not being copied to the invoice when created from shipment. I also added 1 of the fields to the shipment form for testing purposes but it does nit capture that value either.
Do I have this backwards?
After reviewing it into more details, you could override the Prepare Invoice method on SOOrderEntry graph (and also anywhere else is needed).
By overriding the PrepareInvoice Method of SOOrderEntry graph you could add a Handler(RowInserting) to SOInvoiceEntry graph to populate your new Custom field with the value on SOOrder.
See sample below where I'm doing similar action copying value from SOOrder(Header) to ARTran(details).
You can use this sample to achieve your goal: (In your case you could use ARInvoice instead of ARTran)
SOOrderEntry:
public class SOOrderEntry_Extension:PXGraphExtension<SOOrderEntry>
{
public PXAction<SOOrder> prepareInvoice;
[PXUIField(DisplayName = "Prepare Invoice", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select, Visible = false)]
[PXButton]
public virtual IEnumerable PrepareInvoice(PXAdapter adapter)
{
PXGraph.InstanceCreated.AddHandler<SOInvoiceEntry>((graph) =>
{
graph.RowInserting.AddHandler<ARTran>((sender, e) =>
{
//Custom logic goes here
var row = (ARTran)e.Row;
if (row == null)
return;
SOOrder order = Base.Document.Current;
if (order != null)
{
var tranExt = PXCache<ARTran>.GetExtension<ARTranExt>(row);
//Below to be change to your needs
//Here you can look for the SOLine of the SOOrder instead
var orderExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(order);
if (orderExt != null && tranExt != null)
{
tranExt.UsrContactID = orderExt.UsrContactID;
}
//END
}
});
});
return Base.prepareInvoice.Press(adapter);
}
}
You could also find more information and instructions provided by my colleague on the stackoverflow link below:
Acumatica custom field SOLine transferred to ARTran
I'm trying to copy the value from a custom field called "customerExt.UsrRfc" to TaxRegistrationID field but it doesn't work in Customers screen, I'm using Customer_RowPersisting event handler.
This is the customerExt.UsrRfc field:
This is the TaxRegistrationID field:
This is the RowPersisting event:
protected void Customer_RowPersisting(PXCache cache, PXRowPersistingEventArgs e)
{
Customer row = (Customer)e.Row;
if (row == null)
{
return;
}
var customerExt = row.GetExtension<BAccountExt>();
row.TaxRegistrationID = customerExt.UsrRfc;
}
I tried to copy the value to another field like "Account Ref #" and it works fine.
Can you help me with this?
The TaxRegistrationID field in the screenshot is from Location DAC instead of Customer:
You need to change the solution to update the field in the correct view.
I have tried multiple ways, but getting Another process error in the default version of Acumatica 19.106.0020
On top of it i have a customized code on both customer and contact screen, my requirement to clear the value of the custom field that is created in contact table when customer is deleting from the screen AR303000 i need to set null value of the custom field for the deleted contact from the customer.
i have tried by setting value on Customer_RowDeleting event but continuously getting Another process error, below is the screenshot error
Below is the code that i was tried
protected virtual void Customer_RowDeleting(PXCache sender, PXRowDeletingEventArgs e, PXRowDeleting BaseEvent)
{
BaseEvent?.Invoke(sender, e);
Customer rows = e.Row as Customer;
if (rows == null)
return;
if (Base.BAccount.Cache.GetStatus(Base.BAccount.Current) == PXEntryStatus.Deleted)
{
foreach (Contact BACT in PXSelectReadonly<Contact,
Where<Contact.bAccountID, Equal<Required<Contact.bAccountID>>,
And<Contact.contactType, NotEqual<ContactTypesAttribute.bAccountProperty>>>>.Select(Base, rows.BAccountID))
{
ContactMaint congraph = PXGraph.CreateInstance<ContactMaint>();
Contact CTData = PXSelectReadonly<Contact,
Where<Contact.contactID, Equal<Required<Contact.contactID>>>>.Select(Base, BACT.ContactID);
if (CTData != null)
{
congraph.Contact.Current = CTData;
if (congraph.Contact.Current != null)
{
congraph.Contact.SetValueExt<ContactExt.usrKWBAccountId>(congraph.Contact.Current, null);
congraph.Contact.Update(congraph.Contact.Current);
congraph.Save.Press();
}
}
}
}
}
Thanks in advance.
Hi Chris, please find the attached image here
I don't recommend to create graphs during RowDeleting event. If you have Acuminator installed, you will see a warning about creating graphs in event handlers.
Instead, call your custom code during the Persist method. Persist method is called during Delete operation. After the Base persist is finished, your custom code can perform it's work. Try something like this
public class CustomerMaint_Extension : PXGraphExtension<CustomerMaint>
{
public delegate void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
Customer currentCustomer = Base.CurrentCustomer.Current; //get the customer record before it's deleted, i.e. Customer.bAccountID
baseMethod(); //let the base delete process happen first
if (Base.CurrentCustomer.Cache.GetStatus(currentCustomer) == PXEntryStatus.Deleted)
{
using (PXTransactionScope ts = new PXTransactionScope())
{
//here is where you add your code to delete other records
ts.Complete(); //be sure to complete the transaction scope
}
}
}
}
Also you might want to unpublish other customization packages, and see if the error continues without those packages. That is one way to determine the source of the error...by process of elimination.
We have a test DAC called UsrNonRelatedScanField with two fields : OrderNbr and ScanStatus.
Here's our simple query to grab the correct ordernbr and assign it to a SOOrderExt field:
NonRelatedScanField lastScan = PXSelect<NonRelatedScanField,
Where<NonRelatedScanField.orderNbr,
Equal<Required<SOOrder.orderNbr>>>>.Select(Base, row.OrderNbr);
if(lastScan != null)
{
rowExt.UsrNonRelatedScanField = lastScan.ScanStatus;
}
This logic is held in a SOOrder_RowSelecting() method.
Full Method implementation:
protected virtual void SOOrder_RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
{
SOOrder row = (SOOrder)e.Row;
if (row == null) return;
SOOrderExt rowExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(row);
NonRelatedScanField lastScan = PXSelect<NonRelatedScanField,
Where<NonRelatedScanField.orderNbr,
Equal<Required<SOOrder.orderNbr>>>>.Select(Base, row.OrderNbr);
if (lastScan != null)
{
rowExt.UsrNonRelatedScanField = lastScan.ScanStatus;
}
}
Expected Results : Get the current Orders scan status from lastScan DAC
Actual Results: Will populate correctly only on the initial order opened. When selecting other orders the old value is persisting unless I manually refresh the page. When manually refreshed the correct data comes in.
I haven't had any issues in the past with BQL queries, this specific query is not behaving as expected.
Thank you
I have set my invoice to Manual Numbering
And I want to assign the invoice number(RefNbr) - if blank, before saving the invoice (AR301000). I've overridden the RowPersisting event as follow:
public class ARInvoiceEntry_Extension:PXGraphExtension<ARInvoiceEntry>
{
protected void ARInvoice_RowPersisting(PXCache cache, PXRowPersistingEventArgs e, PXRowPersisting InvokeBaseHandler)
{
var row = (ARInvoice)e.Row;
if (row != null)
{
//BB-<timestamp> as inv# for testing only
if (string.IsNullOrEmpty(row.RefNbr))
row.RefNbr = "BB-" + DateTime.Now.ToString("hhmmsstt");
}
if(InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
}
}
If I raised a new invoice w/out any lines and saved it. It correctly assigned the Reference Nbr as expected.
Problem is if I raised a new invoice WITH lines. Its complaining that the RefNbr is blank.
What am I missing ? How do I fix this ? Thanks.
Acumatica's PXDBDefaultAttribute only updates field of the dependent records when both the source field of the parent record and the foreign key field of the dependent record are not empty.
When a dependent record is inserted into the cache, PXDBDefaultAttribute will copy the source field value from the parent record to the field it decorates from the dependent record. While the framework is saving new parent record into the database, it first raises RowPersisting event for the parent record. PXDBDefaultAttribute subscribes to the RowPersisting event of the parent record type to save the original source field value:
to locate the parent record after it was saved to the database
or to use it as a restore point for the dependent field in case of an aborted transaction
Also, PXDBDefaultAttribute subscribes to the RowPersisting event of the dependent record type to update the foreign key field with the actual source field value, that has just been recorded in the database. To update the foreign key field PXDBDefaultAttribute must first locate the parent record using the original source field value obtained earlier in the RowPersisting event handler of the parent record type. In case the foreign key field value appears to be empty, there is no chance for PXDBDefaultAttribute to locate the parent record for it and it simply leaves the dependent field empty. This is what eventually is causing the error "RefNbr cannot be empty".
With all that being said, I believe it won't be possible to achieve the desired results if leaving AR Invoice Reference Number empty until it gets saved to the database. As an alternative, let me suggest to default AR Invoice Reference Number to some constant, like < ENTER >, and at the same time replace it with the actual number within the ARInvoice_RowPersisting handler:
using PX.Data;
using System;
namespace PX.Objects.AR
{
public class ARInvoiceNumberingCstAttribute : ARInvoiceType.NumberingAttribute
{
public const string EnterRefNbr = "<ENTER>";
protected override string GetNewNumber()
{
string newNumber = base.GetNewNumber();
if (string.IsNullOrEmpty(newNumber))
{
newNumber = EnterRefNbr;
}
return newNumber;
}
public override void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
{
if (GetNewNumber() == EnterRefNbr) return;
base.RowPersisting(sender, e);
}
}
public class ARInvoiceEntry_Extension : PXGraphExtension<ARInvoiceEntry>
{
[PXRemoveBaseAttribute(typeof(ARInvoiceType.NumberingAttribute))]
[PXMergeAttributes(Method = MergeMethod.Append)]
[ARInvoiceNumberingCst]
protected void ARInvoice_RefNbr_CacheAttached(PXCache sender)
{ }
protected void ARInvoice_RowPersisting(PXCache cache, PXRowPersistingEventArgs e,
PXRowPersisting InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (ARInvoice)e.Row;
if (row != null)
{
//BB-<timestamp> as inv# for testing only
if (row.RefNbr == ARInvoiceNumberingCstAttribute.EnterRefNbr)
row.RefNbr = "BB-" + DateTime.Now.ToString("hhmmsstt");
}
}
}
}