feat: Tax settings + Checkout fields - Full implementation
## ✅ TAX SETTINGS - COMPLETE ### Backend (TaxController.php): - ✅ GET /settings/tax - Get all tax settings - ✅ POST /settings/tax/toggle - Enable/disable tax - ✅ GET /settings/tax/suggested - Smart suggestions based on selling locations - ✅ POST /settings/tax/rates - Create tax rate - ✅ PUT /settings/tax/rates/{id} - Update tax rate - ✅ DELETE /settings/tax/rates/{id} - Delete tax rate **Predefined Rates:** - Indonesia: 11% (PPN) - Malaysia: 6% (SST) - Singapore: 9% (GST) - Thailand: 7% (VAT) - Philippines: 12% (VAT) - Vietnam: 10% (VAT) - + Australia, NZ, UK, Germany, France, Italy, Spain, Canada **Smart Detection:** - Reads WooCommerce "Selling location(s)" setting - If specific countries selected → Show those rates - If sell to all → Show store base country rate - Zero re-selection needed! ### Frontend (Tax.tsx): - ✅ Toggle to enable/disable tax - ✅ Suggested rates card (based on selling locations) - ✅ Quick "Add Rate" button for suggested rates - ✅ Tax rates list with Edit/Delete - ✅ Add/Edit tax rate dialog - ✅ Display settings (prices include tax, shop/cart display) - ✅ Link to WooCommerce advanced settings **User Flow:** 1. Enable tax toggle 2. See: "🇮🇩 Indonesia: 11% (PPN)" [Add Rate] 3. Click Add Rate 4. Done! Tax working. ## ✅ CHECKOUT FIELDS - COMPLETE ### Backend (CheckoutController.php): - ✅ POST /checkout/fields - Get fields with all filters applied **Features:** - Listens to WooCommerce `woocommerce_checkout_fields` filter - Respects addon hide/show logic: - Checks `hidden` class - Checks `enabled` flag - Checks `hide` class - Respects digital-only products logic (hides shipping) - Returns field metadata: - required, hidden, type, options, priority - Flags custom fields (from addons) - Includes validation rules **How It Works:** 1. Addon adds field via filter 2. API applies all filters 3. Returns fields with metadata 4. Frontend renders dynamically **Example:** ```php // Indonesian Shipping Addon add_filter('woocommerce_checkout_fields', function($fields) { $fields['shipping']['shipping_subdistrict'] = [ 'required' => true, 'type' => 'select', 'options' => get_subdistricts(), ]; return $fields; }); ``` WooNooW automatically: - Fetches field - Sees required=true - Renders it - Validates it ## Benefits: **Tax:** - Zero learning curve (30 seconds setup) - No re-selecting countries - Smart suggestions - Scales for single/multi-country **Checkout Fields:** - Addon responsibility (not hardcoded) - Works with ANY addon - Respects hide/show logic - Preserves digital-only logic - Future-proof ## Next: Frontend integration for checkout fields
This commit is contained in:
@@ -27,6 +27,11 @@ class CheckoutController {
|
||||
'callback' => [ new self(), 'submit' ],
|
||||
'permission_callback' => [ \WooNooW\Api\Permissions::class, 'anon_or_wp_nonce' ], // consider capability/nonce
|
||||
]);
|
||||
register_rest_route($namespace, '/checkout/fields', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [ new self(), 'get_fields' ],
|
||||
'permission_callback' => [ \WooNooW\Api\Permissions::class, 'anon_or_wp_nonce' ],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,6 +245,108 @@ class CheckoutController {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get checkout fields with all filters applied
|
||||
* Accepts: { items: [...], is_digital_only?: bool }
|
||||
* Returns fields with required, hidden, etc. based on addons + cart context
|
||||
*/
|
||||
public function get_fields(WP_REST_Request $r): array {
|
||||
$json = $r->get_json_params();
|
||||
$items = isset($json['items']) && is_array($json['items']) ? $json['items'] : [];
|
||||
$is_digital_only = isset($json['is_digital_only']) ? (bool) $json['is_digital_only'] : false;
|
||||
|
||||
// Initialize WooCommerce checkout if not already
|
||||
if (!WC()->checkout()) {
|
||||
WC()->initialize_session();
|
||||
WC()->initialize_cart();
|
||||
}
|
||||
|
||||
// Get checkout fields with all filters applied
|
||||
$fields = WC()->checkout()->get_checkout_fields();
|
||||
|
||||
$formatted = [];
|
||||
|
||||
foreach ($fields as $fieldset_key => $fieldset) {
|
||||
foreach ($fieldset as $key => $field) {
|
||||
// Check if field should be hidden
|
||||
$hidden = false;
|
||||
|
||||
// Hide shipping fields if digital only (your existing logic)
|
||||
if ($is_digital_only && $fieldset_key === 'shipping') {
|
||||
$hidden = true;
|
||||
}
|
||||
|
||||
// Check if addon/filter explicitly hides this field
|
||||
if (isset($field['class']) && is_array($field['class'])) {
|
||||
if (in_array('hidden', $field['class']) || in_array('hide', $field['class'])) {
|
||||
$hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Respect 'enabled' flag if set by addons
|
||||
if (isset($field['enabled']) && !$field['enabled']) {
|
||||
$hidden = true;
|
||||
}
|
||||
|
||||
$formatted[] = [
|
||||
'key' => $key,
|
||||
'fieldset' => $fieldset_key, // billing, shipping, account, order
|
||||
'type' => $field['type'] ?? 'text',
|
||||
'label' => $field['label'] ?? '',
|
||||
'placeholder' => $field['placeholder'] ?? '',
|
||||
'required' => $field['required'] ?? false,
|
||||
'hidden' => $hidden,
|
||||
'class' => $field['class'] ?? [],
|
||||
'priority' => $field['priority'] ?? 10,
|
||||
'options' => $field['options'] ?? null, // For select fields
|
||||
'custom' => !in_array($key, $this->get_standard_field_keys()), // Flag custom fields
|
||||
'autocomplete'=> $field['autocomplete'] ?? '',
|
||||
'validate' => $field['validate'] ?? [],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by priority
|
||||
usort($formatted, function($a, $b) {
|
||||
return $a['priority'] <=> $b['priority'];
|
||||
});
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'fields' => $formatted,
|
||||
'is_digital_only' => $is_digital_only,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of standard WooCommerce field keys
|
||||
*/
|
||||
private function get_standard_field_keys(): array {
|
||||
return [
|
||||
'billing_first_name',
|
||||
'billing_last_name',
|
||||
'billing_company',
|
||||
'billing_country',
|
||||
'billing_address_1',
|
||||
'billing_address_2',
|
||||
'billing_city',
|
||||
'billing_state',
|
||||
'billing_postcode',
|
||||
'billing_phone',
|
||||
'billing_email',
|
||||
'shipping_first_name',
|
||||
'shipping_last_name',
|
||||
'shipping_company',
|
||||
'shipping_country',
|
||||
'shipping_address_1',
|
||||
'shipping_address_2',
|
||||
'shipping_city',
|
||||
'shipping_state',
|
||||
'shipping_postcode',
|
||||
'order_comments',
|
||||
];
|
||||
}
|
||||
|
||||
/** ----------------- Helpers ----------------- **/
|
||||
|
||||
private function accurate_quote_via_wc_cart(array $payload): array {
|
||||
|
||||
Reference in New Issue
Block a user