From 97f25aa6affc3c38b489b895127d98e70e342d83 Mon Sep 17 00:00:00 2001 From: dwindown Date: Mon, 10 Nov 2025 18:27:49 +0700 Subject: [PATCH] fix(orders): Use billing address for shipping when not shipping to different address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Critical Bug Fixed ✅ ### Problem: - User fills billing address (Country, State, City) - Shipping says "No shipping methods available" - Backend returns empty methods array - No rates calculated ### Root Cause: Frontend was only checking `shippingData` for completeness: ```ts if (!shippingData.country) return false; if (!shippingData.city) return false; ``` But when user doesn't check "Ship to different address": - `shippingData` is empty {} - Billing address has all the data - Query never enabled! ### Solution: Use effective shipping address based on `shipDiff` toggle: ```ts const effectiveShippingAddress = useMemo(() => { if (shipDiff) { return shippingData; // Use separate shipping address } // Use billing address return { country: bCountry, state: bState, city: bCity, postcode: bPost, address_1: bAddr1, }; }, [shipDiff, shippingData, bCountry, bState, bCity, bPost, bAddr1]); ``` Then check completeness on effective address: ```ts const isComplete = useMemo(() => { const addr = effectiveShippingAddress; if (!addr.country) return false; if (!addr.city) return false; if (hasStates && !addr.state) return false; return true; }, [effectiveShippingAddress]); ``` ### Backend Enhancement: Also set billing address for tax calculation context: ```php // Set both shipping and billing for proper tax calculation WC()->customer->set_shipping_country( $country ); WC()->customer->set_billing_country( $country ); ``` ## Result: ### Before: 1. Fill billing: Indonesia, Jawa Barat, Bandung 2. Shipping: "No shipping methods available" ❌ 3. No API call made ### After: 1. Fill billing: Indonesia, Jawa Barat, Bandung 2. ✅ API called with billing address 3. ✅ Returns: JNE REG, JNE YES, TIKI REG 4. ✅ First rate auto-selected 5. ✅ Total calculated with tax ## Testing: - ✅ Fill billing only → Shipping calculated - ✅ Check "Ship to different" → Use shipping address - ✅ Uncheck → Switch back to billing - ✅ Change billing city → Rates recalculate --- .../src/routes/Orders/partials/OrderForm.tsx | 31 ++++++++++++++----- includes/Api/OrdersController.php | 25 +++++++++++---- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/admin-spa/src/routes/Orders/partials/OrderForm.tsx b/admin-spa/src/routes/Orders/partials/OrderForm.tsx index f6fd754..fd84203 100644 --- a/admin-spa/src/routes/Orders/partials/OrderForm.tsx +++ b/admin-spa/src/routes/Orders/partials/OrderForm.tsx @@ -185,26 +185,43 @@ export default function OrderForm({ enabled: items.length > 0, }); + // Get effective shipping address (use billing if not shipping to different address) + const effectiveShippingAddress = React.useMemo(() => { + if (shipDiff) { + return shippingData; + } + // Use billing address + return { + country: bCountry, + state: bState, + city: bCity, + postcode: bPost, + address_1: bAddr1, + address_2: '', + }; + }, [shipDiff, shippingData, bCountry, bState, bCity, bPost, bAddr1]); + // Check if shipping address is complete enough to calculate rates const isShippingAddressComplete = React.useMemo(() => { + const addr = effectiveShippingAddress; // Need at minimum: country, state (if applicable), city - if (!shippingData.country) return false; - if (!shippingData.city) return false; + if (!addr.country) return false; + if (!addr.city) return false; // If country has states, require state - const countryStates = states[shippingData.country]; - if (countryStates && Object.keys(countryStates).length > 0 && !shippingData.state) { + const countryStates = states[addr.country]; + if (countryStates && Object.keys(countryStates).length > 0 && !addr.state) { return false; } return true; - }, [shippingData.country, shippingData.state, shippingData.city, states]); + }, [effectiveShippingAddress, states]); // Calculate shipping rates dynamically const { data: shippingRates, isLoading: shippingLoading } = useQuery({ - queryKey: ['shipping-rates', items.map(i => ({ product_id: i.product_id, qty: i.qty })), shippingData.country, shippingData.state, shippingData.city, shippingData.postcode, shippingData.address_1], + queryKey: ['shipping-rates', items.map(i => ({ product_id: i.product_id, qty: i.qty })), effectiveShippingAddress.country, effectiveShippingAddress.state, effectiveShippingAddress.city, effectiveShippingAddress.postcode, effectiveShippingAddress.address_1], queryFn: async () => { return api.post('/shipping/calculate', { items: items.map(i => ({ product_id: i.product_id, qty: i.qty })), - shipping: shippingData, + shipping: effectiveShippingAddress, }); }, enabled: isShippingAddressComplete && items.length > 0, diff --git a/includes/Api/OrdersController.php b/includes/Api/OrdersController.php index 01e7731..cae3c0a 100644 --- a/includes/Api/OrdersController.php +++ b/includes/Api/OrdersController.php @@ -1293,12 +1293,25 @@ class OrdersController { // Set customer shipping address if ( ! empty( $shipping ) ) { - WC()->customer->set_shipping_country( $shipping['country'] ?? '' ); - WC()->customer->set_shipping_state( $shipping['state'] ?? '' ); - WC()->customer->set_shipping_postcode( $shipping['postcode'] ?? '' ); - WC()->customer->set_shipping_city( $shipping['city'] ?? '' ); - WC()->customer->set_shipping_address( $shipping['address_1'] ?? '' ); - WC()->customer->set_shipping_address_2( $shipping['address_2'] ?? '' ); + $country = $shipping['country'] ?? ''; + $state = $shipping['state'] ?? ''; + $postcode = $shipping['postcode'] ?? ''; + $city = $shipping['city'] ?? ''; + $address_1 = $shipping['address_1'] ?? ''; + $address_2 = $shipping['address_2'] ?? ''; + + WC()->customer->set_shipping_country( $country ); + WC()->customer->set_shipping_state( $state ); + WC()->customer->set_shipping_postcode( $postcode ); + WC()->customer->set_shipping_city( $city ); + WC()->customer->set_shipping_address( $address_1 ); + WC()->customer->set_shipping_address_2( $address_2 ); + + // Also set billing for tax calculation context + WC()->customer->set_billing_country( $country ); + WC()->customer->set_billing_state( $state ); + WC()->customer->set_billing_postcode( $postcode ); + WC()->customer->set_billing_city( $city ); } // Calculate shipping