BPM for barcode friendly part numbers

I respectfully disagree. First, philisophically I have a problem with using the word "ALWAYS" in any practice. It's tough to "ALWAYS" do something a certain way. In IT the usual answer is "IT DEPENDS", and it usually depends on a multitude of factors.

I would argue that assuming an initial state of valid or invalid is tomato vs. tomatoe. It's functionally equivalent to flag something as valid until it fails or invalid until it passes. The limitiations of 4GL don't allow an elegant coding approach here (at least I personally don't know it well enough). In a higher level language like C# I would create a collection of invalid characters and use something like the contains method against a string literal.

As far as testing individual characters, the subset of characters that disrupt a barcode is far smaller than those that don't. It's not practical to test for every other valid unicode character. I agree that coding should be as generic as possible, but that's not always pragmatic. Additionally, I don't know the underlying specifics of the 4GL compiler, but in most languages an IF statement containing multiple conditions will exit at the first failure, so the remaing conditions are never evaluated. If I were to test for every valid character, then every condition would be evaluated. From a sheer performance perspective with respect to CPU clock cycles the winner is clear.

While I agree genericism and future proofing are important concepts in design, performance is equally so. In the grand scheme of application development, my code is ridiculously small, and easily maintainable. Always willing to have a constructive debate on semantics!
Cheers,
Jared

--- In vantage@yahoogroups.com, "wmc84" <wmc20@...> wrote:
>
> Nit-picking here. But Good Programming Practice says you should always assume a character is NOT valid, and test for valid ones. Valid characters are a well defined subset. The Invalid set is subject to expand. eg: in the future you may have a non-English speaking office using a multibyte character set like Unicode or something. Many security holes have also come about through code testing this way, as well.
> -Wayne
>
> --- In vantage@yahoogroups.com, "k99ja04" <jallmond@> wrote:
> > I'm working on a BPM to check and see if a part number is barcode friendly, as in doesn't contain special characters.
> > [[[ snip ]]
> > IF INDEX(STRING(ttOrderDtl.PartNum),' ') NE 0 OR
> > INDEX(STRING(ttOrderDtl.PartNum),'!') NE 0 OR
> > INDEX(STRING(ttOrderDtl.PartNum),'@') NE 0 OR
> > INDEX(STRING(ttOrderDtl.PartNum),'#') NE 0 OR
> > INDEX(STRING(ttOrderDtl.PartNum),'$') NE 0 OR
> > INDEX(STRING(ttOrderDtl.PartNum),'%') NE 0 OR
> > INDEX(STRING(ttOrderDtl.PartNum),'&') NE 0 OR
> > INDEX(STRING(ttOrderDtl.PartNum),'*') NE 0 OR
>
I'm working on a BPM to check and see if a part number is barcode friendly, as in doesn't contain special characters. The majority of our part parts are entered directly into the quote and not actually in the Part master. Then on a new Sales Order, we do a New Line From Quote. I ran a method trace, and it seems like the best place to park the BPM is a PreProcess on SalesOrder.Update. First question, is there a 4GL equivalent to the Visual Basic InStr() I missed? I used INDEX() in an IF for any special characters, and tested if the return is not zero. I would really like to catch the process earlier before the line is actually added to the Sales Order, or alternatively be able to rollback/delete the line if it fails my test. So far this is just an informational popup, but I would like to add some teeth and prevent the update altogether. How do I throw an exception from a 4GL action to halt processing?

In the future I'll do a similar test when parts are added to part master or entered into a quote, but for now, we have a bunch of garbage part numbers already quoted, and I would like to stop the bleeding so things don't get any worse.

Thoughts are appreciated!
Thanks,
Jared

Here's my code:

DEF VAR msg As Character no-undo.
FOR EACH ttOrderDtl WHERE (ttOrderDtl.RowMod='A' OR ttOrderDtl.RowMod='U') no-lock:
IF INDEX(STRING(ttOrderDtl.PartNum),' ') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'!') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'@') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'#') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'$') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'%') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'&') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'*') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'(') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),')') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'=') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'+') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'\\') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'/') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'\"') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'.') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),',') NE 0 THEN DO:
msg = "Naughty, Naughty!" + CHR(10) + "PartNum is not barcode friendly!".
{lib/PublishInfoMsg.i &InfoMsg = msg }.
END.
ELSE DO:
msg = "PartNum is barcode friendly!".
{lib/PublishInfoMsg.i &InfoMsg = msg }.
END.
IF LENGTH(STRING(ttOrderDtl.PartNum)) > 27 THEN DO:
msg = "Maximum length of field PartNum exceeded.".
{lib/PublishInfoMsg.i &InfoMsg = msg }.
END.
END.
EDIT: After looking closer at the trace, I attached the code to SalesOrder.ChangeQuoteLine, and that is about where I want it to fire.
Jared,



