diff --git a/usps_api_bridge.php b/usps_api_bridge.php index 2e4836d..f07a8ff 100644 --- a/usps_api_bridge.php +++ b/usps_api_bridge.php @@ -142,63 +142,39 @@ class Usps_Api_Bridge extends Module return $helper->generateForm([$fields_form]); } + public function calculateRate($params, $shipping_cost, $products, $originalModule) { require_once(dirname(__FILE__) . '/classes/UspsV3Client.php'); - $this->externalLog(['calculateRate' => [$params, $shipping_cost, $products, $originalModule]]); - // --- 1. LEGACY CACHE CHECK --- - // We reuse the old module's cache table to save API calls - $zhCache = false; - $canCache = class_exists('\UspsPsLabels\Cache') && class_exists('\UspsPsLabels\CacheRate'); - if ($canCache) { - // cacheCart() checks if products/address changed. - // If changed, it clears previous rates automatically. - $zhCache = \UspsPsLabels\Cache::cacheCart($params->id); - - if (Validate::isLoadedObject($zhCache)) { - // Check if we already have a rate for this specific Carrier ID - $sql = 'SELECT rate FROM `' . _DB_PREFIX_ . 'uspsl_cache_rate` - WHERE id_cache = ' . (int)$zhCache->id . ' - AND id_carrier = ' . (int)$params->id_carrier; - - $cachedRate = Db::getInstance()->getValue($sql); - - if ($cachedRate !== false && $cachedRate !== null) { - // $this->log("Returning DB Cached Rate: $cachedRate"); // Optional debug - // We must re-add the PrestaShop shipping cost (handling fees) - return (float)$cachedRate + $shipping_cost; - } - } - } - // ----------------------------- - - // 2. Get OAuth Token + // 1. Get OAuth Token $token = $this->getAccessToken(); if (!$token) return false; - // 3. Identify Service + // 2. Identify Service $carrierId = $params->id_carrier; $sql = 'SELECT code FROM `' . _DB_PREFIX_ . 'uspsl_method` WHERE id_carrier = ' . (int)$carrierId; $methodCode = Db::getInstance()->getValue($sql); if (!$methodCode) return false; - // 4. Map Old Code to New API Enum + // 3. Map Old Code to New API Enum $newApiClass = $this->mapServiceCodeToApiClass($methodCode); if (!$newApiClass) return false; - // 5. Pack Products + // 4. Pack Products $packedBoxes = $originalModule->getHelper()->getCarrierHelper()->packProducts($products, $params->id); if (empty($packedBoxes)) return false; - // 6. Initialize Client & Settings + // 5. Setup Client $client = new UspsV3Client($token, (bool)Configuration::get('USPS_BRIDGE_LIVE_MODE')); $totalPrice = 0; + // Determine Price Type $legacyPriceSetting = (int)Configuration::get('USPSL_COMMERCIAL'); $requestedPriceType = ($legacyPriceSetting > 0) ? 'COMMERCIAL' : 'RETAIL'; + // 6. Address Data $originZip = $this->getOriginZip($originalModule); $destAddress = new Address($params->id_address_delivery); @@ -209,18 +185,28 @@ class Usps_Api_Bridge extends Module // 7. Loop through boxes foreach ($packedBoxes as $packedBox) { + // Weight (Lbs) - Min 0.1 to avoid errors $weightInLbs = $this->convertUnit($packedBox->getWeight(), 'g', 'lbs', 3); if ($weightInLbs < 0.1) $weightInLbs = 0.1; + // Dimensions (Inches) $box = $packedBox->getBox(); $length = $this->convertUnit($box->getOuterLength(), 'mm', 'in', 2); $width = $this->convertUnit($box->getOuterWidth(), 'mm', 'in', 2); $height = $this->convertUnit($box->getOuterDepth(), 'mm', 'in', 2); - $category = 'MACHINABLE'; - if ($length > 22 || $width > 18 || $height > 15 || $weightInLbs > 25) { - $category = 'NONSTANDARD'; + // --- PROCESSING CATEGORY LOGIC (FIXED) --- + // "NONSTANDARD" is the V3 equivalent of a generic "Parcel" or "Package". + // We only use "MACHINABLE" if it is strictly Ground Advantage AND fits dimensions. + // For Priority/Express, "NONSTANDARD" finds the correct Retail/Commercial SKU. + $category = 'NONSTANDARD'; + + if ($newApiClass === 'USPS_GROUND_ADVANTAGE') { + if ($length <= 22 && $width <= 18 && $height <= 15 && $weightInLbs >= 0.375 && $weightInLbs <= 25) { + $category = 'MACHINABLE'; + } } + // ----------------------------------------- $payload = [ 'originZIPCode' => $originZip, @@ -232,59 +218,53 @@ class Usps_Api_Bridge extends Module 'priceType' => $requestedPriceType, 'mailingDate' => date('Y-m-d', strtotime('+1 day')), 'processingCategory' => $category, - 'rateIndicator' => 'SP' + 'rateIndicator' => 'SP' // SP = Single Piece (Variable dimensions) ]; + // Handle Flat Rates $flatRateIndicator = $this->mapBoxToRateIndicator($box->getReference()); if ($flatRateIndicator) { $payload['rateIndicator'] = $flatRateIndicator; + // Flat rates technically ignore dims/category, but we keep valid enums } + $this->externalLog(['sendApiRequest' => [$client, $payload, $isInternational, $destAddress, $destZip]]); - // API REQUEST + // --- SEND REQUEST --- $response = $this->sendApiRequest($client, $payload, $isInternational, $destAddress, $destZip); + $this->externalLog(['sendApiRequest' => ['response' => $response]]); - // Retry logic for Commercial rates + // Retry Logic: If Commercial fails, try Retail if (isset($response['error']) && $payload['priceType'] === 'COMMERCIAL') { - $this->log("Commercial failed. Retrying RETAIL."); + // $this->log("Commercial failed (" . $response['error'] . "). Retrying RETAIL."); $payload['priceType'] = 'RETAIL'; $response = $this->sendApiRequest($client, $payload, $isInternational, $destAddress, $destZip); } + // Error Handling if (isset($response['error'])) { - $this->log("API Fatal Error: " . $response['error']); + // $this->log("API Fatal Error: " . $response['error']); + // $this->log("Payload: " . json_encode($payload)); return false; } + // Parse Price if (isset($response['totalBasePrice'])) { $totalPrice += (float)$response['totalBasePrice']; } elseif (isset($response['rateOptions'][0]['totalBasePrice'])) { $totalPrice += (float)$response['rateOptions'][0]['totalBasePrice']; } else { + // $this->log("API Response missing price."); return false; } } - // --- 8. SAVE TO LEGACY CACHE --- - if ($canCache && Validate::isLoadedObject($zhCache)) { - // We use the CacheRate class to save it properly - $newCacheRate = new \UspsPsLabels\CacheRate(); - $newCacheRate->id_cache = $zhCache->id; - $newCacheRate->id_carrier = $params->id_carrier; - $newCacheRate->code = $methodCode; - $newCacheRate->rate = $totalPrice; // Store raw rate (without PS handling fees) - $newCacheRate->save(); - } - // ------------------------------- - return $totalPrice + $shipping_cost; } - /** * Helper to send request with Runtime Caching & Domestic/Intl switching */ private function sendApiRequest($client, $payload, $isInternational, $destAddress, $destZip) { - $this->externalLog(['sendApiRequest' => [$client, $payload, $isInternational, $destAddress, $destZip]]); // 1. Prepare the specific payload for the cache key // We simulate the modifications we are about to do to ensure the key is accurate $cachePayload = $payload; @@ -309,11 +289,9 @@ class Usps_Api_Bridge extends Module if (isset($this->apiRuntimeCache[$cacheKey])) { // Uncomment for deep debugging if needed // $this->log("Returning cached rate for key: " . $cacheKey); - $this->externalLog(['sendApiRequest' => "Returning cached rate for key: " . $cacheKey]); return $this->apiRuntimeCache[$cacheKey]; } - $this->externalLog(['sendApiRequest' => ['cachePayload' => $cachePayload]]); // 4. Perform Request if ($isInternational) { @@ -341,7 +319,6 @@ class Usps_Api_Bridge extends Module if ($shouldCache) { $this->apiRuntimeCache[$cacheKey] = $response; } - $this->externalLog(['sendApiRequest' => ['response' => $response]]); return $response; }