Skip to main content
Question

SO303000 - Error: The entry form (ID: SO303000, title: Invoices) cannot be automated. Object reference not set to an instance of an object.

  • September 29, 2025
  • 6 replies
  • 80 views

Hi, I'm working on a customization project in Acumatica 2024 R2 to allow for split payments for Cash Sales. I have a custom DAC that will store the multiple payments for a cash sale if it's allowed for that invoice. However after trying to add a line counter to the SOInvoiceExt to keep track of the lines added for the split payment I'm unable to load the SO303000 screen. Please see below code for the SOInvoice DAC, Custom DAC and the extended graph. Any help is greatly appreciated.

**SOInvoiceExt**
public class SOInvoiceExt : PXCacheExtension<PX.Objects.SO.SOInvoice>
{
#region UsrSplitPayment
[PXDBBool]
[PXDefault(false)]
[PXUIField(DisplayName="Split Payment")]
public bool? UsrSplitPayment { get; set; }
public abstract class usrSplitPayment : PX.Data.BQL.BqlBool.Field<usrSplitPayment> { }
#endregion


#region UsrSplitLineCntr
[PXDBInt()]
[PXDefault(0)]
//[PXUIField(DisplayName="Split Line Counter", Enabled= false)]
public int? UsrSplitPaymentLineCntr {get; set;}
public abstract class usrSplitPaymentLineCntr: PX.Data.BQL.BqlInt.Field<usrSplitPaymentLineCntr> {}
#endregion

//rest of fields
}
}

**Custom DAC (SplitPaymentLine)**
using System;
using PX.Data;
using PX.Objects.SO;
using PX.Objects.AR;
using PX.Data.BQL;
using PX.Data.BQL.Fluent;
using PX.Objects.CS;
using PX.Objects.CA;

