I am trying to add another address to a Branch in 21.203, similar to the how the DefAddress works. I have extended the Branch DAC and added the field to the extension. I also tried extending the BAccount DAC and table to get this to work but, nothing is working so far. The Address record is being created in the Address table but, the PXDBChildIdentity attribute doesn't seem to be sending back the ID to the Address record. On the screen, when I enter the values for the address, and click Save, all of the values are wiped out and the custom field (UsrCustomAddress) is never populated.
#region UsrCustomAddress
[PXDBInt()]
[PXUIField(DisplayName="Custom Address")]
[PXDBChildIdentity(typeof(Address.addressID))]
public virtual Int32? UsrCustomAddress { get; set; }
public abstract class usrCustomAddress : PX.Data.BQL.BqlInt.Field<usrCustomAddress> { }
#endregion
And, then, I added the View to the BranchMaint extension.
<code>
namespace PX.Objects.CS
{
public class BranchMaint_Extension : PXGraphExtension<BranchMaint>
{
public PXSelect<Address, Where<Address.bAccountID, Equal<Current<BAccount.bAccountID>>,
And<Address.addressID, Equal<Current<BAccountExt.usrCustomAddress>>>>> CustomAddress;
}
}
</code>
Not sure what I'm missing?
TIA!
I handled this by manually calling .Insert() on the specific Data View in the row inserting event and writing it to the field. PXDBChildIdentity then can update the field with the real identity value (on row persist) after the link is established.
protected void _(Events.RowInserting<Branch> e)
{
if (e.Row is null) return;
Address newAddress = CustomAddress.Insert();
BranchExt ext = e.Cache.GetExtension<BranchExt >(e.Row);
ext.UsrCustomAddress = newAddress.AddressID;
}
If anyone knows how to do this in an attribute id love to know. Note, this will only work for newly created records. Any existing records would not have a row inserting event fire and fill your custom field. You need to backfill the data with SQL or just a temporary custom action that you can invoke this code manually.
Related
I'm trying to figure out if it's possible to add fields to the grid on the Process Shipments screen for PO Receipts when the "Prepare Drop-Ship Invoice" action is selected. Specifically, I want to add the POReceipts.BranchID field.
I've studied the source code, and I see where different select commands are run based on the selected action, but I don't understand what drives the grid to show the different set of columns.
I looked in the screen designer in the customization editor, and I don't see a different view or anything, hopefully I'm just missing something simple.
Thanks
Scott
This is how I solved your mystery. First create a user field for SOShipment.
#region UsrReceiptBranchID
[PXInt]
[PXUIField(DisplayName="Receipt Branch ID")]
[GL.Branch()]
public virtual int? UsrReceiptBranchID { get; set; }
public abstract class usrReceiptBranchID : PX.Data.BQL.BqlInt.Field<usrReceiptBranchID> { }
#endregion
Next is the key part. I extended the data view delegate, and added an extra query to pull the POReceipt branch, when the Filter is Drop-Ship.
public virtual IEnumerable orders()
{
List<SOShipment> records = Base.orders().RowCast<SOShipment>().ToList();
if (Base.Filter.Current.Action.Contains("Drop"))
{
foreach (SOShipment sOShipment in records)
{
POReceipt receipt = PXSelect<POReceipt,
Where<POReceipt.receiptNbr,
Equal<Required<POReceipt.receiptNbr>>>>.Select(Base, sOShipment.ShipmentNbr);
if (receipt != null)
{
SOShipmentExt shipExt = PXCache<SOShipment>.GetExtension<SOShipmentExt>(sOShipment);
shipExt.UsrReceiptBranchID = receipt.BranchID;
}
}
return records;
}
else
{
return records;
}
}
I am trying to enable the custom field in Case when the Status is in Closed State. I am working on a customization for Acumatica version 20.114.0020 (2020 R1).
I have created a custom field usrIsNotBillable in CRCase DAC.
[PXDBBool]
[PXUIField(DisplayName="Confirmed Not Billable", Enabled = true)]
public virtual bool? UsrIsNotBillable { get; set; }
public abstract class usrIsNotBillable : PX.Data.BQL.BqlBool.Field<usrIsNotBillable> { }
It is totally working fine when the Case is in other states than Closed. But when the case is closed, every other property gets disabled. But I want this field to be set enabled. So, I override the Row Selected method for CRCaseMaint graph as below:
protected void CRCase_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected InvokeBaseHandler)
{
InvokeBaseHandler?.Invoke(cache, e);
CRCase row = (CRCase) e.Row;
if (row == null) return;
Base.CaseCurrent.Cache.AllowUpdate = true;
Base.CaseCurrent.AllowUpdate = true;
PXUIFieldAttribute.SetEnabled<CRCaseExt.usrIsNotBillable>(cache, row, true);
}
If I use other DAC fields such as IsBillable like this:
PXUIFieldAttribute.SetEnabled<CRCase.isBillable>(cache, row, true);
It just works fine.
I checked other examples too and the implementation is similar to this. I am just not sure why it is not working in this case.
I have also checked if this screen has any existing workflows and it doesn't.
Any help would be appreciated.
Thanks.
Besides writing code to enable the field in RowSelected event, it is also important to add the field in Closed state in the Workflow.
However, if this is also not working deleting contents of CstDesigner of project does the job.
I have 2 scenarios, I am facing issue.
I have 3 DAC, One is Parent and other 2 are child DAC.
I have tab view, and showing data accordingly.
First tab showing Parent DAC data.
Second and third are showing child DAC data.
I am creating new item, Now i press Add(+) button and fill, Information in Parent tab, but not filled any data in 2nd and 3rd tab, when i press SAVE then only parent DAC saved, 2nd and 3rd tab rows not created in database, I want to save 2nd and 3rd Tab DAC also, what should i do?
Another case:
i am not allowing DAC to update, I am setting DAC.Cahce.AllowUpdated = false; all controls are disabled excepting Checkbox, why?
Also when we are on 3rd tab and click on toolbar Navigation button, then 1st tab data doesn't refresh, it is showing previous selected item data. How to fix this issue?
We are not using PXGrid for any child data, there is one to one relation ship between all 3 DAC.
/// Parent View
public SelectFrom<Parent>.View parentTran;
/// Child view
public SelectFrom<Child> .Where<Child.tranId.IsEqual<Parent.nCTranId.AsOptional>>.View
childAnalysis;
#region TranId
// Child DAC Field.
public abstract class tranId:PX.Data.BQL.BqlInt.Field<tranId> {
}
[PXDBInt(IsKey = true)]
[PXDBDefault(typeof(Parent.nCTranId))]
[PXParent(typeof(Select<Parent, Where<Parent.nCTranId, Equal<Current<tranId>>>>))]
public virtual int? TranId {
get; set;
}
#endregion
Take a look at T210 regarding Master-Detail Relationships. It contains a section on inserting a default record, which sounds like what you are trying to do with your child DAC. The training can be found at https://openuni.acumatica.com/courses/development/t210-development-customized-forms-and-master-detail-relationship/ on the Acumatica website. Specifically look at pages 103 and 104.
In step 3 of the section, you will see use of the RowInserted event to insert the child record. Since the RowInserted event fires only when creating a record, making it safe to insert the child record.
See the following code sample. I tried to use as much of your sample as possible, but I merged it with the technique shown in the training guide. Disclaimer: It will compile, but I have not built the prerequisites to fully test the code.
using PX.Data;
using PX.Data.BQL.Fluent;
namespace SSCS.CodeSample
{
public class CodeSampleEntry : PXGraph<CodeSampleEntry, DACParent>
{
#region Data Views
/// Parent View
[PXViewName("Parent View - Primary")]
public SelectFrom<DACParent>.View ParentTran;
/// Child view
[PXViewName("Child View")]
public SelectFrom<DACChild>
.Where<DACChild.tranId.IsEqual<DACParent.nCTranId.FromCurrent>>
.View ChildAnalysis;
#endregion
// RowInserted Event to Create a Default Child Record
// See T210 Master-Detail training guide from Acumatica
protected virtual void _(Events.RowInserted<DACParent> e)
{
// Ensure there isn't a child record already
if (ChildAnalysis.Select().Count == 0)
{
// Save state of IsDirty so that we can restore it
// after inserting the default child record
// so that we don't force the user to save
// unless they actually enter data somewhere
bool oldDirty = ChildAnalysis.Cache.IsDirty;
// Create an object for the child
// and fill in any data that should be
// populated by default
DACChild newChild = new DACChild();
newChild.MyData = "My Value";
// Insert the new child record into the cache
ChildAnalysis.Insert(newChild);
// Restore the IsDirty flag
// The insert above will have marked the view as
// dirty. If it wasn't dirty before, it should be
// treated as if it still isn't
ChildAnalysis.Cache.IsDirty = oldDirty;
}
}
}
[PXCacheName("Parent DAC")]
public class DACParent : IBqlTable
{
#region NCTranId
[PXDBIdentity]
public virtual int? NCTranId { get; set; }
public abstract class nCTranId
: PX.Data.BQL.BqlInt.Field<nCTranId> { }
#endregion
}
[PXCacheName("Child DAC")]
public class DACChild : IBqlTable
{
#region TranId
[PXDBInt(IsKey = true)]
[PXDBDefault(typeof(DACParent.nCTranId))]
[PXParent(typeof(SelectFrom<DACParent>
.Where<DACParent.nCTranId.IsEqual<DACChild.tranId.FromCurrent>>))]
public virtual int? TranId { get; set; }
public abstract class tranId
: PX.Data.BQL.BqlInt.Field<tranId> { }
#endregion
#region MyData
[PXDBString()]
[PXUIField(DisplayName = "My Data Field")]
public virtual string MyData { get; set; }
public abstract class myData
: PX.Data.BQL.BqlString.Field<myData> { }
#endregion
}
}
I've been tasked with adding the default vendor inventory ID to grids in multiple screens in Acumatica. The field does not need to be bound and is disabled. I've gotten it as far as showing the field correctly, but only on saved records. Adding a new line and even refreshing the grid will not display the ID, I have to close the screen or switch to another record and come back before the vendor ID will display, even clicking the save button and refreshing will not cause it show. The client is using this field as a reference point so it's important it shows as soon as they select an item.
Below is the code I have for the Kit Specification screen, I need to figure out a way to make it a little more reactive, at least show properly on a refresh. I have tried using Current<> in the where statement, but that just breaks it entirely and always shows nothing.
public class INKitSpecStkDetExt : PXCacheExtension<PX.Objects.IN.INKitSpecStkDet>
{
#region VendorInventoryCode
public abstract class vendorInventoryCode: IBqlField { }
[PXDBScalar(typeof(Search2<PO.POVendorInventory.vendorInventoryID,
InnerJoin<IN.InventoryItem,
On<PO.POVendorInventory.vendorID, Equal<IN.InventoryItem.preferredVendorID>, And<PO.POVendorInventory.inventoryID, Equal<IN.InventoryItem.inventoryID>>>>,
Where<IN.InventoryItem.inventoryID,Equal<IN.INKitSpecStkDet.compInventoryID>>,
OrderBy<Desc<PO.POVendorInventory.vendorInventoryID>>>))]
[PXString(40, IsUnicode = true)]
[PXUIField(DisplayName = "Vendor Inventory Code", Enabled=false)]
public string VendorInventoryCode{ get; set; }
#endregion
}
Once I have the code nailed down it will be used in several other places. Help very much appreciated! Frustrating to have it so close and not be able to cross the finish line...
Follow up based on feedback from HB_Acumatica, working code is below for reference:
public void INKitSpecStkDet_VendorInventoryCode_FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
var row = e.Row as INKitSpecStkDet;
if (e.Row != null)
{
PO.POVendorInventory vendorInventory = PXSelectReadonly2<PO.POVendorInventory,
InnerJoin<IN.InventoryItem,
On<PO.POVendorInventory.vendorID, Equal<IN.InventoryItem.preferredVendorID>, And<PO.POVendorInventory.inventoryID, Equal<IN.InventoryItem.inventoryID>>>>,
Where<IN.InventoryItem.inventoryID, Equal<Required<IN.INKitSpecStkDet.compInventoryID>>>,
OrderBy<Desc<PO.POVendorInventory.vendorInventoryID>>>.Select(Base, row.CompInventoryID);
e.ReturnValue = vendorInventory != null ? vendorInventory.VendorInventoryID : null;
}
}
PXDBScalar attribute doesn't refresh by itself. Maybe explicitly calling RaiseFieldDefaulting method will refresh it:
object newValue = null;
base.Caches[typeof(INKitSpecStkDet)].RaiseFieldDefaulting<INKitSpecStkDetExt.vendorInventoryCode>(rowINKitSpecStkDet, out newValue);
Using PXFormula instead of PXDBScalar if possible will yield better automatic refresh behavior but has it's own set of limitation as well.
If you're looking for the simplest way that works in most contexts (except when no graph is used like in reports and generic inquiry) that would be the FieldSelecting event.
You can execute BQL and return any desired value from the event. It will be called each time the field is referenced so it should update by itself.
public void INKitSpecStkDet_VendorInventoryCode_FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
PO.POVendorInventory vendorInventory = PXSelectReadonly2<Po.POVendorInventory […]>.Select(Base);
e.ReturnValue = vendorInventory != null ? vendorInventory.VendorInventoryCode : null;
}
I was searching for a solution to add a note to a database row I am creating in a custom table. I found the solution below from Ruslan for accessing the noteid, but I don't understand how this would be used to add a note to the row. I have all the code to create the row, I just need the attributes or function call to actually attach the text of the note to the row.
==================================================================
To have Note record automatically created when a new parent record gets saved, one should invoke the static PXNoteAttribute.GetNoteID(PXCache cache, object data) method when the parent record is inserted in the cache.
For example, to have Note record automatically created when a new Stock Item gets saved, you should subscribe to RowInserted handler for the InventoryItem DAC and call PXNoteAttribute.GetNoteID(...):
public class InventoryItemMaintExt : PXGraphExtension<InventoryItemMaint>
{
public void InventoryItem_RowInserted(PXCache sender, PXRowInsertedEventArgs e)
{
var noteCache = Base.Caches[typeof(Note)];
var oldDirty = noteCache.IsDirty;
PXNoteAttribute.GetNoteID<InventoryItem.noteID>(sender, e.Row);
noteCache.IsDirty = oldDirty;
}
}
The code snippet above can be incorporated into almost any custom BLC with a couple simple changes to replace InventoryItem with a custom DAC.
After implementing Gabriel's suggestions:
I do not seem to get any note in the database. The code compiles and runs fine, but the notes are not generated as far as I can tell. The note id is set in my table, but no data appears in the note table. Please take a look at my code and let me know what needs to change. Also, how do I get the note column into a grid, or does it automatically become available when it is done correctly?
Database field definition:
[NoteID] [uniqueidentifier] NULL
DAC field:
#region NoteID
public abstract class noteID : PX.Data.IBqlField
{
}
protected Guid? _NoteID;
[PXNote()]
public virtual Guid? NoteID
{
get
{
return this._NoteID;
}
set
{
this._NoteID = value;
}
}
#endregion
Code to create record:
private static bool acumaticaException(Exception e, EDImportExceptionMaint excpMaint, LingoRet850 res850)
{
excpMaint.Clear();
var except = new EDImportExcept();
except.ExceptReason = "U";
except.Active = true;
<...field assignments...>
except.OrderNbr = "";
PXNoteAttribute.SetNote(excpMaint.Exception.Cache, excpMaint.Exception.Current,
((PX.Data.PXOuterException)e).InnerMessages[0] + "-" + e.Message);
excpMaint.Exception.Insert(except);
excpMaint.Actions.PressSave();
return true;
}
To set the note of a record, use the SetNote()static function of PXNoteAttribute
PXNoteAttribute.SetNote(Base.Item.Cache, Base.Item.Current, "Hello, World!");
Calling SetNote will also take care of adding the Note record if it doesn't exist, so you don't have to call GetNoteID before setting the note value as in your question.
P.S. There is also a GetNote function which allows you to retrieve the current value of the note:
string note = PXNoteAttribute.GetNote(Base.Item.Cache, Base.Item.Current);