7246100;
#---------------------------------------------------------------
my $pMessageList = [
'The postage price is formatted incorrectly.  It should be formatted like %s.',
'The postage price is too large.  The price must be less than %s.',
'The postage price is too small.  The price must be greater than or equal to %s.',
'The class/location combination you selected were invalid.  Please check and re-enter your selection.',
'The catalogue shipping database does not have any shipping options defined for this location.  Please contact us directly with your order.',
'Free Postage',
'Standard Shipping',
'Your order has exceeded the postage tables defined by the supplier, therefore it is not possible to calculate the postage cost. Please contact the supplier with this information as they will be happy to process your order and will then be able to correct the shipping tables.  <P>Thank you.',
'Please enter a shipping cost.',
'Please select a state or province.',
];
my %ZoneTable = (
"UK" => {
"UndefinedRegion" => [13],
},
"US" => {
"UndefinedRegion" => [18],
"US.AL" => [18],
"US.AK" => [18],
"US.AZ" => [18],
"US.AR" => [18],
"US.CA" => [18],
"US.CO" => [18],
"US.CT" => [18],
"US.DE" => [18],
"US.DC" => [18],
"US.FL" => [18],
"US.GA" => [18],
"US.HI" => [18],
"US.ID" => [18],
"US.IL" => [18],
"US.IN" => [18],
"US.IA" => [18],
"US.KS" => [18],
"US.KY" => [18],
"US.LA" => [18],
"US.ME" => [18],
"US.MD" => [18],
"US.MA" => [18],
"US.MI" => [18],
"US.MN" => [18],
"US.MS" => [18],
"US.MO" => [18],
"US.MT" => [18],
"US.NE" => [18],
"US.NV" => [18],
"US.NH" => [18],
"US.NJ" => [18],
"US.NM" => [18],
"US.NY" => [18],
"US.NC" => [18],
"US.ND" => [18],
"US.OH" => [18],
"US.OK" => [18],
"US.OR" => [18],
"US.PA" => [18],
"US.RI" => [18],
"US.SC" => [18],
"US.SD" => [18],
"US.TN" => [18],
"US.TX" => [18],
"US.UT" => [18],
"US.VT" => [18],
"US.VA" => [18],
"US.WA" => [18],
"US.WV" => [18],
"US.WI" => [18],
"US.WY" => [18],
},
"CA" => {
"UndefinedRegion" => [15],
"CA.AB" => [15],
"CA.BC" => [15],
"CA.MB" => [15],
"CA.NB" => [15],
"CA.NF" => [15],
"CA.NT" => [15],
"CA.NU" => [15],
"CA.NS" => [15],
"CA.ON" => [15],
"CA.PE" => [15],
"CA.PQ" => [15],
"CA.SK" => [15],
"CA.YT" => [15],
},
"AF" => {
"UndefinedRegion" => [15],
},
"AL" => {
"UndefinedRegion" => [17],
},
"AS" => {
"UndefinedRegion" => [15],
},
"AD" => {
"UndefinedRegion" => [17],
},
"AM" => {
"UndefinedRegion" => [17],
},
"AU" => {
"UndefinedRegion" => [16],
},
"AT" => {
"UndefinedRegion" => [14],
},
"AZ" => {
"UndefinedRegion" => [17],
},
"BS" => {
"UndefinedRegion" => [15],
},
"BH" => {
"UndefinedRegion" => [15],
},
"BD" => {
"UndefinedRegion" => [15],
},
"BB" => {
"UndefinedRegion" => [15],
},
"BY" => {
"UndefinedRegion" => [17],
},
"BE" => {
"UndefinedRegion" => [14],
},
"BZ" => {
"UndefinedRegion" => [15],
},
"BJ" => {
"UndefinedRegion" => [15],
},
"BM" => {
"UndefinedRegion" => [15],
},
"BT" => {
"UndefinedRegion" => [15],
},
"BO" => {
"UndefinedRegion" => [15],
},
"BA" => {
"UndefinedRegion" => [17],
},
"BW" => {
"UndefinedRegion" => [15],
},
"BV" => {
"UndefinedRegion" => [15],
},
"BR" => {
"UndefinedRegion" => [15],
},
"IO" => {
"UndefinedRegion" => [16],
},
"BN" => {
"UndefinedRegion" => [15],
},
"BG" => {
"UndefinedRegion" => [14],
},
"KH" => {
"UndefinedRegion" => [15],
},
"CM" => {
"UndefinedRegion" => [15],
},
"CV" => {
"UndefinedRegion" => [15],
},
"KY" => {
"UndefinedRegion" => [15],
},
"CF" => {
"UndefinedRegion" => [15],
},
"TD" => {
"UndefinedRegion" => [15],
},
"CL" => {
"UndefinedRegion" => [15],
},
"CN" => {
"UndefinedRegion" => [15],
},
"CX" => {
"UndefinedRegion" => [16],
},
"CC" => {
"UndefinedRegion" => [16],
},
"CO" => {
"UndefinedRegion" => [15],
},
"KM" => {
"UndefinedRegion" => [15],
},
"CG" => {
"UndefinedRegion" => [15],
},
"CK" => {
"UndefinedRegion" => [16],
},
"HR" => {
"UndefinedRegion" => [14],
},
"CU" => {
"UndefinedRegion" => [15],
},
"CY" => {
"UndefinedRegion" => [14],
},
"CZ" => {
"UndefinedRegion" => [14],
},
"DK" => {
"UndefinedRegion" => [19],
},
"DJ" => {
"UndefinedRegion" => [15],
},
"DM" => {
"UndefinedRegion" => [15],
},
"DO" => {
"UndefinedRegion" => [15],
},
"TL" => {
"UndefinedRegion" => [15],
},
"EC" => {
"UndefinedRegion" => [15],
},
"EG" => {
"UndefinedRegion" => [15],
},
"SV" => {
"UndefinedRegion" => [15],
},
"GQ" => {
"UndefinedRegion" => [15],
},
"EE" => {
"UndefinedRegion" => [14],
},
"FK" => {
"UndefinedRegion" => [15],
},
"FO" => {
"UndefinedRegion" => [17],
},
"FJ" => {
"UndefinedRegion" => [16],
},
"FI" => {
"UndefinedRegion" => [14],
},
"FR" => {
"UndefinedRegion" => [19],
},
"PF" => {
"UndefinedRegion" => [16],
},
"GE" => {
"UndefinedRegion" => [17],
},
"DE" => {
"UndefinedRegion" => [19],
},
"GI" => {
"UndefinedRegion" => [17],
},
"GR" => {
"UndefinedRegion" => [14],
},
"GL" => {
"UndefinedRegion" => [17],
},
"GD" => {
"UndefinedRegion" => [15],
},
"GP" => {
"UndefinedRegion" => [15],
},
"GU" => {
"UndefinedRegion" => [15],
},
"GT" => {
"UndefinedRegion" => [15],
},
"GN" => {
"UndefinedRegion" => [15],
},
"GW" => {
"UndefinedRegion" => [15],
},
"GY" => {
"UndefinedRegion" => [15],
},
"HT" => {
"UndefinedRegion" => [15],
},
"HM" => {
"UndefinedRegion" => [15],
},
"HN" => {
"UndefinedRegion" => [15],
},
"HK" => {
"UndefinedRegion" => [15],
},
"HU" => {
"UndefinedRegion" => [14],
},
"IS" => {
"UndefinedRegion" => [17],
},
"IN" => {
"UndefinedRegion" => [15],
},
"ID" => {
"UndefinedRegion" => [15],
},
"IR" => {
"UndefinedRegion" => [15],
},
"IQ" => {
"UndefinedRegion" => [15],
},
"IE" => {
"UndefinedRegion" => [19],
},
"IL" => {
"UndefinedRegion" => [15],
},
"IT" => {
"UndefinedRegion" => [14],
},
"JM" => {
"UndefinedRegion" => [15],
},
"JP" => {
"UndefinedRegion" => [15],
},
"JO" => {
"UndefinedRegion" => [15],
},
"KZ" => {
"UndefinedRegion" => [17],
},
"KE" => {
"UndefinedRegion" => [15],
},
"KI" => {
"UndefinedRegion" => [16],
},
"KP" => {
"UndefinedRegion" => [15],
},
"KR" => {
"UndefinedRegion" => [15],
},
"KW" => {
"UndefinedRegion" => [15],
},
"KG" => {
"UndefinedRegion" => [17],
},
"LA" => {
"UndefinedRegion" => [15],
},
"LV" => {
"UndefinedRegion" => [14],
},
"LB" => {
"UndefinedRegion" => [15],
},
"LR" => {
"UndefinedRegion" => [15],
},
"LY" => {
"UndefinedRegion" => [15],
},
"LI" => {
"UndefinedRegion" => [17],
},
"LT" => {
"UndefinedRegion" => [14],
},
"LU" => {
"UndefinedRegion" => [14],
},
"MO" => {
"UndefinedRegion" => [16],
},
"MK" => {
"UndefinedRegion" => [17],
},
"MG" => {
"UndefinedRegion" => [15],
},
"MW" => {
"UndefinedRegion" => [15],
},
"MY" => {
"UndefinedRegion" => [15],
},
"MV" => {
"UndefinedRegion" => [15],
},
"ML" => {
"UndefinedRegion" => [15],
},
"MT" => {
"UndefinedRegion" => [14],
},
"MH" => {
"UndefinedRegion" => [15],
},
"MQ" => {
"UndefinedRegion" => [15],
},
"MR" => {
"UndefinedRegion" => [15],
},
"MU" => {
"UndefinedRegion" => [15],
},
"YT" => {
"UndefinedRegion" => [15],
},
"MX" => {
"UndefinedRegion" => [15],
},
"FM" => {
"UndefinedRegion" => [15],
},
"MD" => {
"UndefinedRegion" => [17],
},
"MC" => {
"UndefinedRegion" => [19],
},
"MN" => {
"UndefinedRegion" => [15],
},
"MZ" => {
"UndefinedRegion" => [15],
},
"MM" => {
"UndefinedRegion" => [15],
},
"NA" => {
"UndefinedRegion" => [15],
},
"NR" => {
"UndefinedRegion" => [15],
},
"NP" => {
"UndefinedRegion" => [15],
},
"NL" => {
"UndefinedRegion" => [14],
},
"NC" => {
"UndefinedRegion" => [16],
},
"NZ" => {
"UndefinedRegion" => [16],
},
"NI" => {
"UndefinedRegion" => [15],
},
"NE" => {
"UndefinedRegion" => [15],
},
"NG" => {
"UndefinedRegion" => [15],
},
"NU" => {
"UndefinedRegion" => [16],
},
"NF" => {
"UndefinedRegion" => [16],
},
"MP" => {
"UndefinedRegion" => [15],
},
"NO" => {
"UndefinedRegion" => [17],
},
"OM" => {
"UndefinedRegion" => [15],
},
"PK" => {
"UndefinedRegion" => [15],
},
"PW" => {
"UndefinedRegion" => [15],
},
"PA" => {
"UndefinedRegion" => [15],
},
"PG" => {
"UndefinedRegion" => [16],
},
"PY" => {
"UndefinedRegion" => [15],
},
"PE" => {
"UndefinedRegion" => [15],
},
"PH" => {
"UndefinedRegion" => [15],
},
"PN" => {
"UndefinedRegion" => [16],
},
"PL" => {
"UndefinedRegion" => [14],
},
"PT" => {
"UndefinedRegion" => [14],
},
"PR" => {
"UndefinedRegion" => [15],
},
"QA" => {
"UndefinedRegion" => [15],
},
"RE" => {
"UndefinedRegion" => [16],
},
"RO" => {
"UndefinedRegion" => [14],
},
"RU" => {
"UndefinedRegion" => [17],
},
"RW" => {
"UndefinedRegion" => [15],
},
"GS" => {
"UndefinedRegion" => [16],
},
"KN" => {
"UndefinedRegion" => [15],
},
"LC" => {
"UndefinedRegion" => [15],
},
"VC" => {
"UndefinedRegion" => [15],
},
"WS" => {
"UndefinedRegion" => [16],
},
"SM" => {
"UndefinedRegion" => [17],
},
"ST" => {
"UndefinedRegion" => [15],
},
"SA" => {
"UndefinedRegion" => [15],
},
"SN" => {
"UndefinedRegion" => [15],
},
"SC" => {
"UndefinedRegion" => [15],
},
"SL" => {
"UndefinedRegion" => [15],
},
"SG" => {
"UndefinedRegion" => [16],
},
"SK" => {
"UndefinedRegion" => [14],
},
"SI" => {
"UndefinedRegion" => [14],
},
"SB" => {
"UndefinedRegion" => [16],
},
"SO" => {
"UndefinedRegion" => [15],
},
"ZA" => {
"UndefinedRegion" => [15],
},
"ES" => {
"UndefinedRegion" => [14],
},
"LK" => {
"UndefinedRegion" => [15],
},
"SH" => {
"UndefinedRegion" => [15],
},
"PM" => {
"UndefinedRegion" => [15],
},
"SD" => {
"UndefinedRegion" => [15],
},
"SR" => {
"UndefinedRegion" => [15],
},
"SJ" => {
"UndefinedRegion" => [15],
},
"SZ" => {
"UndefinedRegion" => [15],
},
"SE" => {
"UndefinedRegion" => [14],
},
"CH" => {
"UndefinedRegion" => [17],
},
"SY" => {
"UndefinedRegion" => [15],
},
"TW" => {
"UndefinedRegion" => [15],
},
"TJ" => {
"UndefinedRegion" => [17],
},
"TZ" => {
"UndefinedRegion" => [15],
},
"TH" => {
"UndefinedRegion" => [15],
},
"SS" => {
"UndefinedRegion" => [15],
},
"TG" => {
"UndefinedRegion" => [15],
},
"TK" => {
"UndefinedRegion" => [16],
},
"TO" => {
"UndefinedRegion" => [16],
},
"TT" => {
"UndefinedRegion" => [15],
},
"TN" => {
"UndefinedRegion" => [15],
},
"TR" => {
"UndefinedRegion" => [17],
},
"TM" => {
"UndefinedRegion" => [17],
},
"TC" => {
"UndefinedRegion" => [16],
},
"TV" => {
"UndefinedRegion" => [16],
},
"UG" => {
"UndefinedRegion" => [15],
},
"UA" => {
"UndefinedRegion" => [17],
},
"AE" => {
"UndefinedRegion" => [15],
},
"UY" => {
"UndefinedRegion" => [15],
},
"UM" => {
"UndefinedRegion" => [15],
},
"UZ" => {
"UndefinedRegion" => [17],
},
"VA" => {
"UndefinedRegion" => [17],
},
"VE" => {
"UndefinedRegion" => [15],
},
"VN" => {
"UndefinedRegion" => [15],
},
"VG" => {
"UndefinedRegion" => [15],
},
"VI" => {
"UndefinedRegion" => [15],
},
"WF" => {
"UndefinedRegion" => [15],
},
"EH" => {
"UndefinedRegion" => [15],
},
"YE" => {
"UndefinedRegion" => [15],
},
"CD" => {
"UndefinedRegion" => [15],
},
"ZM" => {
"UndefinedRegion" => [15],
},
"ZW" => {
"UndefinedRegion" => [15],
},
"CW" => {
"UndefinedRegion" => [15],
},
"ME" => {
"UndefinedRegion" => [17],
},
"MF" => {
"UndefinedRegion" => [15],
},
"RS" => {
"UndefinedRegion" => [17],
},
"SX" => {
"UndefinedRegion" => [15],
},
);
my %ClassTable = (
10 => ['Standard Class Post', 0, ''],
11 => ['Signed For', 0, ''],
6 => ['Airmail', 0, ''],
6 => ['Airmail', 0, ''],
6 => ['Airmail', 0, ''],
6 => ['Airmail', 0, ''],
6 => ['Airmail', 0, ''],
6 => ['Airmail', 0, '']
);
my $phashDefinedCategories = 
{
'Normal' => 1,	'B & R only UK Post Free (2nd Class)' => 1,	'Digital Download' => 1,	'Books' => 1,
};
my $sDefaultCategory = 'Normal';
my %ShippingTable = (
10 => 
{
13 => [ {'CalculationBasis' => 6, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 0, 'ShippingCostsIncludeTax' => 1, 'ExcessAction' => 'Error'}, {'Normal' => {'Fixed' => 0, 'PerItem' => 0}, 'B & R only UK Post Free (2nd Class)' => {'Fixed' => 0, 'PerItem' => 0}, 'Digital Download' => {'Fixed' => 0, 'PerItem' => 0}, 'Books' => {'Fixed' => 450, 'PerItem' => 0}, }, ],
},
11 => 
{
13 => [ {'CalculationBasis' => 0, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 0, 'ShippingCostsIncludeTax' => 1, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 1.000000, 'IncrementalCharge' => 128}, { "wt" => 1.00, "cost" => 300}, ],
},
6 => 
{
14 => [ {'CalculationBasis' => 0, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 0, 'ShippingCostsIncludeTax' => 1, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 1.000000, 'IncrementalCharge' => 325}, { "wt" => 0.00, "cost" => 0}, { "wt" => 1.00, "cost" => 500}, { "wt" => 2.00, "cost" => 600}, { "wt" => 3.00, "cost" => 900}, { "wt" => 4.00, "cost" => 1000}, { "wt" => 5.00, "cost" => 1100}, { "wt" => 6.00, "cost" => 1200}, { "wt" => 7.00, "cost" => 1300}, { "wt" => 8.00, "cost" => 1400}, { "wt" => 9.00, "cost" => 1400}, { "wt" => 10.00, "cost" => 1400}, ],
15 => [ {'CalculationBasis' => 0, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 0, 'ShippingCostsIncludeTax' => 1, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 1.000000, 'IncrementalCharge' => 275}, { "wt" => 0.00, "cost" => 0}, { "wt" => 1.00, "cost" => 700}, { "wt" => 2.00, "cost" => 900}, { "wt" => 3.00, "cost" => 1100}, { "wt" => 4.00, "cost" => 1100}, { "wt" => 5.00, "cost" => 1350}, { "wt" => 6.00, "cost" => 1550}, { "wt" => 7.00, "cost" => 1750}, { "wt" => 8.00, "cost" => 1750}, { "wt" => 9.00, "cost" => 1950}, { "wt" => 10.00, "cost" => 2150}, ],
16 => [ {'CalculationBasis' => 0, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 0, 'ShippingCostsIncludeTax' => 1, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 1.000000, 'IncrementalCharge' => 100}, { "wt" => 0.00, "cost" => 0}, { "wt" => 1.00, "cost" => 600}, { "wt" => 2.00, "cost" => 850}, { "wt" => 3.00, "cost" => 1600}, { "wt" => 4.00, "cost" => 1600}, { "wt" => 5.00, "cost" => 1600}, { "wt" => 6.00, "cost" => 1600}, { "wt" => 7.00, "cost" => 1900}, { "wt" => 8.00, "cost" => 1900}, { "wt" => 9.00, "cost" => 2200}, { "wt" => 10.00, "cost" => 2200}, { "wt" => 11.00, "cost" => 2300}, { "wt" => 12.00, "cost" => 2400}, { "wt" => 13.00, "cost" => 2500}, { "wt" => 14.00, "cost" => 2600}, { "wt" => 15.00, "cost" => 2700}, { "wt" => 16.00, "cost" => 2800}, { "wt" => 17.00, "cost" => 2900}, { "wt" => 18.00, "cost" => 3000}, { "wt" => 19.00, "cost" => 3100}, { "wt" => 20.00, "cost" => 3200}, ],
17 => [ {'CalculationBasis' => 0, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 0, 'ShippingCostsIncludeTax' => 1, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 1.000000, 'IncrementalCharge' => 350}, { "wt" => 1.00, "cost" => 450}, { "wt" => 2.00, "cost" => 550}, { "wt" => 3.00, "cost" => 650}, { "wt" => 4.00, "cost" => 650}, { "wt" => 5.00, "cost" => 700}, { "wt" => 6.00, "cost" => 700}, { "wt" => 7.00, "cost" => 850}, { "wt" => 8.00, "cost" => 850}, { "wt" => 9.00, "cost" => 950}, { "wt" => 10.00, "cost" => 950}, ],
18 => [ {'CalculationBasis' => 0, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 0, 'ShippingCostsIncludeTax' => 1, 'ExcessAction' => 'Error'}, { "wt" => 1.00, "cost" => 800}, { "wt" => 2.00, "cost" => 800}, { "wt" => 3.00, "cost" => 1600}, { "wt" => 4.00, "cost" => 1600}, { "wt" => 5.00, "cost" => 1600}, { "wt" => 6.00, "cost" => 1850}, { "wt" => 7.00, "cost" => 1850}, { "wt" => 8.00, "cost" => 2200}, { "wt" => 9.00, "cost" => 2200}, { "wt" => 10.00, "cost" => 2200}, ],
19 => [ {'CalculationBasis' => 0, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 0, 'ShippingCostsIncludeTax' => 1, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 1.000000, 'IncrementalCharge' => 325}, { "wt" => 1.00, "cost" => 400}, { "wt" => 2.00, "cost" => 500}, { "wt" => 3.00, "cost" => 700}, { "wt" => 4.00, "cost" => 800}, { "wt" => 5.00, "cost" => 900}, { "wt" => 6.00, "cost" => 900}, { "wt" => 7.00, "cost" => 1000}, { "wt" => 8.00, "cost" => 1000}, { "wt" => 9.00, "cost" => 1050}, { "wt" => 10.00, "cost" => 1150}, ],
},
);
my $phashWeightConfiguration = 
{
0 => {'UseWeightIfUndefined' => 0, 'DefaultWeight' => '0.098' ,'OptimalWeight' => '' ,}, 
4 => {'UseWeightIfUndefined' => 1, 'DefaultWeight' => '0.085' ,'OptimalWeight' => '' ,}, 
5 => {'UseWeightIfUndefined' => 0, 'DefaultWeight' => '' ,'OptimalWeight' => '' ,}, 
};
my ($ShippingBasis, $SimpleCost, $UnknownRegion, $UnknownRegionCost, $WaiveCharges, $WaiveThreshold);
$ShippingBasis = 'ByZoneClass';
$UnknownRegion = 'Error';
$UnknownRegionCost = 800;
$WaiveCharges = 'No';
$WaiveThreshold = 100000.000000;
my $bPricesIncludesTax = 1;
my $dTaxInclusiveMultiplier = 1.000000;
my $nHandlingCharge = 0;
my $nHandlingProportion = 0;
my %ParentZoneTable = (
"US" => [],
"CA" => [],
);
use strict;
my $UNDEFINED = 'UndefinedRegion';
my $sOnlineError = '';
$::UPS_XPCI_VERSION = '1.0001';
$::UPS_SUCCESSFUL = '1';
$::UPS_FAILED = '0';
$::XML_HEADER = "<?xml version=\"1.0\"?>";
$::UPS_XML_RESPONSE = 'Response';
$::UPS_XML_RESPONSE_STATUS_CODE = 'ResponseStatusCode';
$::UPS_XML_RESPONSE_STATUS_DESCRIPTION = 'ResponseStatusDescription';
$::UPS_XML_ERROR = 'Error';
$::UPS_XML_ERROR_DESCRIPTION = 'ErrorDescription';
$::UPS_XML_ERROR_SEVERITY = 'ErrorSeverity';
$::UPS_XML_ADDRESS_VALIDATION_RESULT = 'AddressValidationResult';
$::UPS_XML_RATED_SHIPMENT = 'RatedShipment';
$::UPS_XML_SERVICE = 'Service';
$::UPS_XML_SERVICE_CODE = 'Code';
$::UPS_XML_TOTAL_CHARGES = 'TotalCharges';
$::UPS_XML_CURRENCY_CODE = 'CurrencyCode';
$::UPS_XML_MONETARY_VALUE = 'MonetaryValue';
$::UPS_XML_RANK = 'Rank';
$::UPS_XML_QUALITY = 'Quality';
$::UPS_XML_ADDRESS = 'Address';
$::UPS_XML_STATE_PROVINCE_CODE = 'StateProvinceCode';
$::UPS_XML_CITY = 'City';
$::UPS_XML_POSTAL_CODE_LOW_END = 'PostalCodeLowEnd';
$::UPS_XML_POSTAL_CODE_HIGH_END = 'PostalCodeHighEnd';
$::UPS_ERROR_SEVERITY_TRANSIENT_ERROR = 'Transient';
$::UPS_ERROR_SEVERITY_HARD_ERROR = 'Hard';
$::UPS_ERROR_SEVERITY_WARNING = 'Warning';
my $ssl_socket;
%::s_Ship_nShippingStatus = ();
%::s_Ship_sShippingError = ();
%::s_Ship_PreliminaryInfoVariables = ();
%::s_Ship_ShippingVariables = ();
$::s_Ship_bPrelimIsHidden = $::FALSE;
$::s_Ship_bShipPhaseIsHidden = $::FALSE;
$::s_Ship_sShippingDescription = '';
$::s_Ship_sHandlingDescription = ''; # not used in this plug-in
$::s_Ship_sShippingCountryName = '';
$::s_Ship_nShipCharges = 0;
$::s_Ship_nShipOptions = 0;
$::s_Ship_nShippingStatus{GetHandlingDescription} = $::SUCCESS;
$::s_Ship_sShippingError{GetHandlingDescription} = '';
$::s_Ship_bDisplayExtraCartInformation = $::FALSE;
%::s_Ship_hShippingClassProviderIDs = ();
%::s_Ship_hBasePlusPerProviderIDs = ();
$::s_Ship_nSSPProviderID = -1;
$::s_Ship_bTaxAppliesToShipping = $::FALSE;
$::s_Ship_sGFSCarrierAndService = '';
$::s_Ship_bInternationalShippingZone = $::FALSE;
$::UPS_CLASSES_NOT_USED = 0;
$::UPS_CLASSES_USED = 1;
$::UPS_BASEPLUSPER_CLASSES_USED = 2;
my %hSSPUsed;
my $bUPS_Available = $::TRUE;
my $sCONFIRM_BY_EMAIL = 'Actinic:ConfirmByEmail';
@::s_arrSortedShippingHashes;
$::SimpleCost = $SimpleCost;
$::ShippingBasis = $ShippingBasis;
$::UnknownRegion = $UnknownRegion;
$::UnknownRegionCost = $UnknownRegionCost;
$::UnknownRegionLabel = $$pMessageList[6];
$::FreeShippingLabel = $$pMessageList[5];
local %::s_hashShipData;
local %::s_hashClassToWeightCost;
my $c_nWeight = 0;
my $c_nQuantity = 1;
my $c_nPrice = 2;
my $c_nSimple = 3;
my $c_nAlternateWeight = 4;
my $c_nMaximumWeight = 5;
my $c_nPerItemShipping = 6;
$::dShippingSupplements = 0;
$::dHandlingSupplements = 0;
$::s_Ship_nAdjustedTotalQuantity = undef;
$::DPD_HOST = "api.dpd.co.uk";
$::DPD_GROUP_HOST = "api.dpdgroup.co.uk";
$::DPD_SSL_PORT = 443;
$::DPD_LOGIN_URL = "/user/?action=login";
$::DPD_GET_SERVICES_URL = "/shipping/network/?";
$::DPD_GET_PICKUP_LOCATIONS = "/organisation/pickuplocation/?";
$::DPD_NETWORK_CODE_SHIP_TO_SHOP = "1^91"; # Ship to Shop network service, 1^91 escaped
$::DPD_SHIP_TO_SHOP_POSTFIX = "_DPDShipToShop";
$::DPD_MAX_RESULTS = 20;
$::DPD_ADD_ALWAYS = "add-always";
%::hashPickupLocations;
$::sGeoSessionID;
$::DPD_WEEKDAY	= 1;
$::DPD_SATURDAY = 2;
$::DPD_SUNDAY	= 4;
$::DPD_ALL		= $::DPD_WEEKDAY | $::DPD_SATURDAY | $::DPD_SUNDAY;
$::g_DPDFilters = 
{
'w'	=> $::DPD_WEEKDAY,
'st'	=> $::DPD_SATURDAY,
'sn'	=> $::DPD_SUNDAY
};
local %::s_hashZoneMinFailed;
if ($::bAjaxCall)
{
return($::SUCCESS);
}
my @arrFuncns =
(
[\&ValidatePreliminaryInput,	'ValidatePreliminaryInput'],
[\&ValidateFinalInput,			'ValidateFinalInput'],
[\&RestoreFinalUI,				'RestoreFinalUI'],
[\&CalculateShipping,			'CalculateShipping'],
[\&IsFinalPhaseHidden,			'IsFinalPhaseHidden'],
[\&GetShippingDescription,		'GetShippingDescription'],
[\&CalculateHandling,			'CalculateHandling'],
);
OpaqueToHash();
my ($parrFunction, $nReturnCode, $sError);
$nReturnCode = $::SUCCESS;
foreach $parrFunction (@arrFuncns)
{
my $pFunction = $$parrFunction[0];
($nReturnCode, $sError) = &$pFunction();
$::s_Ship_nShippingStatus{$$parrFunction[1]} = $nReturnCode;
$::s_Ship_sShippingError{$$parrFunction[1]} = $sError;
}
if(defined $::s_hashShipData{InternationalShipping})
{
$::s_Ship_bInternationalShippingZone = $::s_hashShipData{InternationalShipping};
}
else
{
$::s_Ship_bInternationalShippingZone = $::FALSE;
}
SaveSelectionToOpaqueData();
my $nClassID;
foreach $nClassID (keys(%ClassTable))
{
push (@::s_ShipClassList, $ClassTable{$nClassID}[0]);
}
return($::SUCCESS);
sub ValidatePreliminaryInput
{
if ($ShippingBasis eq 'Simple')
{
return($::SUCCESS, undef);
}
if ($WaiveCharges eq 'Value' &&
CalculatePrice() > $WaiveThreshold)
{
return(SetFreeShipping());
}
if($::s_sDeliveryCountryCode eq '')
{
return(SetUndefinedShipping());
}
if($::s_sDeliveryCountryCode eq $ActinicOrder::REGION_NOT_SUPPLIED)
{
return(SetDefaultCharge());
}
if ($::s_sDeliveryRegionCode eq "" ||
$::s_sDeliveryRegionCode eq $UNDEFINED)
{
if (defined $ParentZoneTable{$::s_sDeliveryCountryCode} &&
$#{$ParentZoneTable{$::s_sDeliveryCountryCode}} == -1)
{
return ($::FAILURE, $$pMessageList[9]);
}
}
my $pProviderList = GetSSPProviderList($::s_sDeliveryCountryCode);
if	(keys %ZoneTable == 0 &&
@$pProviderList == 0	)
{
return(SetDefaultCharge());
}
if($::g_pSSPSetupBlob &&
$$::g_pSSPSetupBlob{1}{'AVSEnabled'} &&
(exists $::g_InputHash{'LocationDeliveryCountry'} || exists $::g_InputHash{DELIVERADDRESSSELECT}))
{
my $sCity = $::g_ShipContact{'ADDRESS3'};
my ($Result, $sSSPError) = DoUPSAddressValidation(ActinicLocations::GetISODeliveryCountryCode(),
ActinicLocations::GetISODeliveryRegionCode(), $sCity, $::g_LocationInfo{DELIVERPOSTALCODE});
if($Result == $::BADDATA)
{
if($sCity eq '')
{
SetUndefinedShipping();
}
return($::FAILURE, $sSSPError);
}
}
return($::SUCCESS, undef);
}
sub ValidateFinalInput
{
if ($ShippingBasis eq 'Simple')
{
return(SimpleValidateFinalInput());
}
if(@::s_arrSortedShippingHashes > 0)
{
return($::SUCCESS, undef);
}
ActinicOrder::GetExtrasettings();
my ($nReturnCode, $sError);
if(@::s_arrSortedShippingHashes == 0)
{
($nReturnCode, $sError) = CalculateMultiPackageShipping();
if($nReturnCode != $::SUCCESS)
{
return($nReturnCode, $sError);
}
}
SaveSelectionToOpaqueData();
return($::SUCCESS, undef);
}
sub RestoreFinalUI
{
if ($ShippingBasis eq 'Simple')
{
return(SimpleRestoreFinalUI());
}
my ($phashShipping, $sClassLabel, $sClassID, $sSelectHTML);
my $sPriceLabelFormat = ' (%s)';
$::s_Ship_nShipOptions = @::s_arrSortedShippingHashes;
my (%hashDPDServices, %hashCodesToServices);
my ($bDPDVarDefined, $bDPDEnabled) = ACTINIC::IsCustomVarDefined("IsDPDEnabled");
my ($bDPDS2SDefined, $bDPDShipToShopEnabled) = ACTINIC::IsCustomVarDefined("IsDPDShipToShopEnabled");
my ($bDPDDateDefined, $bDPDDeliveryDateEnabled) = ACTINIC::IsCustomVarDefined("IsDPDDeliveryDateEnabled");
my $sCountryCode = $::g_LocationInfo{DELIVERY_COUNTRY_CODE};
if ($sCountryCode ne 'UK')
{
$bDPDEnabled = $::FALSE;
}
if ($bDPDEnabled)
{{		
my ($phashWeightToQuantity, $parrSortedWeightKeys, $sWeightList,
$parrShipSeparatePackages, $parrMixedPackages, $sOptimalWeight)
= DivideIntoPackages($c_nWeight, undef);
my $nPackages = scalar @$parrShipSeparatePackages + scalar @$parrMixedPackages;
my $dSumOfWeights = 0.0;
foreach my $dWeight (@$parrSortedWeightKeys)
{
$dSumOfWeights += $$phashWeightToQuantity{$dWeight} * $dWeight;
}
ACTINIC::LoadJsonLib();
my @Response = GetAvailableDPDServices($nPackages, $dSumOfWeights);
if ($Response[0] != $::SUCCESS)
{
if ($Response[4] =~ /^validation/i)
{
return (@Response);
}
ACTINIC::RecordErrors($Response[1], ACTINIC::GetPath());
$bDPDEnabled = $::FALSE;
}
last if (!$bDPDEnabled);
%hashDPDServices = %{$Response[2]};
%hashCodesToServices = %{$Response[3]};
my $sFilename = $::Session->GetSessionFileFolder() . "dpdservices.fil";
@Response = ACTINIC::ReadAndVerifyFileNoChecksum($sFilename);
if ($Response[0] != $::SUCCESS)
{
ACTINIC::RecordErrors($Response[1], ACTINIC::GetPath());
$bDPDEnabled = $::FALSE;
}
last if (!$bDPDEnabled);
my ($sScript) = $Response[2];
eval($sScript);
}}
if (@::s_arrSortedShippingHashes == 1)
{
$phashShipping = $::s_arrSortedShippingHashes[0];
$sClassLabel = $$phashShipping{ShippingLabel};
$bDPDDeliveryDateEnabled = $::FALSE;
my $sDPDName = $$::g_pShippingToDPDClass{$sClassLabel};
if ($sDPDName ne $hashCodesToServices{$::DPD_NETWORK_CODE_SHIP_TO_SHOP}) # ship 2 shop not enabled?
{
$bDPDShipToShopEnabled = $::FALSE;
}
if ($::s_Ship_bDisplayPrices)
{
my (@PriceResponse) =
ActinicOrder::FormatPrice($$phashShipping{Cost},
$::TRUE,
\%::s_Ship_PriceFormatBlob);
$sClassLabel .= sprintf($sPriceLabelFormat, $PriceResponse[2]);
}
$sSelectHTML =
sprintf("%s\n<INPUT TYPE=HIDDEN NAME=ShippingClass VALUE='%s'>",
ACTINIC::HTMLEncode($sClassLabel),
$$phashShipping{ShippingClass});
push(@::s_ShipClassDetailList, GetClassHash($phashShipping->{'ShippingClass'}, $phashShipping->{'ShippingLabel'}, $phashShipping->{'Cost'}));
}
elsif (@::s_arrSortedShippingHashes > 1)
{
$sSelectHTML = "<SELECT ID='lstClass' NAME='ShippingClass'>\n";
my $bShipToShop = $::FALSE;
my $bDPDS2SClassesAvailable = $::FALSE;
my $bDPDDateClassesAvailable = $::FALSE;
my ($sFilter);
my $nDayFilters = 0;
foreach $phashShipping (@::s_arrSortedShippingHashes)
{		
$bShipToShop = $::FALSE;
$sClassLabel = $$phashShipping{ShippingLabel};
if ($bDPDEnabled)
{
my $sDPDName = $$::g_pShippingToDPDClass{$sClassLabel};
if ($sDPDName eq "" ||
($sDPDName ne "" &&
!$hashDPDServices{$sDPDName} &&
$sDPDName ne $::DPD_ADD_ALWAYS)) # and not a non-DPD shipping class that should be always added
{					
next;
}				
if ($hashCodesToServices{$::DPD_NETWORK_CODE_SHIP_TO_SHOP} eq $sDPDName) # the Ship to Shop service?
{
if (!$bDPDShipToShopEnabled)
{						
next;
}
$bShipToShop = $::TRUE;
$bDPDS2SClassesAvailable = $::TRUE;
}
elsif ($sDPDName ne $::DPD_ADD_ALWAYS)
{
if (!$bDPDDateClassesAvailable)
{
$sFilter = $$::g_pDayFilters{$sDPDName};
$nDayFilters |= $$::g_DPDFilters{$sFilter};
if (($nDayFilters & $::DPD_ALL) == $::DPD_ALL)
{
$bDPDDateClassesAvailable = $::TRUE;
}
}
}
}
if ($::s_Ship_bDisplayPrices)
{
my (@PriceResponse) =
ActinicOrder::FormatPrice($$phashShipping{Cost},
$::TRUE,
\%::s_Ship_PriceFormatBlob);
$sClassLabel .= sprintf($sPriceLabelFormat, $PriceResponse[2]);
}
$sClassID = $$phashShipping{ShippingClass};
my $sSelected =
($sClassID eq $::s_hashShipData{'ShippingClass'}) ?
'SELECTED ':
'';
if ($bDPDEnabled)
{				
$sSelectHTML .= sprintf("<OPTION %s Value='%s'%s data-filter='%s'>%s\n",
$sSelected,
$sClassID,
($bShipToShop ? "data-ship2shop='1'" : ""),
$$::g_pDayFilters{$$::g_pShippingToDPDClass{$$phashShipping{ShippingLabel}}},
ACTINIC::HTMLEncode($sClassLabel));
push(@::s_ShipClassDetailList, GetClassHash($sClassID, $$::g_pDayFilters{$$::g_pShippingToDPDClass{$$phashShipping{ShippingLabel}}}, $phashShipping->{'Cost'}));
}
else
{
$sSelectHTML .= sprintf("<OPTION %s Value='%s'>%s\n",
$sSelected, $sClassID, ACTINIC::HTMLEncode($sClassLabel));
push(@::s_ShipClassDetailList, GetClassHash($sClassID, $phashShipping->{'ShippingLabel'}, $phashShipping->{'Cost'}));
}			
}
$sSelectHTML .= "</SELECT>\n";
if (!$bDPDDateClassesAvailable)
{
$bDPDDeliveryDateEnabled = $::FALSE;
}
if (!$bDPDS2SClassesAvailable)
{
$bDPDShipToShopEnabled = $::FALSE;
}
}
if (!$bDPDShipToShopEnabled &&
!$bDPDDeliveryDateEnabled)
{
$bDPDEnabled = $::FALSE;
}	
if($hSSPUsed{$::UPS_CLASSES_USED} == $::TRUE)
{
$::s_Ship_hShippingClassProviderIDs{1} = $::TRUE;
}
elsif ($hSSPUsed{$::UPS_BASEPLUSPER_CLASSES_USED} == $::TRUE)
{
$::s_Ship_hBasePlusPerProviderIDs{1} = $::TRUE;
}	
$::s_Ship_ShippingVariables{$::VARPREFIX . 'SHIPPINGSELECT'} = $sSelectHTML;
if ($bDPDEnabled)
{
my $sHTML = "";
if ($::bPPConfirmationPage)
{
$::g_ShipInfo{'DPDSHIPPINGTYPE'} = $::g_InputHash{'DPDShippingType'};
$::g_ShipInfo{'DPDPICKUPLOCATION'} = $::g_InputHash{'DPDPickupLocation'};
$::g_ShipInfo{'DPDDELIVERYDATE'} = $::g_InputHash{'DPDDeliveryDate'};
my ($sLocation, $sLocationCode) = split(/\|/, $::g_ShipInfo{'DPDPICKUPLOCATION'});
$sHTML .= sprintf("<input type='hidden' name='DPDPickupDefault' id='idDPDPickupDefault' value='%s'>\n", $sLocationCode);
$sHTML .= sprintf("<input type='hidden' name='DPDDateDefault' id='idDPDDateDefault' value='%s'>\n", $::g_ShipInfo{'DPDDELIVERYDATE'});
}
my $IDsToShippingTypes =
{
'1' => 'Standard Delivery',
'2' => 'Specified Day',
'3' => 'Collection Point Pickup'
};		
$sHTML .= "<table>\n";
my ($sID, $sChecked);
foreach $sID (sort keys %$IDsToShippingTypes)
{
if (($::DPD_SPECIFIED_DAY eq $sID) &&
!$bDPDDeliveryDateEnabled)
{
next;
}
if (($::DPD_COLLECTION_POINT_PICKUP eq $sID) &&
!$bDPDShipToShopEnabled)
{
next;
}
if ($::g_ShipInfo{'DPDSHIPPINGTYPE'} eq "")
{
$sChecked = ($sID eq "1") ? " checked" : "";
}
else
{
$sChecked = ($::g_ShipInfo{'DPDSHIPPINGTYPE'} eq $sID) ? " checked" : ""; 
}			
$sHTML .= sprintf("<tr><td><input type='radio' name='DPDShippingType' id='idDPDShippingType$sID' value='%s' onChange='ShippingTypeChanged(this.value);'%s>%s</td></tr>\n",
$sID,
$sChecked,
$$IDsToShippingTypes{$sID});
}
$sHTML .= "</table>\n";		
$::s_Ship_ShippingVariables{$::VARPREFIX . 'SHIPPINGTYPE'} = $sHTML;
}
else
{
$::s_Ship_ShippingVariables{$::VARPREFIX . 'SHIPPINGTYPE'} = "";
$::g_ShipInfo{'DPDSHIPPINGTYPE'} = "";
$::g_ShipInfo{'DPDPICKUPLOCATION'} = "";
$::g_ShipInfo{'DPDDELIVERYDATE'} = "";
}
if ($::bPPConfirmationPage)
{
$::g_sShippingDump = ActinicOrder::GetShippingDump();
}
return($::SUCCESS, undef);
}
sub CalculateShipping
{
if ($ShippingBasis eq 'Simple')
{
return(SimpleCalculateShipping());
}
if(@::s_arrSortedShippingHashes == 0)
{
return($::SUCCESS, undef);
}
if($::s_hashShipData{'ShippingClass'} =~ /^(\d+)_(.+)/)
{
$::s_Ship_nSSPProviderID = $1;
my $bSSPError = $2 eq $sCONFIRM_BY_EMAIL;
my $pSSPProvider = GetUPSSetup();
$::s_Ship_sSSPOpaqueShipData =
sprintf("SSPID=%d;SSPClassRef=%s;OrigZip=%s;OrigCntry=%s;OrigCntryDesc=%s;Pack=%s;Rate=%s;Weight=%.03f;DestCntry=%s;DestPost=%s;Residential=%s;",
$::s_Ship_nSSPProviderID,
$2,
$$pSSPProvider{ShipperPostalCode},
$$pSSPProvider{ShipperCountry},
ACTINIC::GetCountryName($$pSSPProvider{ShipperCountry}),
$$pSSPProvider{'PackagingType'},
$$pSSPProvider{'RateChart'},
$::s_hashShipData{BasisTotal},
$::s_sDeliveryCountryCode,
$::g_ShipContact{'POSTALCODE'},
$::g_ShipContact{'RESIDENTIAL'} ne '' ? 1 : 0
);
if($::s_Ship_nSSPProviderID == 1)
{
if(!$bSSPError)
{
$::s_Ship_bDisplayExtraCartInformation = $::TRUE;
}
}
}
return($::SUCCESS, undef);
}
sub IsFinalPhaseHidden
{
if ($ShippingBasis eq 'Simple')
{
return($::SUCCESS, undef);
}
if ((@::s_arrSortedShippingHashes < 1) ||
(scalar @::s_Ship_sShipProducts == 0))
{
$::s_Ship_bShipPhaseIsHidden = $::TRUE;
}
return($::SUCCESS, undef);
}
sub GetShippingDescription
{
if(defined $::s_hashShipData{ShippingLabel})
{
$::s_Ship_sShippingDescription =
$::s_hashShipData{ShippingLabel};
}
else
{
$::s_Ship_sShippingDescription = '';
}
if(defined $::s_hashShipData{GFSCarrierAndService})
{
$::s_Ship_sGFSCarrierAndService = $::s_hashShipData{GFSCarrierAndService};
}
else
{
$::s_Ship_sGFSCarrierAndService = '';
}
return($::SUCCESS, undef);
}
sub CalculateHandling
{
$::s_Ship_nHandlingCharges = $nHandlingCharge + int (GetTaxExclusiveShipping() * $nHandlingProportion / $ActinicOrder::PERCENTOFFSET);
$::s_Ship_nHandlingCharges += $::dHandlingSupplements;
$::s_Ship_sOpaqueHandleData = sprintf("Handling;%d;", $::s_Ship_nHandlingCharges);
return ($::SUCCESS, undef);
}
sub GetTaxExclusiveShipping
{
my ($phashShipping, $phashSelected);
$phashSelected = undef;
foreach $phashShipping (@::s_arrSortedShippingHashes)
{
if($$phashShipping{ShippingClass} eq $::s_hashShipData{ShippingClass})
{
$phashSelected = $phashShipping;
last;
}
}
if(!defined $phashSelected &&
@::s_arrSortedShippingHashes > 0)
{
$phashSelected = $::s_arrSortedShippingHashes[0];
}
if (defined $phashSelected)
{
%::s_hashShipData = %$phashSelected;
$::s_Ship_nShipCharges = $$phashSelected{Cost};
}
return ($::s_Ship_nShipCharges);
}
sub SimpleValidateFinalInput
{
my (@Response);
if(!defined $::g_InputHash{SHIPPING})
{
return($::SUCCESS, undef);
}
if ($::g_InputHash{SHIPPING})
{
$::g_InputHash{SHIPPING} =~ s/^\s*(.*?)\s*$/$1/gs;
}
if (defined $::g_InputHash{SHIPPING})
{
my $sText = (0 == length $::g_InputHash{SHIPPING}) ? ' ' : $::g_InputHash{SHIPPING};
$::s_Ship_sOpaqueShipData = sprintf("Simple;Error-%s;", $sText);
}
if (!defined $::g_InputHash{'SHIPPING'} ||# if the shipping is undefined, error out
length $::g_InputHash{'SHIPPING'} == 0)
{
return($::FAILURE, $$pMessageList[8]);
}
@Response = ActinicOrder::ReadPrice($::g_InputHash{SHIPPING}, \%::s_Ship_PriceFormatBlob);
if ($Response[0] != $::SUCCESS ||
$Response[2] != int $Response[2])
{
@Response = ActinicOrder::FormatSinglePrice(10000, $::FALSE, \%::s_Ship_PriceFormatBlob);
if ($Response[0] != $::SUCCESS)
{
return($Response[0], $Response[1]);
}
return($::FAILURE, sprintf($$pMessageList[0], $Response[2]));
}
my ($nMaxShipping) = 99999999;
if ($Response[2] >= $nMaxShipping)
{
@Response = ActinicOrder::FormatPrice($nMaxShipping, $::TRUE, \%::s_Ship_PriceFormatBlob);
if ($Response[0] != $::SUCCESS)
{
return($Response[0], $Response[1]);
}
return($::FAILURE, sprintf($$pMessageList[1], $Response[2]));
}
my ($nMinShipping) = 0;
if ($Response[2] < $nMinShipping)
{
@Response = ActinicOrder::FormatPrice($nMinShipping, $::TRUE, \%::s_Ship_PriceFormatBlob);
if ($Response[0] != $::SUCCESS)
{
return($Response[0], $Response[1]);
}
return($::FAILURE, sprintf($$pMessageList[2], $Response[2]));
}
if (defined $::g_InputHash{SHIPPING})
{
$::s_Ship_sOpaqueShipData = sprintf("Simple;%s;", $Response[2]);
if ($bPricesIncludesTax)
{
$::s_Ship_sOpaqueShipData .= sprintf('TaxApplies;%d;', $::s_sShip_bLocationTaxable);
}
OpaqueToHash();
}
return($::SUCCESS, undef);
}
sub SimpleRestoreFinalUI
{
my (@Response);
$::s_Ship_nShipOptions = -1;
my $ePosOrder = $::s_Ship_PriceFormatBlob{"ICURRENCY"};
if ($ePosOrder == 0)
{
$::s_Ship_ShippingVariables{"NETQUOTEVAR:CURRENCYSYMBOL1"} = $::s_Ship_PriceFormatBlob{"SCURRENCY"};
$::s_Ship_ShippingVariables{"NETQUOTEVAR:CURRENCYSYMBOL2"} = '';
}
elsif ($ePosOrder == 1)
{
$::s_Ship_ShippingVariables{"NETQUOTEVAR:CURRENCYSYMBOL1"} = '';
$::s_Ship_ShippingVariables{"NETQUOTEVAR:CURRENCYSYMBOL2"} = $::s_Ship_PriceFormatBlob{"SCURRENCY"};
}
elsif ($ePosOrder == 2)
{
$::s_Ship_ShippingVariables{"NETQUOTEVAR:CURRENCYSYMBOL1"} = $::s_Ship_PriceFormatBlob{"SCURRENCY"} . ' ';
$::s_Ship_ShippingVariables{"NETQUOTEVAR:CURRENCYSYMBOL2"} = '';
}
elsif ($ePosOrder == 3)
{
$::s_Ship_ShippingVariables{"NETQUOTEVAR:CURRENCYSYMBOL1"} = '';
$::s_Ship_ShippingVariables{"NETQUOTEVAR:CURRENCYSYMBOL2"} = $::s_Ship_PriceFormatBlob{"SCURRENCY"} . ' ';
}
if (!defined $::s_hashShipData{'Simple'})
{
@Response = ActinicOrder::FormatSinglePrice($SimpleCost, $::FALSE, \%::s_Ship_PriceFormatBlob);
if ($Response[0] != $::SUCCESS)
{
return($Response[0], $Response[1]);
}
$::s_Ship_ShippingVariables{"NETQUOTEVAR:SHIPPINGVALUE"} = $Response[2];
$::s_hashShipData{'Simple'} = $SimpleCost;
$::s_Ship_sOpaqueShipData = sprintf("Simple;%s;", $SimpleCost);
if ($bPricesIncludesTax)
{
$::s_Ship_sOpaqueShipData .= sprintf('TaxApplies;%d;', $::s_sShip_bLocationTaxable);
}
}
elsif($::s_hashShipData{'Simple'} =~ /Error-/)
{
$::s_hashShipData{'Simple'} =~ s/^Error-\s*(.*?)\s*$/$1/g;
$::s_Ship_ShippingVariables{"NETQUOTEVAR:SHIPPINGVALUE"} = $::s_hashShipData{'Simple'};
}
else
{
$::s_hashShipData{'Simple'} =~ s/^\s*(.*?)\s*$/$1/g;
@Response = ActinicOrder::FormatSinglePrice($::s_hashShipData{'Simple'}, $::FALSE, \%::s_Ship_PriceFormatBlob);
if ($Response[0] != $::SUCCESS)
{
return($Response[0], $Response[1]);
}
$::s_Ship_ShippingVariables{"NETQUOTEVAR:SHIPPINGVALUE"} = $Response[2];
}
if ($bPricesIncludesTax)
{
$::s_Ship_bTaxAppliesToShipping = ActinicOrder::IsTaxApplicableForLocation('TAX_1');
}
else
{
$::s_Ship_bTaxAppliesToShipping = $::TRUE;
}
AddShippingHash({
'ShippingLabel'			=> 'Delivery',
'ShippingClass'			=> 'Default',
'ShippingZone'				=> -1,
'Cost'						=> $::s_hashShipData{'Simple'},
'TaxAppliesToShipping'	=> $::s_sShip_bLocationTaxable,
});
return($::SUCCESS, undef);
}
sub SimpleCalculateShipping
{
if (!defined $::s_hashShipData{'Simple'} ||
$::s_hashShipData{'Simple'} =~ /Error-/)
{
$::s_Ship_nShipCharges = 0;
}
else
{
$::s_Ship_nShipCharges = $::s_hashShipData{'Simple'};
}
return($::SUCCESS, undef);
}
sub CalculateQuantity
{
return($::s_Ship_nTotalQuantity);
}
sub CalculateAdjustedQuantity
{
if (defined $::s_Ship_nAdjustedTotalQuantity)
{
return ($::s_Ship_nAdjustedTotalQuantity);
}
$::s_Ship_nAdjustedTotalQuantity = 0;
$::s_Ship_nNonExcludedCount = 0;
my $i;
for $i (0 .. $#::s_Ship_sShipProducts)
{
if($::s_Ship_sShipProducts[$i] =~ /_/)
{
next;
}
if ($::s_Ship_nExcludeFromShipping[$i] == 1 &&
$::s_Ship_sGFSCarrierAndService eq "")
{
next;
}
if ($::s_Ship_bProduct == 0 &&
$::s_Ship_bUseAssociatedShip[$i] == 0)
{
next;
}
$::s_Ship_nNonExcludedCount++;
$::s_Ship_nAdjustedTotalQuantity +=
($::s_Ship_nShipShipQuantities[$i] *
$::s_Ship_nShipQuantities[$i]);
}
return($::s_Ship_nAdjustedTotalQuantity);
}
sub CalculatePrice
{
my		$j;
if (defined $::s_Ship_nTotalPrice)
{
return ($::s_Ship_nTotalPrice);
}
if (defined $::s_Ship_nSubTotalEx)
{
return ($::s_Ship_nSubTotalEx);
}
if (defined $::s_Ship_nSubTotal)
{
return ($::s_Ship_nSubTotal);
}
$::s_Ship_nTotalPrice = 0;
for $j (0 .. $#::s_Ship_sShipProducts)
{
$::s_Ship_nTotalPrice += ($::s_Ship_nShipPrices[$j] * $::s_Ship_nShipQuantities[$j]);
}
return($::s_Ship_nTotalPrice);
}
sub GetBands
{
if ($::s_sDeliveryRegionCode eq "" ||
$::s_sDeliveryRegionCode eq $UNDEFINED)
{
if ($#{$ParentZoneTable{$::s_sDeliveryCountryCode}} != -1)
{
return (@{$ParentZoneTable{$::s_sDeliveryCountryCode}}); # return this list (has invalid entries stripped)
}
}
if(defined 	$ZoneTable{$::s_sDeliveryCountryCode})
{
if(defined $ZoneTable{$::s_sDeliveryCountryCode}{$::s_sDeliveryRegionCode})
{
return(@{ $ZoneTable{$::s_sDeliveryCountryCode}{$::s_sDeliveryRegionCode} });
}
my $sParentState = ActinicLocations::GetDeliveryParentRegionCode();
if($sParentState ne '' &&
$sParentState ne $::s_sDeliveryRegionCode &&
defined $ZoneTable{$::s_sDeliveryCountryCode}{$sParentState})
{
return(@{ $ZoneTable{$::s_sDeliveryCountryCode}{$sParentState} });
}
if(defined $ZoneTable{$::s_sDeliveryCountryCode}{$UNDEFINED})
{
return(@{ $ZoneTable{$::s_sDeliveryCountryCode}{$UNDEFINED} });
}
}
my @listEmpty = ();
return(@listEmpty);
}
sub GetSSPProviderList
{
my ($sCountryCode) = @_;
my @arrReturn;
if(defined $$::g_pSSPSetupBlob{SupportedRegions} &&
defined $$::g_pSSPSetupBlob{SupportedRegions}{$sCountryCode})
{
my $nProviderID;
foreach $nProviderID ($$::g_pSSPSetupBlob{SupportedRegions}{$sCountryCode})
{
push(@arrReturn, $nProviderID);
}
}
return (\@arrReturn);
}
sub GetUS5DigitZipCode
{
my ($sZipCode) = @_;
if($sZipCode !~ /^\d{5}$/			&&
$sZipCode !~ /^\d{5}-\d{4}$/	&&
$sZipCode !~ /^\d{9}$/)
{
return($::FAILURE, ACTINIC::GetPhrase(-1, 2150));
}
$sZipCode = substr($sZipCode, 0, 5);
return($::SUCCESS, '', $sZipCode);
}
sub CalculatePackageShipping
{
my ($nZoneID, $nClassID, $objBasis, $nCalculationBasis) = @_;
if ($nCalculationBasis == $c_nPerItemShipping)
{
return (CalculatePerItemShipping($nZoneID, $nClassID, $objBasis));
}
my $nCost = 0;
my $bWeightOK = $::TRUE;
my $dMaxWeight = 0.0;	
if ($::s_hashZoneMinFailed{$nZoneID})
{
return($::FALSE, $nCost);
}
my $nMinValue;
if (defined $$::g_ZonesToMinValues{$nZoneID})
{
my $nMinValue = $$::g_ZonesToMinValues{$nZoneID};
my $nTotal = CalculatePrice();
if ($nMinValue > $nTotal)
{
$::s_hashZoneMinFailed{$nZoneID} = $::TRUE;
ACTINIC::LogData("Cart value ($nTotal) lower than min value $nMinValue for the zone $nZoneID, goods cannot be shipped.", $::DC_ORDERSCRIPT);
return($::FALSE, $nCost);
}		
else
{
ACTINIC::LogData("Cart value ($nTotal) higher than or equal to min value $nMinValue for the zone $nZoneID, goods can be shipped.", $::DC_ORDERSCRIPT);
}
}
my $nHighestCost = 0;
my $sCostKey = 'cost';
my $parrBandEntries = $ShippingTable{$nClassID}{$nZoneID};
my $nEntryCount = @$parrBandEntries;
my $phashBandEntry;
if($nEntryCount > 1)
{
$phashBandEntry = $$parrBandEntries[$nEntryCount - 1];
$dMaxWeight = $$phashBandEntry{wt};
if (defined $phashBandEntry->{'costIncTax'})
{
$sCostKey = 'costIncTax';
}
$nHighestCost = $$phashBandEntry{$sCostKey};
}
if($objBasis > $dMaxWeight)
{
my $phashExcessAction = $$parrBandEntries[0];
if($$phashExcessAction{ExcessAction} eq 'Highest')
{
$nCost = $nHighestCost;
}
elsif($$phashExcessAction{ExcessAction} eq 'AddFurther')
{
my $dExtraWeight = $objBasis - $dMaxWeight;
my $sCostSuffix = defined $phashExcessAction->{'IncrementalChargeIncTax'} ?
'IncTax' : '';
my ($dWeightIncrement, $nChargeIncrement) =
($$phashExcessAction{'IncrementalWeight'},
$$phashExcessAction{'IncrementalCharge' . $sCostSuffix});
my $nExtraUnits = int ($dExtraWeight / $dWeightIncrement + 0.999);
$nCost = $nHighestCost +
($nExtraUnits * $nChargeIncrement);
}
elsif($$phashExcessAction{ExcessAction} eq 'Error')
{
$bWeightOK = $::FALSE;
}
}
else
{
my $i;
for($i = 1; $i < $nEntryCount; $i++)
{
$phashBandEntry = $$parrBandEntries[$i];
if($$phashBandEntry{wt} >= $objBasis)
{
$nCost = $$phashBandEntry{$sCostKey};
last;
}
}
}
return($bWeightOK, $nCost);
}
sub GetPerItemQuantities
{
my ($phashCategoryQuantities) = @_;
my $i;
for $i (0 .. $#::s_Ship_sShipProducts)
{
if($::s_Ship_sShipProducts[$i] =~ /_/)
{
next;
}
if ($::s_Ship_nExcludeFromShipping[$i] == 1 &&
$::s_Ship_sGFSCarrierAndService eq "")
{
next;
}
if ($::s_Ship_bUseAssociatedShip[$i] == 0)
{
next;
}
my $sCategory = $::s_Ship_sShipCategories[$i];
if (!defined $phashDefinedCategories->{$sCategory})
{
$sCategory = $sDefaultCategory;
}
my $nTotalQuantity =
$::s_Ship_nShipQuantities[$i] * $::s_Ship_nShipShipQuantities[$i];
if (defined $phashCategoryQuantities->{$sCategory})
{
$phashCategoryQuantities->{$sCategory} += $nTotalQuantity;
}
else
{
$phashCategoryQuantities->{$sCategory} = $nTotalQuantity;
}
}
}
sub CalculateSupplements
{
my %hashShippingSupplementApplied;
my %hashHandlingSupplementApplied;
my $i;
for $i (0 .. $#::s_Ship_sShipProducts)
{
if	($::s_Ship_sShipProducts[$i] =~ /_/)
{
next;
}
if ($::s_Ship_bProduct[$i] ||
($::s_Ship_bUseAssociatedShip[$i] == 1))
{
my $nQuantity = $::s_Ship_nShipQuantities[$i];
if ($::s_Ship_dShipSupplementOnce[$i] == 1)
{
if (defined $hashShippingSupplementApplied{$::s_Ship_sShipProducts[$i]})
{
$nQuantity = 0;
}
else
{
$hashShippingSupplementApplied{$::s_Ship_sShipProducts[$i]} = 1;
$nQuantity = 1;
}
}
$::dShippingSupplements += $nQuantity * $::s_Ship_dShipSupplements[$i];
$nQuantity = $::s_Ship_nShipQuantities[$i];
if ($::s_Ship_dHandSupplementOnce[$i] == 1)
{
if (defined $hashHandlingSupplementApplied{$::s_Ship_sShipProducts[$i]})
{
$nQuantity = 0;
}
else
{
$hashHandlingSupplementApplied{$::s_Ship_sShipProducts[$i]} = 1;
$nQuantity = 1;
}
}
$::dHandlingSupplements += $nQuantity * $::s_Ship_dHandSupplements[$i];
}
}
}
sub CalculatePerItemShipping
{
my ($nZoneID, $nClassID, $phashCategoryQuantities) = @_;
my $nMaxFixedCost = 0;
my $dPerItemCharges = 0;
my $parrBandEntries = $ShippingTable{$nClassID}{$nZoneID};
my $phashZoneClassPerItemCharges = $parrBandEntries->[1];
my $sKeySuffix = '';
if ($bPricesIncludesTax &&
$parrBandEntries->[0]->{'TaxAppliesToShipping'} &&
!$parrBandEntries->[0]->{'ShippingCostsIncludeTax'})
{
$sKeySuffix = 'IncTax';
}
my $sCategory;
foreach $sCategory (keys %$phashCategoryQuantities)
{
my $phashCategory = $phashZoneClassPerItemCharges->{$sCategory};
if ($phashCategory->{'Fixed' . $sKeySuffix} > $nMaxFixedCost)
{
$nMaxFixedCost = $phashCategory->{'Fixed' . $sKeySuffix};
}
my $nQuantity = $phashCategoryQuantities->{$sCategory};
$dPerItemCharges += $phashCategory->{'PerItem' . $sKeySuffix} * $nQuantity;
}
return ($::TRUE, $nMaxFixedCost + $dPerItemCharges);
}
sub CalculateMultiPackageShipping
{
my $dWeightRemainder = 0.0;
my $bNonSeparateShipFound = $::FALSE;
my	($i);
my $dWeight;
my @arrShippingHashes;
my $parrZonesClasses = GetZoneClassCombinations();
my $pProviderList = GetSSPProviderList($::s_sDeliveryCountryCode);
if(@$parrZonesClasses == 0 &&
@$pProviderList == 0)
{
return(SetDefaultCharge());
}
CalculateAdjustedQuantity();
CalculateSupplements();
my %hashCalculationBases = {};
GetZoneClassesByBasis(\%hashCalculationBases, $parrZonesClasses, \@arrShippingHashes);
my $nCalculationBasis;
foreach $nCalculationBasis (keys %hashCalculationBases)
{
my $parrBasisZoneClasses =
$hashCalculationBases{$nCalculationBasis};
my $parrZoneClass;
foreach $parrZoneClass (@$parrBasisZoneClasses)
{
CalculateZoneClassShipping($nCalculationBasis,
$parrZoneClass, \@arrShippingHashes);
}
}
if (@$pProviderList > 0)
{
my ($phashWeightToQuantity, $parrSortedWeightKeys, $sWeightList,
$parrShipSeparatePackages, $parrMixedPackages, $sOptimalWeight)
= DivideIntoPackages($c_nWeight, undef);
my $dSumOfWeights = 0.0;
foreach $dWeight (@$parrSortedWeightKeys)
{
$dSumOfWeights += $$phashWeightToQuantity{$dWeight} * $dWeight;
}
my $nProviderID;
foreach $nProviderID (@$pProviderList)
{
my $bWeightThresholdExceeded = IsWeightThresholdExceeded($nProviderID, $dSumOfWeights);
if($::g_pSSPSetupBlob &&
$$::g_pSSPSetupBlob{$nProviderID}{'RSSEnabled'} &&
$bWeightThresholdExceeded == $::FALSE)
{
my ($nReturnCode, $sSSPError, $parrShippingHashes, $nRateType) = GetUPSRates();
$hSSPUsed{$nRateType} = $::TRUE;
if($nReturnCode != $::SUCCESS)
{
return($nReturnCode, $sSSPError);
}
else
{
push @arrShippingHashes, @$parrShippingHashes;
}
}
}
}
if(@$parrZonesClasses == 0 &&
@arrShippingHashes == 0)
{
return(SetDefaultCharge());
}
if (@arrShippingHashes == 0 &&
scalar @::s_Ship_sShipProducts != 0)
{
return ($::FAILURE, $$pMessageList[7]);
}
@arrShippingHashes = sort{$$a{Cost} <=> $$b{Cost}} @arrShippingHashes;
my @arrLastClasses;
my $phashClass;
foreach $phashClass (@arrShippingHashes)
{
my $bLastClass = 0;
my $nClassID = $phashClass->{'ShippingClass'};
if (defined $ClassTable{$nClassID})
{
$bLastClass = $ClassTable{$nClassID}->[1];
}
if ($bLastClass)
{
push @arrLastClasses, $phashClass;
}
else
{
push @::s_arrSortedShippingHashes, $phashClass;
}
}
push @::s_arrSortedShippingHashes, @arrLastClasses;
return($::SUCCESS, '');
}
sub CalculateZoneClassShipping
{
my ($nCalculationBasis, $parrZoneClass, $parrShippingHashes) = @_;
my ($phashWeightToQuantity, $parrSortedWeightKeys, $sWeightList,
$parrShipSeparatePackages, $parrMixedPackages, $sOptimalWeight)
= DivideIntoPackages($nCalculationBasis, $parrZoneClass);
my $nTotalCost = 0;
my ($nZoneID, $nClassID) = @$parrZoneClass;
my ($bBasisOK, $nPackageCost);
$bBasisOK = $::TRUE;
my $dBasisTotal = 0;
my $dBasis;
foreach $dBasis (@$parrSortedWeightKeys)
{
($bBasisOK, $nPackageCost) =
CalculatePackageShipping($nZoneID, $nClassID,
$dBasis, $nCalculationBasis);
if ($bBasisOK)
{
$nTotalCost +=
$$phashWeightToQuantity{$dBasis} * $nPackageCost;
my $sKey = sprintf('%0.03f', $dBasis);
$::s_hashClassToWeightCost{$nClassID}{$sKey} = $nPackageCost;
$dBasisTotal += $dBasis;
}
else
{
last;
}
}
if ($bBasisOK)
{
if ($::s_Ship_nNonExcludedCount == 0 &&
$dBasisTotal == 0)
{
$nTotalCost = 0.0;
}
my $nCost = ActinicOrder::RoundScientific($nTotalCost + $::dShippingSupplements);
my $phashBandDefinition = GetBandDefinition(@$parrZoneClass);
if (defined $phashBandDefinition->{'FreeOver'} &&
CalculatePrice() > $phashBandDefinition->{'FreeOver'})
{
$nCost = 0;
}
my $bInternational = $::FALSE;
if (defined $phashBandDefinition->{'InternationalZone'})
{
$bInternational = $phashBandDefinition->{'InternationalZone'};
}
push @$parrShippingHashes, {
'ShippingLabel'				=> $ClassTable{$nClassID}[0],
'ShippingClass'				=> $nClassID,
'ShippingZone'					=> $nZoneID,
'Cost'							=> $nCost,
'BasisTotal'					=> $dBasis,
'ShipSeparatePackages'		=> $parrShipSeparatePackages, 
'MixedPackages'				=> $parrMixedPackages, 
'OptimalWeight'				=> $sOptimalWeight,
'TaxAppliesToShipping'		=> $phashBandDefinition->{'TaxAppliesToShipping'},
'ShippingCostsIncludeTax'	=> $phashBandDefinition->{'ShippingCostsIncludeTax'},
'GFSCarrierAndService'		=> $ClassTable{$nClassID}[2],
'InternationalShipping'		=> $bInternational
};
}
}
sub GetZoneClassesByBasis
{
my ($phashCalculationBases, $parrZonesClasses, $parrShippingHashes) = @_;
my $parrZoneClass;
foreach $parrZoneClass (@$parrZonesClasses)
{
my ($nZoneID, $nClassID) = @$parrZoneClass;
my $phashBandDefinition = GetBandDefinition(@$parrZoneClass);
if (defined $phashBandDefinition->{'FreeClass'})
{
my $bInternational = $::FALSE;
if (defined $phashBandDefinition->{'InternationalZone'})
{
$bInternational = $phashBandDefinition->{'InternationalZone'};
}
push @$parrShippingHashes, {
'ShippingLabel'		=> $ClassTable{$nClassID}[0],
'ShippingClass'		=> $nClassID,
'ShippingZone'			=> $nZoneID,
'Cost'					=> 0,
'BasisTotal'			=> 0,
'GFSCarrierAndService'	=> $ClassTable{$nClassID}[2],
'InternationalShipping'	=> $bInternational
};
}
else
{
my $nCalculationBasis = $phashBandDefinition->{'CalculationBasis'};
if (!defined $phashCalculationBases->{$nCalculationBasis})
{
$phashCalculationBases->{$nCalculationBasis} = [];
}
my $parrBasisZoneClasses = $phashCalculationBases->{$nCalculationBasis};
push @$parrBasisZoneClasses, $parrZoneClass;
}
}
return (scalar(keys %$phashCalculationBases) > 0);
}
sub GetBandDefinition
{
my ($nZoneID, $nClassID) = @_;
my $parrBandEntries = $ShippingTable{$nClassID}{$nZoneID};
my $phashBandDefinition = $$parrBandEntries[0];
return ($phashBandDefinition);
}
sub IsWeightThresholdExceeded
{
my $nProviderID 	= shift;
my $dSumOfWeights	= shift;
my $bWeightThresholdExceeded = $::FALSE;
if($::g_pSSPSetupBlob &&
$$::g_pSSPSetupBlob{$nProviderID}{'WEIGHTTHRESHOLD'})
{
my $dWeightThreshold = $$::g_pSSPSetupBlob{$nProviderID}{'WEIGHTTHRESHOLD'};
if (($dWeightThreshold ne '') &&
($dWeightThreshold =~ /^[+]?[\d]*(\.[\d]+)?$/))
{
if ($dWeightThreshold < $dSumOfWeights)
{
$bWeightThresholdExceeded = $::TRUE;
}
}
}
return $bWeightThresholdExceeded;
}
sub DivideIntoPackages
{
my ($nCalculationBasis, $parrZoneClass, $bUseIntegralWeights) = @_;
my $dWeightRemainder = 0.0;
my $nNonSeparateShipCount = 0;
my $dExcludeFromShippingWeight = 0.0;	
my (%hashWeightToQuantity, @arrSortedWeightKeys);
my	($i);
my (@arrShipSeparatePackages, @arrMixedPackages, $parrPackage);
if (($::s_Ship_sGFSCarrierAndService eq "" ||
!defined $::s_Ship_sGFSCarrierAndService) &&
defined $parrZoneClass)
{		
my ($nZoneID, $nClassID) = @$parrZoneClass;
$::s_Ship_sGFSCarrierAndService = $ClassTable{$nClassID}[2];
}	
my $nBasisTotal = -1;
if ($nCalculationBasis == $c_nQuantity)
{
$nBasisTotal = CalculateAdjustedQuantity();
}
elsif ($nCalculationBasis == $c_nPrice)
{
$nBasisTotal = CalculatePrice();
}
elsif ($nCalculationBasis == $c_nPerItemShipping)
{
$nBasisTotal = {};
GetPerItemQuantities($nBasisTotal);
}
if (ref($nBasisTotal) ne '' || $nBasisTotal != -1)
{
$hashWeightToQuantity{$nBasisTotal} = 1;
@arrSortedWeightKeys = ($nBasisTotal);
return(\%hashWeightToQuantity, \@arrSortedWeightKeys, $nBasisTotal);
}
my $dWeightDivisor = 1;	
my $dAltWeightDivisor = 1;
my $sOptimalWeight = '';
if (defined $parrZoneClass)
{
my ($nZoneID, $nClassID) = @$parrZoneClass;
my $parrBandEntries = $ShippingTable{$nClassID}{$nZoneID};
my $phashBandDefinition = $$parrBandEntries[0];
$dWeightDivisor = $phashBandDefinition->{'WeightFactor'};
$dAltWeightDivisor = $phashBandDefinition->{'AltWeightFactor'};
$sOptimalWeight = 
$phashWeightConfiguration->{$nCalculationBasis}->{'OptimalWeight'};
}
else
{
$sOptimalWeight = 
$phashWeightConfiguration->{$c_nWeight}->{'OptimalWeight'};
}
my $dUnitWeight;
for $i (0 .. $#::s_Ship_sShipProducts)
{
my $sProdRef = $::s_Ship_sShipProducts[$i];
if($::s_Ship_sShipProducts[$i] =~ /_/)
{
next;
}
if ($::s_Ship_nExcludeFromShipping[$i] == 1 &&
$::s_Ship_sGFSCarrierAndService eq "")
{
next;
}
if ($nCalculationBasis == $c_nWeight)
{
$dUnitWeight =
GetWeight($i, $phashWeightConfiguration, $dWeightDivisor);
}
elsif ($nCalculationBasis == $c_nAlternateWeight)
{
$dUnitWeight =
GetAltWeight($i, $phashWeightConfiguration,
$dWeightDivisor, $dAltWeightDivisor);
}
elsif ($nCalculationBasis == $c_nMaximumWeight)
{
$dUnitWeight =
GetMaxWeight($i, $phashWeightConfiguration,
$dWeightDivisor, $dAltWeightDivisor);
}
if($::s_Ship_nShipSeparately[$i] == 1 ||
($sOptimalWeight > 0 &&
$dUnitWeight >= $sOptimalWeight))
{
if($bUseIntegralWeights)
{
$dUnitWeight = int($dUnitWeight + 0.9999);
}
if ($::s_Ship_nExcludeFromShipping[$i] == 0)
{
$hashWeightToQuantity{$dUnitWeight} += $::s_Ship_nShipQuantities[$i];
}
my @arrTemp = ($::s_Ship_sShipProducts[$i], $::s_Ship_nShipQuantities[$i], $dUnitWeight);
push @arrShipSeparatePackages, \@arrTemp;
}
else
{
$nNonSeparateShipCount += $::s_Ship_nShipQuantities[$i];
$dWeightRemainder +=
$dUnitWeight * $::s_Ship_nShipQuantities[$i];
if ($::s_Ship_nExcludeFromShipping[$i] == 1)
{	
$dExcludeFromShippingWeight += $dUnitWeight * $::s_Ship_nShipQuantities[$i];
}
my @arrTemp = ($::s_Ship_sShipProducts[$i], $::s_Ship_nShipQuantities[$i], $dUnitWeight);
push @arrMixedPackages, \@arrTemp;
}
}
my $dBasis = 0.0;
if($nNonSeparateShipCount > 0)
{
my $nQuantity = 1;
if($sOptimalWeight ne '' &&
$dWeightRemainder > $sOptimalWeight)
{
my $nCalculatedPackages = int(($dWeightRemainder / $sOptimalWeight) + 0.9999);
if($nCalculatedPackages == $nNonSeparateShipCount)
{
foreach $parrPackage (@arrMixedPackages)
{
$dUnitWeight = $$parrPackage[2];
if($bUseIntegralWeights)
{
$dUnitWeight = int($dUnitWeight + 0.9999);
}
$dBasis = $dUnitWeight;
if ($dExcludeFromShippingWeight > 0)
{
$dBasis -= $dExcludeFromShippingWeight;
}
$hashWeightToQuantity{$dBasis} += $$parrPackage[1];
push @arrShipSeparatePackages, $parrPackage;
}
@arrMixedPackages = ();
}
else
{
$nQuantity =
($nCalculatedPackages < $nNonSeparateShipCount) ?
$nCalculatedPackages :
$nNonSeparateShipCount;
$dWeightRemainder = $dWeightRemainder / $nQuantity;
$dExcludeFromShippingWeight = $dExcludeFromShippingWeight / $nQuantity;
if($bUseIntegralWeights)
{
$dWeightRemainder = int($dWeightRemainder + 0.9999);
if ($dExcludeFromShippingWeight > 0)
{
$dExcludeFromShippingWeight = int($dExcludeFromShippingWeight + 0.9999);
}
}
$dBasis = $dWeightRemainder;
if ($dExcludeFromShippingWeight > 0)
{
$dBasis -= $dExcludeFromShippingWeight;					
}
if ($dBasis != 0.0 ||
$dBasis == $dWeightRemainder)
{
$hashWeightToQuantity{$dBasis} += $nQuantity;
}
my @arrTemp = ('', $nQuantity, $dWeightRemainder);
push @arrMixedPackages, \@arrTemp;
}
}
else
{
if($bUseIntegralWeights)
{
$dWeightRemainder = int($dWeightRemainder + 0.9999);
if ($dExcludeFromShippingWeight > 0)
{
$dExcludeFromShippingWeight = int($dExcludeFromShippingWeight + 0.9999);
}
}
$dBasis = $dWeightRemainder;
if ($dExcludeFromShippingWeight > 0)
{
$dBasis -= $dExcludeFromShippingWeight;
}
if ($dBasis != 0.0 ||
$dBasis == $dWeightRemainder)						
{
$hashWeightToQuantity{$dBasis} += $nQuantity;
}
my @arrTemp = ('', $nQuantity, $dWeightRemainder);
push @arrMixedPackages, \@arrTemp;
}
}
@arrSortedWeightKeys = sort {$b <=> $a} keys %hashWeightToQuantity;
my ($dWeight, $sWeightList);
foreach $dWeight (@arrSortedWeightKeys)
{
$sWeightList .= sprintf("%d@%.03f,", $hashWeightToQuantity{$dWeight}, $dWeight);
}
$sWeightList =~ s/,$//;
return(\%hashWeightToQuantity, \@arrSortedWeightKeys, $sWeightList, 
\@arrShipSeparatePackages, \@arrMixedPackages, $sOptimalWeight);
}
sub GetWeight
{
my ($nIndex, $phashWeightConfiguration, $dWeightDivisor) = @_;
my $dUnitWeight = $::s_Ship_OpaqueDataTables{$::s_Ship_sShipProducts[$nIndex]};
if ($dUnitWeight eq "")
{
$dUnitWeight = $phashWeightConfiguration->{$c_nWeight}->{'DefaultWeight'};
}
if ($dWeightDivisor != 0)
{
$dUnitWeight /= $dWeightDivisor;
}
return ($dUnitWeight);
}
sub GetAltWeight
{
my ($nIndex, $phashWeightConfiguration, $dWeightDivisor, $dAltWeightDivisor) = @_;
my $dUnitWeight = $::s_Ship_dShipAltWeights[$nIndex];
if ($::s_Ship_dShipAltWeights[$nIndex] eq "")
{
my $phashWeightDetails = $phashWeightConfiguration->{$c_nAlternateWeight};
if ($phashWeightDetails->{'UseWeightIfUndefined'})
{
return (GetWeight($nIndex, $phashWeightConfiguration, $dWeightDivisor));
}
$dUnitWeight =
$phashWeightConfiguration->{$c_nAlternateWeight}->{'DefaultWeight'};
}
if ($dAltWeightDivisor != 0)
{
$dUnitWeight /= $dAltWeightDivisor;
}
return ($dUnitWeight);
}
sub GetMaxWeight
{
my ($nIndex, $phashWeightConfiguration, $dWeightDivisor, $dAltWeightDivisor) = @_;
my $dUnitWeight = GetWeight($nIndex, $phashWeightConfiguration, $dWeightDivisor);
my $dAltWeight = GetAltWeight($nIndex, $phashWeightConfiguration, $dWeightDivisor, $dAltWeightDivisor);
if ($dAltWeight > $dUnitWeight)
{
$dUnitWeight = $dAltWeight;
}
return ($dUnitWeight);
}
sub GetZoneClassCombinations
{
my @arrZones = GetBands();
my (%hashZones, $nZoneID, $nClassID, @arrZonesClasses);
foreach $nZoneID (@arrZones)
{
$hashZones{$nZoneID} = 1;
}
foreach $nClassID (keys %ShippingTable)
{
my $phashClass = $ShippingTable{$nClassID};
foreach $nZoneID (keys %$phashClass)
{
if(defined $hashZones{$nZoneID})
{
my @arrClassZone = ($nZoneID, $nClassID);
push @arrZonesClasses, \@arrClassZone;
}
}
}
return(\@arrZonesClasses);
}
sub AddShippingHash
{
my ($phashShipping) = @_;
push @::s_arrSortedShippingHashes, $phashShipping;
push(@::s_ShipClassDetailList, GetClassHash($phashShipping->{'ShippingClass'}, $phashShipping->{'ShippingLabel'}, $phashShipping->{'Cost'}));
}
sub SetDefaultCharge
{
if ($UnknownRegion eq 'Default')
{
AddShippingHash({
'ShippingLabel'			=> $$pMessageList[6],
'ShippingClass'			=> 'Default',
'ShippingZone'				=> -1,
'Cost'						=> $UnknownRegionCost,
'TaxAppliesToShipping'	=> $::s_sShip_bLocationTaxable,
});
return($::SUCCESS, '');
}
return($::FAILURE, $$pMessageList[4]);
}
sub SetFreeShipping
{
AddShippingHash(GetFreeShippingHash());
return($::SUCCESS, '');
}
sub GetFreeShippingHash
{
return({
'ShippingLabel'	=> $$pMessageList[5],
'ShippingClass'	=> '-1',
'ShippingZone'		=> -1,
'Cost'				=> 0,
'BasisTotal'		=> 0
});
}
sub SetUndefinedShipping
{
AddShippingHash({
'ShippingLabel'			=> '',
'ShippingClass'			=> -1,
'ShippingZone'				=> -1,
'Cost'						=> 0,
});
return($::SUCCESS, '');
}
sub OpaqueToHash
{
if(defined $::g_InputHash{ShippingClass})
{
$::s_hashShipData{ShippingClass} = $::g_InputHash{ShippingClass};
}
else
{
%::s_hashShipData =
split (';', $::s_Ship_sOpaqueShipData);
}
}
sub SaveSelectionToOpaqueData
{
if($ShippingBasis eq 'Simple')
{
return;
}
my ($phashShipping, $phashSelected);
$phashSelected = undef;
foreach $phashShipping (@::s_arrSortedShippingHashes)
{
HashToOpaque($phashShipping);
$$phashShipping{'OpaqueData'} = $::s_Ship_sOpaqueShipData;
if($$phashShipping{ShippingClass} eq $::s_hashShipData{ShippingClass})
{
$phashSelected = $phashShipping;
}
}
if(!defined $phashSelected &&
@::s_arrSortedShippingHashes > 0)
{
$phashSelected = $::s_arrSortedShippingHashes[0];
}
if (defined $phashSelected)
{
%::s_hashShipData = %$phashSelected;
}
HashToOpaque($phashSelected);
if (!$phashSelected ||
$$phashSelected{ShippingClass} !~ /^\d+_/)
{
$::s_Ship_sSSPOpaqueShipData = '';
}
}
sub HashToOpaque
{
my $phashSelected = shift;
if (defined $phashSelected)
{
$::s_Ship_sOpaqueShipData =
sprintf("ShippingClass;%s;ShippingZone;%d;BasisTotal;%s;Cost;%d;",
$$phashSelected{ShippingClass},
$$phashSelected{ShippingZone},
$$phashSelected{BasisTotal},
$$phashSelected{Cost});
if ($bPricesIncludesTax)
{
$::s_Ship_sOpaqueShipData .= 
sprintf("TaxApplies;%s;TaxIncluded;%d;TaxMultiplier;%0.06f;",
$$phashSelected{'TaxAppliesToShipping'},
$$phashSelected{'ShippingCostsIncludeTax'},
$dTaxInclusiveMultiplier);
}
if(defined $$phashSelected{OnlineError} &&
$$phashSelected{OnlineError} ne '')
{
$::s_Ship_sOpaqueShipData .=
sprintf('OnlineError;%s;', $$phashSelected{OnlineError});
}
my $sOptimalWeight = $phashSelected->{'OptimalWeight'};
if($sOptimalWeight ne '' &&
$sOptimalWeight > 0)
{
$::s_Ship_sOpaqueShipData .=
sprintf('OptimalWeight;%s;', $sOptimalWeight);
}
$::s_Ship_nShipCharges = $$phashSelected{Cost};
if ($bPricesIncludesTax)
{
$::s_Ship_bTaxAppliesToShipping = $$phashSelected{TaxAppliesToShipping};
}
else
{
$::s_Ship_bTaxAppliesToShipping = $::TRUE;
}
my $sClassID = $$phashSelected{ShippingClass};
my $parrShipSeparatePackages = $phashSelected->{'ShipSeparatePackages'};
my $parrMixedPackages = $phashSelected->{'MixedPackages'};
if(defined $parrShipSeparatePackages &&
defined $parrMixedPackages)
{
my $phashWeightToCost =
(defined $::s_hashClassToWeightCost{$sClassID}) ?
$::s_hashClassToWeightCost{$sClassID} :
undef;
$::s_Ship_sSeparatePackageDetails = '';
$::s_Ship_sMixedPackageDetails = '';
my $parrPackage;
foreach $parrPackage (@$parrShipSeparatePackages)
{
my $sUnitWeight = ($sClassID =~ /^1_/) ?
sprintf('%0.03f', int($$parrPackage[2] + 0.9999)) :
sprintf('%0.03f', $$parrPackage[2]);
my $nUnitCost =
(defined $phashWeightToCost) ?
$$phashWeightToCost{$sUnitWeight} :
0;
$::s_Ship_sSeparatePackageDetails .=
sprintf("%s\t%d\t%0.03f\t%d\n",
$$parrPackage[0], $$parrPackage[1], $$parrPackage[2], $nUnitCost);
}
my $parrSummary =
(@$parrMixedPackages > 0) ?
$$parrMixedPackages[-1] :
undef;
foreach $parrPackage (@$parrMixedPackages)
{
my $sUnitWeight = ($sClassID =~ /^1_/) ?
sprintf('%0.03f', int($$parrPackage[2] + 0.9999)) :
sprintf('%0.03f', $$parrPackage[2]);
my $nUnitCost =
(defined $phashWeightToCost && $parrSummary == $parrPackage) ?
$$phashWeightToCost{$sUnitWeight} :
0;
$::s_Ship_sMixedPackageDetails .=
sprintf("%s\t%d\t%0.03f\t%d\n",
$$parrPackage[0], $$parrPackage[1], $$parrPackage[2], $nUnitCost);
}
}
}
else
{
$::s_Ship_sOpaqueShipData = '';
$::s_Ship_nShipCharges = 0;
$::s_Ship_sSSPOpaqueShipData = '';
}
}
sub ClearUnusedSSPShippingEntries
{
if (CalculateQuantity() == 0)
{
my $sShipKey;
foreach $sShipKey (keys %::g_ShipInfo)
{
if($sShipKey =~ /^\d+_/)
{
delete $::g_ShipInfo{$sShipKey};
}
}
return;
}
}
sub GetUPSRates
{
my @arrShippingHashes;
my (%hashValidClasses, %hashClassToTotal, $sClassID);
my $sShipKey;
foreach $sShipKey (keys %::g_ShipInfo)
{
if($sShipKey =~ /^1_/)
{
delete $::g_ShipInfo{$sShipKey};
}
}
my $pSSPProvider = GetUPSSetup();
my ($nReturnCode, $sError, $sServiceLevelCode, $sRateChart,
$sShipperPostalCode, $sShipperCountry, $sConsigneePostalCode, $sConsigneeCountry,
$nResidential, $sPackagingType) =
GetShipmentDetails();
if($nReturnCode != $::SUCCESS)
{
return($nReturnCode, $sError);
}
my $sRSSRequestDataFormat;
$sRSSRequestDataFormat = $::XML_HEADER;
$sRSSRequestDataFormat .= GetUPSAccessRequestNode($pSSPProvider);
$sRSSRequestDataFormat .= $::XML_HEADER;
$sRSSRequestDataFormat .= "<RatingServiceSelectionRequest xml:lang=\"en-US\">";
$sRSSRequestDataFormat .= GetUPSRequestNode('Rate', 'Shop');
$sRSSRequestDataFormat .= "<PickupType>";
$sRSSRequestDataFormat .= "  <Code>$sRateChart</Code>";
$sRSSRequestDataFormat .= "</PickupType>";
$sRSSRequestDataFormat .= "<Shipment>";
$sRSSRequestDataFormat .= "  <Shipper>";
$sRSSRequestDataFormat .= "    <Address>";
$sRSSRequestDataFormat .= "      <PostalCode>$sShipperPostalCode</PostalCode>";
$sRSSRequestDataFormat .= "      <CountryCode>$sShipperCountry</CountryCode>";
$sRSSRequestDataFormat .= "    </Address>";
$sRSSRequestDataFormat .= "  </Shipper>";
$sRSSRequestDataFormat .= "  <ShipTo>";
$sRSSRequestDataFormat .= "    <Address>";
$sRSSRequestDataFormat .= "      <PostalCode>$sConsigneePostalCode</PostalCode>";
$sRSSRequestDataFormat .= "      <CountryCode>$sConsigneeCountry</CountryCode>";
$sRSSRequestDataFormat .= ($nResidential == 1) ? '<ResidentialAddress/>' : '';
$sRSSRequestDataFormat .= "    </Address>";
$sRSSRequestDataFormat .= "  </ShipTo>";
$sRSSRequestDataFormat .= "  <Service>";
$sRSSRequestDataFormat .= "    <Code>$sServiceLevelCode</Code>";
$sRSSRequestDataFormat .= "  </Service>";
$sRSSRequestDataFormat .= "  <Package>";
$sRSSRequestDataFormat .= "    <PackagingType>";
$sRSSRequestDataFormat .= "      <Code>$sPackagingType</Code>";
$sRSSRequestDataFormat .= "    </PackagingType>";
$sRSSRequestDataFormat .= "    <PackageWeight>";
$sRSSRequestDataFormat .= "      <Weight>%d</Weight>";
$sRSSRequestDataFormat .= "    </PackageWeight>";
$sRSSRequestDataFormat .= "  </Package>";
$sRSSRequestDataFormat .= "		<ShipmentServiceOptions/>";
$sRSSRequestDataFormat .= "</Shipment>";
$sRSSRequestDataFormat .= "</RatingServiceSelectionRequest>";
my ($phashWeightToQuantity, $parrSortedWeightKeys, $sWeightList, 
$parrShipSeparatePackages, $parrMixedPackages, $sOptimalWeight)
= DivideIntoPackages($c_nWeight, undef, $::TRUE);
my $nWeight;
foreach $nWeight (@$parrSortedWeightKeys)
{
if ($nWeight == 0)
{
next;
}
my $sRSSRequestData = sprintf($sRSSRequestDataFormat, $nWeight);
my $parrShippingHashes;
my $pXmlRoot;
($nReturnCode, $sError, $pXmlRoot) =
GetUPSPackageShipping($sRSSRequestData);
if($nReturnCode == $::SUCCESS)
{
my $pXmlRatedShipments = $pXmlRoot->GetChildNodes($::UPS_XML_RATED_SHIPMENT);
my $pXmlRatedShipment;
foreach $pXmlRatedShipment (@{$pXmlRatedShipments})
{
my $sServiceCode = $pXmlRatedShipment->GetChildNode($::UPS_XML_SERVICE)->GetChildNode($::UPS_XML_SERVICE_CODE)->GetNodeValue();
my $sClassID = "1_$sServiceCode";
if(defined $$pSSPProvider{ServiceLevelCode}{$sServiceCode})
{
my $pXmlTotalCharges = $pXmlRatedShipment->GetChildNode($::UPS_XML_TOTAL_CHARGES);
my $sCurrencyCode = $pXmlTotalCharges->GetChildNode($::UPS_XML_CURRENCY_CODE)->GetNodeValue();
my $sMonetaryValue = $pXmlTotalCharges->GetChildNode($::UPS_XML_MONETARY_VALUE)->GetNodeValue();
my $nIntegralCost = int($sMonetaryValue * 100 + 0.999);
$hashClassToTotal{$sClassID} +=
$$phashWeightToQuantity{$nWeight} * $nIntegralCost;
if(!defined $hashValidClasses{$sClassID})
{
$hashValidClasses{$sClassID} = {
'ShippingLabel'	=> GetUPSServiceName($sServiceCode),
'ShippingClass'	=> $sClassID,
'ShippingZone'		=> -1,
'ShipSeparatePackages'	=> $parrShipSeparatePackages, 
'MixedPackages'			=> $parrMixedPackages, 
'OptimalWeight'			=> $sOptimalWeight,
'GFSCarrierAndService'	=> '',
'InternationalShipping'	=> $::FALSE
};
}
$::s_hashClassToWeightCost{$sClassID}{sprintf('%0.03f', $nWeight)} = $nIntegralCost;
}
}
}
elsif ($nReturnCode == $::FAILURE)
{
return(HandleUPSOnlineError($sError, $parrSortedWeightKeys, $phashWeightToQuantity, $sWeightList));
}
else
{
@arrShippingHashes = ();
return($::SUCCESS, '', \@arrShippingHashes, $::UPS_CLASSES_NOT_USED);
}
}
my $nRatingType = $::UPS_CLASSES_NOT_USED;
foreach $sClassID (keys %hashValidClasses)
{
my $phashShipping = $hashValidClasses{$sClassID};
$$phashShipping{BasisTotal} = 0;
$$phashShipping{Cost} = $hashClassToTotal{$sClassID} + $::dShippingSupplements;
push @arrShippingHashes, $phashShipping;
my $dUPSCost = $hashClassToTotal{$sClassID} / 100;
$::g_ShipInfo{$sClassID} = "UPSOnLine%1.2\%0000%0000Success%4%$sServiceLevelCode%$sShipperPostalCode%US%$sConsigneePostalCode%$sConsigneeCountry%000%1%$dUPSCost%0.00%$dUPSCost%-1";
$nRatingType = $::UPS_CLASSES_USED;
}
return($::SUCCESS, '', \@arrShippingHashes, $nRatingType);
}
sub GetUPSPackageShipping
{
my ($sRequestData, $nWeight) = @_;
my (@arrShippingHashes);
my $nRetries = 2;
return(UPS_SendAndReceive('/ups.app/xml/Rate', $sRequestData, $nRetries, 2253));
}
sub HandleUPSOnlineError
{
my ($sResponse, $parrSortedWeightKeys, $phashWeightToQuantity, $sWeightList) = @_;
my ( $sRateChart, $sShipperPostalCode, $sConsigneePostalCode, $sConsigneeCountry, $sPackagingType);
my (@arrShippingHashes);
my $pSSPProvider = GetUPSSetup();
my $nRatingType = $::UPS_CLASSES_NOT_USED;
$sRateChart = $$pSSPProvider{'RateChart'};
$sShipperPostalCode = $$pSSPProvider{'ShipperPostalCode'};
$sPackagingType = $$pSSPProvider{'PackagingType'};
$sConsigneePostalCode = $::g_ShipContact{'POSTALCODE'};
$sConsigneeCountry = ActinicLocations::GetISODeliveryCountryCode();
my $sErrorText = ACTINIC::GetPhrase(-1, 2292, $sResponse);
ACTINIC::RecordErrors($sErrorText, ACTINIC::GetPath());
if($$::g_pSSPSetupBlob{NotifyMerchantOfFailure})
{
my ($Status, $Message) = ACTINIC::SendMail($::g_sSmtpServer,
$$::g_pSSPSetupBlob{FailureEmailAddress},
ACTINIC::GetPhrase(-1, 2291),
$sErrorText,
$$::g_pSSPSetupBlob{FailureEmailAddress});
if ($Status != $::SUCCESS)
{
ACTINIC::RecordErrors("$sErrorText:\n sending$Message" , ACTINIC::GetPath());
}
}
if($$::g_pSSPSetupBlob{ConfirmShippingByEmail})
{
$::g_ShipInfo{"1_$sCONFIRM_BY_EMAIL"} = "UPSOnLine%1.2\%0000%0000Success%4%000%$sShipperPostalCode%US%$sConsigneePostalCode%$sConsigneeCountry%000%1%0.00%0.00%0.00%-1";
$sOnlineError = 'Email';
push @arrShippingHashes, {
'ShippingLabel'	=> GetUPSServiceName($sCONFIRM_BY_EMAIL),
'ShippingClass'	=> "1_$sCONFIRM_BY_EMAIL",
'ShippingZone'		=> -1,
'Cost'				=> 0,
'BasisTotal'		=> 0,
'OnlineError'		=> 'Email',
'GFSCarrierAndService' => '',
'InternationalShipping'	=> $::FALSE
};
}
elsif($$::g_pSSPSetupBlob{UseClassDefaultFormula})
{
$sOnlineError = 'BasePlusIncrement';
my @arrServiceLevelCodes;
if($::s_sDeliveryCountryCode eq 'CA')
{
push @arrServiceLevelCodes, '11', '07', '08';
}
elsif($::s_sDeliveryCountryCode eq 'US')
{
push @arrServiceLevelCodes, '14', '01', '13', '59', '02', '12', '03';
}
else
{
push @arrServiceLevelCodes, '07', '08';
}
my $sServiceLevelCode;
foreach $sServiceLevelCode (@arrServiceLevelCodes)
{
if(defined $$pSSPProvider{'ServiceLevelCode'}{$sServiceLevelCode})
{
my ($nWeight, $nTotalCost);
foreach $nWeight (@$parrSortedWeightKeys)
{
my $nIncrementalUnits =
int(($nWeight / $$pSSPProvider{'ServiceLevelCode'}{$sServiceLevelCode}[3]) + 0.999);
my $nIntegralCost = $$pSSPProvider{'ServiceLevelCode'}{$sServiceLevelCode}[1] +
($$pSSPProvider{'ServiceLevelCode'}{$sServiceLevelCode}[2] * $nIncrementalUnits);
$nTotalCost += $$phashWeightToQuantity{$nWeight} * $nIntegralCost;
my $dUPSCost = $nIntegralCost / 100;
$::g_ShipInfo{"1_$sServiceLevelCode" . "_$nWeight"} = "UPSOnLine%1.2\%0000%0000Success%4%$sServiceLevelCode%$sShipperPostalCode%US%$sConsigneePostalCode%$sConsigneeCountry%000%1%$dUPSCost%0.00%$dUPSCost%-1";
$::s_hashClassToWeightCost{"1_$sServiceLevelCode"}{sprintf('%0.03f', $nWeight)} = $nIntegralCost;
}
my $dUPSCost = $nTotalCost / 100;
$::g_ShipInfo{"1_$sServiceLevelCode"} = "UPSOnLine%1.2\%0000%0000Success%4%$sServiceLevelCode%$sShipperPostalCode%US%$sConsigneePostalCode%$sConsigneeCountry%000%1%$dUPSCost%0.00%$dUPSCost%-1";
push @arrShippingHashes, {
'ShippingLabel'	=> GetUPSServiceName($sServiceLevelCode),
'ShippingClass'	=> "1_$sServiceLevelCode",
'ShippingZone'		=> -1,
'Cost'				=> $nTotalCost,
'BasisTotal'		=> 0,
'OnlineError'		=> 'BasePlusIncrement',
'GFSCarrierAndService' => '',
'InternationalShipping'	=> $::FALSE
};
$nRatingType = $::UPS_BASEPLUSPER_CLASSES_USED;
}
}
}
return($::SUCCESS, '', \@arrShippingHashes,  $nRatingType);
}
sub GetShipmentDetails
{
my ($nReturnCode, $sError, $sServiceLevelCode, $sRateChart,
$sShipperPostalCode, $sShipperCountry, $sConsigneePostalCode, $sConsigneeCountry,
$nResidential, $sPackagingType);
my $pSSPProvider = GetUPSSetup();
$sRateChart = $$pSSPProvider{'RateChart'};
$sShipperPostalCode = $$pSSPProvider{'ShipperPostalCode'};
$sShipperCountry = $$pSSPProvider{'ShipperCountry'};
$sPackagingType = $$pSSPProvider{'PackagingType'};
$sConsigneePostalCode = $::g_ShipContact{'POSTALCODE'};
$sConsigneeCountry = ActinicLocations::GetISODeliveryCountryCode();
if($sConsigneeCountry eq 'CA')
{
if($sConsigneePostalCode !~ /^(\w\d\w)\s{0,1}(\d\w\d)$/)
{
return($::FAILURE, ACTINIC::GetPhrase(-1, 2149));
}
$sConsigneePostalCode =~ s/\s*//g;
$sServiceLevelCode = '11';
}
elsif($sConsigneeCountry eq 'US')
{
my ($nStatus, $sError);
($nStatus, $sError, $sConsigneePostalCode) = GetUS5DigitZipCode($sConsigneePostalCode);
if($nStatus == $::FAILURE)
{
return($nStatus, $sError);
}
if($sRateChart eq '07' or $sRateChart eq '19')
{
$sServiceLevelCode = '02';
}
else
{
$sServiceLevelCode = '03';
}
}
else
{
$sServiceLevelCode = '07';
}
$nResidential = $::g_ShipContact{'RESIDENTIAL'} ne '' ? 1 : 0;
return($::SUCCESS, '', $sServiceLevelCode, $sRateChart,
$sShipperPostalCode, $sShipperCountry, $sConsigneePostalCode, $sConsigneeCountry,
$nResidential, $sPackagingType);
}
sub GetUPSSetup
{
return($$::g_pSSPSetupBlob{1});
}
sub GetUPSServiceName
{
my ($sServiceLevelCode) = @_;
if($sServiceLevelCode eq $sCONFIRM_BY_EMAIL)
{
return(ACTINIC::GetPhrase(-1, 2100));
}
return($$::g_pSSPSetupBlob{1}{ServiceLevelCode}{$sServiceLevelCode}[0]);
}
sub DoUPSAddressValidation
{
my ($sConsigneeCountry, $sConsigneeState, $sConsigneeCity, $sConsigneePostalCode) = @_;
if($sConsigneeCountry ne 'US' ||
$sConsigneeCountry eq '' ||
$sConsigneeCountry eq '---')
{
return($::SUCCESS, '');
}
my $pSSPProvider = GetUPSSetup();
my @arrStates = (
'AK',
'AL',
'AR',
'AZ',
'CA',
'CO',
'CT',
'DC',
'DE',
'FL',
'GA',
'HI',
'IA',
'ID',
'IL',
'IN',
'KS',
'KY',
'LA',
'MA',
'MD',
'ME',
'MI',
'MN',
'MO',
'MS',
'MT',
'NC',
'ND',
'NE',
'NH',
'NJ',
'NM',
'NV',
'NY',
'OH',
'OK',
'OR',
'PA',
'RI',
'SC',
'SD',
'TN',
'TX',
'UT',
'VA',
'VT',
'WA',
'WI',
'WV',
'WY',
);
my $sStatesString = join('|', @arrStates);
if($sStatesString !~ $sConsigneeState)
{
{
if(defined $$pSSPProvider{'RSSEnabled'} && $$pSSPProvider{'RSSEnabled'})
{
my $sErrorText = sprintf(ACTINIC::GetPhrase(-1, 2099),
ACTINIC::GetCountryName("US.$sConsigneeState"));
return($::FAILURE, $sErrorText);
}
return($::SUCCESS, '');
}
}
my ($nStatus, $sError);
($nStatus, $sError, $sConsigneePostalCode) = GetUS5DigitZipCode($sConsigneePostalCode);
if($nStatus == $::FAILURE)
{
return($::FAILURE, $sError);
}
my (@Response);
my $pSSPProvider = GetUPSSetup();
my $sAVRequestData = '';
$sAVRequestData = $::XML_HEADER;
$sAVRequestData .= GetUPSAccessRequestNode($pSSPProvider);
$sAVRequestData .=	$::XML_HEADER;
$sAVRequestData .=	"<AddressValidationRequest xml:lang=\"en-US\">";
$sAVRequestData .=	GetUPSRequestNode('AV');
$sAVRequestData .=	"<Address>";
if($sConsigneeState ne '')
{
$sConsigneeState =~ s/^\w\w\.//;
$sAVRequestData .=	"<StateProvinceCode>$sConsigneeState</StateProvinceCode>";
}
if($sConsigneeCity ne '')
{
$sAVRequestData .=	"<City>$sConsigneeCity</City>";
}
if($sConsigneePostalCode ne '')
{
$sAVRequestData .=	"<PostalCode>$sConsigneePostalCode</PostalCode>";
}
$sAVRequestData .=	"</Address>";
$sAVRequestData .=	"</AddressValidationRequest>";
my $nRetries = 2;
my ($Result, $sMessage, $pXmlRoot) = UPS_SendAndReceive('/ups.app/xml/AV', $sAVRequestData, $nRetries, 2305);
if ($Result != $::SUCCESS)
{
return ($Result, $sMessage);
}
my $bValidationFailed = $::TRUE;
my $raAddressValidationResults = $pXmlRoot->GetChildNodes($::UPS_XML_ADDRESS_VALIDATION_RESULT);
my $pXmlAddressValidationResult;
foreach $pXmlAddressValidationResult (@{$raAddressValidationResults})
{
my $sRank = $pXmlAddressValidationResult->GetChildNode($::UPS_XML_RANK)->GetNodeValue();
my $sQuality = $pXmlAddressValidationResult->GetChildNode($::UPS_XML_QUALITY)->GetNodeValue();
my $sState = $pXmlAddressValidationResult->GetChildNode($::UPS_XML_ADDRESS)->GetChildNode($::UPS_XML_STATE_PROVINCE_CODE)->GetNodeValue();
my $sCity = $pXmlAddressValidationResult->GetChildNode($::UPS_XML_ADDRESS)->GetChildNode($::UPS_XML_CITY)->GetNodeValue();
my $sPostalCodeLow = $pXmlAddressValidationResult->GetChildNode($::UPS_XML_POSTAL_CODE_LOW_END)->GetNodeValue();
my $sPostalCodeHigh = $pXmlAddressValidationResult->GetChildNode($::UPS_XML_POSTAL_CODE_HIGH_END)->GetNodeValue();
if($sState eq $sConsigneeState &&
( (lc($sCity) eq lc($sConsigneeCity) || $sConsigneeCity eq '')) &&
($sConsigneePostalCode eq $sPostalCodeLow ||
($sConsigneePostalCode gt $sPostalCodeLow &&
$sPostalCodeHigh ne '' &&
$sConsigneePostalCode le $sPostalCodeHigh)))
{
$bValidationFailed = $::FALSE;
}
}
if($bValidationFailed)
{
my $sErrorText = ACTINIC::GetPhrase(-1, 2305, ACTINIC::GetPhrase(-1, 2072));
return($::BADDATA, $sErrorText);
}
return($::SUCCESS, '');
}
sub UPS_SendAndReceive
{
my ($sPath, $sRequestData, $nRetries, $nErrorTitlePhrase) = @_;
my (@Response, $sHTTPResponse, $sHTTPHeader, $sHTTPContent, $phashHeader);
while($nRetries &&
$bUPS_Available)
{
@Response = ACTINIC::HTTPS_SendAndReceive('onlinetools.ups.com', 443,
$sPath, $sRequestData, 'POST', $::FALSE, $ssl_socket);
if($Response[0] != $::SUCCESS ||
$Response[2] eq '')
{
$nRetries--;
}
else
{
$sHTTPResponse = $Response[2];
$ssl_socket = $Response[3];
last;
}
}
unless ($sHTTPResponse)
{
$bUPS_Available = $::FALSE;
return($::FAILURE, $Response[1]);
}
@Response = ACTINIC::HTTP_SplitHeaderAndContent($sHTTPResponse);
if($Response[0] != $::TRUE)
{
return($::FAILURE, $Response[1]);
}
$sHTTPHeader = $Response[3];
$sHTTPContent = $Response[4];
$phashHeader = $Response[5];
my $sContentType = $$phashHeader{'Content-Type'};
unless($sContentType)
{
return($::FAILURE, ACTINIC::GetPhrase(-1, 2293));
}
if($sContentType =~ /application\/xml/)
{
my $pParser = new PXML();
my ($sParsedText, $pXmlRoot) = $pParser->Parse($sHTTPContent);
$pXmlRoot = $pXmlRoot->[0];
my ($Result, $sMessage) = ParseUPSResponseNode($pXmlRoot->GetChildNode($::UPS_XML_RESPONSE), $nErrorTitlePhrase);
return($Result, $sMessage, $pXmlRoot);
}
return($::FAILURE, ACTINIC::GetPhrase(-1, 2293));
}
sub GetUPSAccessRequestNode
{
my ($pSSPProvider) = @_;
my $sAccessKey = ACTINIC::DecodeXOREncryption($$pSSPProvider{AccessKey}, $::UPS_ENCRYPT_PASSWORD);
my $sUserName = ACTINIC::DecodeXOREncryption($$pSSPProvider{UserName}, $::UPS_ENCRYPT_PASSWORD);
my $sPassword = ACTINIC::DecodeXOREncryption($$pSSPProvider{Password}, $::UPS_ENCRYPT_PASSWORD);
my $sAccessRequestNode = '';
$sAccessRequestNode .=	"<AccessRequest>";
$sAccessRequestNode .=	"<AccessLicenseNumber>$sAccessKey</AccessLicenseNumber>";
$sAccessRequestNode .=	"<UserId>$sUserName</UserId>";
$sAccessRequestNode .=	"<Password>$sPassword</Password>";
$sAccessRequestNode .=	"</AccessRequest>";
return $sAccessRequestNode;
}
sub GetUPSRequestNode
{
my ($sAction, $sOption) = @_;
my $sRequestNode = '';
$sRequestNode .= "<Request>";
$sRequestNode .= "<TransactionReference>";
$sRequestNode .= "<XpciVersion>$::UPS_XPCI_VERSION</XpciVersion>";
$sRequestNode .= "</TransactionReference>";
$sRequestNode .= "<RequestAction>$sAction</RequestAction>";
if (defined $sOption)
{
$sRequestNode .= "<RequestOption>$sOption</RequestOption>";
}
$sRequestNode .= "</Request>";
return $sRequestNode;
}
sub ParseUPSResponseNode
{
my ($pXmlResponse, $nErrorTitlePhrase) = @_;
my $pXmlStatusCode = $pXmlResponse->GetChildNode($::UPS_XML_RESPONSE_STATUS_CODE);
if (!defined($pXmlStatusCode))
{
return ($::FAILURE, ACTINIC::GetPhrase(-1, $nErrorTitlePhrase, ACTINIC::GetPhrase(-1, 2294)));
}
if ($pXmlStatusCode->GetNodeValue() eq $::UPS_SUCCESSFUL)
{
return($::SUCCESS, '')
}
my $paXmlErrors = $pXmlResponse->GetChildNodes($::UPS_XML_ERROR);
my $pXmlError;
foreach $pXmlError (@$paXmlErrors)
{
my $pXmlErrorSeverity = $pXmlError->GetChildNode($::UPS_XML_ERROR_SEVERITY);
if (!defined($pXmlErrorSeverity))
{
return ($::FAILURE, ACTINIC::GetPhrase(-1, $nErrorTitlePhrase, ACTINIC::GetPhrase(-1, 2294)));
}
my $sSeverity = $pXmlErrorSeverity->GetNodeValue();
my $pXmlErrorDescription = $pXmlError->GetChildNode($::UPS_XML_ERROR_DESCRIPTION);
if (!defined($pXmlErrorDescription))
{
return ($::FAILURE, ACTINIC::GetPhrase(-1, $nErrorTitlePhrase, ACTINIC::GetPhrase(-1, 2294)));
}
my $sErrorDescription = $pXmlErrorDescription->GetNodeValue();
if ($sSeverity eq $::UPS_ERROR_SEVERITY_HARD_ERROR)
{
return ($::BADDATA, ACTINIC::GetPhrase(-1, $nErrorTitlePhrase, $sErrorDescription));
}
elsif ($sSeverity eq $::UPS_ERROR_SEVERITY_TRANSIENT_ERROR) # temporary server problem - failure and not bad data
{
return ($::FAILURE, ACTINIC::GetPhrase(-1, $nErrorTitlePhrase, $sErrorDescription));
}
elsif ($sSeverity eq $::UPS_ERROR_SEVERITY_WARNING)
{
}
else
{
return ($::FAILURE, ACTINIC::GetPhrase(-1, $nErrorTitlePhrase, ACTINIC::GetPhrase(-1, 2294)));
}
}
return($::SUCCESS, '')
}
sub GetGeoSession
{		
my $SSLConnection =  SSLConnection->new($::DPD_HOST, $::DPD_SSL_PORT, $::DPD_LOGIN_URL);
$SSLConnection->SetRequestMethod("POST");	
$SSLConnection->SetHeaderValue("Content-Type", "application/json");
$SSLConnection->SetHeaderValue("Accept", "application/json");
$SSLConnection->SetHeaderValue("Authorization",  "Basic $::sAuthorization");
$SSLConnection->SetHeaderValue("GEOClient",  "account/$::sAccount");
$SSLConnection->SetRequestTimeout(5);
$SSLConnection->SendRequest("");
if ($SSLConnection->GetResponseCode() != 200)
{
my $nRetCode = $SSLConnection->GetResponseCode();
if (401 == $nRetCode)
{
return ($::FAILURE, "DPD authorization error: $nRetCode", "");
}			
else
{
return ($::FAILURE, "DPD integration error: $nRetCode", "");
}		
}	
if ($SSLConnection->GetConnectStatus() == $::FALSE)
{
return ($::FAILURE, sprintf("%s (%s) %s",
"DPD connection failed:",
$SSLConnection->GetResponseCode(),
$SSLConnection->GetConnectErrorMessage(), ""));
}
my $sGeoSession = "";	
if (ref($SSLConnection->GetResponseJSON()) eq 'HASH')
{		
my ($sError, $sType) = GetServerError($SSLConnection->GetResponseJSON());
if ($sError ne "")
{
return ($::FAILURE, "DPD returned an error. $sError", "", "", $sType);
}		
$sGeoSession = $SSLConnection->GetResponseJSON()->{'data'}->{'geoSession'};	
if ($sGeoSession eq "")
{
$sGeoSession = $SSLConnection->GetResponseJSON()->{'data'}->{'dpdsession'};	
}
}
else
{
return ($::FAILURE, "Response format error for the DPD request", "");
}
return ($::SUCCESS, "", $sGeoSession);
}
sub GetAvailableDPDServices
{
my ($nParcels, $dTotalWeight) = @_;
my @Response = GetGeoSession();
if ($Response[0] != $::SUCCESS)
{
return ($::FAILURE, $Response[1], "");
}	
$::sGeoSessionID = $Response[2];
my $sCountryCode = $::g_LocationInfo{DELIVERY_COUNTRY_CODE};
if ($sCountryCode eq 'UK')
{
$sCountryCode =~ s/^UK$/GB/;
}
my $sGetParams = "deliveryDirection=1";
$sGetParams .= "&numberOfParcels=$nParcels";
$sGetParams .= "&shipmentType=0";
$sGetParams .= "&totalWeight=$dTotalWeight";
$sGetParams .= "&deliveryDetails.address.countryCode=$sCountryCode";
$sGetParams .= "&deliveryDetails.address.countryName=" . ACTINIC::EncodeText2($::g_ShipContact{'COUNTRY'}, $::FALSE);
$sGetParams .= "&deliveryDetails.address.locality=" . ACTINIC::EncodeText2($::g_ShipContact{'ADDRESS2'}, $::FALSE);
$sGetParams .= "&deliveryDetails.address.organisation=" . ACTINIC::EncodeText2($::g_ShipContact{'COMPANY'}, $::FALSE);
$sGetParams .= "&deliveryDetails.address.postcode=" . ACTINIC::EncodeText2($::g_ShipContact{'POSTALCODE'}, $::FALSE);
$sGetParams .= "&deliveryDetails.address.property=";
$sGetParams .= "&deliveryDetails.address.street=" . ACTINIC::EncodeText2($::g_ShipContact{'ADDRESS1'}, $::FALSE);
$sGetParams .= "&deliveryDetails.address.town=" . ACTINIC::EncodeText2($::g_ShipContact{'ADDRESS3'}, $::FALSE);
$sGetParams .= "&deliveryDetails.address.county=" . ACTINIC::EncodeText2($::g_ShipContact{'ADDRESS4'}, $::FALSE);
$sCountryCode = $$::g_pSetupBlob{'MERCHANT_COUNTRY_CODE'};
if ($sCountryCode eq 'UK')
{
$sCountryCode =~ s/^UK$/GB/;
}
$sGetParams .= "&collectionDetails.address.countryCode=$sCountryCode";
$sGetParams .= "&collectionDetails.address.countryName=" . ACTINIC::EncodeText2($$::g_pSetupBlob{'COUNTRY'}, $::FALSE);
$sGetParams .= "&collectionDetails.address.locality=" . ACTINIC::EncodeText2($$::g_pSetupBlob{'ADDRESS_2'}, $::FALSE);
$sGetParams .= "&collectionDetails.address.organisation=" . ACTINIC::EncodeText2($$::g_pSetupBlob{'COMPANY_NAME'}, $::FALSE);
$sGetParams .= "&collectionDetails.address.postcode=" . ACTINIC::EncodeText2($$::g_pSetupBlob{'POSTAL_CODE'}, $::FALSE);
$sGetParams .= "&collectionDetails.address.property=";
$sGetParams .= "&collectionDetails.address.street=" . ACTINIC::EncodeText2($$::g_pSetupBlob{'ADDRESS_1'}, $::FALSE);
$sGetParams .= "&collectionDetails.address.town=" . ACTINIC::EncodeText2($$::g_pSetupBlob{'ADDRESS_3'}, $::FALSE);
$sGetParams .= "&collectionDetails.address.county=" . ACTINIC::EncodeText2($$::g_pSetupBlob{'ADDRESS_4'}, $::FALSE);
my $SSLConnection =  SSLConnection->new($::DPD_HOST, $::DPD_SSL_PORT, $::DPD_GET_SERVICES_URL . $sGetParams);
$SSLConnection->SetRequestMethod("GET");	
$SSLConnection->SetHeaderValue("Content-Type", "application/json");
$SSLConnection->SetHeaderValue("Accept", "application/json");	
$SSLConnection->SetHeaderValue("GEOClient",  "account/$::sAccount");
$SSLConnection->SetHeaderValue("GeoSession",  "$Response[2]");
$SSLConnection->SetRequestTimeout(5);
$SSLConnection->SendRequest("");
if ($SSLConnection->GetResponseCode() != 200)
{
my $RetCode = $SSLConnection->GetResponseCode();		
my ($sMsg, $sType) = GetServerError($SSLConnection->GetResponseJSON());
return ($::FAILURE, "DPD integration error: $RetCode." . $sMsg, "");
}
if ($SSLConnection->GetConnectStatus() == $::FALSE)
{
return ($::FAILURE, sprintf("%s (%s) %s",
"DPD connection failed:",
$SSLConnection->GetResponseCode(),
$SSLConnection->GetConnectErrorMessage(), ""));
}
my (%hashServiceCodes, %hashServices);
if (ref($SSLConnection->GetResponseJSON()) eq 'HASH')
{
my ($sError, $sType) = GetServerError($SSLConnection->GetResponseJSON());
if ($sError ne "")
{			
return ($::FAILURE, "DPD returned an error. Error message:$sError", "", "", $sType);
}
my $ServiceItem;
if (ref($SSLConnection->GetResponseJSON()->{'data'}) eq 'ARRAY')
{
foreach $ServiceItem(@{$SSLConnection->GetResponseJSON()->{'data'}})
{
if (!exists $ServiceItem->{'network'})
{
return ($::FAILURE, "Response format error for the DPD request", "");
}
my ($sDesc, $sCode);
$sDesc = $ServiceItem->{'network'}->{'networkDescription'};
$sCode = $ServiceItem->{'network'}->{'networkCode'};
$hashServices{$sDesc} = $sCode;
$hashServiceCodes{$sCode} = $sDesc;				
}			
}
}
else
{
return ($::FAILURE, "Response format error for the DPD request", "");
}
return ($::SUCCESS, "", \%hashServices, \%hashServiceCodes, "");
}
sub GetServerError
{
my ($hashErrors) = shift;
my ($sErrorMessage, $sType);
if ((ref($hashErrors) eq 'HASH') &&
(defined $hashErrors->{'error'}) &&		 
(ref($hashErrors->{'error'}) eq 'HASH'))
{		
my $sError = sprintf("%s error, %s (code %s). Field: %s.",
$hashErrors->{'error'}->{'errorType'},
$hashErrors->{'error'}->{'errorMessage'},
$hashErrors->{'error'}->{'errorCode'},
$hashErrors->{'error'}->{'obj'});
$sErrorMessage .= $sError;
$sType = $hashErrors->{'error'}->{'errorType'};
}
return ($sErrorMessage, $sType);
}
sub GetPickupLocations
{	
my ($sGeoSession, $sPostalCode, $sCountryCode) = @_;	
my $sGetParams = "filter=nearAddress";
if ($sCountryCode eq 'UK')
{
$sCountryCode =~ s/^UK$/GB/;
}	
$sGetParams .= "&countryCode=$sCountryCode";
$sGetParams .= "&searchPageSize=$::DPD_MAX_RESULTS";
$sGetParams .= "&searchPage=1&searchCriteria=&maxDistance=10";
$sGetParams .= "&searchAddress=" . ACTINIC::EncodeText2($sPostalCode);
my $SSLConnection =  SSLConnection->new($::DPD_GROUP_HOST, $::DPD_SSL_PORT, $::DPD_GET_PICKUP_LOCATIONS . $sGetParams);
$SSLConnection->SetRequestMethod("GET");	
$SSLConnection->SetHeaderValue("Content-Type", "application/json");
$SSLConnection->SetHeaderValue("Accept", "application/json");	
$SSLConnection->SetHeaderValue("GEOClient",  "account/$::sAccount");
$SSLConnection->SetHeaderValue("GeoSession",  "$sGeoSession");
$SSLConnection->SetRequestTimeout(5);
$SSLConnection->SendRequest("");
if ($SSLConnection->GetResponseCode() != 200)
{
my $RetCode = $SSLConnection->GetResponseCode();
my ($sMsg, $sType) = GetServerError($SSLConnection->GetResponseJSON());
my $sError = "DPD integration error: $RetCode" . $sMsg;
ACTINIC::RecordErrors($sError, ACTINIC::GetPath());
return ($::FAILURE, $sError);
}
if ($SSLConnection->GetConnectStatus() == $::FALSE)
{
my $sError = sprintf("%s (%s) %s",
"DPD connection failed:",
$SSLConnection->GetResponseCode(),
$SSLConnection->GetConnectErrorMessage());
ACTINIC::RecordErrors($sError, ACTINIC::GetPath());
return ($::FAILURE, $sError);
}
my $sJsonText = "";
if (ref($SSLConnection->GetResponseJSON()) eq 'HASH')
{
$sJsonText = $SSLConnection->GetResponseContent();
}
else
{
return ($::FAILURE, "DPD response format error");
}	
return ($::SUCCESS, $sJsonText);
}
return ($::SUCCESS);
sub GetClassHash
{
my ($sClassID, $sClassTitle, $sAmount, $sDetail) = @_;
if (!defined $sDetail)
{
$sDetail = '';
}
my $hResult = {'identifier' => $sClassID,
'label' => $sClassTitle,
'detail' => $sDetail,
'amount' => ActinicOrder::FormatPriceWithoutSymbol($sAmount),
};
return ($hResult);
}