Auto Generate Number in Acumatica - c#

I want to create an auto generate number which similar with auto increment for segment ID in Lot/Serial Classes, like in this picture.
Lot/Serial Classes
After I check the code, I notice that it uses PXLineNbr
public abstract class segmentID : PX.Data.IBqlField
{
}
protected Int16? _SegmentID;
[PXDBShort(IsKey = true)]
[PXUIField(DisplayName="Segment Number", Enabled=false)]
[PXLineNbr(typeof(INLotSerClass))]
[PXDefault()]
public virtual Int16? SegmentID
{
get
{
return this._SegmentID;
}
set
{
this._SegmentID = value;
}
}
After I try and apply it in my code, the auto generated number doesn't appear. So I was wandering if I miss something else. Thank you in advance

The pattern I'm using for PXLineNbr is to declare a line number counter field in the master table and a line number field in the detail table. It's simple and it works. LineNbr value is computed automatically from the counter by PXLineNbr attribute.
The LineCntr field:
public class MasterDAC : IBqlTable
{
#region LineCntr
public abstract class LineCntr : IBqlField { }
[PXDBInt]
[PXDefault(0)]
public virtual int? LineCntr { get; set; }
#endregion
}
The LineNbr field:
public class DetailDAC : IBqlTable
{
#region LineNbr
public abstract class lineNbr : IBqlField { }
[PXDBInt(IsKey = true)]
[PXDefault]
[PXLineNbr(typeof(MasterDAC.LineCntr))]
public virtual int? LineNbr { get; set; }
#endregion
}

Have you checked Example 7.1: Numbering Detail Data Records in the T200 training available at Acumatica Open University? It explains in detail, how the PXLineNbr attribute should be used to automatically number detail data records.

Related

How can I perform Acumatica BQL JOINs in PXSelector?

I have a Selector field where I want to show 5 columns from 3 different table. They are
The Item, item desctiption, item class, item class description and the default warehouse.
I have found the DACs and the fields.
InventoryItem.inventoryID;
InventoryItem.descr;
INItemClass.itemClassID;
INItemClass.descr;
INItemSite.siteID;
I have also written the [PXSelector] attribute containing the JOIN.
#region Field of Me
public abstract class fieldOfMe : BqlString.Field<fieldOfMe> { }
[PXUIField(DisplayName = "Field Of Me")]
[PXSelector(typeof(
Search2<InventoryItem.inventoryID,
LeftJoin<INItemClass,
On<INItemClass.itemClassID, Equal<InventoryItem.itemClassID>>,
LeftJoin<INItemSite,
On<INItemSite.inventoryID, Equal<InventoryItem.dfltSiteID>>>>>),
typeof(InventoryItem.inventoryCD),
typeof(InventoryItem.descr),
typeof(INItemClass.itemClassCD),
typeof(INItemClass.descr),
typeof(INItemSite.siteID),
ValidateValue = false
)]
public string FieldOfMe { get; set; }
#endregion
This is the screen.
I want to learn the way I can find the default Warehouse. How can I edit the code to see the active warehouse name? I want the one with checkbox.
I have done the biggest part of the task but I still need some help to finish this.
I have modified your select query to do what you want.
Note that in later versions of Acumatica the dfltSiteID field in InventoryItem table will become obsolete and removed. InventoryItemCurySettings.dfltSiteID should be used instead. I have added a second query to demonstrate the use of InventoryItemCurySettings.
#region Field of Me
public abstract class fieldOfMe : BqlString.Field<fieldOfMe> { }
[PXUIField(DisplayName = "Field Of Me")]
[PXSelector(typeof(
Search2<InventoryItem.inventoryID,
InnerJoin<INItemClass,
On<INItemClass.itemClassID, Equal<InventoryItem.itemClassID>>,
LeftJoin<INSite,
On<InventoryItem.dfltSiteID, Equal<INSite.siteID>>>>>),
typeof(InventoryItem.inventoryCD),
typeof(InventoryItem.descr),
typeof(INItemClass.itemClassCD),
typeof(INItemClass.descr),
typeof(INSite.siteCD),
ValidateValue = false
)]
public string FieldOfMe { get; set; }
#endregion
Using InventoryItemCurySettings.dfltSiteID
#region Field of Me2
public abstract class fieldOfMe2 : BqlString.Field<fieldOfMe2> { }
[PXUIField(DisplayName = "Field Of Me2")]
[PXSelector(typeof(
Search2<InventoryItem.inventoryID,
InnerJoin<INItemClass,
On<InventoryItem.itemClassID, Equal<INItemClass.itemClassID>>,
LeftJoin<InventoryItemCurySettings,
On<InventoryItem.inventoryID, Equal<InventoryItemCurySettings.inventoryID>,
And<InventoryItemCurySettings.curyID, Equal<Current<AccessInfo.baseCuryID>>>>,
LeftJoin<INSite,
On<InventoryItemCurySettings.dfltSiteID, Equal<INSite.siteID>>>>>>),
typeof(InventoryItem.inventoryCD),
typeof(InventoryItem.descr),
typeof(INItemClass.itemClassCD),
typeof(INItemClass.descr),
typeof(INSite.siteCD),
ValidateValue = false
)]
public string FieldOfMe2 { get; set; }
#endregion

