How To Add Database Context To Filter In Net Core 2 0

 

Hello everybody,

today I want to write a short description on how to add database context to IAuthorizationFilter.

Imagine that you have following filter:

public class HasApprovedFilter : AttributeIAuthorizationFilter
{
    private SomeDbContext _context;
    public HasApprovedFilter(SomeDbContext context)
    {
        this._context = context;
    }
 
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = context.HttpContext.User;
        if (!user.Identity.IsAuthenticated)
        {
            return;
        }
 
        var userAdd = _context.;
 
        //var um = context.HttpContext.RequestServices.GetService();
    }
}

As you can see, that filter requires in constructor SomeDbContext. How to add pass it to this filter?

Basically you'll need two steps:

1. At Startup class, in method ConfigureServices add following line:

services.AddScoped<HasApprovedFilter>();

right before line services.AddMvc();

2. At needed controller or Action of controller you can make like this:

[ServiceFilter(typeof(HasApprovedFilter))]
public class HistoryController : Controller

such approach will allow you to have access to database at Filter level. 

Summary

ServiceFilter is entity which takes care about connecting your filter to dependency injection container. You can use it in order to pass not only DataBaseContext, but some other staff.

No Comments

 

Add a Comment
 

 

Types Of Fliters In Net Core

 

Hello everybody,

today I want to make a short document about filters in .Net Core.

There are by default four types of filters:

  1. Filters that implement IAuthorizationFilter which allow us to implement OnAuthorization method which allow us to add custom security logic.
  2. Filters that implement IActionFilter has two methods: OnActionExecuting, OnActionExecuted. Those two methods executed before and after action executing.
  3. Filters that implement IExceptionFilter has method OnException, which allows to handle exceptions.
  4. Filters that implement IResultFilter has two methods: OnResultExecuting, OnResultExecuted.

Filters can be applied at controller or at action level. That gives following distinction in their lifetime, if some filter is applied at Controller level, then it will be executed at each action of a controller, which means some filter can make additional workload on your code.

Below goes example of creating filter, which by default generates exception:

public class HasApprovedFilter : AttributeIAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        throw new NotImplementedException();
    }
}

That filter is useless, but take note of class Attribute. 

General convention of IAuthorizationFilter is like this: in case if Authorization is correct, then method returns void, if not, we need to set result value on the context object.

No Comments

Add a Comment
 

 

How To Override Create Prepayment In Acumatica Without Usage Of Custom Delegate

 

Hello everybody,

today I want to share with you on how to override Action CreatePrepayment in Acumatica.

General rule of overriding methods in Acumatica according to T300 manual is like this:

  1. Create delegate.
  2. Add [PXOverride] over your method which is named exactly as base method ( in our case CreatePrepayment )
  3. Add your implementation

For example you can achieve it like this:

//Create your delegate
public delegate void CreatePrepaymentDelegate();
 
[PXOverride]
public void CreatePrepayment(CreatePrepaymentDelegate baseDel)
{
    // your code of overriding
}
 

Way from manual T300 is perfectly workable, but  I propose you to use feature of .Net which is named Action, which is declared very much exactly as mentioned delegate. 

Then your code for overriding will look pretty much the same, you'll just need use Action and omit delegate declaration:

[PXOverride]
public void CreatePrepayment1(Action baseDel)
{
         // your code of overriding
}

with such simple trick you can save a bit of time on typing.

No Comments

Add a Comment
 

 

How To Override Part Of C Code In Automation Steps In Acumatica

 

Hello everybody,

today I want to share one interesting gotcha which took plenty of efforts from mine side in order to understand it.

Recently I got an assignment to modify behaviour of Create Prepayment action at purchase orders. 

If to look at CreatePreapyment implementation, it has following part of code:

 
if (this.Document.Current == null)
  return;
