I added a FieldUpdated event handler on DueDate and examined the call stack to find the method which modify DueDate:

It's the [Terms(...)] attribute that contains the logic:
#region TermsID
public abstract class termsID : IBqlField
{
}
/// <summary>
/// The <see cref="PX.Objects.CS.Terms">credit terms</see> associated with the document (unavailable for prepayments and debit adjustments).\
/// Defaults to the <see cref="Vendor.TermsID">credit terms of the vendor</see>.
/// </summary>
[PXDBString(10, IsUnicode = true)]
[PXDefault(typeof(Search<Vendor.termsID,
Where<Vendor.bAccountID, Equal<Current<APInvoice.vendorID>>,
And<Current<APInvoice.docType>, NotEqual<APDocType.debitAdj>>>>),
PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "Terms", Visibility = PXUIVisibility.Visible)]
[APTermsSelector]
[Terms(typeof(APInvoice.docDate), typeof(APInvoice.dueDate), typeof(APInvoice.discDate), typeof(APInvoice.curyOrigDocAmt), typeof(APInvoice.curyOrigDiscAmt))]
public virtual string TermsID
{
get;
set;
}
#endregion
Inside the Terms attribute you'll find the code modifying due date.
You could look into providing a custom attribute or bypassing that logic.
public static void CalcTermsDates(Terms terms, DateTime? docDate, out DateTime? dueDate, out DateTime? discDate)
{
dueDate = null;
discDate = null;
if (docDate != null && terms != null)
{
DateTime DocDate = docDate.Value;
switch (terms.DueType)
{
case TermsDueType.FixedNumberOfDays:
dueDate = DocDate.AddDays((double)terms.DayDue00);
break;
case TermsDueType.Prox:
DateTime sameDayOfNextMonth = DocDate.AddMonths(1);
DateTime firstDayOfNextMonth = new DateTime(sameDayOfNextMonth.Year, sameDayOfNextMonth.Month, 1);
dueDate = firstDayOfNextMonth.AddDays((double)terms.DayDue00);
break;
case TermsDueType.DayOfNextMonth:
dueDate = new PXDateTime(DocDate.Year, DocDate.Month, (int)terms.DayDue00).AddMonths(1);
break;
case TermsDueType.DayOfTheMonth:
int monthShift = DocDate.Day > (int)terms.DayDue00 ? 1 : 0;
dueDate = new PXDateTime(DocDate.Year, DocDate.Month, (int)terms.DayDue00).AddMonths(monthShift);
break;
case TermsDueType.Custom:
int nextmonth = 0;
if (DocDate.Day >= terms.DayFrom00 && DocDate.Day <= terms.DayTo00)
{
if (terms.DayDue00 <= terms.DayTo00)
{
nextmonth = 1;
}
dueDate = new PXDateTime(DocDate.Year, DocDate.Month, (int)terms.DayDue00).AddMonths(nextmonth);
}
if (DocDate.Day >= terms.DayFrom01 && DocDate.Day <= terms.DayTo01)
{
if (terms.DayDue01 <= terms.DayTo01)
{
nextmonth = 1;
}
dueDate = new PXDateTime(DocDate.Year, DocDate.Month, (int)terms.DayDue01).AddMonths(nextmonth);
}
break;
case TermsDueType.EndOfMonth:
dueDate = new DateTime(DocDate.Year, DocDate.Month, 1).AddMonths(1).AddDays(-1);
break;
case TermsDueType.EndOfNextMonth:
dueDate = new DateTime(DocDate.Year, DocDate.Month, 1).AddMonths(2).AddDays(-1);
break;
default:
break;
}
if (terms.InstallmentType == TermsInstallmentType.Multiple)
{
discDate = dueDate;
}
else
{
switch (terms.DiscType)
{
case TermsDueType.FixedNumberOfDays:
discDate = DocDate.AddDays((double)terms.DayDisc);
break;
case TermsDueType.Prox:
DateTime sameDayOfNextMonth = DocDate.AddMonths(1);
DateTime firstDayOfNextMonth = new DateTime(sameDayOfNextMonth.Year, sameDayOfNextMonth.Month, 1);
discDate = firstDayOfNextMonth.AddDays((double)terms.DayDisc);
break;
case TermsDueType.DayOfNextMonth:
discDate = new PXDateTime(DocDate.Year, DocDate.Month, (int)terms.DayDisc).AddMonths(1);
break;
case TermsDueType.DayOfTheMonth:
int monthShift;
if (terms.DueType == TermsDueType.DayOfNextMonth && DocDate.Day <= (int)terms.DayDue00)
monthShift = DocDate.Day >= (int)terms.DayDisc ? 1 : 0;
else if (terms.DueType == TermsDueType.EndOfNextMonth)
monthShift = DocDate.Day >= (int)terms.DayDisc ? 1 : 0;
else
monthShift = DocDate.Day > (int)terms.DayDue00 ? 1 : 0;
discDate = new PXDateTime(DocDate.Year, DocDate.Month, (int)terms.DayDisc).AddMonths(monthShift);
break;
case TermsDueType.EndOfMonth:
discDate = new DateTime(DocDate.Year, DocDate.Month, 1).AddMonths(1).AddDays(-1);
break;
case TermsDueType.EndOfNextMonth:
discDate = new DateTime(DocDate.Year, DocDate.Month, 1).AddMonths(2).AddDays(-1);
break;
default:
break;
}
}
if (discDate > dueDate)
{
discDate = dueDate;
}
}
}
You can create a new attribute (ex: MyTerms) deriving from an existing one (ex: Terms) and override methods (ex: CalcTerms). You then redefine/extend the field that is using that attribute (TermsID) so it uses your attribute (MyTerms) instead of (Terms).
The major issue is when the code you want to change is in methods that can't be overridden, in that case you need to copy paste the code wholesale in your custom attribute and forgo inheritance.
The starting point for that is the Attribute Code. To get it, use VIEW SOURCE feature in customization project editor:

Then use 'Find in files' to get the attribute code:
