I've added three custom decimal? field on Requisition master (RQ302000) and need to prorate the total value of those three fields to the each Requisition line using line base total cost/master total cost ratio and displayed the result as Additional Cost (also custom decimal? field) of each line.
This calculation should be triggered when those three new field are updated.
What I don't understand are:
1. What events should be modify related to this needs
2. If it is event on master field, how to get value from line extension field
3. If it is event on line field, how to get value from master extension field
Requisition Custom Screen
1. "What events should be modify related to this needs?"
In a scenario, like yours, one should use combination of RowInserted, RowUpdated and RowDeleted handlers:
The RowInserted event handler is used to implement the business logic for:
Inserting the detail data records in a one-to-many relationship.
Updating the master data record in a many-to-one relationship.
Inserting or updating the related data record in a one-to-one relationship.
The RowUpdated event handler is used to implement the business logic of:
Updating the master data record in a many-to-one relationship.
Inserting or updating the detail data records in a one-to-many relationship.
Updating the related data record in a one-to-one relationship.
The RowDeleted event handler is used to implement the business logic of:
Deleting the detail data records in a one-to-many relationship.
Updating the master data record in a many-to-one relationship.
Deleting or updating the related data record in a one-to-one relationship.
Also FieldUpdated handlers be considered for your scenario:
The FieldUpdated event handler is used to implement the business logic associated with changes to the value of the DAC field in the following cases:
Assigning the related fields of the data record containing the modified field their default values or updating them
Updating any of the following:
The detail data records in a one-to-many relationship
The related data records in a one-to-one relationship
The master data records in a many-to-one relationship
Refer to API Reference in Help -> Acumatica Framework -> API Reference -> Event Model and T200 developer class for additional information and examples on Acumatica Framework event model.
2. "If it is event on master field, how to get value from line extension field?"
In Acumatica custom fields are declared via DAC extensions. To access the DAC extension object, you can use the following methods:
The GetExtension() generic method available for each DAC instance:
ContactExt contactExt = curLead.GetExtension<ContactExt>();
The GetExtension(object) generic method declared within the non-generic PXCache class
ContactExt contactExt = Base.LeadCurrent.Cache.GetExtension<ContactExt>(curLead);
or
ContactExt contactExt = Base.Caches[typeof(Contact)].GetExtension<ContactExt>(curLead);
The GetExtension(object) static generic method of the PXCache generic class
ContactExt contactExt = PXCache<Contact>.GetExtension<ContactExt>(curLead);
To get value from line extension field, you should first select records from the Lines data view, then use one of the methods described above to access instance of DAC extension class, for instance:
foreach(RQRequisitionLine line in Base.Lines.Select())
{
RQRequisitionLineExt lineExt = line.GetExtension<RQRequisitionLineExt>();
}
3. "If it is event on line field, how to get value from master extension field"
That's an easy one: same 3 approaches described above, this time applied to Current property of the primary Document data view, for instance:
Base.Document.Current.GetExtension<RQRequisitionExt>();
Related
First, created a custom field in the PM.PMTask DAC called usrNumberofPanel
Second, created a custom field in the PM.PMProject DAC usrTotalPanels.
Want each of the lines from task to update the total number of panels on the project, so modified the attribute for PM.PMTask.userNumberofPanel and added a PXFormula as shown below to add the SumCalc.
[PXDBDecimal]
[PXUIField(DisplayName="Number of Panels")]
[PXFormula(null, typeof(SumCalc<PX.Objects.CT.ContractExt.usrTotalPanels>))]
Made sure the attributes for the Total Panel and set as follows to make sure no one types into the field.
[PXDBDecimal]
[PXUIField(DisplayName="Total Panels", Enabled = false)]
Any thoughts would be appreciated.
It's a known issue that SumCalc doesn't work properly across DACs that are linked with PXParent relationships.
I can only recommend to use a RowSelected or FieldSelecting graph event handlers to compute the sum instead of a solution involving DAC attributes. You can add a comment explaining the limitation of DAC attributes in the event handler if you are seeking Acumatica ISV Certification for your solution.
I've created some code that uses a number of fields (eg: length, width, LbsPerInch) to calculate the order quantity. I've used this:
cache.SetValue<SOLine.orderQty>(e.Row, dQO);
That part work perfectly. After that's updated, I need to have the line update the rest of the line as though the orderQty was updated manually.
I tried using the following, but that must not be correct, as it seems to just completely hand the form when it runs.
Base.Transactions.Update(row);
Base.Transactions.View.RequestRefresh();
Thanks in advance!
When the user modifies a field on screen it raises the FieldUpdated event for that field.
The base graph you are extending can declare handlers for these FieldUpdated events. The issue could be that the OrderQty field events aren't raised and the base graph uses those events to update other fields which values depend on OrderQty.
The SetValue method changes field value without raising field events.
The SetValueExt method changes field value and raises field events.
You can try using SetValueExt instead of SetValue:
cache.SetValueExt<SOLine.orderQty>(e.Row, dQO);
This is usually sufficient. The most stubborn refresh issues can usually be dealt with by calling the RaiseRowUpdated method to raise the row events after the field has been modified.
I noticed that graph.Caches[typeof(MyDac)] is quite useful in Acumatica to manipulate values in other DACs. What would be the proper way to use it, for example when I update a value in another DAC? Do I have to persist right after it, or will it be saved to DB when I run graph.Action.PressSave(), even if I am not using this DAC in any of the view in the graph?
The PXGraph.Caches collections contains DAC objects loaded in memory on the server. In a graph it is accessed directly as this.Caches and in an extension it is accessed through Base.Caches. While not technically acurate it can be thought of as a global DAC cache. The DAC objects contained in DataView.Cache is a subset of the DAC objects in PXGraph.Caches.
The usual pattern for persisting records is by using graph DataViews. The PXGraph.Action.PressSave() will persist the records when the current record of the primary DataView of the graph is properly set.
The primary DataView of the graph is declared in the ASPX PrimaryView property of the PXDataSource element:
<px:PXDataSource ID="ds" runat="server" PrimaryView="Document" TypeName="PX.Objects.SO.SOOrderEntry" >
Setting PrimaryView.Current record in the BLC graph is required for the Save action to persist the DataViews records.
It is also possible to persist DAC objects in DataViews without invoking the Save action. To do so you would perform a CRUD operation and then Persist the DAC objects in the DataView using:
DataView.Insert(DAC)
DataView.Update(DAC)
DataView.Delete(DAC)
DataView.Cache.Persist(PXDBOperation.Insert)
DataView.Cache.Persist(PXDBOperation.Update)
DataView.Cache.Persist(PXDBOperation.Delete)
While it is possible to do the same operations on PXGraph.Caches there are few situation where this is required in user code because using DataView is the preferred method of persisting DAC objects and access to DataViews is available when you have the BLC.
Where PXGraph.Caches is especially useful is when working with PXGraph instead of a BLC, often found in the context of generic code. You can access DataViews of the BLC like SOOrderEntry.Document with a reference pointing to the SOOrderEntry BLC. However if all you got is a reference to the PXGraph base class of the BLC you don't have access to the DataViews. In that case you can still access the DAC objects in PXGraph.Caches. By knowing the type of the DataView DAC objects for example SOOrderEntry.Document is of SOOrder type and will be in PXGraph.Caches[typeof(SOOrder)]. For user code this pattern is more common in custom attributes that have access to the PXGraph object but not the BLC. In BLC it's more common to work on the DataView.Cache instead of PXGraph.Cache.
Another possible scenario where accessing PXGraph.Caches could be useful is when you have many DataViews of the same type. If you want to query all updated DAC objects of a same type that are in multiple DataViews you can itereate on Caches[typeof(DAC)].Updated. It's also quite common to simply fetch a reference to a Cache object using Caches[typeof(DAC)] that will then be passed to another Acumatica framework method by input parameter. In this case it's often a matter of convenience because the same Cache reference could be obtain through DataView.Cache.
I'am designing a new invoicing application. There are a number of features that I don't know how to implement in Core Data. I ask you for help with the following.
To keep things simple assume that there are 2 entities, Invoice entity and Detail entity with to-many relationship 'invoiceDetails' and to-one relationship 'detailInvoice'. Here are my questions.
Detail entity should have attribute 'sequenceNumber' which should be auto-generated when the user adds new detail. For each invoice the sequenceNumber should start at 1 and be incremented as the user adds new details. The sequenceNumber should be used to sort details within their invoice.
Detail entity has also attributes 'numberOfItems' and 'price'. It also should have attribute 'amount' which should be auto-generated as product of numberOfItems and price.
Invoice entity should have attribute 'netAmount' which should be generated as the sum of all detail amounts.
Invoice entity should have attribute 'vat' which should be auto-generated as an expression from netAmount.
Invoice entity should also have attribute 'totalAmount' auto-generated as a sum of netAmount and vat.
Invoice entity should have attribute 'dueTo' auto-generated from current date plus some number of days.
How do I accomplish this in Core Data application? Thanks.
/Mikael
Detail entity should have attribute 'sequenceNumber' which should be auto-generated when the user adds new detail.
You'll have to assign this value yourself. What I'd do is store the highest sequence number as metadata on the persistent store file (see NSPersistentStoreCoordinator's metadata-related methods). Any time you create a new instance, read the highest number from the store metadata, increment it, use that value on the new instance, and then save the new value back to store metadat.
2-6. Calculated attributes
These are generally handled by subclassing NSManagedObject and then overriding the setter methods on attributes whose value affects other attributes. For example, based on #2, the setter for your price attribute would look something like:
- (void)setPrice:(NSDecimalNumber *)price
{
[self willChangeValueForKey:#"price"];
[self setPrimitiveValue:price forKey:price];
[self didChangeValueForKey:#"price"];
// Now calculate the new value for "amount" and set it on self.
}
Follow the same pattern for each case. You can also use key-value observing to watch for changes on these attributes, but I find custom accessors to be clearer and less error-prone.
I am trying to do exactly same thing as post in NSFetchResultsController + sectionNameKeyPath + section order, i.e. basically use 2 tables, let's say Categories <-->> Events. Category table consists of category field only, while Event consists of name, dateTimestamp.
I defined relationship 'category' in Events table and try to use that relationship as sectionNameKeyPath when creating fetchedResultsController:
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"category.category" cacheName:#"Root"];
Finally, I pre-populated Category table with some categories upon loading of the app (and verified with .dump that table is populated correctly)
Yet, I simulator fails on:
return [[self.fetchedResultsController sections] count];
I did extensive search and most people either suggest using one of the fields in the table as sectionNameKeyPath (this works!) or transient property (works too!) However, I just want to use relationship as it seems very logical to me in this case where events belong to some categories and there could be categories without events. Am I wrong in my assumption that relationship can be used as sectionNameKeyPath? The original link at the top of the question suggests it works, but guy does not know why or how. Documentation is very weak on what can be used as sectionNameKeyPath, so any help will be highly appreciated.
A relationship gets you a pointer to a managed object. It seems logical, though, that the sectionNameKeyPath parameter should be a key path that leads to a string, since NSFetchedResultsSectionInfo's name property is a string. The fetched results controller will follow that key path for each fetched object and group the objects into sections based on what they return for that key path, and it'll also use those strings as the names of their respective sections. You can't use a managed object for the name -- you have to use some string property of the managed object.
So, your Category entity must have an attribute that distinguishes one category from another, right? Use that as the key path and (as you've seen) everything will work out.
BTW, I think it's useful to try to get out of the database (rows/fields) mindset and try to think in object-oriented terms like entity and attribute. A big selling point of Core Data is that it provides an abstraction layer that hides the storage mechanism. Thinking in terms of tables is like thinking about blocks and sectors when you're reading or writing a file.
Caleb, thank you for your answer. I do believe my understanding was wrong to some degree. What I had was an entity Category and entity Event. Category has a string field 'category', thus 'category.category' path (first 'category' is relationship in the Event entity)
What I did not take in account, though, is that if there are no events, fetchresultscontroller cannot fetch anything (similar to 'left join')
What I wanted is to show categories even if there are no events. Relationship 'category' will not return anything in this case as there is nothing to return/sort/categorize.
What I had to do (wrong or right - not sure yet) is to treat [managed] object created from Category entity as a separate object in case there are no events and place in the table. When there is one event per category, I can switch to the original method of [automatic] showing events sorted by categories.
This is interesting issue of starting point (empty entities with relationships) where I feel core data is more confusing than traditional relationship database. I also believe that's why all books/articles/reports carefully stay away from this topic. In other words, I could not find analog of "left join" in core data. May be I am wrong because I am relatively new to all this. Below is the description of the entities:
Category <-->> Event
Category - parent
Category.category - attribute of type String
Category.event - relationship to Event entity
Event - child
Event.name - attribute of type String
Event.category - relationship to Category entity
Each event belongs to one category. Category may have multiple events.
Categories should be shown even if there are no events for this category.
I was trying to put Events under fetchresultscontroller. May be I should switch to Category first and then calculate cell based on category.event relationship, not the other way around - did not try that yet.