Calculating a UPS Shipment Number From a Tracking Number

I may be the only person who ever needs to calculate a UPS Shipment Number. The project I’m working on requires me to create this value for a custom commerical invoice.

The format of the shipment number is

123X59M7CH

and the tracking number format is

1Z 123X56 66 2075 4864

Sort of the same … but not. The first part of the shipment number is your account number. That was -easy-. Second part, a bit more involved. Turns out, that this number is generated using base26 encoding. Base26 is rarely used but it is here. I’ve put together a custom tag which does the conversion for you. Just pass it a tracking number.

<cfset trackingNum=”1Z 123X56 66 2075 4864″>
<cf_base26Encoder trackingNum=”#trackingNum#” output=”shipNum”>
<cfoutput>ShipmentNumber:#shipNum#</cfoutput>

——save code below as base26Encoder.cfm ——
<CFPARAM NAME=”attributes.trackingNum” DEFAULT=”string”>
<CFIF NOT isDefined(“attributes.trackingNum”)>
<CFTHROW MESSAGE=’Error in custom Tag <B></B>:<BR>Attribute trackingNum not found’>
</CFIF>
<CFIF attributes.trackingNum eq “”>
<CFTHROW MESSAGE=’Error in custom Tag <B></B>:<BR>Attribute trackingNum can not be empty’>
</CFIF>
<!— UPS Tracking Number Anatomy
1st 2 digits – Lead numbers
next 5 – Acct Number
next 2 – service class
next 7 tracking number
last digit – check digit
—>
<cfscript>
//remove any spaces
tN1 = replace(attributes.trackingNUm,” “, “”,”all”);
//get the tracking number and check digit
tN = right(tN1,8);
//strip the check digit
tN = left(tN,7);
//get the account number
aNum = replace(replace(left(tN1,7), tN, “”),”1Z”, “”);
//do the base26 calculations based on UPS requirements
//discard remainders so use int()
p1 = int(tN/26^4);
p2 = int((tN-(p1*26^4))/26^3);
p3 = int((tN-(p1*26^4)-(p2*26^3))/26^2);
p4 = int((tN-(p1*26^4)-(p2*26^3)-(p3*26^2))/26);
p5 = int((tN-(p1*26^4)-(p2*26^3)-(p3*26^2)-(p4*26)));
//set up conversion lists
l1 = “0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25”;
l2 = “3,4,7,8,9,B,C,D,F,G,H,J,K,L,M,N,P,Q,R,S,T,V,W,X,Y,Z”;
l3 = p1 & “,” & p2 & “,” & p3 & “,” & p4 & “,” & p5;
</cfscript>
<cfset sNum = “”>
<!— loop through our base26 numbers, find in l1 and get corresponding value from l2 —>
<cfloop list=”#l3#” index=”x”>
<cfset v= listgetat(l2,listFind(l1,x))>
<cfset sNum = snum & v>
</cfloop>
<CFSET “caller.#attributes.output#” = aNUm&sNUm>
Hope someone else finds this useful.

Advertisements

2 Responses to Calculating a UPS Shipment Number From a Tracking Number

  1. * disregard previous – html filter killed the code*

    Handy little custom tag.

    Couple of minor suggests/pet peeves…

    <CFIF NOT isDefined(”attributes.trackingNum”)>

    For a long time now all scopes have always been defined at all times, so rather than using isDefined() its better to use StructKeyExists(). isDefined() does a scope scan, even if you specify a scope in the variable you’re checking. StructKeyExists(attributes,”trackingNum”) will just look for trackingNum in the attributes scope. isDefined(“attributes.tracking”) will look for variables.attributes.trackingNum, attributes.trackingNum, attributes.attributes.trackingNum, and so on. If you know where your variable is supposed to be, then I prefer to always check that location specifically.

    <CFSET “caller.#attributes.output#” = aNUm&sNUm>

    Probably personal preference, but array style references always seem cleaner/clearer to me.

    <CFSET caller[attributes.output] = aNUm&sNUm>

    Now this next one is a CF8 thing…. Its long been held that looping array is more efficient than looping lists. Don’t get me wrong lists have their place and I use them frequently. However, in CF8 you can now specify an array in a cfloop. Also, using implicit array and struct definitions in CF8.0.1 you can define arrays of data like this very quickly and easily.

    l1 = “0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25″;
    l2 = “3,4,7,8,9,B,C,D,F,G,H,J,K,L,M,N,P,Q,R,S,T,V,W,X,Y,Z”;
    l3 = p1 & “,” & p2 & “,” & p3 & “,” & p4 & “,” & p5;
    <cfloop list=”#l3#” index=”x”>
    <cfset v= listgetat(l2,listFind(l1,x))>
    <cfset sNum = snum & v>
    </cfloop>

    replace with
    l1 = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25];
    l2 = [3,4,7,8,9,’B’,’C’,’D’,’F’,’G’,’H’,’J’,’K’,’L’,’M’,’N’,’P’,’Q’,’R’,’S’,’T’,’V’,’W’,’X’,’Y’,’Z’];
    l3 = [p1, p2, p3, p4,p5];
    <cfloop array=”#l3#” index=”x”>
    <cfset v= l2[l1[x]]>
    <cfset sNum = snum & v>
    </cfloop>

    Not a huge difference and untested I admit, but it does make it a little easier to read.

    Now if only I could write like this on my own blog 😉 Catch you on EE.

  2. sidfishes says:

    isdefined is an old habit of mine from ancient times (CF 4 i think)…

    I use the CF8 implicit arrays about 1/2 the time (or rather when I remember )

    All good suggestions…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: