How Override Persist Method In Acumatica

 

Hello everybody,

today I want to show sample of code on overriding Persist method in Acumatica. 

Consider following scenario, you need to modify saving logic of screen Purchase Orders in Acumatica. How you can achieve this? Following steps can help you to do this:

  1. Create extension class for POOrderEntry
  2. Override Perist method

Both of those details implemented below:

public class POOrderEntryExt : PXGraphExtension<POOrderEntry>
{
 
    [PXOverride]
    public void Persist(Action del)
    {
        //Here you can add some of your code that should be executed before persisting PO Order to database
        del();
    }
 
}

With such simple steps you can modify persisting logic to any needed behaviour. Or even turn it off.

No Comments

Add a Comment
 

 

How To Hide Tab In Acumatica

 

Hello everybody,

today I want to share piece of knowledge about Acumatica controls. Recently I was asked to add one more tab to page Checks and payments.

Let's suppose it was named Allocations. It should be displayed only if type of payment is set to Prepayment.

Such kind of behaviour is possible to implement via three steps:

  1. Add tab with name Allocations
  2. Set VisibleExp to the following: 
    VisibleExp="DataControls[&quot;edDocType&quot;].Value==PPM"
    
  3. Set BindingContext to the form like this:
    BindingContext="form"
    

Or just more detailed picture:

</px:PXTabItem>
	<px:PXTabItem Text="PO Allocations" VisibleExp="DataControls[&quot;edDocType&quot;].Value==PPM" BindingContext="form">
 <Template>

Initially I was puzzled for what does &quot; stands for, but as you probably understand from your html knowledge it stands for quotes.

1 Comment

  • Peter Flaske said

    One more way could be use AllowSelect of Cache

    Adjustments.Cache.AllowSelect =
    doc.DocType != ARDocType.CashSale
    && doc.DocType != ARDocType.CashReturn;

Add a Comment
 

 

Deleteddatabaserecord In Acumatica 2

 

Hello everybody,

today I want to write few words about interesting feature of Acumatica: column DeletedDatabaseRecord. As you may know plenty of ERP systems have strategy of holding everything that ever passed through them, and even if user has huge desire to ask ERP system to forget something via deletion of some entity, ERP "pretends" that record was deleted, but leaves it in db. 

From this standpoint Acumatica can be exception. It allows to preserve records that lived inside of it. Allows mean can preserve or not preserve. 

How programmer can achieve this kind of behaviour? Via column DeletedDatabaseRecord. 

If you want Acumatica to hold some kind of entities in db, just add this column to your table and don't add it to DAC class and you'll have all set up. In case of delete request Acumatica will leave that record in db, but user will not be able to see recrods that he deleted. 

Few more reminders about DeletedDatabaseRecord column. 

