Using ChatGPT to modify Acumatica to Default Warehouse on a Sales Order based on User Profile’s Default Warehouse

Today I was working on an implementation with a customer and we were reviewing their workflow processes. We wanted to default the Warehouse on the Sales Order based on the person entering it. In Acumatica this is normally not possible with out of the box functionality. There is no field on the Branch which defaults a Warehouse, so this would require a new custom field and custom coding. But we also have the default warehouse on the User Profile. The issue is that field is only used with the Mobile App for Warehouse Management.

Either way a customization is required to get the result desired. When thinking of what to customize, the smallest changes and changes to area’s that most likely won’t be modified by Acumatica much in the future are always the best pathway. In this case I decided to use the User’s Default Warehouse from their profile as the source so that I didn’t have to make a new field on the Branch. Honestly, either way would’ve been fine.

To start, I went into Sales Order screen and used the inspect element feature to identify the DAC and field for Warehouse. This was SOLine and SiteID.

Inspect Element using Customization drop down selection or holding Ctrl+Alt on your keyboad.

I did the same inspect element on my User Profile and identified UserPreferences DefaultSite for the Data Class and Data Field.

Let me preface this with I’m not a programmer. I have a background with Crystal Reports and I’ve used SQL commands but I could not write C# or even a Macro in Excel.

I used ChatGPT to code for me.

ChatGPT is a great TOOL, and is even better when you give it a good prompt. I went with the following:

In Acumatica, I would like to create a Customization to the Sales Order Screen SO301010 to default the Warehouse on the Detail lines, SOLine.SiteID, to pull from the current users profile in UserPreferences.DefaultSite if it is entered, otherwise it shoudl follow the normal logic, but this should superceed it.

ChatGPT 5.0 thought it out and gave me some code back.

Results from ChatGPT

Well, there’s a few things this is doing for us. Most of the instructions here aren’t super clear. With the results shown above, this is telling me first, it’s a Graph Extension. Sometimes you will have a DAC Extension, so there is a distinction with how this is presented.

Either way, you need to have a Customization Package to do this. No problem!

Go to Customization Projects, select the + to add a new line and give it a name. Then Save it. There are some considerations here, like, do you have other Customizations? In the other Customizations is this screen also altered. If it is, there could be a conflict between a new package and an existing package. At that point you may want to review with a Developer. If your system is pretty vanilla, you’re probably good.

Here I added DEMO as my new Customization Package.

My next step is to go into DEMO by clicking on the hyperlink. This will open the Customization screen. I then selected CODE and the + sign. Here I’m selecting Graph Extension. This is what ChatGPT was referencing from above.

Acumatica Customization Package showing adding a new Graph Extension.

Next, ChatGPT also told me the Class Name. That’s where it said I wanted to change SOOrderEntry. At first glance that’s confusing because I don’t see that anywhere when I do my Inspect Element or as a User. In the Class Name lookup you’ll find numerous references to SOOrderEntry. The way I like to think about this is you always want the most generic version and the most specific to what you’re doing. After extensively scanning this list, I eventually found the one I wanted PX.Objects.SO.SOOrderEntry.

Base Graph selection. Always select the most basic option for the Workspace you’re modifying. SO and SOOrderEntry.

After some processing the Code will populate. There’s a lot of stuff here I don’t really understand, again, not a programmer.

The first list in the code shows all the elements in the Acumatica Framework that the system will use. Then the second portion, the Name Space is like a template of code.

Acumatica’s Customization screen for CODE

When I compare this to what ChatGPT gave me the first thing I want to do is compare the “using” to the code given to me. Most of the elements ChatGPT put in it’s code output are listed here, except for one: using PX.SM. I went ahead and added this to the bottom of the using list.

Then Chat GPT gave me code for namespace. I replaced the entire namespace section down to the ending bracket with what ChatGPT gave me. That’s it!

Now I selected Save.

I want to test the code before I do anything that might damage my system. So I go to Publish and select “Validate Current Project”. DO NOT select Publish Current Project. It’d fail but always validate first.