Can I use PXDBScalar in a DAC Extension to query records from the same table?

I'm very new to the Acumatica world. The task I've been given is to add a field to SOLine that will show a sum of the quantity to be shipped within the next 30 days for the same inventory item on the given SOLine. The request was to have the field be available on a General Inquiry.
I've created a DAC Extension and figured that a field with the PXDBScalar should do it.
However, when I add the field to a Generic Inquiry, I get the same value for all of the records displayed. It's as if when trying to query the same table it is using just one of the item's aggregate value instead of recalculating for each item/row displayed in the GI.
Is there something that I've done incorrectly in the PXDBScalar formula? Am I missing a reference to the current record?
namespace PX.Objects.SO
{
public class SOLineExt : PXCacheExtension<PX.Objects.SO.SOLine>
{
#region DaysOf
public class int_DaysInFuture : PX.Data.BQL.BqlInt.Constant<int_DaysInFuture>
{
public int_DaysInFuture()
: base(30)
{
}
}
#endregion
#region UsrSalesInNextThirtyDays
public abstract class usrSalesInNextThirtyDays : PX.Data.IBqlField {}
[PXDecimal(2)]
[PXUIField(DisplayName="Sales in next 30 days")]
[PXDBScalar(typeof(Search4<SOLine.openQty,
Where<SOLine.inventoryID, Equal<SOLine.inventoryID>
,And< Where<DateDiff<SOLine.shipDate, Today, DateDiff.day>, LessEqual< int_DaysInFuture > >>
>
,Aggregate<GroupBy<SOLine.inventoryID, Sum<SOLine.openQty>>>
>))]
public virtual Decimal? UsrSalesInNextThirtyDays{ get; set; }
#endregion
}
}
So adding this field shows that the linking to InventoryItem was working as expected.
#region UsrSalesInNextThirtyDays2
public abstract class usrSalesInNextThirtyDaysTwo : PX.Data.IBqlField {}
[PXDecimal(2)]
[PXUIField(DisplayName="Base Price")]
[PXDBScalar(typeof(Search<InventoryItem.basePrice,
Where<InventoryItem.inventoryID, Equal<SOLine.inventoryID>>
>))]
public virtual Decimal? UsrSalesInNextThirtyDaysTwo{ get; set; }
#endregion
So I tried adding an inner join to force the line to InventoryItem using the inventoryID field but the resulting query is still returning the sum of the last item returned as the sum result for all records in the result set.
public abstract class usrSalesInNextThirtyDays : PX.Data.IBqlField {}
[PXDecimal(2)]
[PXUIField(DisplayName="Sales in next 30 days")]
[PXDBScalar(typeof(
Search5<SOLine.openQty,
InnerJoin<InventoryItem,
On<SOLine.inventoryID, Equal<InventoryItem.inventoryID>>>,
Where<DateDiff<SOLine.shipDate, Today, DateDiff.day>, LessEqual< int_DaysInFuture > >,
Aggregate<Sum<SOLine.openQty>>
>))]
public virtual Decimal? UsrSalesInNextThirtyDays{ get; set; }
I feel like I'm getting closer to forcing the DAC to generate the correct query but I'm not there yet.
You cannot reference the table in a circular manner this way because of how the sub-queries are generated.

Add custom attribute to a class generated by Entity Framework

