Progress Openedge Where Clause Depending On If Condition - progress-4gl

I have a combo-box widget. I want to create a WHERE statement that depends of the combo box.
If the value of the combo-box is 0 all customers should be shown. If the value differs from 0 only the customer matching the combo-box value should be shown.
Example :
Assign CB-Customer.
For each Customer WHERE /* after that*/
/* something like this
IF CB-CUstomer = 0 THEN ASSIGN Customer.CustNum >= 0 .
ELSE ASSIGN Customer.CustNum = CB-Customer .
*/
I've seen a code like this before but I couldn't figure out how it works.

Instead of combining both conditions in the WHERE phrase (which will probably kill any chance of using an index), why not define a query and open it depending on the CB-Customer.
DEFINE QUERY q-Customer FOR Customer.
IF CB-Customer = 0 THEN DO:
OPEN QUERY q-Customer
FOR EACH Customer
WHERE Customer.CustNum >= 0
NO-LOCK
.
END.
ELSE DO:
OPEN QUERY q-Customer
FOR EACH Customer
WHERE Customer.CustNum = CB-Customer
NO-LOCK
.
END.
GET FIRST q-Customer.
REPEAT WHILE AVAILABLE Customer:
/* Whatever you want to do with the Customer record, for example: */
DISPLAY Customer.
GET NEXT q-Customer.
END.
You can do a lot of fancy tricks with queries, especially dynamic ones, but this should work in pretty much every recent version of OpenEdge.

FOR EACH Customer
WHERE (CB-CUstomer = 0 AND Customer.CustNum >= 0)
OR Customer.CustNum = CB-CUstomer:
END.
This should do what you want, if I understand your question correctly.

I understand your question like this:
If the combo-box has the value zero you want to display all customers. Otherwise you want to display only the customers matching the customer number?
You can do a WHERE clause with a condition in it, but I would avoid it. In this case the query is very basic but when they get more complicated (and they do) where clauses with conditions in them are quite hard to read and understand. But I guess it's also about personal choices.
FOR EACH Customer NO-LOCK WHERE
Customer.CustNum = (IF CB-Customer = 0 THEN Customer.CustNum ELSE CB-Customer ):
MESSAGE Customer.CustNum.
END.
I would prefer doing either two separate FOR EACH'es:
IF CB-Customer = 0 THEN DO:
/* TABLE-SCAN is a quite new thing, might not work in your version */
FOR EACH Customer NO-LOCK TABLE-SCAN:
MESSAGE Customer.CustNum.
END.
END.
ELSE DO:
FOR EACH Customer NO-LOCK WHERE Customer.CustNum = CB-Customer:
MESSAGE Customer.CustNum.
END.
END.
Or a QUERY with separate QUERY-PREPARE statements:
DEFINE QUERY q FOR Customer.
DEFINE VARIABLE cQuery AS CHARACTER NO-UNDO.
ASSIGN CB-Customer.
IF CB-Customer = 0 THEN DO:
cQuery = "FOR EACH Customer NO-LOCK".
END.
ELSE DO:
cQuery = "FOR EACH Customer NO-LOCK WHERE Customer.CustNum = " + QUOTER(cb-customer).
END.
MESSAGE cQuery.
QUERY q:QUERY-PREPARE(cQuery).
QUERY q:QUERY-OPEN().
GET FIRST q.
REPEAT WHILE AVAILABLE Customer:
MESSAGE Customer.CustNum.
GET NEXT q.
END.

Related

Call function only for unsuppressed records

