Progress 4GL BUFFER-COPY FAILS - progress-4gl

DO ON ENDKEY UNDO, LEAVE:
FIND FIRST STUDENT NO-LOCK WHERE ST-ID = "TEST" NO-ERROR.
IF AVAILABLE STUDENT THEN
DO:
CREATE SCHOOL no-error.
BUFFER-COPY STUDENT EXCEPT STUDENT.Location
SCHOOL ASSIGN SCHOOL.Location = "MY LOCATION" NO-ERROR.
IF ERROR-STATUS:ERROR THEN
DO:
DO i = 1 TO ERROR-STATUS:NUM-MESSAGES:
MESSAGE
" Error no " ERROR-STATUS:GET-NUMBER(i)
" txt: " ERROR-STATUS:GET-MESSAGE(i) VIEW-AS ALERT-BOX.
STOP.
END.
END.
END.
END.
This query is working fine but some time it was creating Empty Record. buffer-copy through some error that why it create empty record but i am not able verify the error because code was happen in LIVE. please help me how to FIX the problem. what type of error buffer-copy will through. 1000 times working fine 1 time it will FAIL. i know this is data defect but how to FIX. otherwise what type of errors BUFFER-COPY through.

Since you really don't know what errors occur - you need to start there.
To track general errors after a NO-ERROR statement you can do something like:
IF ERROR-STATUS:ERROR THEN DO:
DO i = 1 TO ERROR-STATUS:NUM-MESSAGES:
/* Replace MESSAGE with some kind of logging */
MESSAGE
"Error number " i
" error no " ERROR-STATUS:GET-NUMBER(i)
" txt: " ERROR-STATUS:GET-MESSAGE(i) VIEW-AS ALERT-BOX.
END.
END.
Once you have the specific error number(s) you can search the Progress Knowledge Base for more information.

I would write the code as follow. If you use no-error always handle the error. When assigning/buffer copy check if no error then do a validate to confirm that triggers fire correctly. The error handling code can be put in its own procedure and then just called every time you wish to process error messages. (amended it a bit as I would write it, I do not always trap all errors, but error handling depends on various things, but it is a good way during testing to make sure code is setup correctly. Afterwards can remove error handling where not needed before deploying.)
FIND FIRST STUDENT NO-LOCK WHERE ST-ID = "TEST" NO-ERROR.
IF AVAILABLE STUDENT THEN
DO:
CREATE SCHOOL.
BUFFER-COPY STUDENT EXCEPT STUDENT.Location
TO SCHOOL ASSIGN SCHOOL.Location = "MY LOCATION"
NO-ERROR.
IF ERROR-STATUS:ERROR THEN
DO:
/* insert error handling - for example as as per #Jensd */
/* this is in case there is something wrong with buffer copy */
/* maybe a required field is left empty? */
END.
ELSE DO:
VALIDATE SCHOOL NO-ERROR. /* good idea as it raises any issues with triggers */
IF ERROR-STATUS:ERROR THEN
DO:
/* insert error handling - for example as as per #Jensd */
END.
END.
END.

I will not make a buffer-copy from one to other table stright, you might have index issues as i think you are having.
Better define a temp-table like the target table.
Then make a buffer copy to the temp-table.
Set the rigth key into the temp-table.
Then make a buffer copy from the temp table with the right key fields to the target table.
Try as follow:
Define temp-table t-school like school.
find first student no-lock and so on.
buffer-copy student to t-school.
Assign t-school.location = "whatever".
buffer-copy t-school to school.
Voila!

One possible way to debug the issue is to use "Recent Messages" under Help in AppBuilder provided you are able to run the code directly from it.

Related

How to explicitly save or close a recordset when a form deactivates?

