5614151; #--------------------------------------------------------------- my $pMessageList = [ 'The shipping price is formatted incorrectly. It should be formatted like %s.', 'The shipping price is too large. The price must be less than %s.', 'The shipping 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 catalog shipping database does not have any shipping options defined for this location. Please contact us directly with your order.', 'Free Shipping', 'Standard Shipping', 'Your order has exceeded the shipping tables defined by the supplier, therefore it is not possible to calculate the shipping 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.

Thank you.', 'Please enter a shipping cost.', 'Please select a state or province.', ]; my %ZoneTable = ( "UK" => { "UndefinedRegion" => [1, 10], }, "US" => { "UndefinedRegion" => [9], "US.AL" => [9], "US.AK" => [9], "US.AZ" => [9], "US.AR" => [9], "US.CA" => [9], "US.CO" => [9], "US.CT" => [9], "US.DE" => [9], "US.DC" => [9], "US.FL" => [9], "US.GA" => [9], "US.HI" => [9], "US.ID" => [9], "US.IL" => [9], "US.IN" => [9], "US.IA" => [9], "US.KS" => [9], "US.KY" => [9], "US.LA" => [9], "US.ME" => [9], "US.MD" => [9], "US.MA" => [9], "US.MI" => [9], "US.MN" => [9], "US.MS" => [9], "US.MO" => [9], "US.MT" => [9], "US.NE" => [9], "US.NV" => [9], "US.NH" => [9], "US.NJ" => [9], "US.NM" => [9], "US.NY" => [9], "US.NC" => [9], "US.ND" => [9], "US.OH" => [9], "US.OK" => [9], "US.OR" => [9], "US.PA" => [9], "US.RI" => [9], "US.SC" => [9], "US.SD" => [9], "US.TN" => [9], "US.TX" => [9], "US.UT" => [9], "US.VT" => [9], "US.VA" => [9], "US.WA" => [9], "US.WV" => [9], "US.WI" => [9], "US.WY" => [9], }, "CA" => { "UndefinedRegion" => [9], "CA.AB" => [9], "CA.BC" => [9], "CA.MB" => [9], "CA.NB" => [9], "CA.NF" => [9], "CA.NU" => [9], "CA.NT" => [9], "CA.NS" => [9], "CA.ON" => [9], "CA.PE" => [9], "CA.PQ" => [9], "CA.SK" => [9], "CA.YT" => [9], }, "AF" => { "UndefinedRegion" => [9], }, "AL" => { "UndefinedRegion" => [9], }, "DZ" => { "UndefinedRegion" => [9], }, "AD" => { "UndefinedRegion" => [9], }, "AO" => { "UndefinedRegion" => [9], }, "AI" => { "UndefinedRegion" => [9], }, "AQ" => { "UndefinedRegion" => [9], }, "AG" => { "UndefinedRegion" => [9], }, "AR" => { "UndefinedRegion" => [9], }, "AM" => { "UndefinedRegion" => [9], }, "AW" => { "UndefinedRegion" => [9], }, "AU" => { "UndefinedRegion" => [9], }, "AT" => { "UndefinedRegion" => [8], }, "AZ" => { "UndefinedRegion" => [9], }, "BS" => { "UndefinedRegion" => [9], }, "BH" => { "UndefinedRegion" => [9], }, "BD" => { "UndefinedRegion" => [9], }, "BB" => { "UndefinedRegion" => [9], }, "BY" => { "UndefinedRegion" => [9], }, "BE" => { "UndefinedRegion" => [8], }, "BZ" => { "UndefinedRegion" => [9], }, "BJ" => { "UndefinedRegion" => [9], }, "BM" => { "UndefinedRegion" => [9], }, "BT" => { "UndefinedRegion" => [9], }, "BO" => { "UndefinedRegion" => [9], }, "BA" => { "UndefinedRegion" => [9], }, "BW" => { "UndefinedRegion" => [9], }, "BV" => { "UndefinedRegion" => [9], }, "BR" => { "UndefinedRegion" => [9], }, "IO" => { "UndefinedRegion" => [9], }, "BN" => { "UndefinedRegion" => [9], }, "BG" => { "UndefinedRegion" => [9], }, "BF" => { "UndefinedRegion" => [9], }, "BU" => { "UndefinedRegion" => [9], }, "BI" => { "UndefinedRegion" => [9], }, "KH" => { "UndefinedRegion" => [9], }, "CM" => { "UndefinedRegion" => [9], }, "CV" => { "UndefinedRegion" => [9], }, "KY" => { "UndefinedRegion" => [9], }, "CF" => { "UndefinedRegion" => [9], }, "TD" => { "UndefinedRegion" => [9], }, "CL" => { "UndefinedRegion" => [9], }, "CN" => { "UndefinedRegion" => [9], }, "CX" => { "UndefinedRegion" => [9], }, "CC" => { "UndefinedRegion" => [9], }, "CO" => { "UndefinedRegion" => [9], }, "KM" => { "UndefinedRegion" => [9], }, "CG" => { "UndefinedRegion" => [9], }, "CK" => { "UndefinedRegion" => [9], }, "CR" => { "UndefinedRegion" => [9], }, "CI" => { "UndefinedRegion" => [9], }, "HR" => { "UndefinedRegion" => [9], }, "CU" => { "UndefinedRegion" => [9], }, "CY" => { "UndefinedRegion" => [9], }, "CZ" => { "UndefinedRegion" => [9], }, "DK" => { "UndefinedRegion" => [8], }, "DJ" => { "UndefinedRegion" => [9], }, "DM" => { "UndefinedRegion" => [9], }, "DO" => { "UndefinedRegion" => [9], }, "TL" => { "UndefinedRegion" => [9], }, "EC" => { "UndefinedRegion" => [9], }, "EG" => { "UndefinedRegion" => [9], }, "SV" => { "UndefinedRegion" => [9], }, "GQ" => { "UndefinedRegion" => [9], }, "ER" => { "UndefinedRegion" => [9], }, "EE" => { "UndefinedRegion" => [9], }, "ET" => { "UndefinedRegion" => [9], }, "FK" => { "UndefinedRegion" => [9], }, "FO" => { "UndefinedRegion" => [9], }, "FJ" => { "UndefinedRegion" => [9], }, "FI" => { "UndefinedRegion" => [8, 9], }, "FR" => { "UndefinedRegion" => [8], }, "GF" => { "UndefinedRegion" => [9], }, "PF" => { "UndefinedRegion" => [9], }, "TF" => { "UndefinedRegion" => [9], }, "GA" => { "UndefinedRegion" => [9], }, "GM" => { "UndefinedRegion" => [9], }, "GE" => { "UndefinedRegion" => [9], }, "DE" => { "UndefinedRegion" => [8], }, "GH" => { "UndefinedRegion" => [9], }, "GI" => { "UndefinedRegion" => [9], }, "GR" => { "UndefinedRegion" => [8], }, "GL" => { "UndefinedRegion" => [9], }, "GD" => { "UndefinedRegion" => [9], }, "GP" => { "UndefinedRegion" => [9], }, "GU" => { "UndefinedRegion" => [9], }, "GG" => { "UndefinedRegion" => [8], }, "GT" => { "UndefinedRegion" => [9], }, "GN" => { "UndefinedRegion" => [9], }, "GW" => { "UndefinedRegion" => [9], }, "GY" => { "UndefinedRegion" => [9], }, "HT" => { "UndefinedRegion" => [9], }, "HM" => { "UndefinedRegion" => [9], }, "HN" => { "UndefinedRegion" => [9], }, "HK" => { "UndefinedRegion" => [9], }, "HU" => { "UndefinedRegion" => [9], }, "IS" => { "UndefinedRegion" => [9], }, "IN" => { "UndefinedRegion" => [9], }, "ID" => { "UndefinedRegion" => [9], }, "IR" => { "UndefinedRegion" => [9], }, "IQ" => { "UndefinedRegion" => [9], }, "IE" => { "UndefinedRegion" => [9], }, "IL" => { "UndefinedRegion" => [9], }, "IT" => { "UndefinedRegion" => [8], }, "JM" => { "UndefinedRegion" => [9], }, "JE" => { "UndefinedRegion" => [8], }, "JP" => { "UndefinedRegion" => [9], }, "JO" => { "UndefinedRegion" => [9], }, "KZ" => { "UndefinedRegion" => [9], }, "KE" => { "UndefinedRegion" => [9], }, "KI" => { "UndefinedRegion" => [9], }, "KP" => { "UndefinedRegion" => [9], }, "KR" => { "UndefinedRegion" => [9], }, "KW" => { "UndefinedRegion" => [9], }, "KG" => { "UndefinedRegion" => [9], }, "LA" => { "UndefinedRegion" => [9], }, "LV" => { "UndefinedRegion" => [9], }, "LB" => { "UndefinedRegion" => [9], }, "LS" => { "UndefinedRegion" => [9], }, "LR" => { "UndefinedRegion" => [9], }, "LY" => { "UndefinedRegion" => [9], }, "LI" => { "UndefinedRegion" => [9], }, "LT" => { "UndefinedRegion" => [9], }, "LU" => { "UndefinedRegion" => [8], }, "MO" => { "UndefinedRegion" => [9], }, "MK" => { "UndefinedRegion" => [9], }, "MG" => { "UndefinedRegion" => [9], }, "MW" => { "UndefinedRegion" => [9], }, "MY" => { "UndefinedRegion" => [9], }, "MV" => { "UndefinedRegion" => [9], }, "ML" => { "UndefinedRegion" => [9], }, "MT" => { "UndefinedRegion" => [9], }, "MH" => { "UndefinedRegion" => [9], }, "MQ" => { "UndefinedRegion" => [9], }, "MR" => { "UndefinedRegion" => [9], }, "MU" => { "UndefinedRegion" => [9], }, "YT" => { "UndefinedRegion" => [9], }, "MX" => { "UndefinedRegion" => [9], }, "FM" => { "UndefinedRegion" => [9], }, "MD" => { "UndefinedRegion" => [9], }, "MC" => { "UndefinedRegion" => [9], }, "MN" => { "UndefinedRegion" => [9], }, "MS" => { "UndefinedRegion" => [9], }, "MA" => { "UndefinedRegion" => [9], }, "MZ" => { "UndefinedRegion" => [9], }, "MM" => { "UndefinedRegion" => [9], }, "NA" => { "UndefinedRegion" => [9], }, "NR" => { "UndefinedRegion" => [9], }, "NP" => { "UndefinedRegion" => [9], }, "NL" => { "UndefinedRegion" => [8], }, "NC" => { "UndefinedRegion" => [9], }, "NZ" => { "UndefinedRegion" => [9], }, "NI" => { "UndefinedRegion" => [9], }, "NE" => { "UndefinedRegion" => [9], }, "NG" => { "UndefinedRegion" => [9], }, "NU" => { "UndefinedRegion" => [9], }, "NF" => { "UndefinedRegion" => [9], }, "MP" => { "UndefinedRegion" => [9], }, "NO" => { "UndefinedRegion" => [9], }, "OM" => { "UndefinedRegion" => [9], }, "PK" => { "UndefinedRegion" => [9], }, "PW" => { "UndefinedRegion" => [9], }, "PA" => { "UndefinedRegion" => [9], }, "PG" => { "UndefinedRegion" => [9], }, "PY" => { "UndefinedRegion" => [9], }, "PE" => { "UndefinedRegion" => [9], }, "PH" => { "UndefinedRegion" => [9], }, "PN" => { "UndefinedRegion" => [9], }, "PL" => { "UndefinedRegion" => [9], }, "PT" => { "UndefinedRegion" => [8], }, "PR" => { "UndefinedRegion" => [9], }, "QA" => { "UndefinedRegion" => [9], }, "RE" => { "UndefinedRegion" => [9], }, "RO" => { "UndefinedRegion" => [9], }, "RU" => { "UndefinedRegion" => [9], }, "RW" => { "UndefinedRegion" => [9], }, "GS" => { "UndefinedRegion" => [9], }, "KN" => { "UndefinedRegion" => [9], }, "LC" => { "UndefinedRegion" => [9], }, "VC" => { "UndefinedRegion" => [9], }, "WS" => { "UndefinedRegion" => [9], }, "SM" => { "UndefinedRegion" => [9], }, "ST" => { "UndefinedRegion" => [9], }, "SA" => { "UndefinedRegion" => [9], }, "SN" => { "UndefinedRegion" => [9], }, "SC" => { "UndefinedRegion" => [9], }, "SL" => { "UndefinedRegion" => [9], }, "SG" => { "UndefinedRegion" => [9], }, "SK" => { "UndefinedRegion" => [9], }, "SI" => { "UndefinedRegion" => [9], }, "Sb" => { "UndefinedRegion" => [9], }, "SO" => { "UndefinedRegion" => [9], }, "ZA" => { "UndefinedRegion" => [9], }, "ES" => { "UndefinedRegion" => [8], }, "LK" => { "UndefinedRegion" => [9], }, "SH" => { "UndefinedRegion" => [9], }, "PM" => { "UndefinedRegion" => [9], }, "SD" => { "UndefinedRegion" => [9], }, "SR" => { "UndefinedRegion" => [9], }, "SJ" => { "UndefinedRegion" => [9], }, "SZ" => { "UndefinedRegion" => [9], }, "SE" => { "UndefinedRegion" => [8], }, "CH" => { "UndefinedRegion" => [9], }, "SY" => { "UndefinedRegion" => [9], }, "TW" => { "UndefinedRegion" => [9], }, "TJ" => { "UndefinedRegion" => [9], }, "TZ" => { "UndefinedRegion" => [9], }, "TH" => { "UndefinedRegion" => [9], }, "TG" => { "UndefinedRegion" => [9], }, "TK" => { "UndefinedRegion" => [9], }, "TO" => { "UndefinedRegion" => [9], }, "TT" => { "UndefinedRegion" => [9], }, "TN" => { "UndefinedRegion" => [9], }, "TR" => { "UndefinedRegion" => [9], }, "TM" => { "UndefinedRegion" => [9], }, "TC" => { "UndefinedRegion" => [9], }, "TV" => { "UndefinedRegion" => [9], }, "UG" => { "UndefinedRegion" => [9], }, "UA" => { "UndefinedRegion" => [9], }, "AE" => { "UndefinedRegion" => [9], }, "UY" => { "UndefinedRegion" => [9], }, "UM" => { "UndefinedRegion" => [9], }, "UZ" => { "UndefinedRegion" => [9], }, "VU" => { "UndefinedRegion" => [9], }, "VA" => { "UndefinedRegion" => [9], }, "VE" => { "UndefinedRegion" => [9], }, "VN" => { "UndefinedRegion" => [9], }, "VG" => { "UndefinedRegion" => [9], }, "VI" => { "UndefinedRegion" => [9], }, "WF" => { "UndefinedRegion" => [9], }, "EH" => { "UndefinedRegion" => [9], }, "YE" => { "UndefinedRegion" => [9], }, "CD" => { "UndefinedRegion" => [9], }, "ZM" => { "UndefinedRegion" => [9], }, "ZW" => { "UndefinedRegion" => [9], }, ); my %ClassTable = ( 11 => ['Royal Mail', 0], 14 => ['SwiftAir', 0], 12 => ['Swift Air', 0], 15 => ['Royal Mail Special Delivery', 0] ); my $phashDefinedCategories = { 'Sp Del' => 1, }; my $sDefaultCategory = 'Sp Del'; my %ShippingTable = ( 14 => { 8 => [ {'CalculationBasis' => 1, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 1, 'ShippingCostsIncludeTax' => 0, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 1.000000, 'IncrementalCharge' => 250}, { "wt" => 1, "cost" => 925}, { "wt" => 2, "cost" => 1040}, { "wt" => 3, "cost" => 1140}, { "wt" => 4, "cost" => 1140}, { "wt" => 5, "cost" => 1340}, { "wt" => 6, "cost" => 1340}, { "wt" => 7, "cost" => 1340}, { "wt" => 8, "cost" => 1740}, { "wt" => 9, "cost" => 1740}, { "wt" => 10, "cost" => 1740}, ], }, 12 => { 9 => [ {'CalculationBasis' => 1, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 1, 'ShippingCostsIncludeTax' => 0, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 1.000000, 'IncrementalCharge' => 400}, { "wt" => 1, "cost" => 1140}, { "wt" => 2, "cost" => 1580}, { "wt" => 3, "cost" => 1880}, { "wt" => 4, "cost" => 1880}, { "wt" => 5, "cost" => 2180}, { "wt" => 6, "cost" => 2180}, { "wt" => 7, "cost" => 2180}, { "wt" => 8, "cost" => 2800}, { "wt" => 9, "cost" => 2800}, { "wt" => 10, "cost" => 2800}, ], }, 11 => { 1 => [ {'CalculationBasis' => 1, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 1, 'ShippingCostsIncludeTax' => 0, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 1.000000, 'IncrementalCharge' => 135}, { "wt" => 1, "cost" => 595}, { "wt" => 2, "cost" => 695}, { "wt" => 3, "cost" => 895}, { "wt" => 4, "cost" => 895}, { "wt" => 5, "cost" => 995}, { "wt" => 6, "cost" => 995}, { "wt" => 7, "cost" => 995}, { "wt" => 8, "cost" => 1195}, { "wt" => 9, "cost" => 1195}, { "wt" => 10, "cost" => 1195}, ], }, 15 => { 10 => [ {'CalculationBasis' => 1, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 1, 'ShippingCostsIncludeTax' => 0, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 1.000000, 'IncrementalCharge' => 200}, { "wt" => 1, "cost" => 795}, { "wt" => 2, "cost" => 895}, { "wt" => 3, "cost" => 1095}, { "wt" => 4, "cost" => 1095}, { "wt" => 5, "cost" => 1295}, { "wt" => 6, "cost" => 1295}, { "wt" => 7, "cost" => 1295}, { "wt" => 8, "cost" => 1495}, { "wt" => 9, "cost" => 1495}, { "wt" => 10, "cost" => 1495}, ], }, ); my $phashWeightConfiguration = { 0 => {'UseWeightIfUndefined' => 0, 'DefaultWeight' => '0.06' ,'OptimalWeight' => '' ,}, 4 => {'UseWeightIfUndefined' => 0, 'DefaultWeight' => '0.06' ,'OptimalWeight' => '' ,}, 5 => {'UseWeightIfUndefined' => 0, 'DefaultWeight' => '' ,'OptimalWeight' => '' ,}, }; my ($ShippingBasis, $SimpleCost, $UnknownRegion, $UnknownRegionCost, $WaiveCharges, $WaiveThreshold); $ShippingBasis = 'ByZoneClass'; $UnknownRegion = 'Error'; $UnknownRegionCost = 300; $WaiveCharges = 'No'; $WaiveThreshold = 100000.000000; my $bPricesIncludesTax = 0; 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 = ""; $::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; $::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; 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; } 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); } 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; if (@::s_arrSortedShippingHashes == 1) { $phashShipping = $::s_arrSortedShippingHashes[0]; $sClassLabel = $$phashShipping{ShippingLabel}; if ($::s_Ship_bDisplayPrices) { my (@PriceResponse) = ActinicOrder::FormatPrice($$phashShipping{Cost}, $::TRUE, \%::s_Ship_PriceFormatBlob); $sClassLabel .= sprintf($sPriceLabelFormat, $PriceResponse[2]); } $sSelectHTML = sprintf("%s\n", $sClassLabel, $$phashShipping{ShippingClass}); } elsif (@::s_arrSortedShippingHashes > 1) { $sSelectHTML = "\n"; } 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; 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 = ''; } 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; } 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) { 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_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; 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) { 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; } 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'} }; } } sub GetZoneClassesByBasis { my ($phashCalculationBases, $parrZonesClasses, $parrShippingHashes) = @_; my $parrZoneClass; foreach $parrZoneClass (@$parrZonesClasses) { my ($nZoneID, $nClassID) = @$parrZoneClass; my $phashBandDefinition = GetBandDefinition(@$parrZoneClass); if (defined $phashBandDefinition->{'FreeClass'}) { push @$parrShippingHashes, { 'ShippingLabel' => $ClassTable{$nClassID}[0], 'ShippingClass' => $nClassID, 'ShippingZone' => $nZoneID, 'Cost' => 0, 'BasisTotal' => 0 }; } 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 (%hashWeightToQuantity, @arrSortedWeightKeys); my ($i); my (@arrShipSeparatePackages, @arrMixedPackages, $parrPackage); 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) { 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); } $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]; my @arrTemp = ($::s_Ship_sShipProducts[$i], $::s_Ship_nShipQuantities[$i], $dUnitWeight); push @arrMixedPackages, \@arrTemp; } } 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); } $hashWeightToQuantity{$dUnitWeight} += $$parrPackage[1]; push @arrShipSeparatePackages, $parrPackage; } @arrMixedPackages = (); } else { $nQuantity = ($nCalculatedPackages < $nNonSeparateShipCount) ? $nCalculatedPackages : $nNonSeparateShipCount; $dWeightRemainder = $dWeightRemainder / $nQuantity; if($bUseIntegralWeights) { $dWeightRemainder = int($dWeightRemainder + 0.9999); } $hashWeightToQuantity{$dWeightRemainder} += $nQuantity; my @arrTemp = ('', $nQuantity, $dWeightRemainder); push @arrMixedPackages, \@arrTemp; } } else { if($bUseIntegralWeights) { $dWeightRemainder = int($dWeightRemainder + 0.9999); } $hashWeightToQuantity{$dWeightRemainder} += $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; } 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 .= ""; $sRSSRequestDataFormat .= GetUPSRequestNode('Rate', 'Shop'); $sRSSRequestDataFormat .= ""; $sRSSRequestDataFormat .= " $sRateChart"; $sRSSRequestDataFormat .= ""; $sRSSRequestDataFormat .= ""; $sRSSRequestDataFormat .= " "; $sRSSRequestDataFormat .= "