I am trying to use a custom attribute on a Entity class generated automatically by the Entity Framework.
The problem is how to add an property attribute on an existing field?
Here the point where I am right now:
// the custom attribute class
public class MyCustomAttribute : Attribute
{
public String Key { get; set; }
}
// Entity Framework class generated automatically
public partial class EntityClass
{
public String Existent { get; set; }
//...
}
// set a metadata class for my entity
[MetadataType(typeof(EntityClassMetaData))]
public partial class EntityClass
{
// if I add a new property to the entity, it works. This attribute will be read
[MyCustomAttribute(Key = "KeyOne" )]
public int newProp { get; set; }
}
public class EntityClassMetaData
{
// adding the custom attribute to the existing property
[MyCustomAttribute(Key = "keyMeta") ]
public String Existent { get; set; }
}
Running this test:
[TestMethod]
public void test1()
{
foreach (var prop in typeof(EntityClass).GetProperties())
{
var att = prop.GetCustomAttribute<MyCustomAttribute>();
if (att != null)
{
Console.WriteLine($"Found {att.Key}");
}
}
}
will produce:
Found KeyOne
Or the Metadata class store the attribute in a different way or only works for data annotations.
I am stuck here, how can I set and read custom attributes of the generated class without having to edit the generated file?
I came across this same problem today. I figured EF magic would do the trick and map the attribute across to each model property. Turns out it does, but only for EF data annotations and I couldn't find an answered solution to pull out custom attributes so made this function. Hope it helps dude.
private object[] GetMetadataCustomAttributes(Type T, string propName)
{
if (Attribute.IsDefined(T, typeof(MetadataTypeAttribute)))
{
var metadataClassType =
(T.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault() as
MetadataTypeAttribute).MetadataClassType;
var metaDataClassProperty = metadataClassType.GetProperty(propName);
if (metaDataClassProperty != null)
{
return metaDataClassProperty.GetCustomAttributes(true);
}
}
return null;
}
I believe if you want to set an attribute in the metadata class, you have to use this syntax:
public class EntityClassMetaData
{
// adding the custom attribute to the existing property
[MyCustomAttribute(Key = "keyMeta") ]
public String Existent;
}
You must not have { get; set; } on your pre-existing property - just the property with the correct name and datatype.

How do I add a new table to a Telerik Open Access MVC project?

I've inherited a MVC project that seems to use Telerik Open Access to handle data instead of using something I'm more familiar with like entity framework. I'm trying to understand the whole concept of how to work with this data method, but right now I'm just needing to find out how to add a table. I've limited my code examples to one table, but in reality there are dozens of them.
So I see that the class OpenAccessContext.cs has a database connection string, but it also has a IQueryable item made up of the class tblMaterial. The tblMaterial class is defined in tblMaterial.cs. I don't understand how this class is connected to the SQL database version of tblMaterial (so feel free to educate me on that).
I have a table called tblContacts in the SQL database. What do I need to do to connect it to my project? There's no "update from database" option when I right click any object in the solution (because they're all just classes). Will I need to create a new class manually called tblContacts.cs? If so, how do I connect it to the database version of tblContacts? Am I going to need to manually change multiple classes to add the table (OpenAccessContext, MetadataSources, Repository, etc.)?
I tried to keep this as one simple question (how do I add a table) so I don't get dinged, but any light you can shine on the Telerik Open Access would be helpful. (Please don't ding me for asking that!) I checked out the Telerik documentation here: http://docs.telerik.com/data-access/developers-guide/code-only-mapping/getting-started/fluent-mapping-getting-started-fluent-mapping-api , but it's related to setting up a new open access solution. I need to know how to modify one (without ruining the already working code). Thank you in advance for your help!
Here's the solution as seen in Visual Studio:
Open Access
Properties
References
OpenAccessContext.cs
OpenAccessMetadataSources.cs
Repository.cs
tblMaterial.cs
Here's the code:
OpenAccessContext.cs
namespace OpenAccess
{
public partial class OpenAccessContext : OpenAccessContext
{
static MetadataContainer metadataContainer = new OpenAccessMetadataSource().GetModel();
static BackendConfiguration backendConfiguration = new BackendConfiguration()
{
Backend = "mssql"
};
private static string DbConnection = ConfigurationManager.ConnectionStrings["ConnString"].ConnectionString;
private static int entity = ConfigurationManager.AppSettings["Entity"] == "" ? 0 : int.Parse(ConfigurationManager.AppSettings["Entity"]);
public OpenAccessContext() : base(DbConnection, backendConfiguration, metadataContainer)
{
}
public IQueryable<tblMaterial> tblMaterials
{
get
{
return this.GetAll<tblMaterial>(); //.Where(a => a.EntityId == entity);
}
}
}
}
OpenAccessMetadataSources.cs
namespace OpenAccess
{
public class OpenAccessMetadataSource : FluentMetadataSource
{
protected override IList<MappingConfiguration> PrepareMapping()
{
var configurations = new List<MappingConfiguration>();
// tblMaterial
var materialConfiguration = new MappingConfiguration<tblMaterial>();
materialConfiguration.MapType(x => new
{
MaterialId = x.MaterialId,
MaterialName = x.MaterialName,
MaterialDescription = x.MaterialDescription,
MaterialActive = x.MaterialActive,
MaterialUsageType = x.MaterialUsageType,
AddDate = x.AddDate,
AddBy = x.AddBy,
ModDate = x.ModDate,
ModBy = x.ModBy
}).ToTable("tblMaterial");
materialConfiguration.HasProperty(x => x.MaterialId).IsIdentity(KeyGenerator.Autoinc);
}
}
}
Repository.cs
namespace OpenAccess
{
public class Repository : IRepository
{
#region private variables
private static OpenAccessContext dat = null;
#endregion private varibles
#region public constructor
/// <summary>
/// Constructor
/// </summary>
public Repository()
{
if (dat == null)
{
dat = new OpenAccessContext();
}
}
#endregion public constructor
#region Material (tblMaterials)
public int CreateMaterial(tblMaterial itm)
{
try
{
dat.Add(itm);
dat.SaveChanges();
return itm.MaterialId;
}
catch (Exception)
{
return 0;
}
}
}
}
tblMaterial.cs
namespace OpenAccess
{
public class tblMaterial
{
public int MaterialId { get; set; }
public string MaterialName { get; set; }
public string MaterialDescription { get; set; }
public bool MaterialActive { get; set; }
public int MaterialUsageType { get; set; }
public DateTime? AddDate { get; set; }
public string AddBy { get; set; }
public DateTime? ModDate { get; set; }
public string ModBy { get; set; }
}
}
In the case of tblContacts, I would suggest to you the following workflow for extending the model:
Add a new class file that will hold the definition of the tblContact POCO class. In this class add properties that will correspond to the columns of the table. The types of the properties should logically match the datatypes of the table columns.
In the OpenAccessMetadataSource class, add a new MappingConfiguration<tblContact> for the tblContact class and using explicit mapping provide the mapping details that logically connect the tblContact class with the tblContacts table. Make sure to add both the existing and the new mapping configurations to the configurations list.
Expose the newly added class through an IQueryable<tblContact> property in the context. This property will allow you to compose LINQ queries against the tblContacts table.
Regarding the Repository class, it seems like it is related to the custom logic of the application. It surely is not a file generated by Data Access. Therefore, you need to discuss it in your team.
I also strongly advise you against using OpenAccess in the namespaces of your application. This is known to interfere with the Data Access' namespaces during build time and at some point it causes runtime errors.
I hope this helps.

