Fix RajaOngkir address integration bugs and styling issues
This commit is contained in:
@@ -18,6 +18,7 @@ interface Address {
|
|||||||
email?: string;
|
email?: string;
|
||||||
phone?: string;
|
phone?: string;
|
||||||
is_default: boolean;
|
is_default: boolean;
|
||||||
|
formatted_address?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AddressSelectorProps {
|
interface AddressSelectorProps {
|
||||||
@@ -148,14 +149,22 @@ export function AddressSelector({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Address */}
|
{/* Address */}
|
||||||
<p className="text-sm text-gray-600 mt-2">
|
<div className="text-sm text-gray-600 mt-2">
|
||||||
{address.address_1}
|
{address.formatted_address ? (
|
||||||
{address.address_2 && `, ${address.address_2}`}
|
<p className="whitespace-pre-wrap">{address.formatted_address}</p>
|
||||||
</p>
|
) : (
|
||||||
<p className="text-sm text-gray-600">
|
<>
|
||||||
{address.city}, {address.state} {address.postcode}
|
<p>
|
||||||
</p>
|
{address.address_1}
|
||||||
<p className="text-sm text-gray-600">{address.country}</p>
|
{address.address_2 && `, ${address.address_2}`}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{address.city}, {address.state} {address.postcode}
|
||||||
|
</p>
|
||||||
|
<p>{address.country}</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ interface CheckoutField {
|
|||||||
interface DynamicCheckoutFieldProps {
|
interface DynamicCheckoutFieldProps {
|
||||||
field: CheckoutField;
|
field: CheckoutField;
|
||||||
value: string;
|
value: string;
|
||||||
|
valueLabel?: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
countryOptions?: { value: string; label: string }[];
|
countryOptions?: { value: string; label: string }[];
|
||||||
stateOptions?: { value: string; label: string }[];
|
stateOptions?: { value: string; label: string }[];
|
||||||
@@ -41,6 +42,7 @@ interface SearchOption {
|
|||||||
export function DynamicCheckoutField({
|
export function DynamicCheckoutField({
|
||||||
field,
|
field,
|
||||||
value,
|
value,
|
||||||
|
valueLabel,
|
||||||
onChange,
|
onChange,
|
||||||
countryOptions = [],
|
countryOptions = [],
|
||||||
stateOptions = [],
|
stateOptions = [],
|
||||||
@@ -54,9 +56,11 @@ export function DynamicCheckoutField({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a value but no options yet, we might need to load it
|
// If we have a value and a label, inject it into searchOptions so it renders properly when mounted
|
||||||
// This handles pre-selected values
|
if (value && valueLabel && searchOptions.length === 0) {
|
||||||
}, [field.type, field.search_endpoint, value]);
|
setSearchOptions([{ value, label: valueLabel }]);
|
||||||
|
}
|
||||||
|
}, [field.type, field.search_endpoint, value, valueLabel]);
|
||||||
|
|
||||||
// Handle API search for searchable_select
|
// Handle API search for searchable_select
|
||||||
const handleApiSearch = async (searchTerm: string) => {
|
const handleApiSearch = async (searchTerm: string) => {
|
||||||
|
|||||||
@@ -345,10 +345,18 @@ export default function Addresses() {
|
|||||||
<div className="text-sm text-gray-700 space-y-1 mb-4">
|
<div className="text-sm text-gray-700 space-y-1 mb-4">
|
||||||
<p className="font-medium">{address.first_name} {address.last_name}</p>
|
<p className="font-medium">{address.first_name} {address.last_name}</p>
|
||||||
{address.company && <p>{address.company}</p>}
|
{address.company && <p>{address.company}</p>}
|
||||||
<p>{address.address_1}</p>
|
{address.formatted_address ? (
|
||||||
{address.address_2 && <p>{address.address_2}</p>}
|
<p className="whitespace-pre-wrap">{address.formatted_address}</p>
|
||||||
<p>{address.city}, {address.state} {address.postcode}</p>
|
) : (
|
||||||
<p>{address.country}</p>
|
<>
|
||||||
|
<p>{address.address_1}</p>
|
||||||
|
{address.address_2 && <p>{address.address_2}</p>}
|
||||||
|
{[address.city, address.state, address.postcode].filter(Boolean).length > 0 && (
|
||||||
|
<p>{[address.city, address.state, address.postcode].filter(Boolean).join(', ')}</p>
|
||||||
|
)}
|
||||||
|
<p>{address.country}</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{address.phone && <p className="pt-2">Phone: {address.phone}</p>}
|
{address.phone && <p className="pt-2">Phone: {address.phone}</p>}
|
||||||
{address.email && <p>Email: {address.email}</p>}
|
{address.email && <p>Email: {address.email}</p>}
|
||||||
</div>
|
</div>
|
||||||
@@ -429,6 +437,7 @@ export default function Addresses() {
|
|||||||
key={field.key}
|
key={field.key}
|
||||||
field={field}
|
field={field}
|
||||||
value={getFieldValue(field.key)}
|
value={getFieldValue(field.key)}
|
||||||
|
valueLabel={getFieldValue(field.key + '_label')}
|
||||||
onChange={(v) => setFieldValue(field.key, v)}
|
onChange={(v) => setFieldValue(field.key, v)}
|
||||||
countryOptions={countryOptions}
|
countryOptions={countryOptions}
|
||||||
stateOptions={stateOptions}
|
stateOptions={stateOptions}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ interface SavedAddress {
|
|||||||
email?: string;
|
email?: string;
|
||||||
phone?: string;
|
phone?: string;
|
||||||
is_default: boolean;
|
is_default: boolean;
|
||||||
|
formatted_address?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Checkout() {
|
export default function Checkout() {
|
||||||
@@ -389,7 +390,13 @@ export default function Checkout() {
|
|||||||
state: addressData.state,
|
state: addressData.state,
|
||||||
city: addressData.city,
|
city: addressData.city,
|
||||||
postcode: addressData.postcode,
|
postcode: addressData.postcode,
|
||||||
destination_id: undefined,
|
// Include custom fields for shipping calculation (e.g., RajaOngkir destination_id)
|
||||||
|
...Object.fromEntries(
|
||||||
|
(shipToDifferentAddress ? shippingCustomFields : billingCustomFields).map(f => [
|
||||||
|
f.key.replace(/^(shipping_|billing_)/, ''),
|
||||||
|
customFieldData[f.key] || ''
|
||||||
|
])
|
||||||
|
),
|
||||||
},
|
},
|
||||||
items,
|
items,
|
||||||
});
|
});
|
||||||
@@ -795,7 +802,16 @@ export default function Checkout() {
|
|||||||
<div className="bg-primary/5 border-2 border-primary rounded-lg p-4 text-sm">
|
<div className="bg-primary/5 border-2 border-primary rounded-lg p-4 text-sm">
|
||||||
<p className="font-semibold">{sel.label}{sel.is_default && <span className="ml-2 text-xs bg-green-100 text-green-700 px-1.5 rounded">Default</span>}</p>
|
<p className="font-semibold">{sel.label}{sel.is_default && <span className="ml-2 text-xs bg-green-100 text-green-700 px-1.5 rounded">Default</span>}</p>
|
||||||
<p>{sel.first_name} {sel.last_name}</p>
|
<p>{sel.first_name} {sel.last_name}</p>
|
||||||
<p className="text-gray-600">{sel.address_1}, {sel.city}, {sel.state} {sel.postcode}</p>
|
{sel.formatted_address ? (
|
||||||
|
<p className="text-gray-600 whitespace-pre-wrap">{sel.formatted_address}</p>
|
||||||
|
) : (
|
||||||
|
<div className="text-gray-600">
|
||||||
|
<p>{sel.address_1}</p>
|
||||||
|
{[sel.city, sel.state, sel.postcode].filter(Boolean).length > 0 && (
|
||||||
|
<p>{[sel.city, sel.state, sel.postcode].filter(Boolean).join(', ')}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
})()}
|
})()}
|
||||||
@@ -866,7 +882,7 @@ export default function Checkout() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{billingCustomFields.map(field => (
|
{billingCustomFields.map(field => (
|
||||||
<DynamicCheckoutField key={field.key} field={field} value={customFieldData[field.key] || ''} onChange={v => handleCustomFieldChange(field.key, v)} countryOptions={countryOptions} stateOptions={billingStateOptions} />
|
<DynamicCheckoutField key={field.key} field={field} value={customFieldData[field.key] || ''} valueLabel={customFieldData[`${field.key}_label`]} onChange={v => handleCustomFieldChange(field.key, v)} countryOptions={countryOptions} stateOptions={billingStateOptions} />
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -908,7 +924,16 @@ export default function Checkout() {
|
|||||||
<div className="bg-primary/5 border-2 border-primary rounded-lg p-4 text-sm">
|
<div className="bg-primary/5 border-2 border-primary rounded-lg p-4 text-sm">
|
||||||
<p className="font-semibold">{sel.label}</p>
|
<p className="font-semibold">{sel.label}</p>
|
||||||
<p>{sel.first_name} {sel.last_name}</p>
|
<p>{sel.first_name} {sel.last_name}</p>
|
||||||
<p className="text-gray-600">{sel.address_1}, {sel.city} {sel.postcode}</p>
|
{sel.formatted_address ? (
|
||||||
|
<p className="text-gray-600 whitespace-pre-wrap">{sel.formatted_address}</p>
|
||||||
|
) : (
|
||||||
|
<div className="text-gray-600">
|
||||||
|
<p>{sel.address_1}</p>
|
||||||
|
{[sel.city, sel.state, sel.postcode].filter(Boolean).length > 0 && (
|
||||||
|
<p>{[sel.city, sel.state, sel.postcode].filter(Boolean).join(', ')}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
})()}
|
})()}
|
||||||
@@ -926,7 +951,7 @@ export default function Checkout() {
|
|||||||
{getShippingField('shipping_country') && <div><label className="block text-sm font-medium mb-1">Country</label><SearchableSelect options={countryOptions} value={shippingData.country} onChange={v => setShippingData({ ...shippingData, country: v })} placeholder="Select country" disabled={countries.length === 1} /></div>}
|
{getShippingField('shipping_country') && <div><label className="block text-sm font-medium mb-1">Country</label><SearchableSelect options={countryOptions} value={shippingData.country} onChange={v => setShippingData({ ...shippingData, country: v })} placeholder="Select country" disabled={countries.length === 1} /></div>}
|
||||||
{getShippingField('shipping_state') && <div><label className="block text-sm font-medium mb-1">State</label>{shippingStateOptions.length > 0 ? <SearchableSelect options={shippingStateOptions} value={shippingData.state} onChange={v => setShippingData({ ...shippingData, state: v })} placeholder="Select state" /> : <input type="text" value={shippingData.state} onChange={e => setShippingData({ ...shippingData, state: e.target.value })} className="w-full border rounded-lg px-4 py-2" />}</div>}
|
{getShippingField('shipping_state') && <div><label className="block text-sm font-medium mb-1">State</label>{shippingStateOptions.length > 0 ? <SearchableSelect options={shippingStateOptions} value={shippingData.state} onChange={v => setShippingData({ ...shippingData, state: v })} placeholder="Select state" /> : <input type="text" value={shippingData.state} onChange={e => setShippingData({ ...shippingData, state: e.target.value })} className="w-full border rounded-lg px-4 py-2" />}</div>}
|
||||||
{getShippingField('shipping_postcode') && <div><label className="block text-sm font-medium mb-1">Postcode</label><input type="text" value={shippingData.postcode} onChange={e => setShippingData({ ...shippingData, postcode: e.target.value })} className="w-full border rounded-lg px-4 py-2" /></div>}
|
{getShippingField('shipping_postcode') && <div><label className="block text-sm font-medium mb-1">Postcode</label><input type="text" value={shippingData.postcode} onChange={e => setShippingData({ ...shippingData, postcode: e.target.value })} className="w-full border rounded-lg px-4 py-2" /></div>}
|
||||||
{shippingCustomFields.map(field => <DynamicCheckoutField key={field.key} field={field} value={customFieldData[field.key] || ''} onChange={v => handleCustomFieldChange(field.key, v)} countryOptions={countryOptions} stateOptions={shippingStateOptions} />)}
|
{shippingCustomFields.map(field => <DynamicCheckoutField key={field.key} field={field} value={customFieldData[field.key] || ''} valueLabel={customFieldData[`${field.key}_label`]} onChange={v => handleCustomFieldChange(field.key, v)} countryOptions={countryOptions} stateOptions={shippingStateOptions} />)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -538,6 +538,9 @@ class CheckoutController
|
|||||||
header('Server-Timing: app;dur=' . round((microtime(true) - $__t0) * 1000, 1));
|
header('Server-Timing: app;dur=' . round((microtime(true) - $__t0) * 1000, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-save checkout addresses to user account
|
||||||
|
$this->auto_save_checkout_addresses($order, $payload);
|
||||||
|
|
||||||
// Clear WooCommerce cart after successful order placement
|
// Clear WooCommerce cart after successful order placement
|
||||||
// This ensures the cart page won't re-populate from server session
|
// This ensures the cart page won't re-populate from server session
|
||||||
if (function_exists('WC') && WC()->cart) {
|
if (function_exists('WC') && WC()->cart) {
|
||||||
@@ -1141,4 +1144,105 @@ class CheckoutController
|
|||||||
}
|
}
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-save checkout addresses to the user's address book if they are new.
|
||||||
|
*/
|
||||||
|
private function auto_save_checkout_addresses(\WC_Order $order, array $payload): void
|
||||||
|
{
|
||||||
|
$user_id = $order->get_customer_id();
|
||||||
|
if (!$user_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$addresses = get_user_meta($user_id, 'woonoow_addresses', true);
|
||||||
|
if (!is_array($addresses)) {
|
||||||
|
$addresses = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to check if address matches existing
|
||||||
|
$is_duplicate = function ($new_addr, $type) use ($addresses) {
|
||||||
|
foreach ($addresses as $addr) {
|
||||||
|
if ($addr['type'] !== $type && $addr['type'] !== 'both') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Compare essential fields
|
||||||
|
$match = true;
|
||||||
|
$check_fields = ['first_name', 'last_name', 'address_1', 'city', 'country'];
|
||||||
|
foreach ($check_fields as $f) {
|
||||||
|
$v1 = trim(strtolower((string)($addr[$f] ?? '')));
|
||||||
|
$v2 = trim(strtolower((string)($new_addr[$f] ?? '')));
|
||||||
|
if ($v1 !== $v2) {
|
||||||
|
$match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($match) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper to build address array
|
||||||
|
$build_address = function ($type, $data, $custom_fields, $addresses) {
|
||||||
|
$new_id = empty($addresses) ? 1 : max(array_column($addresses, 'id')) + 1;
|
||||||
|
$addr = [
|
||||||
|
'id' => $new_id,
|
||||||
|
'label' => ucfirst($type) . ' ' . $new_id,
|
||||||
|
'type' => $type,
|
||||||
|
'is_default' => empty($addresses), // default if it's the first one
|
||||||
|
];
|
||||||
|
|
||||||
|
$standard_fields = ['first_name', 'last_name', 'company', 'address_1', 'address_2', 'city', 'state', 'postcode', 'country', 'email', 'phone'];
|
||||||
|
foreach ($standard_fields as $f) {
|
||||||
|
if (isset($data[$f])) {
|
||||||
|
$addr[$f] = sanitize_text_field($data[$f]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom fields matching prefix
|
||||||
|
if (is_array($custom_fields)) {
|
||||||
|
foreach ($custom_fields as $k => $v) {
|
||||||
|
if (strpos($k, $type . '_') === 0) {
|
||||||
|
$addr[$k] = sanitize_text_field($v);
|
||||||
|
} elseif (!isset($addr[$type . '_' . $k]) && !isset($addr[$k])) {
|
||||||
|
// Some custom fields might not have the prefix if they apply to both
|
||||||
|
// Or they are sent without prefix by frontend in payload[type]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also, payload[type] can contain custom fields directly because frontend sends them without prefix!
|
||||||
|
foreach ($data as $k => $v) {
|
||||||
|
if (!in_array($k, $standard_fields) && !in_array($k, ['ship_to_different'])) {
|
||||||
|
$addr[$type . '_' . $k] = sanitize_text_field($v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
$changed = false;
|
||||||
|
|
||||||
|
// Check billing
|
||||||
|
if (!empty($payload['billing'])) {
|
||||||
|
if (!$is_duplicate($payload['billing'], 'billing')) {
|
||||||
|
$billing_addr = $build_address('billing', $payload['billing'], $payload['custom_fields'] ?? [], $addresses);
|
||||||
|
$addresses[] = $billing_addr;
|
||||||
|
$changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check shipping
|
||||||
|
$ship_to_different = !empty($payload['shipping']['ship_to_different']);
|
||||||
|
if ($ship_to_different && !empty($payload['shipping'])) {
|
||||||
|
if (!$is_duplicate($payload['shipping'], 'shipping')) {
|
||||||
|
$shipping_addr = $build_address('shipping', $payload['shipping'], $payload['custom_fields'] ?? [], $addresses);
|
||||||
|
$addresses[] = $shipping_addr;
|
||||||
|
$changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($changed) {
|
||||||
|
update_user_meta($user_id, 'woonoow_addresses', array_values($addresses));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ class AddressController {
|
|||||||
|
|
||||||
$addresses = array_values($addresses);
|
$addresses = array_values($addresses);
|
||||||
|
|
||||||
|
foreach ($addresses as &$address) {
|
||||||
|
$address['formatted_address'] = apply_filters('woonoow_format_address', '', $address);
|
||||||
|
}
|
||||||
|
|
||||||
return new WP_REST_Response($addresses, 200);
|
return new WP_REST_Response($addresses, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,11 @@ add_filter('woocommerce_checkout_fields', function ($fields) {
|
|||||||
$fields['billing']['billing_last_name']['type'] = 'hidden';
|
$fields['billing']['billing_last_name']['type'] = 'hidden';
|
||||||
$fields['billing']['billing_last_name']['default'] = 'ID';
|
$fields['billing']['billing_last_name']['default'] = 'ID';
|
||||||
$fields['billing']['billing_last_name']['required'] = false;
|
$fields['billing']['billing_last_name']['required'] = false;
|
||||||
|
|
||||||
|
// Make first_name take full width since last_name is hidden
|
||||||
|
if (isset($fields['billing']['billing_first_name'])) {
|
||||||
|
$fields['billing']['billing_first_name']['class'] = ['form-row-wide'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isset($fields['billing']['billing_country'])) {
|
if (isset($fields['billing']['billing_country'])) {
|
||||||
$fields['billing']['billing_country']['type'] = 'hidden';
|
$fields['billing']['billing_country']['type'] = 'hidden';
|
||||||
@@ -115,6 +120,11 @@ add_filter('woocommerce_checkout_fields', function ($fields) {
|
|||||||
$fields['shipping']['shipping_last_name']['type'] = 'hidden';
|
$fields['shipping']['shipping_last_name']['type'] = 'hidden';
|
||||||
$fields['shipping']['shipping_last_name']['default'] = 'ID';
|
$fields['shipping']['shipping_last_name']['default'] = 'ID';
|
||||||
$fields['shipping']['shipping_last_name']['required'] = false;
|
$fields['shipping']['shipping_last_name']['required'] = false;
|
||||||
|
|
||||||
|
// Make first_name take full width since last_name is hidden
|
||||||
|
if (isset($fields['shipping']['shipping_first_name'])) {
|
||||||
|
$fields['shipping']['shipping_first_name']['class'] = ['form-row-wide'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isset($fields['shipping']['shipping_country'])) {
|
if (isset($fields['shipping']['shipping_country'])) {
|
||||||
$fields['shipping']['shipping_country']['type'] = 'hidden';
|
$fields['shipping']['shipping_country']['type'] = 'hidden';
|
||||||
@@ -137,7 +147,8 @@ add_filter('woocommerce_checkout_fields', function ($fields) {
|
|||||||
|
|
||||||
// Check if cart needs shipping
|
// Check if cart needs shipping
|
||||||
$needs_shipping = true;
|
$needs_shipping = true;
|
||||||
if (function_exists('WC') && WC()->cart) {
|
// If cart is empty, we assume it's for Address Book in My Account where we want fields visible
|
||||||
|
if (function_exists('WC') && WC()->cart && !WC()->cart->is_empty()) {
|
||||||
$needs_shipping = WC()->cart->needs_shipping();
|
$needs_shipping = WC()->cart->needs_shipping();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,3 +236,79 @@ add_action('woonoow/shipping/before_calculate', function ($shipping, $items) {
|
|||||||
// Clear shipping cache to force recalculation
|
// Clear shipping cache to force recalculation
|
||||||
WC()->session->set('shipping_for_package_0', false);
|
WC()->session->set('shipping_for_package_0', false);
|
||||||
}, 10, 2);
|
}, 10, 2);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 4. Save destination_id to Order Meta on SPA Checkout
|
||||||
|
// ============================================================
|
||||||
|
add_action('woocommerce_checkout_order_processed', function ($order_id, $payload) {
|
||||||
|
// Extract and save destination_id from shipping payload
|
||||||
|
if (!empty($payload['shipping']['destination_id'])) {
|
||||||
|
update_post_meta($order_id, '_shipping_destination_id', sanitize_text_field($payload['shipping']['destination_id']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract and save destination_id from billing payload
|
||||||
|
if (!empty($payload['billing']['destination_id'])) {
|
||||||
|
update_post_meta($order_id, '_billing_destination_id', sanitize_text_field($payload['billing']['destination_id']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to custom_fields array if present
|
||||||
|
if (!empty($payload['custom_fields']['shipping_destination_id'])) {
|
||||||
|
update_post_meta($order_id, '_shipping_destination_id', sanitize_text_field($payload['custom_fields']['shipping_destination_id']));
|
||||||
|
}
|
||||||
|
if (!empty($payload['custom_fields']['billing_destination_id'])) {
|
||||||
|
update_post_meta($order_id, '_billing_destination_id', sanitize_text_field($payload['custom_fields']['billing_destination_id']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save labels too if they exist, useful for backend viewing
|
||||||
|
if (!empty($payload['custom_fields']['shipping_destination_id_label'])) {
|
||||||
|
update_post_meta($order_id, '_shipping_destination_id_label', sanitize_text_field($payload['custom_fields']['shipping_destination_id_label']));
|
||||||
|
}
|
||||||
|
if (!empty($payload['custom_fields']['billing_destination_id_label'])) {
|
||||||
|
update_post_meta($order_id, '_billing_destination_id_label', sanitize_text_field($payload['custom_fields']['billing_destination_id_label']));
|
||||||
|
}
|
||||||
|
}, 10, 2);
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 5. Format address display for SPA saved addresses
|
||||||
|
// ============================================================
|
||||||
|
add_filter('woonoow_format_address', function ($formatted, $address) {
|
||||||
|
// If a snippet has already formatted it, skip
|
||||||
|
if (!empty($formatted)) {
|
||||||
|
return $formatted;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $address['type'] ?? 'billing';
|
||||||
|
$is_billing = $type === 'billing' || $type === 'both';
|
||||||
|
|
||||||
|
// Look for destination_id_label
|
||||||
|
$label = '';
|
||||||
|
if (!empty($address['destination_id_label'])) {
|
||||||
|
$label = $address['destination_id_label'];
|
||||||
|
} elseif ($is_billing && !empty($address['billing_destination_id_label'])) {
|
||||||
|
$label = $address['billing_destination_id_label'];
|
||||||
|
} elseif (!$is_billing && !empty($address['shipping_destination_id_label'])) {
|
||||||
|
$label = $address['shipping_destination_id_label'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a Rajaongkir label, construct a clean Indonesian address
|
||||||
|
if ($label) {
|
||||||
|
$parts = [];
|
||||||
|
if (!empty($address['address_1'])) {
|
||||||
|
$parts[] = $address['address_1'];
|
||||||
|
}
|
||||||
|
if (!empty($address['address_2'])) {
|
||||||
|
$parts[] = $address['address_2'];
|
||||||
|
}
|
||||||
|
// Append the Rajaongkir province/city/subdistrict string
|
||||||
|
$parts[] = $label;
|
||||||
|
|
||||||
|
if (!empty($address['postcode'])) {
|
||||||
|
$parts[] = $address['postcode'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// The SPA uses whitespace-pre-wrap so newline \n works for visual separation
|
||||||
|
return implode("\n", $parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $formatted;
|
||||||
|
}, 10, 2);
|
||||||
|
|||||||
Reference in New Issue
Block a user