this.Save.Press();
APInvoiceEntry instance = PXGraph.CreateInstance<APInvoiceEntry>();
if (this.Document.Current.PrepaymentRefNbr == null)
{

if to put simply it checks if current purchase order already has a prepayment, and in case if it has, then give to a user error message. 

Imagine, that you need to modify that behaviour for some reason. How can you achieve it? One of the ways is to modify C# code, which is pretty good way, and workable. 

But let's try another way, automation steps.

Before I'll continue I want to say very important think: Automation Steps has priority over anything. It means that in case of conflict, Automation steps instruction will have priority over anything.

If your code says to Acumatica to do A, but automation step says to do B, Acumatica will do B. If your security settings says to do A, but Automation step says to do B, Acumatica will do B, and not A.

I can continue that list on and on but remember, Automation steps has priority over anything. 

For me they become some kind of pain points especially when dealing with Purchase orders. 

Take a look at another screenshot:

basically it says that on Step NL Prepaid action CreatePrepayment will be disabled. 

And whatever you'll do with a code or security, or even js hacking, menu item Create Prepayment will be disabled.

But if you take out checkbox Disabled, then Create Prepayment will become enabled.

Now let's back to the question, how to set value of PrepaymentRefNbr to null with Automation Steps.

You can use following steps for this:

  1. go to Automation steps screen
  2. Choose for Screen ID Purchase Orders
  3. For Step ID type NL Prepaid
  4. Go to Actions tab, and find there Action CreatePrepayment.
  5. Click on Fill with values as shown on the screen:

6. In the pop up window pick PrepaymentRefNbr as shown at screenshot:

7. Click on close.

After all of those manipulations in debugger ( well, not only in debugger ) you'll eyewitness miracle, value of PrepaymentRefNbr will be null independently of created before Prepayments.

also if to look at tab other information PrepaymentRefNbr showed something different:

as you can see from screenshot Prepayment Ref Nbr wasn't empty, in reality it showed as empty.

Conclusion

As usually I don't write conclusions, but here I want to write a few. First of all, if you write some code in Acumatica and it doesn't behave as you'd like, take a look at Automation steps, maybe there is the reason of your problems.

Second, if you want to override some code and don't want to create customization, C# and so on, take a look at Automation steps. With such a trick you can quickly help to your customer and also puzzle him about level of your knowledge of Acumatica.

No Comments

Add a Comment
 

 

Creating Custom Autonumber Autogenerated Id In Acumatica

 

If you want to create custom Attribute for autonumbering field you need:

  1. Create Setup page for configuring autonumbering field or maybe you can use existing
  2. Create Attribute which you'll add to your entity
  3. Add attribute to field that you need increment 

Create Setup page for configuration autonumbering field

Setup.cs like this:

    [System.SerializableAttribute()]
    [PXPrimaryGraph(typeof(CurrencyMaint))]
    [PXCacheName("Your company Preferences")]
    public class Setup : PX.Data.IBqlTable
    {
        #region DocumentRefNbr
        public abstract class documentRefNbr : PX.Data.IBqlField
        {
        }
        protected string _DocumentRefNbr;
        [PXDBString(15, IsUnicode = true)]
        [PXDefault("00010")]
        [PXUIField(DisplayName = "Document Last Ref. Number")]
        public virtual string DocumentLastDocNbr
        {
            get
            {
                return this._DocumentRefNbr;
            }
            set
            {
                this._DocumentRefNbr = value;
            }
        }
        #endregion
        #region ReturnDocRefNbr
        public abstract class returnLastDocNbr : PX.Data.IBqlField
        {
        }
        protected string _ReturnLastDocRefNbr;
        [PXDBString(15, IsUnicode = true)]
        [PXDefault("00010")]
        [PXUIField(DisplayName = "Return Documnet Ref. Number")]
        public virtual string ReturnLastDocNbr
        {
            get
            {
                return this._ReturnLastDocRefNbr;
            }
            set
            {
                this._ReturnLastDocRefNbr = value;
            }
        }
        #endregion
        
        #region AutoNumbering
        public abstract class autoNumbering : PX.Data.IBqlField
        {
        }
        protected bool? _AutoNumbering;
        [PXDBBool()]
        [PXDefault(true, PersistingCheck = PXPersistingCheck.Nothing)]
        [PXUIField(DisplayName = "Auto Numbering")]
        public virtual bool? AutoNumbering
        {
            get
            {
                return this._AutoNumbering;
            }
            set
            {
                this._AutoNumbering = value;
            }
        }
        #endregion
    }

We create 3 fields:

  1. DocumentLastDocNbr - for setting start position to numbering;
  2. ReturnLastDocNbr - for get last number from db
  3. AutoNumbering bool field for setting auto or manual numbering

Next, create graph SetupMaint for Setup page:

public class SetupMaint : PXGraph<SetupMaint>
    {
        public PXSave<Setup> Save;
        public PXCancel<Setup> Cancel;
 
        public PXSelect<Setup> LastNumbers;
    }

Next, create view, page YC101000:

<%@ Page Language="C#" MasterPageFile="~/MasterPages/FormView.master" AutoEventWireup="true" ValidateRequest="false" CodeFile="YC101000.aspx.cs" Inherits="Page_YC101000" Title="Untitled Page" %>
<%@ MasterType VirtualPath="~/MasterPages/FormView.master" %>
 
<asp:Content ID="cont1" ContentPlaceHolderID="phDS" Runat="Server">
	<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" TypeName="CurrencyApplication.SetupMaint" PrimaryView="LastNumbers">
	</px:PXDataSource>
</asp:Content>
<asp:Content ID="cont2" ContentPlaceHolderID="phF" Runat="Server">
	<px:PXFormView ID="form" runat="server" DataSourceID="ds" Style="z-index100" Width="100%" DataMember="LastNumbers" TabIndex="800">
		<Template>
			<px:PXLayoutRule runat="server" StartRow="True" ControlSize="SM" LabelsWidth="SM"/>
			<px:PXTextEdit ID="edLastDocNbr" runat="server" DataField="DocumentLastDocNbr">
			</px:PXTextEdit>
			<px:PXTextEdit ID="edReturnLastDocNbr" runat="server" DataField="ReturnLastDocNbr">
			</px:PXTextEdit>
		   <px:PXCheckBox ID="edAutoNumbering" runat="server" AlignLeft="True" DataField="AutoNumbering" Text="Auto Numbering">
			</px:PXCheckBox>
		</Template>
		<AutoSize Container="Window" Enabled="True" MinHeight="200" />
	</px:PXFormView>
</asp:Content>

                     

 

Create Attribute

After it, you can create attribute:

public class AutoNumberAttribute : PXEventSubscriberAttribute,
                                        IPXFieldDefaultingSubscriberIPXFieldVerifyingSubscriber,
                                        IPXRowPersistingSubscriberIPXRowPersistedSubscriber
    {
        public const string NewValue = "";
 
        private bool _AutoNumbering;
        private Type _AutoNumberingField;
        private BqlCommand _LastNumberCommand;
 
     
        public virtual Type LastNumberField { getprivate set; }
        public static void SetLastNumberField<Field>(PXCache sender, object row, Type lastNumberField)
            where Field : IBqlField
        {
            foreach (PXEventSubscriberAttribute attribute in sender.GetAttributes<Field>(row))
            {
                if (attribute is AutoNumberAttribute)
                {
                    AutoNumberAttribute attr = (AutoNumberAttribute)attribute;
                    attr.LastNumberField = lastNumberField;
                    attr.CreateLastNumberCommand();
                }
            }
        }
 
       public AutoNumberAttribute(Type autoNumbering)
        {
            if (autoNumbering != null &&
                (typeof(IBqlSearch).IsAssignableFrom(autoNumbering) ||
                 typeof(IBqlField).IsAssignableFrom(autoNumbering) && autoNumbering.IsNested))
            {
                _AutoNumberingField = autoNumbering;
            }
            else
            {
                throw new PXArgumentException("autoNumbering");
            }
        }
 
        public AutoNumberAttribute(Type autoNumbering, Type lastNumberField)
            : this(autoNumbering)
        {
            LastNumberField = lastNumberField;
            CreateLastNumberCommand();
        }
 
        private void CreateLastNumberCommand()
        {
            _LastNumberCommand = null;
 
            if (LastNumberField != null)
            {
                if (typeof(IBqlSearch).IsAssignableFrom(LastNumberField))
                    _LastNumberCommand = BqlCommand.CreateInstance(LastNumberField);
                else if (typeof(IBqlField).IsAssignableFrom(LastNumberField) && LastNumberField.IsNested)
                    _LastNumberCommand = BqlCommand.CreateInstance(typeof(Search<>), LastNumberField);
            }
 
            if (_LastNumberCommand == nullthrow new PXArgumentException("lastNumberField");
        }
 
        public override void CacheAttached(PXCache sender)
        {
            BqlCommand command = null;
            Type autoNumberingField = null;
            if (typeof(IBqlSearch).IsAssignableFrom(_AutoNumberingField))
            {
                command = BqlCommand.CreateInstance(_AutoNumberingField);
                autoNumberingField = ((IBqlSearch)command).GetField();
            }
            else
            {
                command = BqlCommand.CreateInstance(typeof(Search<>), _AutoNumberingField);
                autoNumberingField = _AutoNumberingField;
            }
            PXView view = new PXView(sender.Graph, true, command);
            object row = view.SelectSingle();
            if (row != null)
            {
                _AutoNumbering = (bool)view.Cache.GetValue(row, autoNumberingField.Name);
            }
        }
 
        public virtual void FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
        {
            if (_AutoNumbering)
            {
                e.NewValue = NewValue;
            }
        }
 
        public virtual void FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
        {
            if (_AutoNumbering && PXSelectorAttribute.Select(sender, e.Row, _FieldName, e.NewValue) == null)
            {
                e.NewValue = NewValue;
            }
        }
 
        protected virtual string GetNewNumber(PXCache sender, Type setupType)
 
        { 
 
            if (_LastNumberCommand == null)
                CreateLastNumberCommand();
            PXView view = new PXView(sender.Graph, false, _LastNumberCommand);
            object row = view.SelectSingle();
            if (row == nullreturn null;
            long number;
 
            string lastNumber = (string)view.Cache.GetValue(row, LastNumberField.Name);
            number = Int64.Parse(lastNumber);
            number = number + 3;
 
         lastNumber = "0000" + Convert.ToString(number);
 
            view.Cache.SetValue(row, LastNumberField.Name, lastNumber);
            PXCache setupCache = sender.Graph.Caches[setupType];
            setupCache.Update(row);
            setupCache.PersistUpdated(row);
            return lastNumber;
        }
 
        public virtual void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
        {
            if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Insert)
            {
                Type setupType = BqlCommand.GetItemType(_AutoNumberingField);
                string lastNumber = GetNewNumber(sender, setupType);
                if (lastNumber != null)
                {
                    sender.SetValue(e.Row, _FieldOrdinal, lastNumber);
                }
            }
        }
 
        public virtual void RowPersisted(PXCache sender, PXRowPersistedEventArgs e)
        {
            if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Insert &&
                e.TranStatus == PXTranStatus.Aborted)
            {
                sender.SetValue(e.Row, _FieldOrdinal, NewValue);
                Type setupType = BqlCommand.GetItemType(_AutoNumberingField);
                sender.Graph.Caches[setupType].Clear();
            }
        }
    }

Pay attention that for correct work you must implement interfaces, and ovveride standart methods(RowPersisting.... etc, all that need)

Main function for generating new walue is GetNewNumber; New walue will be bigger on 3;

Ok, here we described method - 

SetLastNumberField()

This method we call from graph before when we click save data to database in event RowPersisting:

Add attribute to field that you need increment;

public class CurrencyMaint : PXGraph<CurrencyMaintUsrCurrency>
    {
 
        public PXFilter<ExchangeDateFilter> Filter;
        public PXSelect<UsrCurrencyWhere<UsrCurrency.currencyExchangeDateEqual<Current<ExchangeDateFilter.exchangeDate>>>> Currencies;
 
        public PXSetup<Setup> AutoNumSetup;
 
 
        public CurrencyMaint()
        {
            Setup setup = AutoNumSetup.Current;
        }
 
        protected virtual void UsrCurrency_RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
        {
            UsrCurrency doc = (UsrCurrency)e.Row;
            if (sender.GetStatus(doc) == PXEntryStatus.Inserted)
            {
                AutoNumberAttribute.SetLastNumberField<UsrCurrency.extRefNbr>(
                    sender, doc,
                    typeof(Setup.returnLastDocNbr));
            }
        }
    }

So for example I have DAC class and field ExtRefNbr I want to set autonumber attrbute, to do this try: [AutoNumber(typeof(Setup.autoNumbering))]

Of course don`t forget to create aspx page :)

[System.SerializableAttribute()]
    public class UsrCurrency : PX.Data.IBqlTable
    {
        
        public abstract class extRefNbr : PX.Data.IBqlField
        {
 
        }
        protected string _ExtRefNbr;
        [PXDBString(40, IsUnicode = true, InputMask = ">CCCCCCCCCCCCCCC")]
        [PXDefault("00000000")]
        [PXUIField(DisplayName = "Document Ref.")]
         [AutoNumber(typeof(Setup.autoNumbering))]
        public virtual string ExtRefNbr
        {
            get
            {
                return this._ExtRefNbr;
            }
            set
            {
                this._ExtRefNbr = value;
 
            }
        }
 
        #region CurrencyID
        #endregion         #region CurrencyName         #endregion         #region CurrencyRate             #endregion         #region CurrencyCC                 #endregion     }

And after configure Setup page:

And try to add new rows, and test autonumber:

Enjoy the result )

2 Comments

  • Angie said

    Hi Yuriy,

    Excuse me, I have a question...

    What is ExchangeDateFilter?.

    But I don't know how implement these filters

    I hope your answer. Thank very much :)

  • docotor said

    Update:

    below goes class ExchangeDateFilter:

    [Serializable]
    public class ExchangeDateFilter : IBqlTable
    {
    #region ExchangeDate
    public abstract class exchangeDate : PX.Data.IBqlField
    {
    }
    [PXDate()]
    [PXDefault( typeof(AccessInfo.businessDate))]
    [PXUIField(DisplayName = "Currency ExchangenDate")]
    [PXSelector(
    typeof(Search<UsrCurrency.currencyExchangeDate>))]

    public virtual DateTime? ExchangeDate { get; set; }
    #endregion
    }

    This filter not need for autonumbering, it is for select all from UsrCurrency table where date equal current date.

Add a Comment
 

 

Entity Framework One To Many Relationship

 

Hello everybody,

today I want to make short post on how to confiugre one to many relationship in Entity Framework 6.

Imagine following: One group can have multiple students. So one to many relationship. For this purpose you can use following convention configuration agreement:

public class Student
{
    public int StudentId { getset; }
    public string StudentName { getset; }
}
 
public class Group
{
    public int GroupId { getset; }
    public string GroupName { getset; }
    public string Department { getset; }
 
    public List<Student> Students { getset; }
}

In presented example class group includes navigation property Students. 

Another convention is like this:

public class Student
{
    public int StudentId { getset; }
    public string StudentName { getset; }
 
    public Group CurrentGroup { getset; }
}
 
public class Group
{
    public int GroupId { getset; }
    public string GroupName { getset; }
    public string Department { getset; }
 
    public List<Student> Students { getset; }
}

as you can see, difference is that Student instance by itself knows to which Group it belongs. 

No Comments

Add a Comment
 

 

Complete Description Of Arguments In Jforex Submitorder

 

Hello everybody,

here I want to leave fragment of code related to submitOrder:

public void onStart(IContext context) throws JFException {
     IEngine engine = context.getEngine();
     IHistory history = context.getHistory();
     Instrument instrument = Instrument.EURUSD;
     context.setSubscribedInstruments(java.util.Collections.singleton(instrument), true);
     
     ITick lastTick = history.getLastTick(instrument);
     double price = lastTick.getAsk() + instrument.getPipValue() * 5;
     double sl = lastTick.getAsk() - instrument.getPipValue() * 20;
     double tp = lastTick.getAsk() + instrument.getPipValue() * 10;
     long gtt = lastTick.getTime() + TimeUnit.SECONDS.toMillis(30); //withdraw after 30 secs
     IOrder order = engine.submitOrder("BuyStopOrder", instrument, OrderCommand.BUYSTOP, 0.1, price, 20, sl, tp, gtt, "My comment"); 
 }

For me it was a challenge to find sequence of arguments to pass into submit order related to stop loss, take profit.

No Comments

Add a Comment
 

 

New Class In Acumatica Pximpersonationcontext

 

Hello everybody,

here I want to document new scope in Acumatica: PXImpersonationContext. 

As often is the case try to look at presented code:

var thr = new Thread(
                                   () =>
                                   {
                                       try
                                       {
                                           using (new PXImpersonationContext(PX.Data.Update.PXInstanceHelper.ScopeUser))
.

.

.

}
                            foreach (var thread in threads)
                            {
                                lock (thisLock)
                                {
                                    thread.Start();
                                }
                            }
 
                            foreach (var thread in threads)
                            {
                                thread.Join();
                            }

It does the following: creates threads, inside of threads users some logic for persistance to database through graphs and then waits for joining threads.

Recently I've had following problem. I've created processing screen that worked great in manual mode, but absoultely refused to work in Automation schedule mode. Refused means thread was crashed without any exception message.

Adding PXImpersonationContext solved the issue for me.

Now you may wonder, how PXImpersonationContext achieves it?

Let's consult with JetBrains decompiler:

public class PXImpersonationContext : IDisposable
{
  private IPrincipal a;
  private readonly WindowsImpersonationContext b;
 
  public PXImpersonationContext()
    : this(PXDatabase.Companies.Length != 0 ? "sys@" + PXDatabase.Companies[0] : "sys")
  {
  }
 
  public PXImpersonationContext(string userName)
    : this(userName, new string[0])
  {
  }
 
  public PXImpersonationContext(string userName, string[] roles)
  {
    WindowsIdentity windowsIdentity = (WindowsIdentitynull;
    IPrincipal principal = (IPrincipalnew GenericPrincipal((IIdentitynew GenericIdentity(userName), roles);
    IdentitySection section = (IdentitySection) WebConfigurationManager.GetSection("system.web/identity");
    if (section != null && section.Impersonate && HttpContext.Current != null)
    {
      HttpWorkerRequest service = (HttpWorkerRequest) ((IServiceProvider) HttpContext.Current).GetService(typeof (HttpWorkerRequest));
      if (service != null)
      {
        IntPtr userToken = service.GetUserToken();
        if (userToken != IntPtr.Zero)
          windowsIdentity = new WindowsIdentity(userToken);
      }
    }
    this.a = PXContext.PXIdentity.User;
    PXContext.PXIdentity.User = principal;
as you can see from decompiled code sample, PXImpersonationContext uses user name "sys@currentlyloggedcompany" then it tries to create WindowsIdentity user and then uses that user for processing your staff.
Pretty cool idea, huh?
One more addition.
PX.Data.Update.PXInstanceHelper.ScopeUser also will give you "sys@currentlyloggedcompany" which leads me to to conclusion that "sys@currentlyloggedcompany" is most cool user in Acumatica system.
Also it is not presented in table users which means that such a user is some kind of virtual user.

If you become puzzled from this long technical description, then go straight to the summary:
Summary

If your processing screen crashes with no reason in automation schedule mode, add to your code 

using (new PXImpersonationContext(PX.Data.Update.PXInstanceHelper.ScopeUser))

{

    // here shold be your processing logic

}

and try again. In mine case such addition "resurected" mine automation schedules. Hopefully yours will also come back to life.

No Comments

 

Add a Comment
 

 

Manage Serialization In Net Core 2 0

 

Hello everybody,

today I want to write a short notice on how to manage uppercase/lowercase options for serialization in .Net Core.

In mine practice I often had situation, when javascript or typescript code sends me some staff in lowercase class names, but in C# I'm used to Upper case class names. 

Another option that you sometime can need is switching between xml and json serialization. How those options can be managed in .Net Core 2.0 ?

For both of those options ( and even more ) you can use pipeline management of Startup class of ConfigureServices method.

For example if you need to have xml serialization output you can use following code:

public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc()
                .AddMvcOptions(o => o.OutputFormatters.Add(
                    new XmlDataContractSerializerOutputFormatter()));

And if you want to switch default naming from camel case to Upper case, then following code in Startup class can be added:

public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc()
                .AddJsonOptions(o =>
                {
                    if (o.SerializerSettings.ContractResolver != null)
                    {
                        var castedResolver = o.SerializerSettings.ContractResolver
                            as DefaultContractResolver;
                        castedResolver.NamingStrategy = null;
                    }
                });
        }

with such changes you can easier or harder to manipulate your input/output from .Net MVC controllers

No Comments

Add a Comment
 

 

Required Instead Of Current In Acumatica

 

Hello everybody,

today I want to fulfill my promise that I gave to one of my readers. 

He left at mine blog following question:

How do we use required<> in place of current<> and pass some string constant for selector? That question was asked in context of this article. 

That is good question and I also want to add that answer on it will be at least to some degree disappointing. First of all, if you work with selectors, you can't use Required. Required is intended for use in Graphs.

But if you want to use some constant filtering conditions, you don't need Required attribute at all. You can use mechanism of Constants, which works perfectly without Required. 

Take a look at the following declaration:

public static class OrderTypes
{
    public const string N = "N";
    public const string C = "C";
 
    public class closed : Constant<String>
    {
        public closed()
            : base(C)
        {
        }
    }
 
    public class open : Constant<String>
    {
        public open() : base(N)
        {
        }
    }
}

As you can see, we declared two cosntants for order types: N and C.

Below goes code that shows how can you create selector that will filter Orders by OrderTypes with usage of those constants:

class Test : PXCacheExtension<SOOrder>
{
    #region UsrPreviousTermsId
 
    public abstract class usrPreviousTermsId : IBqlField
    {
    }
 
    [PXSelector(typeof(Search<SOOrder.orderNbrWhere<SOOrder.statusEqual<OrderTypes.closed>>>))]
    [PXUIField(DisplayName = "Linked sales order")]
    [PXDBString(10, IsUnicode = true)]
    public string UsrLinkedSalesOrder { getset; }
 
    #endregion
}

Such code will give you selector that give you a list of orders with status closed. Also this sample of code shows that for selector you don't need Required in order to filter by some constant value.

 

 

1 Comment

Add a Comment