namespace SplitPayment
{
public static class SplitLineTypes
{
public const string Payment = "P";
public class payment : BqlString.Constant<payment>
{
public payment() : base(Payment) { }
}
}

[Serializable]
[PXCacheName("SplitPaymentLine")]
public class SplitPaymentLine : PXBqlTable, IBqlTable
{
#region LineNbr
[PXDBInt(IsKey = true)]
[PXLineNbr(typeof(SOInvoiceExt.usrSplitPaymentLineCntr))]
public virtual int LineNbr { get; set; }
public abstract class lineNbr : PX.Data.BQL.BqlInt.Field<lineNbr> { }
#endregion

#region RefNbr
[PXDBString(15,IsKey = true, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Ref Nbr")]
[PXDBDefault(typeof(SOInvoice.refNbr), PersistingCheck = PXPersistingCheck.Nothing)]
//[PXParent(typeof(SelectFrom<SOInvoice>
// .Where<SOInvoice.docType.IsEqual<SplitPaymentLine.docType.FromCurrent>
// .And<SOInvoice.refNbr.IsEqual<SplitPaymentLine.refNbr.FromCurrent>>>))]
public virtual string RefNbr { get; set; }
public abstract class refNbr : PX.Data.BQL.BqlString.Field<refNbr> { }
#endregion

#region DocType
[PXDBString(3, IsKey = true, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Doc Type")]
[PXDBDefault(typeof(SOInvoice.docType), PersistingCheck = PXPersistingCheck.Nothing)]
[PXParent(typeof(Select<SOInvoice,
Where<SOInvoice.docType, Equal<Current<SplitPaymentLine.docType>>,
And<SOInvoice.refNbr, Equal<Current<SplitPaymentLine.refNbr>>>>>))]
public virtual string DocType { get; set; }
public abstract class docType : PX.Data.BQL.BqlString.Field<docType> { }
#endregion

//rest of fields
}
}

**Graph**
public class SOInvoiceEntry_Extension : PXGraphExtension<PX.Objects.SO.SOInvoiceEntry>
{
public SelectFrom<SplitPaymentLine>
//.Where<SplitPaymentLine.companyID.IsEqual<SOInvoice.companyID.FromCurrent>
.Where<SplitPaymentLine.docType.IsEqual<SOInvoice.docType.FromCurrent>
.And<SplitPaymentLine.refNbr.IsEqual<SOInvoice.refNbr.FromCurrent>>>
.View SplitPayments;


//[PXLineNbr(typeof(SOInvoiceExt.usrSplitPaymentLineCntr))]
protected void SplitPaymentLine_LineNbr_CacheAttached(PXCache sender) { }

public PXSetup<SOSetup> soSetup;

#region Event Handlers
//SOInvoice
protected void _(Events.FieldUpdated<SOInvoice, SOInvoice.docType> e)
{
var row = (SOInvoice)e.Row;
if(row == null) return;

PXTrace.WriteInformation("Doc Type: {0}",row.DocType);
//clear usrSplitPayment
row.GetExtension<SOInvoiceExt>().UsrSplitPayment = false;

//Trigger RowSelected event
e.Cache.RaiseRowSelected(row);
}

#region SOInvoice.RowSelected
protected void _(Events.RowSelected<SOInvoice> e)
{
PXTrace.WriteInformation("Event RowSelected: SOInvoice");
var row = e.Row;
if(row == null) return;
bool allowSplitPayment = false;
if(!string.IsNullOrEmpty(row.DocType))
{
allowSplitPayment = row.DocType == ARDocType.CashSale;
//Enable/Disable Split Payment Checkbox
PXUIFieldAttribute.SetEnabled<SOInvoiceExt.usrSplitPayment>(e.Cache, row, allowSplitPayment);

//Fetch Split Payment value
bool splitPayment = row.GetExtension<SOInvoiceExt>()?.UsrSplitPayment == true;
PXTrace.WriteInformation("Split Payment: {0}", splitPayment);

//Enable/Disable Grid actions
SplitPayments.Cache.AllowSelect = splitPayment;
SplitPayments.Cache.AllowInsert = splitPayment;
SplitPayments.Cache.AllowDelete = splitPayment;
SplitPayments.Cache.AllowUpdate = splitPayment;
//SplitPayments.Cache.AllowSave = false;

//rest of business logic

}
}
#endregion

//SOInvoice Row Persisting
protected void _(Events.RowPersisting<SOInvoice> e)
{
var row = e.Row;
if(row == null) return;

if(row.DocType == ARDocType.CashSale)
{
bool splitPayment = row.GetExtension<SOInvoiceExt>()?.UsrSplitPayment == true;
if(splitPayment)
{

var splitLineCount = SplitPayments.Cache.Cached
.OfType<SplitPaymentLine>()
.Count(l => SplitPayments.Cache.GetStatus(l) != PXEntryStatus.Deleted
&& !string.IsNullOrEmpty(l.LineDocType) // Use DocType, not SplitDocType
&& l.PayAmount > 0);
//SplitPayments.Cache.Cached
// .OfType<SplitPaymentLine>()
// .Count(l => SplitPayments.Cache.GetStatus(l) != PXEntryStatus.Deleted
// && l.DocType != null);
//SplitPayments.Cache.Cached
//.OfType<SplitPaymentLine>()
//.Count(l => SplitPayments.Cache.GetStatus(l) != PXEntryStatus.Deleted);
//SplitPayments.Cache.Cached.Count(x => x != null && ((SplitPaymentLine)x).DocType != null);//SplitPayments.Select().Count();
PXTrace.WriteInformation("Lines: {0}", splitLineCount);
if(splitLineCount < 2)
{
throw new PXException("At least two split payment lines are required when 'Split Payment' is enabled.");
}

//Loop thru each SplitPayment Line


}
else
{
//Delete lines
foreach (SplitPaymentLine line in SplitPayments.Select())
{
SplitPayments.Delete(line);
}
}
}

}




//SplitPaymentLine
protected void _(Events.RowSelected<SplitPaymentLine> e)
{
var row = e.Row;
if(row == null) return;

var docType = row.LineDocType ;
PXTrace.WriteInformation("Line Doc Type: {0}", docType);

//rest of logic

}

//rest of event handlers


#endregion


#region Business Logic
private int GetNextLineNumber()
{
var invoice = Base.Document.Current;
if (invoice == null) return 1;

var maxLine = SplitPayments.Select().RowCast<SplitPaymentLine>()
// .Where(x => x.DocType == invoice.docType &&
// x.RefNbr == invoice.RefNbr)
.Max(x => x.LineNbr);

return maxLine + 1;
}



#endregion


}
}

 I added a new tab to the SO303000 screen for entering the multiple payment info. This tab is shown based on a checkbox and the records are linked to the invoice by the refnbr and doctype. The line counter is setup similar to the grid in the Details tab

6 replies

mohammadnawaz51
Jr Varsity I
Forum|alt.badge.img+4

@mecheann Can you review the below article?

https://asiablog.acumatica.com/index.php/2019/04/this-form-cannot-be-automated-investigation/#:~:text=In%20case%20there%20is%20any,message%20is%20even%20more%20confusing.


Dmitrii Naumov
Acumatica Moderator
Forum|alt.badge.img+7
  • Acumatica Moderator
  • September 29, 2025

@mecheann It usually means you have UI elements not aligning with your DACs. But I think the bigger question is why you even need the code there? SO303000 allows multiple payments OOTB. You probably just need to use a different document type instead. That would be a much better route. Otherwise you are going too deep in the release process, adjusting GL posting, etc.


  • Author
  • Freshman I
  • September 29, 2025

@Dmitrii Naumov the requirement for the customization is for Cash Sales so the doc type cannot be changed unfortunately.

@mohammadnawaz51 I’m using a VM to do the development. The screen only loads after I restart the VM despise no change in the code.


Dmitrii Naumov
Acumatica Moderator
Forum|alt.badge.img+7
  • Acumatica Moderator
  • September 29, 2025

@mecheann  I’d recommend to rethink the requirement. I don’t think users really insist that it’s a Cash Sale specifically. You can have SO document type like “cash sale with multiple payments” that behaves in a similar way but generates SO of type invoice instead.

 

Otherwise you’ll face a lot of challenges when it comes to GL posting, including ones that leave GL accounts unbalanced and things like that. 


  • Author
  • Freshman I
  • September 29, 2025

@Dmitrii Naumov Unfortunately, having the doc type be different is non-negotiable. The compromise that was made is for a transfer account setup instead


Chris Hackett
Community Manager
Forum|alt.badge.img
  • Acumatica Community Manager
  • November 5, 2025

Hi ​@mecheann were you able to find a solution? Thank you!