feat: migrate shipping to form-level and integrate flags.json as single source of truth

Shipping Migration:
- Move shipping configuration from product-level to form-level
- Add form shipping tab in form settings (no_shipping, flat_rate, free_shipping)
- Update FlatRate to register at form level instead of product level
- Update checkout logic to read from form settings
- Support percentage-based flat rate calculation
- Simplify shipping method IDs (flat_rate, free_shipping)

Currency Flags Integration:
- Add formipay_get_all_currency_flags() to read from admin/assets/json/flags.json
- Remove hardcoded CURRENCY_FLAGS emoji map from VariationField.js
- Create CurrencyFlag component to render base64 flag images
- Localize currency_flags to window.formipayProductDetails
- Update shipping info display in admin order details

Benefits:
- Form-level shipping prevents multiplying shipping costs per product
- Single source of truth for currency flags (flags.json)
- Better support for future cart system
- Consistent with e-commerce standards
This commit is contained in:
dwindown
2026-04-23 08:12:40 +07:00
parent 0094a3571c
commit 008188b790
13 changed files with 2819 additions and 797 deletions

View File

@@ -15,8 +15,9 @@ class FlatRate extends Shipping {
parent::__construct();
add_filter( 'formipay/product-config/tab:shipping/method', [$this, 'add_shipping_method'], 15 );
add_filter( 'formipay/product-config/tab:shipping', [$this, 'add_shipping_settings'], 15 );
// Register flat rate as a form-level shipping method
add_filter( 'formipay/form-settings/tab:shipping/method', [$this, 'add_shipping_method'], 15 );
add_filter( 'formipay/form-settings/tab:shipping', [$this, 'add_shipping_settings'], 15 );
// Add to order details
add_filter( 'formipay/order/order-details', [$this, 'add_shipping_to_order_details'], 99, 3 );
@@ -33,113 +34,133 @@ class FlatRate extends Shipping {
}
/**
* Add flat rate settings to form shipping configuration
* These fields are shown when "Flat Rate" is selected as the shipping method
*/
public function add_shipping_settings($fields) {
// Get global currencies configuration
$global_currencies = get_global_currency_array();
// Basic flat rate fields (type and label)
$flat_rate_fields = array(
$this->shipping_method.'_group' => array(
'type' => 'group_title',
'label' => __( 'Flat Rate Setup', 'formipay' ),
'description' => __( 'Configure flat rate shipping cost for this form', 'formipay' ),
'dependency' => array(
array(
'key' => 'product_type',
'value' => 'physical',
'section' => 'general'
),
array(
'key' => 'shipping_method',
'value' => 'flat_rate'
)
'key' => 'shipping_enabled',
'value' => 'flat_rate'
),
'dependencies' => '&&',
'group' => 'started'
),
$this->shipping_method.'_type' => array(
'type' => 'select',
'label' => __( 'Type', 'formipay' ),
'options' => array(
'fixed' => __( 'Fixed', 'formipay' ),
'percentage' => __( 'Percentage', 'formipay' )
'fixed' => __( 'Fixed Amount', 'formipay' ),
'percentage' => __( 'Percentage of Order Total', 'formipay' )
),
'value' => 'fixed',
'dependency' => array(
array(
'key' => 'product_type',
'value' => 'physical',
'section' => 'general'
),
array(
'key' => 'shipping_method',
'value' => 'flat_rate'
)
'key' => 'shipping_enabled',
'value' => 'flat_rate'
),
'dependencies' => '&&',
),
$this->shipping_method.'_amount' => array(
'type' => 'number',
'label' => __( 'Amount', 'formipay' ),
'value' => '10',
'dependency' => array(
array(
'key' => 'product_type',
'value' => 'physical',
'section' => 'general'
),
array(
'key' => 'shipping_method',
'value' => 'flat_rate'
)
),
'dependencies' => '&&',
),
$this->shipping_method.'_label' => array(
'type' => 'text',
'label' => __( 'Label', 'formipay' ),
'description' => __( 'This will be shown in Order Review and Order Details', 'formipay' ),
'value' => __( 'Shipping Fee', 'formipay' ),
'dependency' => array(
array(
'key' => 'product_type',
'value' => 'physical',
'section' => 'general'
),
array(
'key' => 'shipping_method',
'value' => 'flat_rate'
)
),
'dependencies' => '&&',
'group' => 'ended'
),
);
// Add per-currency amount fields
foreach ($global_currencies as $currency) {
// Get the currency code (first part of triple) - this is used for meta key suffix
$currency_code = formipay_get_currency_data_by_value($currency['currency'], 'symbol');
$step = ($currency['decimal_digits'] ?? 2) > 0 ? pow(10, -($currency['decimal_digits'] ?? 2)) : 1;
$is_last = ($currency === end($global_currencies));
$flat_rate_fields[$this->shipping_method.'_amount_'.$currency_code] = array(
'type' => 'number',
'label' => sprintf(__( 'Amount (%s)', 'formipay' ), $currency_code),
'description' => $is_last ? __( 'Shipping cost for this form (not per-product)', 'formipay' ) : '',
'step' => $step,
'min' => 0,
'placeholder' => $is_last ? __( 'Enter Amount...', 'formipay' ) : __( 'Auto', 'formipay' ),
'dependency' => array(
'key' => 'shipping_enabled',
'value' => 'flat_rate'
),
'group' => $is_last ? 'ended' : null,
);
}
// Merge fields into the main fields array
foreach($flat_rate_fields as $key => $value){
$fields[$key] = $value;
}
return $fields;
}
/**
* Add shipping cost to order details
*
* @param array $details Order details array
* @param int $form_id Product/form ID
* @param array $order_data Order data from submission
* @return array Updated order details
*/
public function add_shipping_to_order_details( $details, $form_id, $order_data ) {
if( formipay_get_post_meta($form_id, 'product_type') == 'physical' && formipay_get_post_meta($form_id, 'shipping_method')){
if ( formipay_get_post_meta($form_id, 'product_type') == 'physical' && formipay_get_post_meta($form_id, 'shipping_method') == 'flat_rate' ) {
$amount = floatval( formipay_price_format( formipay_get_post_meta( $form_id, 'flat_rate_amount' ) ) );
$flat_rate_type = formipay_get_post_meta($form_id, 'flat_rate_type');
$flat_rate_label = formipay_get_post_meta($form_id, 'flat_rate_label');
if( formipay_get_post_meta($form_id, 'flat_rate_type') == 'percentage' ) {
$price = floatval( formipay_get_post_meta($form_id, 'product_price') );
$calculate = $price * $amount / 100;
// Get the selected currency from request (same way Order class does it)
$currency = isset($_REQUEST['currency']) ? sanitize_text_field( wp_unslash($_REQUEST['currency']) ) : (string) formipay_default_currency('code');
// Get flat rate amount - check for currency-specific first, then fallback to base
$flat_rate_amount = formipay_get_post_meta($form_id, 'flat_rate_amount_' . $currency);
if (empty($flat_rate_amount)) {
$flat_rate_amount = formipay_get_post_meta($form_id, 'flat_rate_amount');
}
$amount = floatval( formipay_price_format($flat_rate_amount) );
// For percentage-based, calculate from actual product price paid
if ( $flat_rate_type == 'percentage' ) {
// Find the actual product price from order details (already currency-aware)
$product_price = 0;
foreach ($details as $item) {
if (isset($item['context']) && $item['context'] == 'product') {
// Use the first product's amount (already in selected currency)
$product_price = floatval($item['amount']);
break;
}
}
// If no product found in details, fallback to lookup by currency
if ($product_price == 0) {
$regular_key = 'setting_product_price_regular_' . $currency;
$sale_key = 'setting_product_price_sale_' . $currency;
$regular_price = formipay_get_post_meta($form_id, $regular_key);
$sale_price = formipay_get_post_meta($form_id, $sale_key);
$product_price = ($sale_price !== '' && $sale_price !== null) ? floatval($sale_price) : floatval($regular_price);
}
$calculate = $product_price * $amount / 100;
$amount = floatval($calculate);
}
$details[] = [
'item' => formipay_get_post_meta($form_id, 'flat_rate_label'),
'item' => $flat_rate_label,
'amount' => $amount,
'subtotal' => $amount
];
}
return $details;
}