I use two string formulas in my details section to determine which countries to suppress. Formula1 refers to the Company field, and I suppress certain values:
{Countries}
In Formula2 i call a function with a parameter like #Formula1:
function(#formula1)
But the problem is that the function still takes the suppressed countries into account. Is this normal behavior, or do I need something extra like whileprintingdata to narrow my results.
When i debug the function the first time the function is called the incoming string is country1 even though I specified it to suppress that one. How can I ensure only unsuppressed values are passed to the function?
You could write additional logic into Formula2 to perform the same check that Formula1 does. Something like:
IF ({Countries} = 10) THEN
function("Company A")
ELSE IF ({Countries} = 14) THEN
function("Company B")
// ELSE IF....
ok got it your edit makes it clear...
what you can do is use the same supress condition in your formula 2 and pass the value to function.
some thing like below:
if your supress condition is
if {Countries} in (10,11,12)
then true
else false
then use the same in function 2 as
if {Countries} <> (10,11,12)
then function(formula1)
else any message as you wish

How to add the IF THEN ELSE in the BY/BREAK BY keyword of FOR EACH?

here is the sample that i want to ask:
FOR EACH table-name.... NO LOCK BY (IF TRUE THEN sort-this ELSE sort-that + sort-that2).
END.
This would result in an error.
if it is just
FOR EACH .. NO LOCK BY (IF TRUE THEN sort-this ELSE sort-that).
END.
then there is no error. Progress would accept the code
What is need is if condition is true then sort by one field else sort by two or more fields
If you are in a modern enough version of Progress, then you could construct a dynamic query. This will be more efficient in terms of run time as well as getting you round your problem as having IF statements in your query predicate will make index selection hard.
QueryString = "for each table no-lock...".
if true then
QueryString = QueryString + " by sort-this".
else
QueryString = QueryString + " by sort-that by sort-other".
create query QueryHandle.
QueryHandle:add-buffer(buffer table:handle).
QueryHandle:query-prepare(QueryString).
QueryHandle:query-open.
do while QueryHandle:get-next():
/*some stuff*/
end.
QueryHandle:query-close.
delete object QueryHandle.
As per the previous reply, this is not supported.
Typically, you'd have to prepare the result into a temp-table first, using a logical field in the temp-table for the result of your IF THEN ELSE expression.
Unfortunately, this syntax is not supported. As per the documentation, BREAK/BY expects an expression following it but not a statement.

How to get count of wod_qty_req?

Table :wod_det
In wod_det table i want count for wod_qty_req for all orders
means wod_nbr is a work order for each order there will be component items will be there i.e wod_part for these wod_part there will be wod_qty_req so what i want is each work order i nee count of wod_qty_req for all wod_part i tried like this
{us/mf/mfdtitle.i}
def var site like si_site no-undo.
for first si_mstr no-lock where si_mstr.si_domain = global_domain:
site = si_site.
end.
for each wod_det where wod_domain = global_domain and wod_site = site no-lock break by wod_nbr by wod_part:
if first-of(wod_nbr) then
do:
accumulate wod_qty_req (count).
if last-of(wod_part) then
do:
disp wod_nbr wod_part wod_qty_req (accum count wod_qty_req).
end.
end.
end.
i don't know whether this is correct or not but i didnt get any output for this plz help me out this to resolve?
Thanks in advance..
Here is the Image of table wod_det
answer:
define variable iCount as integer no-undo.
{us/mf/mfdtitle.i}
def var site like si_site no-undo.
def var total like wod_qty_req initial 0 no-undo.
for first si_mstr no-lock where si_mstr.si_domain = global_domain:
site = si_site.
end.
for each wod_det no-lock where wod_domain = global_domain
and wod_site = site break by wod_nbr:
disp wod_nbr wod_part wod_qty_req(total by wod_nbr).
end.
Edited: based on more information.
I personally never use the ACCUM, ACCUMULATE etc functions but that might be my personal preferences.
NB: I don't know exactly what fields correspond to the information in the image so I will describe this in "pseudo" code. Exchange [WORK_ORDER_FIELD] field names to match the correct field in your database!
define variable iCount as integer no-undo.
{us/mf/mfdtitle.i}
def var site like si_site no-undo.
for first si_mstr no-lock where si_mstr.si_domain = global_domain:
site = si_site.
end.
for each wod_det no-lock where wod_domain = global_domain
and wod_site = site
break by wod_det.[WORK_ORDER_FIELD]:
iCount = 0.
if first-of(wod_det.[WORK_ORDER_FIELD]) then do:
iCount = iCount + 1.
end.
display od_det.[WORK_ORDER_FIELD] iCount.
end.

Progress 4GL: Buffer handle attribute for all fields

I am fairly new to Progress and even newer to handles so apologies if I have missed anything obvious, I've looked online but am yet to find what I'm looking for.
I am running a dynamic query similar to the below, in this example after then query is run, the "age" field of the corrresponding record is displayed on screen, I understand how this is done from the buffer-field attribute-method, but my question is how do I display the entire record, is there an equivalent attribute method, or have I misunderstood something crucial?. Thank you for your time. :
def var tbl as character no-undo.
def var fld as character no-undo.
def var qh as handle no-undo.
def var bh as handle no-undo.
def var fh as handle no-undo.
assign tbl = "customer".
assign fld = "age".
create buffer bh for table tbl.
create query qh.
qh:set-buffers( bh ).
qh:query-prepare( "for each " + tbl + " where name = 'tom'" ).
qh:query-open.
do transaction:
qh:get-first( no-lock ).
fh = bh:buffer-field( fld ).
display fh:buffer-value.
end.
delete object bh.
delete object qh
There's no "easy" way to display the entire record in one statement the way you can with a static "DISPLAY table-name" statement. You can get the count of fields (buffer-handle:NUM-FIELDS) and then step through the individual fields and display their values using
DO i = 1 to bh:NUM-FIELDS:
DISPLAY bh:BUFFER-FIELD(i):BUFFER-VALUE WITH DOWN.
DOWN.
END.
create query qh.
qh:set-buffers( bh ).
qh:query-prepare( "for each " + tbl + " where name = 'tom'" ).
qh:query-open.
qh:GET-FIRST().
DO while qh:QUERY-OFF-END = False:
DO i = 1 TO bh:NUM-FIELDS:
display bh:BUFFER-FIELD (i):NAME STRING(bh:BUFFER-FIELD(i):BUFFER-VALUE) with down.
down.
END.
qh:GET-NEXT ().
END.

ABL inserting and displaying table data

I apologies for this being a very simple question but as a first time user of ABL open edge and im stuck. I have enter values into a table like so
METHOD PRIVATE VOID POPULATETABLE ( ):
DEFINE VARIABLE I AS INTEGER.
DO I = 0 TO 100:
CREATE TEST.
ASSIGN TEST.CUSTOMER_NAME="SMITH"
TEST.ORDER_NUMBER=I
TEST.ORDER="BOOKS"
TEST.COST=45.00
TEST.CUSTOMER_NAME = "JACKSON"
TEST.ORDER_NUMBER=I
TEST.ORDER="PAPER CLIPS"
TEST.COST=1.7.
ASSIGN TEST.CUSTOMER_NAME="JONES"
TEST.ORDER_NUMBER =I
TEST.ORDER="PENCILS"
TEST.COST=2.50
TEST.CUSTOMER_NAME = "TURNER"
TEST.ORDER_NUMBER = I
TEST.ORDER="PENS"
TEST.COST=0.7.
END.
END METHOD.
and I'm trying to display them using this
FOR EACH TEST:
DISPLAY TEST.COST TEST.CUSTOMER_NAME TEST.ORDER TEST.ORDER_NUMBER.
RETURN.
END.
However the result only shows the last row of data entered. can anyone help, I'm even unsure on whether the display function is right or the assign is.
The "return" in your FOR EACH is causing the code to leave the loop after the first record. Delete that statement and you'll see all the records.
FOR EACH TEST:
DISPLAY TEST.COST
TEST.CUSTOMER_NAME
TEST.ORDER
TEST.ORDER_NUMBER.
RETURN. /* this is why you're only seeing one record - */
/* get rid of this and you'll see all the records */
END.
I would avoid assigning an order# of 0. It's just asking to confuse people.
define variable i as integer no-undo.
do i = 1 to 100:
create test.
assign
test.order_number = i
test.customer = "smith" /* you need some way to get */
test.order = "books" /* actual data for the rest */
test.cost = random( 10, 100) /* of the fields... */
.
end.
And then review the orders with:
for each test no-lock:
display test.
end.
Yeah, all I needed was a create statement per each assign for each record and that worked. Thanks everyone, the working coded looks like:
CREATE TEST.
ASSIGN TEST.CUSTOMER_NAME="SMITH"
TEST.ORDER_NUMBER=I
TEST.ORDER="BOOKS"
TEST.COST=45.00.
CREATE TEST.
ASSIGN TEST.CUSTOMER_NAME = "TAYLOR"
TEST.ORDER_NUMBER=I
TEST.ORDER="PAPER CLIPS"
TEST.COST=1.7.
CREATE TEST.
ASSIGN TEST.CUSTOMER_NAME="THOMPSON"
TEST.ORDER_NUMBER =I
TEST.ORDER="PENCILS"
TEST.COST=2.50.
CREATE TEST.
ASSIGN TEST.CUSTOMER_NAME = "TURNER"
TEST.ORDER_NUMBER = 2
TEST.ORDER="PENS"
TEST.COST=0.7.
FOR EACH TEST WHERE TEST.COST > 1.3 BY TEST.ORDER_NUMBER:
DISPLAY TEST.
END.