From here you can Publish to the tenant you’re currently logged into but more importantly we can check that our code is correct by Validate Current Project.

After Validating you’ll get back some error messages. I usually just copy these and give them back to ChatGPT but the issue with this example was even though I told it the field name in UserPreferences was DefaultSite it labeled it as DefaultSiteID. There was another problem with the code, but this one I was able to identify from the error and specifically told it to ChatGPT. We went back and forth two more times with errors.

ChatGPT isn’t perfect and doesn’t know everything. This is where your knowledge and the knowledge of your Partner can be very helpful.

When you do this with ChatGPT it does not return the full code, it gives the code starting from after the NameSpace, so when you copy it in you want to leave the NameSpace and the last bracket. If you mess this up, which I have so don’t worry if you do, you can always ask Chat GPT to give you the FULL CODE.

Once you’ve worked through the errors the last step is to Publish the Project. When you Publish, the system restarts the Website. This means it will interrupt anyone doing data entry, so make sure you’re the only person in there or do this after hours.

Now check your results and see what you got!

Here’s my full code:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using PX.Common;
using PX.Data;
using PX.Objects.AP;
using PX.Objects.AR;
using PX.Objects.CA;
using PX.Objects.CM;
using PX.Objects.CR;
using PX.Objects.CS;
using PX.Objects.DR;
using PX.Objects.EP;
using PX.Objects.GL;
using PX.Objects.IN;
using PX.Objects.PM;
using PX.Objects.PO;
using PX.Objects.TX;
using POLine = PX.Objects.PO.POLine;
using POOrder = PX.Objects.PO.POOrder;
using PX.CarrierService;
using PX.Concurrency;
using CRLocation = PX.Objects.CR.Standalone.Location;
using ARRegisterAlias = PX.Objects.AR.Standalone.ARRegisterAlias;
using PX.Objects.AR.MigrationMode;
using PX.Objects.Common;
using PX.Objects.Common.Discount;
using PX.Objects.Common.Extensions;
using PX.CS.Contracts.Interfaces;
using Message = PX.CarrierService.Message;
using PX.Data.DependencyInjection;
using PX.Data.WorkflowAPI;
using PX.LicensePolicy;
using PX.Objects.SO.GraphExtensions.CarrierRates;
using PX.Objects.SO.GraphExtensions.SOOrderEntryExt;
using PX.Objects.SO.Attributes;
using PX.Objects.Common.Attributes;
using PX.Objects.Common.Bql;
using OrderActions = PX.Objects.SO.SOOrderEntryActionsAttribute;
using PX.Data.BQL.Fluent;
using PX.Objects.IN.InventoryRelease;
using PX.Data.BQL;
using PX.Objects.IN.InventoryRelease.Utility;
using PX.Objects.SO.Standalone;
using PX.Objects.IN.InventoryRelease.Accumulators.QtyAllocated;
using PX.Objects.Common.Interfaces;
using static PX.Objects.Common.Discount.DiscountEngine;
using PX.Objects;
using PX.Objects.SO;
using PX.SM;

namespace PX.Objects.SO
{
    public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
    protected void _(Events.FieldDefaulting<SOLine.siteID> e, PXFieldDefaulting baseMethod)
    {
        if (e.Row == null)
        {
            baseMethod?.Invoke(e.Cache, e.Args);
            return;
        }

        // Get current user's prefs and its extension
        var prefs = UserPreferences.PK.Find(Base, PXAccess.GetUserID());
        var prefsExt = prefs?.GetExtension<UserPreferenceExt>(); // holds DefaultSite

        int? defaultSiteID = prefsExt?.DefaultSite;

        if (defaultSiteID.HasValue)
        {
            e.NewValue = defaultSiteID;
            e.Cancel = true; // supersede normal defaulting
            return;
        }

        baseMethod?.Invoke(e.Cache, e.Args); // fall back to standard logic
    }
}
}

Next
Next

Success Story: How Clive Coffee Grew Their Business with Acumatica