"; $sRSSRequestDataFormat .= " $sShipperPostalCode"; $sRSSRequestDataFormat .= " $sShipperCountry"; $sRSSRequestDataFormat .= "
"; $sRSSRequestDataFormat .= " "; $sRSSRequestDataFormat .= " "; $sRSSRequestDataFormat .= "
"; $sRSSRequestDataFormat .= " $sConsigneePostalCode"; $sRSSRequestDataFormat .= " $sConsigneeCountry"; $sRSSRequestDataFormat .= ($nResidential == 1) ? '' : ''; $sRSSRequestDataFormat .= "
"; $sRSSRequestDataFormat .= "
"; $sRSSRequestDataFormat .= " "; $sRSSRequestDataFormat .= " $sServiceLevelCode"; $sRSSRequestDataFormat .= " "; $sRSSRequestDataFormat .= " "; $sRSSRequestDataFormat .= " "; $sRSSRequestDataFormat .= " $sPackagingType"; $sRSSRequestDataFormat .= " "; $sRSSRequestDataFormat .= " "; $sRSSRequestDataFormat .= " %d"; $sRSSRequestDataFormat .= " "; $sRSSRequestDataFormat .= " "; $sRSSRequestDataFormat .= " "; $sRSSRequestDataFormat .= ""; $sRSSRequestDataFormat .= ""; 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 }; } $::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}; 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' }; } 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' }; $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) (\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 .= ""; $sAVRequestData .= GetUPSRequestNode('AV'); $sAVRequestData .= "
"; if($sConsigneeState ne '') { $sConsigneeState =~ s/^\w\w\.//; $sAVRequestData .= "$sConsigneeState"; } if($sConsigneeCity ne '') { $sAVRequestData .= "$sConsigneeCity"; } if($sConsigneePostalCode ne '') { $sAVRequestData .= "$sConsigneePostalCode"; } $sAVRequestData .= "
"; $sAVRequestData .= "
"; 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[2]; $sHTTPContent = $Response[3]; $phashHeader = $Response[4]; 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 .= ""; $sAccessRequestNode .= "$sAccessKey"; $sAccessRequestNode .= "$sUserName"; $sAccessRequestNode .= "$sPassword"; $sAccessRequestNode .= ""; return $sAccessRequestNode; } sub GetUPSRequestNode { my ($sAction, $sOption) = @_; my $sRequestNode = ''; $sRequestNode .= ""; $sRequestNode .= ""; $sRequestNode .= "$::UPS_XPCI_VERSION"; $sRequestNode .= ""; $sRequestNode .= "$sAction"; if (defined $sOption) { $sRequestNode .= "$sOption"; } $sRequestNode .= ""; 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, '') } return ($::SUCCESS);