I apologize if this is too vague a question, but we're having the following problem:
We use a search form to find a record, then load it in a bound-control form where changes are made. Then we return to the search form and open another record. When we do that, the form's BeforeUpdate property fires a 3020 error, "Update without Add New or Edit" and stepping through the code it's referring to the FIRST opened record. This is strange because there is no explicit update call, but after much trial and error I think the error is thus:
Record #1 is opened via the form and changes are made. Record #2 is opened on that same form without closing the first recordset. Even though we now re-opened the form with the second record, Access still assumes we're editing record 1, i.e. that we're trying to edit 2 records concurrently. Same as though we had a datasheet form and we edited one row and then tried to edit a second row without saving the first. What I want to be able to do is have it automatically do an update on the first record when the form deactivates so loading a new record doesn't cause this conflict.
So the bottom line is this: **Is there a way, on say the form's Dirty or Deactivate event, that we can force the form's recordset to update and close ** before loading a second record?
I hope I made this clear enough, it's a complex problem, so any small guidance would help. Btw, you may ask, "Why are you running the same code to open the same form twice?" Good question! Hey it's an old and badly written app (the thing has GoSubs in it for Pete's sake) but I have no choice but to make the best of a bad situation.
EDIT: I was asked to post code, which is reasonable, but it's in several different places. So I have the data form, it has a "Search" button to go back to the search form for opening another record. The search button on the data entry form is:
Private Sub CommandSearch_Click()
On Error GoTo Err_CommandSearch_Click
DoCmd.OpenForm "Reference Form", acNormal 'This is the form that does the searching
Exit_CommandSearch_Click:
Exit Sub
Err_CommandSearch_Click:
MsgBox Err.Description
Resume Exit_CommandSearch_Click
End Sub
When a record is selected on that search form, then the new form is opened with this code. Now this is where it gets tricky. I'm not the original programmer, as I said I think it was written in Access 97 by someone after taking an hour to read "Access for Dummies" :). But it always looks like only one copy of the form is open, so maybe it's re-opening it?
Public Sub CommandLoadCase_Click()
Dim LoadUTUCaseNumber As String, lengthUTUCaseNumber As Integer
lengthUTUCaseNumber = InStr(Forms![Reference Form]![Reference Query SubForm]![UTU Case Number], "-") - 1
If (lengthUTUCaseNumber = 0) Then
LoadUTUCaseNumber = ""
Else
LoadUTUCaseNumber = Left$(Forms![Reference Form]![Reference Query SubForm]![UTU Case Number], lengthUTUCaseNumber)
End If
On Error Resume Next
Forms![Case Appeal_Add-On Form].UTUCaseKeyToFind = LoadUTUCaseNumber
DoCmd.OpenForm "Case Appeal_Add-On Form", acNormal, , , , , LoadUTUCaseNumber
'Case Appeal Add On Form referred to here is the data entry form I refer to above.
End Sub
Finally, the Error 3020 (Update without Add/Edit) is occurring after it executes this line on the data entry form. (I know the code is complicated which is why I didn't enter it at first).
Private Sub Form_BeforeUpdate(Cancel As Integer)
[UTU Claim Sequence Key] = [UTU Claim Alpha Sequence] & CStr([UTU Claim Numeric Sequence])
End Sub

How to get userlibs in the Rexx-interface to SDSF

The rexx interface to SDSF works fine to get information about spoolfiles. I am trying to list the use of userlibs for output-files. And do a "ISFEXEC O" followed by (in a loop)
ISFACT O TOKEN(..) PARM(NP ?) (PREFIX JDS_ DELAYED DELAYED2
In an inner loop I can then run through each JDX_xxxx. variable. Sofar so good. But how do I get information on userlib no. 2,3,4 ...? JDS_ODUSRLIB. gives me the first userlib. But how do I get hold of the other 7 userlibs?
I have tried for find any example bit with no luck and the documentation is not very clear on this subject.
Have you tried the JDS_ODUSRLIB. stem? Something like
do jx=1 to JDS_DDNAME.0 /* loop for all rows returned */
say "UsrLib for " JDS_DDNAME.jx " is " JDS_ODUSRLIB.jx
end

What is the code to create unposted (upost-order) orders in progress 4gl language?

What is the code to create unposted (upost-order) orders in progress 4gl language?
This is the code to find upost orders.
FIND upost-order WHERE
upost-order.company = company AND
upost-order.order-number = order.order-number
NO-ERROR.
IF NOT AVAILABLE upost-order THEN
FIND upost-order WHERE
upost-order.company = company AND
upost-order.order-number =
STRING(INTEGER(SUBSTRING(order.order-number,3,6)))
NO-LOCK NO-ERROR.
I'll assume you mean you want to know how to create a new record for this entity.
That would be
DO TRANSACTION:
CREATE upost-order.
/* Now if you want to assign arbitrary values to the key, */
ASSIGN upost-order.company = company /* supposing you want to create for the same one you found */
upost.order-number = cOrderNumber NO-ERROR. /* Whatever character value you want to assign to the number. */
/* If you want to ask the user to input the values, on a GUI environment you could do this, instead of the ASSIGN above*/
UPDATE upost-order NO-ERROR.
/* You'll notice I used no-error in both. Now if an error happened, display it to the user */
IF ERROR-STATUS:ERROR THEN DO:
MESSAGE ERROR-STATUS:GET-MESSAGE(1) VIEW-AS ALERT-BOX.
UNDO, LEAVE.
END.
END.
Since I'm addressing more your question on how to create, of course the code above is not thorough or at its best. You could have more than one message, and in a transaction block, I'd probably name it, and prompt for the fields before creating and assigning, thus keeping the transaction at minimal time and as little as possible.
I hope this helps with your question.

** No sls-discount record is available. (91)

/*
sls-discount i sthe database name
*/Good day. I have another problem in regards again to value change of a specific browse.
my code for Add Button:
FIND CURRENT {&FIRST-TABLE-IN-QUERY-DIALOG-FRAME} NO-LOCK NO-ERROR.
IF AVAIL {&FIRST-TABLE-IN-QUERY-DIALOG-FRAME} THEN DO:
ASSIGN mode = 3.
ASSIGN BROWSE BROWSE-13:READ-ONLY = NO
sls-discount.dsc-amount:READ-ONLY IN BROWSE BROWSE-13 = NO
sls-discount.dsc-discount:READ-ONLY IN BROWSE BROWSE-13 = NO
sls-discount.log:READ-ONLY IN BROWSE BROWSE-13 = NO.
{&OPEN-QUERY-BROWSE-13}
IF BROWSE-13:num-iterations ne 0 then
BROWSE BROWSE-13:SELECT-FOCUSED-ROW().
BROWSE BROWSE-13:INSERT-ROW("BEFORE"). /*will cause value-change in browse*/
APPLY 'entry' TO sls-discount.dsc-discount IN BROWSE BROWSE-13.
RUN disp-enable.
END.
my code for browse-13:
ASSIGN xpromo-no = {&FIRST-TABLE-IN-QUERY-BROWSE-13}.promo-no
mdlrid = {&FIRST-TABLE-IN-QUERY-BROWSE-13}.record-id.
MESSAGE xpromo-no mdlrid VIEW-AS ALERT-BOX INFO.
OPEN QUERY BROWSE-16 FOR EACH sls.sls-disccount-model WHERE sls.sls-disccount-model.promo-no = xpromo-no
AND sls.sls-disccount-model.record-id = mdlrid NO-LOCK,
EACH sls.sls-model WHERE sls.sls-model.car-model = sls.sls-disccount-model.car-model NO-LOCK.
if I uncomment the entire value-change in a browse, it will display the message "** No DBName record is available. (91)" when clicking the add button. but if commented/removed, it will not display the message.
hope you can help me again .. Thanks
You've doubtless run into the infamous "partial field auto-completion" feature / bug. What that means is - if you cite a variable with no local definition for it, the Progress compiler will search all the available tables for a field that starts with the same sequence of letters and use that instead.
What the error is telling you is that the compiler associated a field with something in the "DBName" table, and when the code tries to access that variable, the DBName buffer isn't in scope - which is understandable since accessing it wasn't your intention.
My guess is that you've mis-specified a BROWSE construct. Looking at your first set of constructs, you've got two different forms:
BROWSE BROWSE-13:READ-ONLY = NO
sls-discount.dsc-amount:READ-ONLY IN BROWSE BROWSE-13 = NO
I'll bet one of these is wrong, and the compiler is associating the illegal "browse" citation with a field in DBName that starts with "Browse".
If you compile the code to an XREF file, it'll tell you which variable it's trying to associate with the DBName table. Correct that, and you'll be good to go.
I figure out what the problem on the code.. on click of Add button, the row inside browse-13 was arrange because of the {&OPEN-QUERY-BROWSE-13}. after that, because of BROWSE BROWSE-13:SELECT-FOCUSED-ROW(), it focused on the TOP date on browse and last, BROWSE BROWSE-13:INSERT-ROW("BEFORE"), created a blank data row at the very top. thus triggering value-changed of browse-13. on my value-changed on browse-13, the beginning of the code was to get current browse-13. since it was focused on the blank row, it resulted in an error with no query being found. sorry if i did not include the syntax get current in my first example.

progress 4gl :i want to avoid error messages while running the program

DEFINE TEMP-TABLE ttservice NO-UNDO
FIELD ad-num AS CHARACTER
INDEX ttprimary AS UNIQUE ad-num .
ASSIGN ttservice.ad-num = vehicles.ad-num NO-ERROR
In this, how to avoid error messages when i am adding duplicate records,
situation is:
when i try to add the duplicate records in temp table it doesnot accept,it is ok ,but it display error messages while running a program,iwant to suppres that error messages.,and avoid the duplicate adding records
You can test for the existence of a duplicate key before you try to create it.
(Filling in the blanks.)
DEFINE TEMP-TABLE ttservice NO-UNDO
FIELD ad-num AS CHARACTER /* you have a "num" field defined as character? that's misleading */
INDEX ttprimary AS UNIQUE ad-num .
for each vehicle no-lock: /* perhaps ad-num is non-unique in the vehicle table? */
find ttservice where ttservice.ad-num = vehicles.ad-num no-error.
if available ttservice then
do:
message "oops!". /* or whatever it is you want when a duplicate occurs... */
end.
else
do:
create ttservice.
ASSIGN ttservice.ad-num = vehicles.ad-num.
end.
end.
Here's another way to get unique ad-num values from the vehicles table:
DEFINE TEMP-TABLE ttservice NO-UNDO
FIELD ad-num AS CHARACTER
INDEX ttprimary AS UNIQUE ad-num .
FOR EACH vehicles NO-LOCK
BREAK BY vehicles.ad-num:
IF FIRST-OF(vehicles.ad-num) THEN
DO:
CREATE ttservice.
ASSIGN ttservice.ad-num = vehicles.ad-num.
END.
END.
Two valued answers have already been added by great professionals but i would like to add mine with minor changes.
def TEMP-TABLE ttservice NO-UNDO
FIELD iservid AS INT
INDEX tt-primary AS UNIQUE iservid.
VEHICLELOOP:
for each vehicles use-index <index-name>
NO-LOCK:
IF CAN-FIND(first ttservice
where ttservice.iservid = vehicles.iservid)
THEN
NEXT VEHICLELOOP.
ELSE DO:
create ttservice.
ASSIGN ttservice.iservid = vehicles.iservid.
END. /* VEHICLELOOP */
So I read the answers and think they're sufficient to fix your particular issue. But here's the general way of thinking you should assume when coding for Progress OpenEdge:
Adding no-error to statements (when they allow it) will "suppress errors", though sometimes they're inevitable and suppressing does no good to the stability of your application, always think of treating them (and displaying errors is a part of this).
Whether you choose to check for existence of a record prior to creation or just set your query to not iterate for undesired (repeated) records is up to you. I advise you to check for performance with different approaches (especially in doing a for each for a large table) to see which one is more satisfactory.
So here's my personal suggestion:
for each vehicles no-lock:
if can-find(first ttService where ttService.ad-num = vehicles.ad-num) then
next.
create ttService.
assign ttService.ad-num = vehicles.ad-num no-error.
if error-status:error then
message "Something went horribly wrong:" + error-status:get-message(1)
view-as alert-box error.
end.
In my example above, the error will only be shown if the assign actually fails. It is not likely to happen, I just wanted to show how the usage of no-error (and its treatment) works.
Anyway, hope it helps!