How to update tables in fields after amending existing information - openedge

I currently need to add to tables that update each time the user clicks the "Save" button on a program that updates any of their information.
When any of the fields are amended, in order that we have a log of the changes, I need to create a record on a "slinfo" table including things like:
Customer name (slinfo.name)
Customer account (slinfo.acode)
The date (slinfo.date)
And slinfo.seq, which is the sequence number for each change.
How would I go about doing something like this?

you can handle this with Database Triggers
https://documentation.progress.com/output/ua/OpenEdge_latest/pdsoe/PLUGINS_ROOT/com.openedge.pdt.langref.help/rfi1424920170039.html
you have also access to the old field value and the new field value in the trigger statement
simple write procedure:
TRIGGER PROCEDURE FOR WRITE OF Customer
CREATE slinfo.
ASSIGN
slinfo.name = Customer.NAME
slinfo.acode = Customer.account
slinfo.date = TODAY
slinfo.seq = NEXT-VALUE(seqSlinfo)
.

/* presumably you already have a save trigger to save the amended values
* this code should go in that trigger, probably at the end
*/
/* I, obviously, have no way to know where the actual values for
* these variables are supposed to come from. if the logic for
* that is simple enough you might even be able to eliminate the variables
*/
define variable nameVariable as character no-undo.
define variable acodeVariable as character no-undo. /* I'm guessing that this is a character field */
define variable seqVariable as integer no-undo.
/* do something to populate the variables...
*/
/* since you are apparently already saving changes to slinfo
* you just need to add a few fields
*/
assign
slinfo.name = nameVariable
slinfo.acode = acodeVariable
slinfo.date = today
slinfo.seq = seqVariable
.

Related

how to add and display specific records from code_mstr to temp-table in progress 4gl?

I want to create a temp-table for generalized code maintenance(code_mstr) this is what I have written for the temp-table creation
define temp-table tt_gcm no-undo
field tt_fldname like code_fldname
field tt_value like code_value
field tt_cmmt like code_cmmt
field tt_group like code_group
field tt_domain like global_domain
index tt_idx
tt_domain
tt_fldname
tt_value.
and after this I defined a form for the same
form
code_fldname
code_value
code_cmmt
code_group
with frame a side-labels
I also gave prompt-for for code_fldname and code_value because I want it to have user input
prompt-for code_fldname
editing:
/*wrote the mnfnp05.i logic here that enabled the input */
/*similarly for code_value as well */
now I only want to display the records that I enter via the input field, I don't want to display all the records that are present in the code_mstr, is there a way to display those specific recprds?
You really shouldn't use a db field for the prompt-for. In the long term that is going to lead to you having record locking and transaction scoping issues. It is much better to use a variable instead. Something like this:
define variable codeName as character no-undo.
update codeName.
find tt_gcm where tt_gcm.fldName = codeName no-error.
if available tt_gcm then
display tt_gcm with frame a.

Create keywords list WITHIN Create with MANY to MANY relation

I'd like to build a simple creation/edition system where one can create a Challenge that contains many Keywords. Here is the data I created:
Challenge <---- MANY to MANY -----> Keywords
Now, I'm struggling building my Challenge creation form because I'm trying to embed the keywords selection on the CREATE page of the challenge itself. Here is what it looks like:
Screenshot for Challenge creation
This is the Challenge creation page. It contains name, description (challenge model fields) and also a dropdown that goes with a grid, both dedicated to keywords for this challenge. Thanks to a script, I successfully add an keyword item to the Grid datasource when the user selects a value:
/**
* Adds a Keyword to the list of Keywords
* If already added then does nothing.
* #param {Widget} widget - widget that triggered the event.
* #param {Keyword} newValue - record object of selected keyword.
*/
function addKeyword(widget, newValue) {
var ds = app.pages.CreateChallenge.descendants.KeywordsGrid.datasource;
ds.items.push({
KeywordId: newValue._key,
KeywordName: newValue.Name
});
widget.value = null;
}
Once the keywords are added, my problem is that I don't know how to link the newly filled grid full of keywords to the New Challenge in creation so that it populates it with those keywords. Here is what it looks like on the Grid object:
Screenshot for grid
Can I achieve that with bindings only? Should I script it?
Any help is welcome. I guess my question is actually : how to bind a grid to an object in creation (and edition) on a relation between this object and other ones?
Finally solved it after hours. I used :
#datasources.Challenge.modes.create.item.ChallengeKeyword
It allows to handle the data for the item that is being processed by the creation "mode".

Unique id for label input pairs

