how-do-i-validate-phone-number & dob-in-Openedge /Progress 4gl-programming - openedge

define var num as integer label "S.No".
define var name as char label "NAME ".
define var dob as date label "DOB" format "99/99/9999".
define var mobnum as int label "Mobile No:" format "9999999999" .
assign num = 1.
repeat num = 1 to 3:
display num with frame a.
update dob with frame a.
if dob >= today then do:
display " enter correctly".
update dob with frame a.
end.
else if dob < today then do:
update name with frame a.
end.
update name with frame a.
update mobnum with frame a.
if mobnum >= 1000000000 and mobnum < 10000000000 then
do:
display mobnum with frame a.
display num column-label "S.NO" with frame b.
display name column-label "NAME"
dob column-label "DOB"
mobnum column-label "mobile number"
with frame b down.
down with frame b.
end.
else
do:
display "enter exactly 10 digits please".
update mobnum with frame a.
end.
end.
In this Code I want to validate dob to > Todays date and mobilenum not less than 9 digits
and i want to display the output and in step by step process it moves to S.no, Dob,Name ,Mobile num..
But in my code when i enter mobile num it doesn't display anything in frame b.
and i want to add 3 records.. but it doen't allow me to update..
please help to solve this issue

Date-based validation (comparison) is reasonably simple in ABL: use the =, >, < , <>, >=, <= operators.
define var dob as date label "DOB" format "99/99/9999".
IF dob <= TODAY THEN
MESSAGE "Date is not in the future".

You can verify dob conditions using the following expression:
DEF VAR dob AS DATE NO-UNDO FORMAT "99/99/9999".
ASSIGN dob = TODAY.
/*
ASSIGN dob = TODAY.
ASSIGN dob = TODAY + 30. - always days
ASSIGN dob = TODAY - 30. - always days
*/
IF dob = TODAY THEN
MESSAGE "dob is today.".
ELSE IF dob < TODAY THEN
MESSAGE "dob is smaller than today.".
ELSE IF dob > TODAY THEN
MESSAGE "dob is in the future.".
ELSE
MESSAGE "dob is not assigned.".
And for the MobileNum:
You can use LENGTH(mobnum) to get the quantity of digits into the number... like:
This make possible to create separate message for each of the lengths of mobnum.
DEF VAR mobnum AS INT NO-UNDO FORMAT "9999999999" INITIAL "99999999".
DEF VAR ii AS INT NO-UNDO.
ii = LENGTH(mobnum).
CASE ii:
//You can create a when for each of the sizes
WHEN 9 THEN DO:
DISPLAY "Number OK".
END.
OTHERWISE DO:
DISPLAY "Number is not 9-digit long".
END.
END CASE.
But if you want to make it simple, just use:
DEF VAR mobnum AS INT NO-UNDO FORMAT "9999999999" INITIAL "99999999".
DEF VAR ii AS INT NO-UNDO.
ii = LENGTH(mobnum).
IF ii = 9 THEN DO:
DISPLAY "Number correct".
END. ELSE DO:
DISPLAY "Number incorrect".
END.

Related

Progess 4GL - How to filter multiple records using AND operator in one table field?

