6401065;
#---------------------------------------------------------------
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" => [13],
},
"US" => {
"UndefinedRegion" => [10],
},
"CA" => {
"UndefinedRegion" => [10],
},
"AT" => {
"UndefinedRegion" => [11],
},
"BE" => {
"UndefinedRegion" => [11],
},
"BG" => {
"UndefinedRegion" => [11],
},
"CY" => {
"UndefinedRegion" => [11],
},
"CZ" => {
"UndefinedRegion" => [11],
},
"DK" => {
"UndefinedRegion" => [11],
},
"EE" => {
"UndefinedRegion" => [11],
},
"FI" => {
"UndefinedRegion" => [11],
},
"FR" => {
"UndefinedRegion" => [11],
},
"DE" => {
"UndefinedRegion" => [11],
},
"GR" => {
"UndefinedRegion" => [11],
},
"HU" => {
"UndefinedRegion" => [11],
},
"IE" => {
"UndefinedRegion" => [11],
},
"IT" => {
"UndefinedRegion" => [11],
},
"LV" => {
"UndefinedRegion" => [11],
},
"LT" => {
"UndefinedRegion" => [11],
},
"LU" => {
"UndefinedRegion" => [11],
},
"MT" => {
"UndefinedRegion" => [11],
},
"NL" => {
"UndefinedRegion" => [11],
},
"PL" => {
"UndefinedRegion" => [11],
},
"PT" => {
"UndefinedRegion" => [11],
},
"RO" => {
"UndefinedRegion" => [11],
},
"SK" => {
"UndefinedRegion" => [11],
},
"SI" => {
"UndefinedRegion" => [11],
},
"ES" => {
"UndefinedRegion" => [11],
},
"SE" => {
"UndefinedRegion" => [11],
},
);
my %ClassTable = (
6 => ['Royal Mail Intl Standard', 0, ''],
8 => ['Example Class - 2 Day', 0, ''],
10 => ['Royal Mail First Class', 0, '']
);
my $phashDefinedCategories =
{
'Normal' => 1,
};
my $sDefaultCategory = 'Normal';
my %ShippingTable = (
10 =>
{
13 => [ {'CalculationBasis' => 2, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 1, 'ShippingCostsIncludeTax' => 0, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 100000.000000, 'IncrementalCharge' => 6000}, { "wt" => 100000, "cost" => 4500}, { "wt" => 300000, "cost" => 8500}, { "wt" => 500000, "cost" => 12500}, ],
},
6 =>
{
10 => [ {'CalculationBasis' => 2, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 1, 'ShippingCostsIncludeTax' => 0, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 100000.000000, 'IncrementalCharge' => 8500}, { "wt" => 100000, "cost" => 8500}, { "wt" => 300000, "cost" => 12500}, { "wt" => 500000, "cost" => 17500}, ],
},
8 =>
{
11 => [ {'CalculationBasis' => 2, 'WeightFactor' => 1.000000, 'AltWeightFactor' => 1.000000, 'TaxAppliesToShipping' => 1, 'ShippingCostsIncludeTax' => 0, 'ExcessAction' => 'AddFurther', 'IncrementalWeight' => 100000.000000, 'IncrementalCharge' => 8500}, { "wt" => 100000, "cost" => 8500}, { "wt" => 300000, "cost" => 12500}, { "wt" => 500000, "cost" => 18500}, ],
},
);
my $phashWeightConfiguration =
{
0 => {'UseWeightIfUndefined' => 0, 'DefaultWeight' => '0.25' ,'OptimalWeight' => '' ,},
4 => {'UseWeightIfUndefined' => 0, 'DefaultWeight' => '0.25' ,'OptimalWeight' => '' ,},
5 => {'UseWeightIfUndefined' => 0, 'DefaultWeight' => '' ,'OptimalWeight' => '' ,},
};
my ($ShippingBasis, $SimpleCost, $UnknownRegion, $UnknownRegionCost, $WaiveCharges, $WaiveThreshold);
$ShippingBasis = 'ByZoneClass';
$UnknownRegion = 'Default';
$UnknownRegionCost = 12500;
$WaiveCharges = 'No';
$WaiveThreshold = 100000.000000;
my $bPricesIncludesTax = 0;
my $dTaxInclusiveMultiplier = 1.000000;
my $nHandlingCharge = 1000;
my $nHandlingProportion = 0;
my %ParentZoneTable = (
"US" => [10, ],
"CA" => [10, ],
);
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;
$::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 ",
ACTINIC::HTMLEncode($sClassLabel),
$$phashShipping{ShippingClass});
push(@::s_ShipClassDetailList, GetClassHash($phashShipping->{'ShippingClass'}, $phashShipping->{'ShippingLabel'}, $phashShipping->{'Cost'}));
}
elsif (@::s_arrSortedShippingHashes > 1)
{
$sSelectHTML = "\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("%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(" %s\n",
$sSelected, $sClassID, ACTINIC::HTMLEncode($sClassLabel));
push(@::s_ShipClassDetailList, GetClassHash($sClassID, $phashShipping->{'ShippingLabel'}, $phashShipping->{'Cost'}));
}
}
$sSelectHTML .= " \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(" \n", $sLocationCode);
$sHTML .= sprintf(" \n", $::g_ShipInfo{'DPDDELIVERYDATE'});
}
my $IDsToShippingTypes =
{
'1' => 'Standard Delivery',
'2' => 'Specified Day',
'3' => 'Collection Point Pickup'
};
$sHTML .= "
\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 .= "";
$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,
'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 .= "";
$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[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 .= "";
$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, '')
}
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);
}