0
votes

I'm trying to extend the calculation of terms for invoices (Field TermsID, on Bills and Ajustments screen (AP301000)

The standard behaviour of Acumatica is : When you update the termsID field, it automatically updates the dueDate field and the discountDate field

So my idea was to look at the code in APInvoiceEntry and look for the TermsID_FieldUpdated

protected virtual void APInvoice_TermsID_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
    Terms terms = (Terms)PXSelectorAttribute.Select<APInvoice.termsID>(sender, e.Row);

    if (terms != null && terms.InstallmentType != TermsInstallmentType.Single)
    {
        foreach (APAdjust adj in Adjustments.Select())
        {
            Adjustments.Cache.Delete(adj);
        }
    }
}

But it doesnt seem to do anything except delete the cache of the adjustments. And I dont know what to extend in order to add another way of calculating the terms dates.

(The goal is to allow the following calculation : - I take the invoice date, add 30 days, if i'm below the 10th day of the next month, i set the due date to the 10th day of the next month, else I set the due date to the 10th day of the next next month).

Thanks,

2
Ultimately, what fields are you trying to modify, APInvoice.dueDate and APInvoice.discDate? - Hugues Beauséjour
Also, for which document type do you want to modify DueDate? I'm asking because most of the logic seem to apply only for Prepayment. - Hugues Beauséjour
Yes i'm trying to modify both these fields, but instead of rewriting the logic behind the terms behaviour I wanted to reutilize It, but I fail to find how the termsID is used to update duedate and discDate. I want it to work mainly for bills. (Bills and adjustments screen). - Maxime

2 Answers

1
votes

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

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: enter image description here

Then use 'Find in files' to get the attribute code: enter image description here

0
votes

Implement a APInvoice_DueDate_FieldUpdated event handler, it will be raised when the term is changed.