Acumatica Vendor ID segment key different to BIZZACCOUNT segment length - acumatica

I want to know if it's possible to setup Vendor ID segment keys with a less number of characters than the BiZAccounts..
For example I have created branch Keys like USBRANCH1 but now I want to create Vendor ID like V001 but not give the user possibility to enter more than 4 characters?
I'm not sure why BIZaccount is tied to vendors, customers, employees in terms of segment keys as this reduces flexibility and changing key length and type
Thanks

You can do the following to achieve that:
1. Create your own Segmented Key like below:
Create Cache Extension to the Vendor's DAC to change Default VENDOR Segmented Key which is inherited from BIZACCTto yours MYVENDOR like below:
using PX.Data;
using PX.Data.EP;
using PX.Objects.AP;
using PX.Objects.CR;
using PX.Objects.GL;
using System;
namespace ClassLibrary1.DAC
{
public class VendorExt:PXCacheExtension<Vendor>
{
[MyVendorRaw(IsKey = true)]
[PXDefault]
[PXFieldDescription]
public virtual string AcctCD { get; set; }
}
[PXDBString(30, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Vendor", Visibility = PXUIVisibility.Visible)]
public sealed class MyVendorRawAttribute : AcctSubAttribute
{
public MyVendorRawAttribute() : this(null)
{
}
public MyVendorRawAttribute(Type where)
{
Type type = BqlCommand.Compose(new Type[]
{
typeof(Search2<, , >),
typeof(Vendor.acctCD),
typeof(LeftJoin<Contact, On<Contact.bAccountID, Equal<Vendor.bAccountID>, And<Contact.contactID, Equal<Vendor.defContactID>>>, LeftJoin<Address, On<Address.bAccountID, Equal<Vendor.bAccountID>, And<Address.addressID, Equal<Vendor.defAddressID>>>>>),
(where == null) ? typeof(Where<Match<Current<AccessInfo.userName>>>) : BqlCommand.Compose(new Type[]
{
typeof(Where2<, >),
typeof(Where<Match<Current<AccessInfo.userName>>>),
typeof(And<>),
where
})
});
PXDimensionSelectorAttribute pxdimensionSelectorAttribute;
this._Attributes.Add(pxdimensionSelectorAttribute = new PXDimensionSelectorAttribute("MYVENDOR", type, typeof(Vendor.acctCD), new Type[]
{
typeof(Vendor.acctCD),
typeof(Vendor.acctName),
typeof(Vendor.vendorClassID),
typeof(Vendor.status),
typeof(Contact.phone1),
typeof(Address.city),
typeof(Address.countryID)
}));
pxdimensionSelectorAttribute.DescriptionField = typeof(Vendor.acctName);
this._SelAttrIndex = this._Attributes.Count - 1;
this.Filterable = true;
((PXDimensionSelectorAttribute)this._Attributes[this._SelAttrIndex]).CacheGlobal = true;
}
public const string DimensionName = "MYVENDOR";
}
}
After this Vendor CD will work with 4 length. But as you can see on the screenshot below there will be some issues with the existing records. So I recommend you to do this only in case if the system has not existing Vendors.
Also after doing this I assume you to check all the pages which use Vendor for correct working.

Related

How to get DAC record from Note Table

Does anyone have a code snippet on how to go from RefNoteId => DAC when I dont know what dac type the note is attached to?
I have made it this far (row.RefNoteID is what I am starting from)
Note note = PXSelect<Note, Where<Note.noteID, Equal<Required<Note.noteID>>>>.Select(this, row.RefNoteID);
Type recordType = Type.GetType(note.EntityType);
PXCache recordCache = Caches[recordType];
How can I now do a PXSelect<recordType, Where<recodType.noteID, Equal<Required<recordType.noteID>>>>.Select(GRAPH) ? The recordType could be any DAC in the system that has a noteID.
Thanks
The below code works for me and it is based on the way Acumatica gets the record inside the PXRefNoteSelectorAttribute.PrimaryRow_RowPersisted.
The problem with this approach is that this will work for Header entities like SOOrder, INRegister, SOInvoice, SOShipment, and others. But for "detail" entities like SOLine, INTran, and others this approach will work only if that corresponding record has some Note related to Text/File. Acumatica is adding records corresponding to their NoteID into the Note table only if that detail records have some Note/Text. My best guess is that this is done in order to avoid over-spamming the Note table.
using PX.Data;
using PX.Objects.SO;
using System;
using System.Collections;
using System.Linq;
using System.Web.Compilation;
namespace SearchByNoteID
{
// Acuminator disable once PX1016 ExtensionDoesNotDeclareIsActiveMethod extension should be constantly active
public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
{
public PXAction<SOOrder> searchByNoteID;
[PXUIField(DisplayName ="Search by Note ID")]
[PXButton(CommitChanges = true)]
public virtual IEnumerable SearchByNoteID(PXAdapter adapter)
{
var order = adapter.Get<SOOrder>().FirstOrDefault();
if(order!=null)
{
//
//...
//
Guid? noteID = GetNoteID();
object record = GetRecordByNoteID(noteID);
//
//... do whatever you want with the record
//
}
return adapter.Get();
}
protected object GetRecordByNoteID(Guid? noteID)
{
var type = GetEntityType(this.Base, noteID);
if(type==null) return null;
object entityRow = new EntityHelper(this.Base).GetEntityRow(type, noteID);
return entityRow;
}
protected Type GetEntityType(PXGraph graph, Guid? noteID)
{
if (noteID == null)
{
return null;
}
Note note = PXSelectBase<Note, PXSelect<Note, Where<Note.noteID, Equal<Required<Note.noteID>>>>.Config>.SelectWindowed(graph, 0, 1, new object[]
{
noteID
});
if (note == null || string.IsNullOrEmpty(note.EntityType))
{
return null;
}
return PXBuildManager.GetType(note.EntityType, false);
}
}
}

How can I restrict Site ID (Whse) selection to only the current branch?

While it is easy in thought to just limit records to INSite.branchID = AccessInfo.branchID, the need is a bit more complex. I thought that I found a simple solution when looking at the DAC for INTran to find:
#region SiteID
public abstract class siteID : PX.Data.BQL.BqlInt.Field<siteID> { }
protected Int32? _SiteID;
[IN.SiteAvail(typeof(INTran.inventoryID), typeof(INTran.subItemID))]
[PXDefault(typeof(INRegister.siteID))]
[PXForeignReference(typeof(FK.Site))]
[InterBranchRestrictor(typeof(Where<SameOrganizationBranch<INSite.branchID, Current<INRegister.branchID>>>))]
public virtual Int32? SiteID
{
get
{
return this._SiteID;
}
set
{
this._SiteID = value;
}
}
#endregion
which has an intriguing attribute for InterBranchRestrictor. After a little digging, I found this attribute actually is used rather widely in Acumatica, but it appears to be limited to only Report Graphs and enabling the feature for Inter-Branch Transactions. Easy enough, I enabled the feature and tried an Inventory Issue again. No luck. I still could select a site id for a different branch.
So far, I only have limited control by creating a graph extension on INIssueEntry to set and validate the site ID. But what I really want is to limit the selector to only site id's of the current branch.
protected void _(Events.FieldDefaulting<INTran.siteID> e)
{
PXResultset<INSite> Results = PXSelect<INSite, Where<INSite.branchID, Equal<Current<AccessInfo.branchID>>>>.Select(Base);
if (Results.Count == 1)
{
foreach (PXResult<INSite> result in Results)
{
INSite site = result;
e.NewValue = site.SiteID;
e.Cancel = true;
}
}
}
protected void _(Events.FieldVerifying<INTran.siteID> e)
{
int? siteID = (int?)e.NewValue;
INTran row = (INTran)e.Row;
INSite site = PXSelect<INSite, Where<INSite.siteID, Equal<Required<INSite.siteID>>,
And<INSite.branchID, Equal<Current<AccessInfo.branchID>>>>>.Select(Base, siteID);
if(siteID != null && site?.SiteID == null)
{
PXUIFieldAttribute.SetError<INTran.siteID>(e.Cache, row, "Invalid Warehouse for Branch");
}
}
I really want to leverage what it seems like
[InterBranchRestrictor(typeof(Where<SameOrganizationBranch<INSite.branchID, Current<INRegister.branchID>>>))]
does, but clearly I just don't understand what it does. Alternatively, if I could add a where clause to
[IN.SiteAvail(typeof(INTran.inventoryID), typeof(INTran.subItemID))]
then I could restrict the list to the current branch that way, but I'm struggling to make that work as well. (Problems around implementing the PXForeignReference attribute in the extension needed to override the field definition.)
How can I restrict (in a manner that can be replicated efficiently throughout Acumatica) branch specific records to only site ID's of the current branch?
The InterBranchRestrictorAttribute is checking the graph to be working from a Report or the Inter-Branch Transactions feature to be turned on in the IsReportOrInterBranchFeatureEnabled method, so you need to remove this from your implementation.
You can write your own PXResrictorAttribute in the way shown in the example below:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]
public class CustomRestrictorAttribute: PXRestrictorAttribute
{
public CustomRestrictorAttribute(Type where) : base(CustomRestrictorAttribute.EmptyWhere, "Restrictor Message.", Array.Empty<Type>())
{
this._interBranchWhere = where;
}
protected override BqlCommand WhereAnd(PXCache sender, PXSelectorAttribute selattr, Type Where)
{
return base.WhereAnd(sender, selattr, this._interBranchWhere);
}
private static readonly Type EmptyWhere = typeof(Where<True, Equal<True>>);
protected Type _interBranchWhere;
}
And apply it to the DAC field like below:
[SiteAvail(typeof(SOLine.inventoryID), typeof(SOLine.subItemID))]
[PXParent(typeof(Select<SOOrderSite, Where<SOOrderSite.orderType, Equal<Current<SOLine.orderType>>, And<SOOrderSite.orderNbr, Equal<Current<SOLine.orderNbr>>, And<SOOrderSite.siteID, Equal<Current2<SOLine.siteID>>>>>>), LeaveChildren = true, ParentCreate = true)]
[PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIRequired(typeof(IIf<Where<SOLine.lineType, NotEqual<SOLineType.miscCharge>>, True, False>))]
[InterBranchRestrictor(typeof(Where2<SameOrganizationBranch<INSite.branchID, Current<SOOrder.branchID>>,
Or<Current<SOOrder.behavior>, Equal<SOBehavior.qT>>>))]
[CustomInterBranchRestrictor(typeof(Where<INSite.branchID,Equal<Current<SOOrder.branchID>>>))]
protected virtual void SOLine_SiteID_CacheAttached(PXCache cache)
{
}

Acumatica Custom Selector SQL Error - Incorrect Syntax near the keyword

I am trying to create a custom Parts selector to search all records if there is no vendor selected, if a vendor is selected I want to filter the selector's results to only include Parts with the vendor selected.
Using PXCustomSelectorAttribute:
SQL Error: Incorrect syntax near the keyword 'AND'
Incorrect syntax near the keyword 'ORDER'
[PXNonInstantiatedExtension]
public class SO_SOLine_ExistingColumn : PXCacheExtension<PX.Objects.SO.SOLine>
{
#region InventoryID
[PXMergeAttributes(Method = MergeMethod.Replace)] //was append
[PXUIField(DisplayName = "Part #")]
[PartSelector(typeof(SOLineExt.usrCusVendor))]
public int? InventoryID { get; set; }
#endregion
}
public class PartSelector : PXCustomSelectorAttribute
{
[Serializable]
[PXProjection(typeof(
Select2<atcVendorItem,
LeftJoin<InventoryItem,
On<InventoryItem.inventoryCD, Equal<atcVendorItem.inventoryCD>, And<InventoryItem.inventoryID, Equal<atcVendorItem.inventoryID>>>>>), Persistent = false)]
public class atcPartView : IBqlTable
{
// DAC W/Inventory Item Table mapped and joined with Vendor Items
}
//Selected table
private Type _MfgField;
////way to have multiple description fields
public PartSelector(Type MfgField) : base(typeof(atcPartView.inventoryID))
{
_MfgField = MfgField;
}
protected virtual IEnumerable GetRecords()
{
var cache = this._Graph.Caches[BqlCommand.GetItemType(_MfgField)];
var cbs = (BAccount)cache.Current;
// make mfgfield
if (cbs != null)
{
foreach (atcPartView p in PXSelect<atcPartView, Where<atcPartView.vendorID, Equal<Required<SOLineExt.usrMfg>>>>.Select(_Graph, cbs.BAccountID))
{
yield return p;
}
}
else
{
foreach (atcPartView p in PXSelect<atcPartView, Where<atcPartView.vendorID, IsNotNull>>.Select(_Graph))
{
yield return p;
}
}
}
}
I was able to find another exception while debugging in visual studio,
my issue was because I was trying to cast the field as BAccount when referencing the SOLine Extension. Changing the cast fixed my issue

How to implement auto generating document number on custom screen

I have a requirement to create a opportunity like screen and I do not know how to implement the auto generating the document number for newly created document
I am looking forward someone to help me on this issue.
The following steps I have used and I have attached the code for review. I am getting error while saving and not generating the number
I have create a numbering sequence for Memo In document
I have created a DAC for Sequence number setup
region MemoInOrderId
public abstract class memoInOrderId : PX.Data.IBqlField
{
}
protected string _MemoInOrderId;
[PXDBString(10, IsUnicode = true)]
[PXDefault("MEMOIN")]
[PXSelector(typeof(Numbering.numberingID),
DescriptionField = typeof(Numbering.descr))]
[PXUIField(DisplayName = "Memo In Order Nbr")]
public virtual string MemoInOrderId
{
get
{
return this._MemoInOrderId;
}
set
{
this._MemoInOrderId = value;
}
}
#endregion
I have added Auto Generation Sequence number to MemoIn DAC
`
#region OrderNbr
public abstract class orderNbr : PX.Data.IBqlField
{
}
[PXDBString(10, IsUnicode = true, IsKey = true, InputMask = ">CCCCCCCCCCCCCCC")]
[PXUIField(DisplayName = "Order Nbr", Visibility = PXUIVisibility.SelectorVisible)]
[AutoNumber(typeof(MemoSetUp.memoInOrderId), typeof(AccessInfo.businessDate))]
[PXSelector(typeof(MemoIN.orderNbr),
new Type[]
{
typeof(MemoIN.orderNbr),
typeof(MemoIN.orderDate),
typeof(MemoIN.vendorId)
})]
public virtual string OrderNbr { get; set; }
#endregion
In the configuration Screen I have selected numbering sequence used for memo in document
While saving the Memo In document I am getting the following error
I have noticed the Order number is not initialized to "NEW" and it is showing "SELECT"
I have gone through CASetup , CMSetup , ARSetup DAC code and not able to figure out the difference.
If we want to use a numbering sequence it is very straight forward in Acumatica. You should have a setup/preferences field somewhere that defines which numbering sequence you will use for your document number field.
Here is an example of a setup field using a selector to pick a numbering sequence:
// Setup field indicating which numbering sequence to use.
public abstract class myNumberingID : PX.Data.IBqlField
{
}
protected String _MyNumberingID;
[PXDBString(10, IsUnicode = true)]
[PXSelector(typeof(Numbering.numberingID), DescriptionField = typeof(Numbering.descr))]
[PXUIField(DisplayName = "My Numbering Sequence")]
public virtual String MyNumberingID
{
get
{
return this._MyNumberingID;
}
set
{
this._MyNumberingID = value;
}
}
Next, in your document number field you will use the AutoNumberAttribute to define the field as a consumer of a numbering sequence. Below is an example of a number field using the defined number sequence configured in the setup table above (Assumes "MyNumberingID" exists in DAC/Table "MySetup").
// Field using the numbering sequence...
public abstract class myNumberField : PX.Data.IBqlField
{
}
protected String _MyNumberField;
[PXDBString(15, IsUnicode = true, IsKey = true, InputMask = ">CCCCCCCCCCCCCCC")]
[PXUIField(DisplayName = "My Number", Visibility = PXUIVisibility.SelectorVisible)]
[AutoNumber(typeof (MySetup.myNumberingID), typeof (AccessInfo.businessDate))]
[PXDefault]
public virtual String MyNumberField
{
get
{
return this._MyNumberField;
}
set
{
this._MyNumberField = value;
}
}
Edit: Make sure in the graph building the documents to include a PXSetup view to the setup table.
Now when you insert and persist a new record on the DAC that contains your number field, the next numbering sequence value will be used (unless the numbering sequence is configured for manual numbering then the user must provide a value).
For a more complex configuration when there are multiple numbering sequences used based on specific conditions/field values, you can look at PX.Objects.IN.INRegister.RefNbr for an example. Look at INDocType.Numbering and how it changes the numbering sequence based on INRegister.docType (shown below). Another example would be sales order order types related to the sales order document.
public class NumberingAttribute : AutoNumberAttribute
{
public NumberingAttribute()
: base(typeof(INRegister.docType), typeof(INRegister.tranDate),
new string[] { Issue, Receipt, Transfer, Adjustment, Production, Change, Disassembly },
new Type[] { typeof(INSetup.issueNumberingID), typeof(INSetup.receiptNumberingID), typeof(INSetup.receiptNumberingID), typeof(INSetup.adjustmentNumberingID), typeof(INSetup.kitAssemblyNumberingID), typeof(INSetup.kitAssemblyNumberingID), typeof(INSetup.kitAssemblyNumberingID) }) { ; }
}

Is there a easy way to customize Acumatica build-in dropdown list?

For example, in screen CR301000, source field is having 5 items right now, but I want to have 6 different items listing here, please advice how to do it. Thanks.
You can do it a few ways.
1) Customization to create a custom string/int list then override the dac attribute in the BLC to point to your custom list.
First create the custom stringlist:
public class CustomSourceAttribute : PXStringListAttribute
{
public const string _LEADPROSPECT = "1";
public const string _INITIALCONTACT = "2";
public const string _QUALIFIED = "3";
public const string _INITIALPRICE = "4";
public const string _PROPOSALSENT = "5";
public const string _POSITIVEPROPOSAL = "6";
public const string _VERBALCOMMIT = "7";
public const string _READYFORCONTRACT = "R";
public const string _CONTRACTSENT = "8";
public const string _CONTRACTSIGNED = "9";
public const string _CLOSEDLOST = "0";
public const string _TARGET = "T";
public CustomSourceAttribute()
: base(new string[]
{
_LEADPROSPECT,
_INITIALCONTACT,
_QUALIFIED,
_INITIALPRICE,
_PROPOSALSENT,
_POSITIVEPROPOSAL,
_VERBALCOMMIT,
_READYFORCONTRACT,
_CONTRACTSENT,
_CONTRACTSIGNED,
_CLOSEDLOST,
_TARGET
},
new string[]
{
"Lead/Prospecting",
"Initial Contact",
"Qualified",
"Initial Pricing Sent",
"Proposal Sent",
"Positive Proposal Discussions",
"Verbal Commitment",
"Ready for Contract",
"Contract Sent",
"Contract Signed",
"Closed Lost",
"Target"
})
{
}
}
Then override the dac attribute in a BLC Extension:
[PXDBString(1, IsFixed = true)]
[PXUIField(DisplayName = "Stage")]
[CustomSourceAttribute]
[PXDefault(CustomSourceAttribute._INITIALCONTACT)]
[PXMassUpdatableField]
protected void CROpportunity_StageID_CacheAttached(PXCache cache)
{
}
This sample is from the Opportunity screen but the same holds true w/ leads
2) Automation step to provide the new values.
See here for automation step locations
the second way doesn't require customization but does take more work if there is already automation steps defined. You need to create the custom list for each step that exists.
For something like Leads, I'd go with option 1 as there is tons of steps
Add the Values you want:
Just a quick note if you chose to add via automation - to add a new field click in the white space of the lookup field - You will see the current values - in the white space below - dbl click.
Here is an example of creating Acumatica dropdown list field. Add constatnt fields and Pair them in Tuple<T1, T2>[].
public class Operators
{
public const string And = "&&";
public const string Or = "||";
public class UI
{
public const string And = "And";
public const string Or = "Or";
}
public class OperatorsAttribute : PXStringListAttribute
{
public OperatorsAttribute() : base(new Tuple<string, string>[]
{
Pair(And, UI.And),
Pair(Or, UI.Or)
})
{}
}
}
Then you use the nested OperatorsAttribute attribute class on the field like this.
#region Operator
public abstract class operators : BqlString.Field<operators> { }
[PXDBString(15)]
[PXDefault(1)]
[OperatorsAttribute]
[PXUIField(FieldName = "Operators", DisplayName = "Operators")]
public virtual string Operators { get; set; }
#endregion
To change already existing fields, create Extension classes for DACs and Graphs. Here is the detailed documentation for creating extension classes — Acumatica ERP Customization Guide

Resources