I want to check and filter only if the table has value1 = 005 and value1 = 009. But it seems below query is not helping me. I dont know where I am making mistakes. Kindly help to solve this. Note - I cannot use where not as it may have many different value stored in value1 field
DEFINE TEMP-TABLE test NO-UNDO
FIELD value1 AS CHARACTER
.
EMPTY TEMP-TABLE test.
CREATE test.
ASSIGN
value1 = "005".
CREATE test.
ASSIGN
value1 = "009".
CREATE test.
ASSIGN
value1 = "001".
FOR EACH test NO-LOCK
WHERE value1 <> ""
AND (value1 = "005" AND value1 = "009")
:
MESSAGE YES.
END.
You can use can-find
if can-find(first test WHERE value1 = "005")
AND can-find(first test WHERE value1 = "009")
then message yes.
It is safest to always use can-find(first if you're looking for a non-unique value
It looks like you're looking for an OR ooperation, rather than AND.
If you want to check if both records are present you could do :
DEFINE VARIABLE isPresent005 AS LOGICAL NO-UNDO.
DEFINE VARIABLE isPresent009 AS LOGICAL NO-UNDO.
DEFINE VARIABLE bothPresents AS LOGICAL NO-UNDO.
FIND FIRST test WHERE test.value1 = "005" NO-LOCK NO-ERROR.
isPresent005 = AVAIL test.
FIND FIRST test WHERE test.value1 = "009" NO-LOCK NO-ERROR.
isPresent009 = AVAIL test.
bothPresents = isPresent005 AND isPresent009.
But, if you only want to get these 2 records, you should use OR :
FOR EACH test WHERE test.value1 = "005" OR test.value1 = "009" NO-LOCK :
/*do stuff*/
END.
Another option if you are, maybe, looking for some additional fields might look something like this:
define buffer test005 for test.
define buffer test009 for test.
for each test005 no-lock where test005.customer = 1 and test005.value1 = "005",
each test009 no-lock where test009.customer = 1 and test009.value1 = "009":
display test005.customer.
end.
Use OR instead of AND to search the records...
This will return records if value1 = 005 OR value1 = 009.
FOR EACH test NO-LOCK
WHERE value1 <> ""
AND (value1 = "005" OR value1 = "009")
:
MESSAGE YES.
END.
Is not possible to search using your way, because value1 cannot be two values at once, it's always one OR another.

To enter the correct mob number and dob

To enter the correct mob number and dob we have to check the code in progres
Use LENGTH(STRING(mobnum)). This will convert it to a character string and give you the length of it. You can check to see if it is 10 characters long.
Making minimal changes to your approach, something along these lines should work:
define var mobnum as INT64 label "Mobile No:" format "9999999999" .
update mobnum.
if mobnum >= 1000000000 and mobnum < 10000000000 then
do:
display "Correct number".
end.
else
do:
display "enter exactly 10 digits please".
update mobnum.
end.
You can use LENGTH(mobnum) to get the quantity of digits into the number... like:
This make possible to create separate message for each of the lengths of mobnum.
DEF VAR mobnum AS INT NO-UNDO FORMAT "9999999999" INITIAL "99999999".
DEF VAR ii AS INT NO-UNDO.
ii = LENGTH(mobnum).
CASE ii:
WHEN 9 THEN DO:
DISPLAY "Number OK".
END.
OTHERWISE DO:
DISPLAY "Number is not 9-digit long".
END.
END CASE.
But if you want to make it simple, just use:
DEF VAR mobnum AS INT NO-UNDO FORMAT "9999999999" INITIAL "99999999".
DEF VAR ii AS INT NO-UNDO.
ii = LENGTH(mobnum).
IF ii = 9 THEN DO:
DISPLAY "Number correct".
END. ELSE DO:
DISPLAY "Number incorrect".
END.

QUERY formula to return only the latest record per email based on timestamp

Having this Google Sheet QUERY formula:
=QUERY(FORMULARIO!A4:Q, " select G,E,K,M,N,L,O,Q where not upper(J) = 'PAGÓ' and A > date '"&TEXTO(HOY()-3, "yyyy-mm-dd")&"' and E is not null and O is not null and Q contains '#' and not Q matches '"&TEXTJOIN("|", 1, QUERY(FORMULARIO!J4:Q, "select Q where upper(J) = 'PAGÓ' ", 0))&"'", 0)
How to return only one record per email (column Q) being the most recent one looking at date which is at column A?
TEST SHEET:
https://docs.google.com/spreadsheets/d/1jBMo42cbylrNHcf9b9QLI4oDw0H0oomCGrEAuPsBlA8/edit#gid=1465616509
Under tab "results" is the working query where i only need to filter out duplicates based on recency, leaving only the most recent one, and the unique identifier being email
EDIT
(following OP's shared demo sheet)
The formula to use would be:
=SORT(SORTN(QUERY(FORMULARIO!A4:Q, " select G,E,K,M,N,L,O,Q where not upper(J) = 'PAGÓ' and A > datetime '"&TEXTO(AHORA()-3, "yyyy-mm-dd HH:mm:ss")&"' and E is not null and O is not null and Q contains '#' and not Q matches '"&TEXTJOIN("|", 1, QUERY(FORMULARIO!J4:Q, "select Q where upper(J) = 'PAGÓ' ", 0))&"'", 1),9^9,2,2,1),1,1)
What was changed:
We wrapped your original query with the SORTN function to find and exclude all duplicate emails from column Q except the most recent one based on the Timestamp from column A.
We -one more time- wrapped the results with the SORT function to have our final results sorted by the Timestamp.
Pro tip
An extra change was also made within the main query by changing
and A > date '"&TEXT(HOY()-3, "yyyy-mm-dd")&"'
to
and A > datetime '"&TEXT(AHORA()-3, "yyyy-mm-dd HH:mm:ss")&"'
So we changed TODAY to NOW.
By doing this we count dates making use of both the date and time present in a timestamp instead of just the date.
(There is still room for minor improvements/alterations but out of the scope of this question.)
Original answer
Taking for granted that your formula works as expected (cannot check it without a test sheet) but produces multiple rows you can use the limit clause in the end of your formula.
=QUERY(FORMULARIO!A4:Q, " select G,E,K,M,N,L,O,Q where not upper(J) = 'PAGÓ' and A > date '"&TEXTO(HOY()-3, "yyyy-mm-dd")&"' and E is not null and O is not null and Q contains '#' and not Q matches '"&TEXTJOIN("|", 1, QUERY(FORMULARIO!J4:Q, "select Q where upper(J) = 'PAGÓ' ", 0))&"' limit 1", 0)

How to add html tables in progress 4gl?

I created a temp table in Progress4gl and I need to email the data from temp table using html syntax. Which means I need to link all the fields in temp table to html table and email.
The fields in temp table are:
Part_ID, CustomerPartID, customer
Please help me with this.
Well you could just output the HTML. Something like this:
define temp-table tt_test
field f1 as integer
field f2 as character
field f3 as date
.
create tt_test.
assign
f1 = 1
f2 = "abc"
f3 = today
.
create tt_test.
assign
f1 = 2
f2 = "xyz"
f3 = today + 30
.
output to value( "mytable.html" ).
put unformatted "<table>" skip.
for each tt_test:
put unformatted substitute( " <tr><td>&1</td><td>&2</td><td>&3</td></tr>", f1, f2, f3 ) skip.
end.
put unformatted "</table>" skip.
output close.
For creating an html table you can (ab)use the power of datasets combined with serialize-name:
/* Write some awesome ABL code here, or load an existing snippet! */
define temp-table ttparts serialize-name "tr"
field part_id as char serialize-name "td"
field customerPartID as char serialize-name "td"
field customer as char serialize-name "td"
.
define dataset ds serialize-name "table" for ttparts .
define buffer bupart for ttparts.
create bupart.
assign
bupart.part_id = "one"
bupart.customer = "A"
.
create bupart.
assign
bupart.part_id = "two"
bupart.customer = "B"
.
def var lcc as longchar no-undo.
dataset ds:write-xml( "longchar", lcc, true ).
message string( lcc ).
https://abldojo.services.progress.com:443/#/?shareId=5d1618c14b1a0f40c34b8bc8
An updated version which accepts any temp-table handle and will return a longchar containing the HTML table:
function createHtmlTable returns longchar (
i_ht as handle
):
def var lcc as longchar.
def var hds as handle.
def var hb as handle.
def var ic as int.
create dataset hds.
hds:serialize-name = "table". // not needed if stripped below
create buffer hb for table i_ht.
hb:serialize-name = "tr".
hds:add-buffer( hb ).
do ic = 1 to hb:num-fields:
hb:buffer-field( ic ):serialize-name = "td".
end.
hds:write-xml( "longchar", lcc, true ).
// remove xml declaration
lcc = substring( lcc, index( lcc, "<", 2 ) ).
entry( 1, lcc, ">" ) = "<table". // see comment above
return lcc.
finally:
delete object hds.
end finally.
end function.
define temp-table ttparts
field part_id as char
field customerPartID as char
field customer as char
.
define buffer bupart for ttparts.
create bupart.
assign
bupart.part_id = "one"
bupart.customer = "A"
.
create bupart.
assign
bupart.part_id = "two"
bupart.customer = "B"
.
message string( createHtmlTable( temp-table ttparts:handle ) ).
https://abldojo.services.progress.com/#/?shareId=5d1760d84b1a0f40c34b8bcd
There is no built-in method to do this. One way that I could think of is converting the temp table to json and then json to HTML. I believe there are several utilities to convert json to html table. You can convert temp table to json in ABL using WRITE-JSON method.
An example from Progress documentation:
DEFINE VARIABLE cTargetType AS CHARACTER NO-UNDO.
DEFINE VARIABLE cFile AS CHARACTER NO-UNDO.
DEFINE VARIABLE lFormatted AS LOGICAL NO-UNDO.
DEFINE VARIABLE lRetOK AS LOGICAL NO-UNDO.
DEFINE TEMP-TABLE ttCust NO-UNDO LIKE Customer.
/* Code to populate the temp-table */
ASSIGN
cTargetType = "file"
cFile = "ttCust.json"
lFormatted = TRUE.
lRetOK = TEMP-TABLE ttCust:WRITE-JSON(cTargetType, cFile, lFormatted).

Code to add x minutes to time in fillin field

I'm a beginner to programming and to Progress, first post on StackOverflow, hope I'm posting in the right place!
I have a fillin field where I enter a time (hh:mm), character format. I also have two arrows, one pointing forward and one backward, and I want them to add / subtract 20 min respectively when pushed.
What would be a good way to write code for this? Turn the current time value to integer and seconds past midnight and then add /subtract 1200 sec? How would I get the result back to a hh:mm format to display in the fillin?
Any help greatly appreciated!
/Ellen
I think this will do what you need. Have the ON CHOOSE triggers on your arrow buttons run the changeMins procedure. Pass in the character time string from your fill-in and either "Add" or "Subtract". The output value will be the new adjusted time string. You can then set the screen value of your fill-in to that output value.
DEFINE VARIABLE cTime AS CHARACTER NO-UNDO.
cTime = "12:45".
RUN changeMins (INPUT-OUTPUT cTime, INPUT "Add").
MESSAGE cTime VIEW-AS ALERT-BOX INFORMATION BUTTONS OK.
PROCEDURE changeMins:
DEFINE INPUT-OUTPUT PARAMETER pcTime AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pcAction AS CHARACTER NO-UNDO.
DEFINE VARIABLE iHr AS INTEGER NO-UNDO.
DEFINE VARIABLE iMn AS INTEGER NO-UNDO.
/* Split the time string into hours and minutes */
ASSIGN
iHr = INTEGER(ENTRY(1, pcTime, ":"))
iMn = INTEGER(ENTRY(2, pcTime, ":"))
NO-ERROR.
IF ERROR-STATUS:ERROR THEN RETURN.
/* Adjust the time */
CASE pcAction:
WHEN "Add" THEN iMn = iMn + 20.
WHEN "Subtract" THEN iMn = iMn - 20.
END CASE.
/* Correct for boundaries */
IF iMn > 59 THEN
ASSIGN
iMn = iMn - 60
iHr = iHr + 1.
IF iMn < 0 THEN
ASSIGN
iMn = iMn + 60
iHr = iHr - 1.
IF iHr > 23 THEN iHr = iHr - 24.
IF iHr < 0 THEN iHr = iHr + 24.
/* Build the new time string */
pcTime = STRING(iHr, "99") + ":" + STRING(iMn, "99").
END PROCEDURE.
In this example, change the cTime string to different times and run it.
The Progress TIME function returns the integer number of seconds past midnight. So your idea of converting to such an integer is consistent with other uses within the 4gl which is a positive.
Personally I would keep the UI and the internal storage independent. So I would probably have a variable for the hour, another for the minute and a 3rd for seconds (if you need that). And I would use integers, rather than characters, for all 3.
I've no idea what version of Progress or what the environment that you are running in is but this quick and dirty little snippet might have some useful tidbits:
define variable hh as integer no-undo format ">9".
define variable mm as integer no-undo format "99".
define variable ss as integer no-undo format "99".
define variable myTime as integer no-undo.
form
hh mm ss
with
frame a
.
on value-changed of hh in frame a do:
if integer( self:screen-value ) > 23 then
do:
hh = 23.
display hh with frame a.
end.
end.
on value-changed of mm in frame a do:
if integer( self:screen-value ) > 59 then
do:
mm = 59.
display mm with frame a.
end.
end.
on value-changed of ss in frame a do:
if integer( self:screen-value ) > 59 then
do:
ss = 59.
display ss with frame a.
end.
end.
update hh mm ss with frame a.
myTime = (( hh * 3600 ) + ( mm * 60 ) + ss ).
display string( myTime, "hh:mm:ss am" ).
Deriving from TheDrooper's Code I would probably write something like the following code to fully utilize the built-in functions.
Shorter code that is still at least as easy to understand is often preferable.
Also consider that Progress does not have an optimizing compiler. If you can replace replace several simple statements with less statements (even if those are more complex and powerful than needed) the code is not only more maintainable but also faster.
DEFINE VARIABLE cTime AS CHARACTER NO-UNDO.
cTime = "12:45".
RUN changeMins (INPUT-OUTPUT cTime, INPUT "Add").
MESSAGE cTime VIEW-AS ALERT-BOX INFORMATION BUTTONS OK.
PROCEDURE changeMins:
DEFINE INPUT-OUTPUT PARAMETER pcTime AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pcAction AS CHARACTER NO-UNDO.
DEFINE VARIABLE iMn AS INTEGER NO-UNDO.
iMn = INTEGER(ENTRY(1, pcTime, ":")) * 60
+ INTEGER(ENTRY(2, pcTime, ":"))
NO-ERROR. /* Calculate minutes since midnight */
IF ERROR-STATUS:ERROR THEN RETURN.
/* Adjust the time */
CASE pcAction:
WHEN "Add" THEN iMn = iMn + 20.
WHEN "Subtract" THEN iMn = iMn - 20.
END CASE.
/* Build the new time string */
pcTime = string(iMn * 60, 'HH:MM'). /* Convert minutes to seconds and convert the result to a string */
END PROCEDURE.
If you need to allow for higher hour values (eg. if the field doesn't represent a time of day but a time interval) you can't just convert the resulting integer with the string function. In that case you could write
pcTime = string(iMn / 60, '99') + ':' + string(iMn mod 60, '99').
(Both TheDrooper and Tom Bascom seem to assume time of day.)

Resources