I'm trying to generate unique id for label & input pairs.
After googling I now know that, unlike with handlebars, there is no array #index syntax extension in spacebars yet (also anybody knows why Blaze development has been inactive since the version 0.1 for past 5 months?).
So I ended up using the JS Array .map() solution inspired by this blog post and other posts. However, this solution returns label & input pairs of objects which DOM appears to be rendering the same on 'pagination' through Session.
Live example: http://meteorpad.com/pad/NXLtGXXD4yhYr9LHC
When clicking on first set of "Non-Indexed IDs" checkboxes, then next/previous, DOM will display new set of checkboxes correctly.
However clicking on the second set of "Indexed IDs" checkboxes below, then next/previous, DOM seems to retain the same checkboxes because one selected from the previous page remains checked on the next page.
What am I doing wrong or missing?
I also put the code on github for quick testing & refinement:
The solution, which I've found by looking at the ObserveSequence source, appears to be to give your generated objects a unique field called _id (generated like {{questionId}}:{{questionIndex}}:{{choiceIndex}}). See this meteorpad: http://meteorpad.com/pad/2EaLh8ZJncnqyejSr
I don't know enough about Meteor internals to say why, but this comment seems relevant:
// 'lastSeqArray' contains the previous value of the sequence
// we're observing. It is an array of objects with '_id' and
// 'item' fields. 'item' is the element in the array, or the
// document in the cursor.
//
// '_id' is whichever of the following is relevant, unless it has
// already appeared -- in which case it's randomly generated.
//
// * if 'item' is an object:
// * an '_id' field, if present
// * otherwise, the index in the array
//
// * if 'item' is a number or string, use that value
//
// XXX this can be generalized by allowing {{#each}} to accept a
// general 'key' argument which could be a function, a dotted
// field name, or the special #index value.
When the _id is absent, it uses the index in the array, so I guess ObserveSequence assumes it's the same object with changed fields, rather than a different object, so it re-uses the old elements rather than destroying them and recreating them. I suppose the name _id is chosen so that it works well with arrays generated by .fetch() on a Minimongo cursor.
I don't know if this is documented behaviour, or if it might change in the future.

modify field value in a crossfilter after insertion

I need to modify a field value for all records in a crossfilter before inserting new records.
The API doesn't say anything about it. Is there a way to do that ?
Even if it's a hack that would be really useful to me.
Looking at the code, the data array is held as a private local variable inside the crossfilter function so there's no way to get at it directly.
With that said, it looks like Crossfilter really tries to minimize the number of copies of the data it makes. So callback functions like the ones passed into crossfilter.dimension or dimension.filter are passed the actual records themselves from the data array (using the native Array.map) so any changes to make to the records will be made to the main records.
With that said, you obviously need to be very careful that you're not changing anything that is relied on by the existing dimensions, filters or groups. Otherwise you'll end up with data that doesn't agree with the internal Crossfilter structures and chaos will ensue.
The cool thing about .remove is it only removes entries that match the currently applied filters. So if you create a 'unique dimension' that returns a unique value for every entry in your dataset (like an ID column), you can have a function like this:
function editEntry(id, changes) {
uniqueDimension.filter(id); // filter to the item you want to change
var selectedEntry = uniqueDimension.top(1)[0]; // get the item
_.extend(selectedEntry, changes); // apply changes to it
ndx.remove(); // remove all items that pass the current filter (which will just be the item we are changing
ndx.add([selectedEntry]); // re-add the item
uniqueDimension.filter(null); // clear the filter
dc.redrawAll(); // redraw the UI
}
At the least you could do a cf.remove() then readd the adjusted data with cf.add().

How can I write a macro to check to for a related record in another table?

I would like to write a macro that will run every time a record is entered into a certain table. What I want the macro to do is check to see if there is a related record in a different table, and if so, to change a field of data type Yes/No to Yes. If no related record is defined yet, I would do nothing, as the default for the Yes/No field is No.
The two tables are related in this way.
So when a record is entered in to the tblOrders table, I need to check if a related record exists in the tblRecipes table, and I would like to use a macro to do so if possible, as I will be repeating this action for every Order entered.
Is this possible, and if so, what would the macro language be please?
The usual thing would be to use forms for data entry and to check the Recipes table in the before update event for the control, for example.
Private Sub CustomerPartNumber_BeforeUpdate(Cancel As Integer)
If Not IsNull(DLookup( _
"CustPartNum", "tblRecipes", "CustPartNum=" & Me.CustomerPartNumber)) Then
Me.AYesNoField = True
Else
Me.AYesNoField = False
End If
End Sub
However, in Access 2010, you can use data macros. This example will run whenever CustomerPartNumber is changed in Orders.

Resources