To return an exception instead of an informational message is very similar:



ASSIGN Msg = " Maximum length of field PartNum exceeded.".

{lib/PublishEx.i &ExMsg = Msg &ExType = {&MESSAGE_ERR}}

RETURN.





Note: if you include the "RETURN", it doesn't process the rest of the code.
Leaving the return out will still create an exception, but it will process
the remaining code, which is usually not desirable.



HTH.



Kevin Simon

SimsTrak Consulting



From: vantage@yahoogroups.com [mailto:vantage@yahoogroups.com] On Behalf Of
k99ja04
Sent: Thursday, August 12, 2010 7:33 PM
To: vantage@yahoogroups.com
Subject: [Vantage] BPM for barcode friendly part numbers





I'm working on a BPM to check and see if a part number is barcode friendly,
as in doesn't contain special characters. The majority of our part parts are
entered directly into the quote and not actually in the Part master. Then on
a new Sales Order, we do a New Line From Quote. I ran a method trace, and it
seems like the best place to park the BPM is a PreProcess on
SalesOrder.Update. First question, is there a 4GL equivalent to the Visual
Basic InStr() I missed? I used INDEX() in an IF for any special characters,
and tested if the return is not zero. I would really like to catch the
process earlier before the line is actually added to the Sales Order, or
alternatively be able to rollback/delete the line if it fails my test. So
far this is just an informational popup, but I would like to add some teeth
and prevent the update altogether. How do I throw an exception from a 4GL
action to halt processing?

In the future I'll do a similar test when parts are added to part master or
entered into a quote, but for now, we have a bunch of garbage part numbers
already quoted, and I would like to stop the bleeding so things don't get
any worse.

Thoughts are appreciated!
Thanks,
Jared

Here's my code:

DEF VAR msg As Character no-undo.
FOR EACH ttOrderDtl WHERE (ttOrderDtl.RowMod='A' OR ttOrderDtl.RowMod='U')
no-lock:
IF INDEX(STRING(ttOrderDtl.PartNum),' ') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'!') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'@') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'#') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'$') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'%') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'&') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'*') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'(') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),')') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'=') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'+') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'\\') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'/') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'\"') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),'.') NE 0 OR
INDEX(STRING(ttOrderDtl.PartNum),',') NE 0 THEN DO:
msg = "Naughty, Naughty!" + CHR(10) + "PartNum is not barcode friendly!".
{lib/PublishInfoMsg.i &InfoMsg = msg }.
END.
ELSE DO:
msg = "PartNum is barcode friendly!".
{lib/PublishInfoMsg.i &InfoMsg = msg }.
END.
IF LENGTH(STRING(ttOrderDtl.PartNum)) > 27 THEN DO:
msg = "Maximum length of field PartNum exceeded.".
{lib/PublishInfoMsg.i &InfoMsg = msg }.
END.
END.





[Non-text portions of this message have been removed]
Nit-picking here. But Good Programming Practice says you should always assume a character is NOT valid, and test for valid ones. Valid characters are a well defined subset. The Invalid set is subject to expand. eg: in the future you may have a non-English speaking office using a multibyte character set like Unicode or something. Many security holes have also come about through code testing this way, as well.
-Wayne

--- In vantage@yahoogroups.com, "k99ja04" <jallmond@...> wrote:
> I'm working on a BPM to check and see if a part number is barcode friendly, as in doesn't contain special characters.
> [[[ snip ]]
> IF INDEX(STRING(ttOrderDtl.PartNum),' ') NE 0 OR
> INDEX(STRING(ttOrderDtl.PartNum),'!') NE 0 OR
> INDEX(STRING(ttOrderDtl.PartNum),'@') NE 0 OR
> INDEX(STRING(ttOrderDtl.PartNum),'#') NE 0 OR
> INDEX(STRING(ttOrderDtl.PartNum),'$') NE 0 OR
> INDEX(STRING(ttOrderDtl.PartNum),'%') NE 0 OR
> INDEX(STRING(ttOrderDtl.PartNum),'&') NE 0 OR
> INDEX(STRING(ttOrderDtl.PartNum),'*') NE 0 OR