How to extend mef using custom attributes?

Is it possible to add some additional attributes to my components which are then set/hydrated using some custom logic/perhaps from a data store? Similar to adding some custom builder strategy in cab/unity ?
UPDATE
e.g.
assuming a class has these properties
[MyImport] string name1 { get; set }
[MyImport] MyType name2 { get; set }
[MyGuid] Guid { get; set; }
with custom attributes MyImport and MyGuid which are resolved by an "extension" to MEF ( which gets executed after the [imports] are resolved ) and has code along these lines
// property SET
var valu = myDBStore.GetValue( instanceGUID, propertyInfo.Name);
propertyInfo.SetValue( instance, TypeDescripter.GetConverter(valu).ConvertTo(propertyType), null);
// property GET - for example only, used during dehydration outside of MEF !
var valu = propertyInfo.GetValue( instance, null);
myDBStore.SetValue( instanceGUID, propertyInfo.Name, TypeDescripter.GetConverter(valu).ConvertTo(typeof(string));
// the above is pseudo code only, pls no comments on correct args/syntax :)
EDIT
components which are then set/hydrated using some custom logic/perhaps from a data store
One can do this via an "ExportFactory".
// "ExportFactory"
public sealed class DataStoreProvider
{
[Export(typeof(Model))]
public Model Item
{
get
{
return [custom logic];
}
}
}
public class NeedsModel
{
[Import(typeof(Model))]
public Model Item { get; set; }
}
Initial Answer
This is possible through MEF's Lazy<T, TMetadata>.
public interface ISomeMetadata
{
string UsefulInfo { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class ExportBaseAttribute : ExportAttribute, ISomeMetadata
{
public ExportBaseAttribute(string usefulInfo)
:base(typeof(BaseExport))
{
UsefulInfo = usefulInfo;
}
public string UsefulInfo { get; private set; }
}
// BaseExport class is not needed.. just showing advanced attribute usage.
public abstract class BaseExport { }
[ExportBase("Useful Filter Information")]
public class SomeExport : BaseExport
{
}
Then, in your host (composer), you can
[ImportMany(typeof(BaseExport))]
Lazy<BaseExport, ISomeMetadata>[] _baseExports
After you compose, you can run a LINQ filter using .Metadata
var goodExports = from export in _baseExports
where export.Metadata.UsefulInfo ...
select export;

Categories

Resources