DeletedDatabaseRecord should be of type bit,  not null and by default 0 ( which says no, record wasn't deleted ). As soon as user will press on delete button instead of sql delete command will be executed sql update command. And user will get a feeling that he deleted record while in reality he just hide record from other users. 

2 Comments

  • Maksym said

    What is a way to see in UI or in WEB service deleted records?

  • docotor said

    There is not way to see them. Only in database

 

Add a Comment
 

 

Multithreading In Acumatica

 

Hello everybody,

today I want to say a few words about my practice of usage Multithreading in Acumatica. I have a dedicated server which has 40 cores. And I have a need to read plenty of records from some other system. General schema is like this:

  1. Create some graph instance.
  2. Fill created graph instance data views with needed data
  3. At created graph instance call method graphInstance.Actions.PressSave();

And that schema is perfectly workable, but for my disappoitment my 40 cores were not busy at all with doing something useful. So I had to figure out how to use them more fully. 

Here are some fragments of code that I've used:

            int numberOfLogicalCores = Environment.ProcessorCount;
            List<Thread> threads = new List<Thread>(numberOfLogicalCores);
 
            int sizeOfOneChunk = (customers.Count / numberOfLogicalCores) + 1;
 
            for (int i = 0; i < numberOfLogicalCores; i++)
            {
                int a = i;
                var thr = new Thread(
                    () =>
                    {
                        var portionsCustomers = customers.Skip(a * sizeOfOneChunk).Take(sizeOfOneChunk).ToList();
                        InsertCustomersFromList(portionsCustomers);
                    }
                );
                thr.Name = $"thr{i}";
 
                threads.Add(thr);
            }
 
            foreach (var thread in threads)
            {
                thread.Start();
            }
 
            foreach (var thread in threads)
            {
                thread.Join();
            }

As you can see from presented code it doesn't do anything fancy. Get number of logical processors, for each logical processor creates Thread, each thread creates it's own graph instance ( in function InsertCustomersFromList), called method Actions.PressSave().

Also in graph I have the following syncrhonization object:

private Object thisLock = new Object();

Take a look at how lock is used:

            custMaint.Clear(); // This graph was created before
.
.
.
//some filling fields logic             custMaint.DefAddress.SetValueExt<Address.addressLine1>(custMaint.DefAddress.Current, customer.AddressLine1);             custMaint.DefAddress.SetValueExt<Address.addressLine2>(custMaint.DefAddress.Current, customer.AddressLine2);             custMaint.DefAddress.SetValueExt<Address.city>(custMaint.DefAddress.Current, customer.City);             custMaint.DefAddress.SetValueExt<Address.countryID>(custMaint.DefAddress.Current, customer.Country);             custMaint.DefAddress.SetValueExt<Address.state>(custMaint.DefAddress.Current, customer.StateCode);             custMaint.DefAddress.SetValueExt<Address.postalCode>(custMaint.DefAddress.Current, customer.PostalCode);             var subAccount = PXSelect<SubWhere<Sub.subCDEqual<Required<Sub.subCD>>>>                 .Select(custMaint, customer.SalesSubaccount.Replace("-""")).FirstOrDefault();             if (subAccount != null)             {                 var subAcTyped = subAccount.GetItem<Sub>();                 custMaint.DefLocation.SetValueExt<LocationExtAddress.cSalesSubID>(custMaint.DefLocation.Current,                     subAcTyped.SubID);             }             lock (thisLock)             {                 custMaint.Actions.PressSave();             }

And that's it with multithreading. In my tests I've got improvement in 3 times! In sigle threaded mode 110 recoreds went into Acumatica during 3 minutes, but with multithreading feature records went there in 1 minute. 

No Comments

Add a Comment
 

 

Some Notes On Buttons Creation In Acumatica

 

Hello everybody,

today I want to write few words about buttons usage in Acumatica.

So, first of all, if you just need to add button at your form, you can use following syntax:

public PXAction<PrimaryDACClass> SomeAction;

[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Some Action")]
protected virtual void someAction()
{
      ...
}

Following syntax will create a button for you in the top buttons panel of Acumatica.

Take note of CommitChanges=true attribute. In case if you need just some activity without persistance to database, then you can set it to false. But if you want button to save some changes to database then set it always to true.

There is one more way of declaring buttons:

[PXButton]
[PXUIField(DisplayName = "Some action")]
protected virtual IEnumerable someAction(PXAdapter adapter)
{
...
return adapter.Get();
}

take note that in comparison with previous time method of button returns ienumerable, and also in the end has return adapter.Get();

This type of calling should be used if button is called from processing page or if button initializes background operation.

No Comments

Add a Comment
 

 

Some Digging In Tables Of Acumatica

 

Hello everybody,

today I want to document one useful sql query that can be beneficial for cases if you need to dig in Acumatica tables, and need quickly to find type of some particular column.

Here it is:

select * from information_schema.columns where table_name = 'SOLine' and COLUMN_NAME like '%OrderQty%'

that sql will let you know all information about column OrderQty.

No Comments

Add a Comment
 

 

Unit Test Acumatica Pxgraph

 

Hello everybody,

today I want to make another description of how to cover with Unit Tests Graphs in Acumatica. 

First of all I want to say that Acumatica controllers or as Acumatica names them graphs do not have any way to inject any dependency from interface. In such case it can be useful idea to use Microsoft Fakes library. 

So in order to unit test Acumatica code you will have a need to make mix of Acumatica code with Microsoft shims. Imagine that you have following graph declaration:

public class PAllocs : PXGraph<PaymentsAllocation>

and somewhere in your graph you have method like this:

        [PXButton(Tooltip = "Saves values from grid")]
        [PXUIField(DisplayName = "Save")]
        public virtual IEnumerable Save1(PXAdapter adapter)
        {
            var inputtedPoOrders = this.VendorOrders.Select()
            .ToList().Where(a => a.GetItem<POOrder>().GetExtension<POOrderExt>().AllocatedAmount > 0
            ||
                                    a.GetItem<POOrder>().GetExtension<POOrderExt>().AllocatedAmount < 0
            ).ToList();
 
            string currentDif = this.ReportItemFilter.Current.DifAmt.ToString();
 
            var apPayment = this.ReleasedPayments.Current;
            
            if (apPayment.CuryOrigDocAmt != ReportItemFilter.Current.TargetAmt)
            {
                VendorFilter.Ask("""Choose another item or modify po ords in bottom grid",
                    "Please choose another check or modify Amounts in bottom grid",
                    MessageButtons.OK, MessageIcon.Error);
                return adapter.Get();
            }
 
 
            if (inputtedPoOrders.Count == 0 || currentDif != "0.0000")
            {
                var vfRow = this.VendorFilter.Current;
                this.VendorFilter.Ask(vfRow, "Input at least one row""You should enter at least one Allocation payment, and Diff should be 0.0000",
                    MessageButtons.OK, MessageIcon.Error);
                return adapter.Get();
            }
        }

how to unit test it with Microsoft fakes?

Preparation

As I already mentioned in one of my articles, you'll need to prepare your test solution for creation of needed graphs. First step will be to make app.config in your test solution.

Below goes app.config that you'll need to add to your solution with tests. 

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <sectionGroup name="system.web" type="System.Web.Configuration.SystemWebSectionGroup, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
      <section name="pxaccess" type="PX.Data.PXAccessSection, PX.Data" />
      <section name="pxdatabase" type="PX.Data.PXDatabaseSection, PX.Data" />
      <section name="pxtrace" type="PX.Data.PXTraceSection, PX.Data" />
      <section name="pxtranslate" type="PX.Data.PXTranslationSection, PX.Data" />
      <section name="basicAuth" type="PX.Export.Authentication.BasicAuthenticationSection, PX.Export" />
      <section name="formsAuth" type="PX.Export.Authentication.FormsAuthenticationSection, PX.Export" />
      <section name="multiAuth" type="PX.Export.Authentication.AuthenticationManagerSection, PX.Export" />
      <section name="externalAuth" type="PX.Export.Authentication.ExternalAuthenticationSection, PX.Export" />
      <section name="webDAV" type="PX.Export.WebDAV.WebDAVSection, PX.Export" />
      <section name="activeDirectory" type="PX.Data.Access.ActiveDirectorySection, PX.Data" />
      <section name="attachments" type="PX.Data.EP.DynamicAttachmentSection, PX.Data" />
      <section name="odata" type="PX.Api.OData.ODataConfigurationSection, PX.Api.OData" />
      <section name="fullTrustAssemblies" type="System.Web.Configuration.FullTrustAssembliesSection, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" allowDefinition="MachineToApplication" />
    </sectionGroup>
    <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
    <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  </configSections>
  <connectionStrings>
    <remove name="ProjectX" />
    <add name="ProjectX" providerName="System.Data.SqlClient" connectionString="Data Source=DESKTOP-0EC1M8R;Initial Catalog=MohawkDB;Integrated Security=False;User ID=sa;Password=123" />
  </connectionStrings>
  <location inheritInChildApplications="false">
    <system.web>
      <machineKey validationKey="187C46E676C11D108F5AB1CEAB16A4CABFB1DEF8A77B54045FBAE8556E015744C6A5099963847BD36D3B673752455ABBC4528CF24A0F2EE3F1B3AC49397C0F51" decryptionKey="2134B0CA7E59C637F365F74FA10727B6C2BDC2E9D26ADE2A" validation="SHA1" />
      <trust level="Full" originUrl="" />
      <fullTrustAssemblies />
      <pxdatabase defaultProvider="PXSqlDatabaseProvider">
        <providers>
          <remove name="PXSqlDatabaseProvider" />
          <add name="PXSqlDatabaseProvider" type="PX.Data.PXSqlDatabaseProvider, PX.Data" connectionStringName="ProjectX" companyID="" secureCompanyID="False" />
        </providers>
      </pxdatabase>
      <pxaccess defaultProvider="PXDatabaseAccessProvider">
        <providers>
          <remove name="PXDatabaseAccessProvider" />
          <add name="PXDatabaseAccessProvider" type="PX.Data.PXDBFeatureAccessProvider, PX.Data" applicationName="/" administratorRole="Administrator" />
        </providers>
      </pxaccess>
      <membership defaultProvider="PXActiveDirectorySyncMembershipProvider">
        <providers>
          <remove name="PXActiveDirectorySyncMembershipProvider" />
          <remove name="MySQLMembershipProvider" />
          <add name="PXActiveDirectorySyncMembershipProvider" type="PX.Data.PXActiveDirectorySyncMembershipProvider, PX.Data" mainProviderType="PX.Data.PXDatabaseMembershipProvider" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="true" applicationName="/" requiresUniqueEmail="false" passwordFormat="Clear" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="1" passwordAttemptWindow="10" passwordStrengthRegularExpression="" />
        </providers>
      </membership>
      <siteMap enabled="true" defaultProvider="PXWizardSiteMapProvider">
        <providers>
          <remove name="PXWikiProvider" />
          <remove name="PXWizardSiteMapProvider" />
          <remove name="PXDatabaseSiteMapProvider" />
          <remove name="MySqlSiteMapProvider" />
          <!-- that can be planted into machine.config by mysql .net connector installation -->
          <remove name="PXOuterSiteMapProvider" />
          <add name="PXDatabaseSiteMapProvider" type="PX.Data.PXDatabaseSiteMapProvider, PX.Data" securityTrimmingEnabled="true" table="PX.SM.SiteMap" />
          <add name="PXWizardSiteMapProvider" type="PX.Objects.WZ.PXWizardSiteMapProvider, PX.Objects" securityTrimmingEnabled="true" />
          <add name="PXWikiProvider" type="PX.Data.PXWikiProvider, PX.Data" securityTrimmingEnabled="true" />
        </providers>
      </siteMap>
    </system.web>
  </location>
</configuration>

so many details in app.config are needed because with shims you can't modify everything in your graph. 

Next goes code, that allows you to create any graph in case if your app.config is ready:

public class GraphCreator<Twhere T : PXGraphnew()
{
    public static T CreateInstance()
    {
        string userName = "admin";
        using (var pxLoginScope = new PXLoginScope(userName, new string[0]))
        {
            var membershipUser = (MembershipUsernull;
 
            membershipUser = Membership.GetUser(pxLoginScope.UserName);
            if (membershipUser != null)
            {
                pxLoginScope.UserName = membershipUser.UserName;
                userName = PXContext.PXIdentity.User.Identity.Name;
                if (pxLoginScope.CompanyName != null)
                {
                    string[] rolesForUser = System.Web.Security.Roles.GetRolesForUser(pxLoginScope.UserName);
                    if (rolesForUser == null || rolesForUser.Length == 0)
                        throw new PXException("You are not allowed to login to the company {0}."new object[1]
                        {
                            (object) pxLoginScope.CompanyName
                        });
                    else if (
                        !Enumerable.Contains<string>((IEnumerable<string>) PXDatabase.AvailableCompanies,
                            pxLoginScope.CompanyName))
                        throw new PXException("You are not allowed to login to the company {0}."new object[1]
                        {
                            (object) pxLoginScope.CompanyName
                        });
                }
                PXLogin.SetBranchID(pxLoginScope.UserName, pxLoginScope.CompanyName, pxLoginScope.Branch);
               
            }
        }
        var gr = PXGraph.CreateInstance<T>();
        return gr;
    }
}

With such preparation you can try to shim and unit test your code. While I should warn you it will not be an easy job.

2 Comments

  • Jonathan said

    Question Yuriy, are you able to integrate your Unit test into a CI/CD tool like TeamCity or something of that nature with this approach?

  • docotor said

    Hi Jonathan, no I didn't try to integrate it with CI/CD. But I suppose it can be great mental exercise. Also I think this is achievable task

Add a Comment
 

 

Types Of Screens In Acumatica

 

Hello everybody,

today  I want to describe one more feature of Acumatica: types of screens in Acumatica. In Acumatica majority of screens are the following:

  1. Data Entry pages
  2. Maintenance pages
  3. Inquiery pages
  4. Setup pages
  5. Processing pages
  6. Reports.

Now let's talk in more details about those screens:

Data entry pages

As usually data entry pages located at following icon in the red rectangle.

These pages are the most frequently used pages of the Acumatica. Typically, data entry pages are used for the input of business documents, such as sales orders in a distribution module.

Maintenance pages

As usually maintenance pages located at following icon in the red rectangle.

Maintenance pages are those intended for data entry but used more rarely than the main data entry pages. Typically, maintenance pages are helper pages on which you input data that is used on the data entry and processing pages of the Acumatica. For instance, because you specify a customer for each sales order, you need a page for entering and editing customers. The most appropriate way to classify the customer edit page depends on how frequently the page is used. Because the customer is a helper entity for sales orders, you can consider Customers as maintenance page.

Inquiry pages

As usually inquiry pages located at following icon in the red rectangle.

By using inquiry pages, the user can view data narrowed by the selection criteria they have specified; these pages are similar to reports but designed for online viewing. Typically, an inquiry page consists of a form that provides selection parameters and a grid that lists the retrieved data.

Setup pages

As usually setup pages located at following icon in the red rectangle.

Setup pages

As usually setup pages located at following icon in the red rectangle.

Setup pages provide the configuration parameters for the Acumatica. On every setup page, you set up the consistent configuration for particular functionality of the Acumatica. A set of configuration parameters is stored in a single record in the corresponding setup table of the database. By using a setup page, a user can edit this record: for example, enable or disable a feature, specify default values, or set the initial numbers of documents. Setup pages are used very rarely, usually in the very beginning of application implementation and use.

Processing pages

As usually processing pages located at following icon in the red rectangle

On a processing page, users can invoke an operation on multiple selected records at once. For instance, user need to cancel orders according to some criteria. He has two options: for each order navigate to Sales Orders screen, open proper sales order, press at Actions -> Cancel Order. Another option is to use processing screen. Processing page can automate this process.

User can check (checkboxes in red rectangle) sales orders for cancel, and then click on process button (in blue rectangle there are two buttons: PROCESS and PROCESS ALL).

PROCESS ALL will automatically check all checkboxes and cancel them.

Reports

As usually reports pages located at following icon in the red rectangle

In Acumatica user can create reports and design printable forms of documents.

In Acumatica user can create reports and design printable forms of documents.

Now you have needed knowledge that you can use in order to write super detailed task for your Acumatica developer.

No Comments

Add a Comment
 

 

Types Of Controls In Acumatica

 

Hello everybody,

today I want to describe with some screenshots kind of controls that you can get from Acumatica developer like me or any other Acumatica programmer.

Main purpose of this blog post is the following. Imagine, that you want to write super detailed instructions for Acumatica developers. After reading this article I suppose you'll be able to write instructions to them with any kind of detalization. Or even super detailed.

First of all, I want to point that as usually Acumatica pages consists of two parts: top and bottom. Top part often is named summary, and bottom is considered to be detalization of top. Take a look at following screenshot:

At presented screeshot red part presents summary, and blue part presents details. Also you can notice that details part can contain some tabs, but about them I'll mention later. 

Now let's see other types of controls:

Selector

It can look like this:

In selector Acumatica developer can customize columns that will be displayed. Also list in selector can be changable. In the presented screenshot we can change order of columns, names and content. As usually selectors are used for displaying data from some table in db.  Also selectors can be in used in the grid. One more feature of selectors there are used for navigating between different records. On the screen AR301000 (Invoices and Memos) selector used for navigate between different invoices. Take note also that with selector you can search for needed record. One more feature that often is added to selectors is Autonumbering. It works according to the following scenario. Imagine that needs to create some new entity. In such case he opens needed form, and start to enter values for it. In the field for selector while record is not persisted in database it will be written <New>. And after persisitng to database <New> will be replaced with some generated value. Programmers can generate for you any numbering sequence that follow any kind of logic.

Take a look at screenshot of another selector:

DateTime

As name suggests you can use this kind of controls for entering date time values. It can look like this:

or like this:

and for selecting time it can look like this: 

Dropdown list. 

Some times there is a need to have fixed set of values in combo box. In html environment it is often named select. 

Drop down list can look like this: 

Currency input

For USA customers it is not very needed feature, but for out USA customers currency input is very useful control. It has two presentations: 

and if you click on it you'll see following window: 

Presented screenshot have not many benefits because it has conversion between USD to USD, but for some international compaines it meant a lot. BTW, it is very simple to integrate Acumatica with any currency exchange providers. For example I've created integration with Ukrainian National bank or Belorussian National bank. 

Textbox

Probably the most used and needed control. It can be displayed like this:

Also Acumatica allows to create some filtering for textbox. One of the examples is allow to enter into textbox only numbers. Additional detail. It is possible to program interaction between controls. Most common example display enter in one column some number, enter in another column percentage, press enter and third column show multiplication of first column on second column.

Tabs

In case if you have meny details parts, and don't want to create many screens you can use tabs. Tabs in Acumatica look like this:

Navigation and persistance buttons

Acumatica also has mechanism for navigating via records with navigation buttons. Navigation buttons look like this:

Besides navigation buttons at presented screenshot there are buttons Save, Cancel, Insert, Copy/Paste.

Customizing grid

Grid in Acumatica can have different sets of buttons. Below goes list of different standard options: 

  • Add, Delete, Fit to Screen, and Export buttons to the toolbar. This set is typically used for a grid on a simple edit page that consists of the single grid.
  • Refresh, Add, Delete, Fit to Screen, and Export buttons to the toolbar. This set is typically used for a grid that displays the detail data on a master-detail page
  • Refresh, Fit to Screen, and Export buttons to the toolbar. This set is typically used for grids on inquiry and processing pages
  • Refresh and Fit to Screen buttons to the toolbar
  • Without toolbar.

And some screenshots:

And also Acumatica allows to customize grid with your custom buttons and even with enabling/disabling them depending from condition.

Take a look at following screenshot:

At presented screenshot button Load Documents is enabled, and button View document is disabled. 

Action buttons

Also you can add custom buttons to the top of page. Take a look at another screenshot:

with red rectangle I've highlighted button Release. It was added with some C# code. 

Besides adding just buttons, Acumatica allows to add buttons with menu. It can look like this:

At presented screenshot fragment Reports is button, with three menu items: Invenotry Edit Details, Inventory Register Detailed, Inventory Item Labels.

No Comments

Add a Comment
 

 

How To Override Action Create At Form So301000 Or Sales Orders

 

Hello everybody,

today I want to document one important piece of functionality in Acumatica. Sales order screen. This is very important screen and has many staff. One of the important screens in it is "Sales orders"  screen. From prospective of understanding Acumatica source code of method "Actions" has plenty of food for mind.

Take a look at it's declaration:

                public PXAction<SOOrder> action;
[PXUIField(DisplayName = "Actions", MapEnableRights = PXCacheRights.Select)] [PXButton] protected virtual IEnumerable Action(PXAdapter adapter, [PXInt] [PXIntList(new int[] { 1, 2, 3, 4, 5 }, new string[] { "Create Shipment""Apply Assignment Rules",                 "Create Invoice""Post Invoice to IN""Create Purchase Order" })] int? actionID, [PXDate] DateTime? shipDate, [PXSelector(typeof(INSite.siteCD))] string siteCD, [SOOperation.List] string operation, [PXString()] string ActionName ) {

}

From this declaration alone you can see that in order to have a button with menu items you need to 

  1. Declare member of type PXAction with lower case name ( in case of "Sales order" screen member is named action )
  2. Create method with Uppser case name ( in case of "Sales order" screen method is named Action )
  3. Besides passing PXAdatapter into method pass also in method integer which is attributted with PXInt, PXIntList
  4. Other attributes and their purpose you can figure out by yourself 

Here I want to leave piece of code that allows you to append this method with your own piece of functionality:

public delegate IEnumerable ActionDelegate(PXAdapter adapter, Nullable<Int32> actionID, Nullable<DateTime> shipDate,
   String siteCD, String operation, String ActionDelegateName);
 
[PXOverride]
public IEnumerable Action(PXAdapter adapter, Nullable<Int32> actionID, Nullable<DateTime> shipDate, String siteCD,
    String operation, String ActionName, ActionDelegate baseMethod)
{
    var currentSoOrder = Base.CurrentDocument.Current;
//and other code }

If to summarize, you'll need:

  1. Declare delegate
  2. Declare action

2 Comments

  • Prathyusha said

    I am doing a customization where I have to override 'create shipment' action and change the locationID when saving into SOShipLine table. Can you please let me know how to do it?

  • docotor said

    Hi Prathyusha, I've wrote you an email. Please describe a bit better what you need

Add a Comment