'0','fee_fixed'=>'0','shipping_cost'=>'0','packaging_cost'=>'0','threshold'=>'15','confirmed'=>false]; } public static function get(): array { return array_merge(self::defaults(), (array) get_option('owwc_settings', [])); } public static function number($value, float $max = 1000000000): string { if (is_array($value) || is_object($value)) { // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Plain-text validation error is caught by the admin handler and escaped for HTML or sent as JSON to textContent; escaping here would corrupt those messages. throw new InvalidArgumentException(__('Use a numeric value.', 'orderworth')); } $value = trim((string) $value); if (!preg_match('/^(?:[0-9]+(?:\.[0-9]+)?|\.[0-9]+)$/D', $value) || !is_finite((float) $value) || (float) $value > $max) { // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Plain-text validation error is caught by the admin handler and escaped for HTML or sent as JSON to textContent; escaping here would corrupt those messages. throw new InvalidArgumentException(__('Enter a non-negative number within the allowed range.', 'orderworth')); } return $value; } public static function percentage($value): string { return \OrderWorth\Engine::normalize_percentage(self::number($value, 100)); } public static function sanitize(array $input): array { $out = self::defaults(); foreach (['fee_percent','fee_fixed','shipping_cost','packaging_cost','threshold'] as $key) { $out[$key] = in_array($key, ['fee_percent','threshold'], true) ? self::percentage($input[$key] ?? '') : self::number($input[$key] ?? ''); } $out['confirmed'] = !empty($input['confirmed']); return $out; } public static function assumptions($order): array { $settings = self::get(); $rules = apply_filters('owwc_cost_rules', $settings, $order); $out = []; foreach (['fee_percent','fee_fixed','shipping_cost','packaging_cost','confirmed'] as $key) { $out[$key] = $rules[$key] ?? $settings[$key]; } $out['currency'] = get_woocommerce_currency(); $out['decimals'] = wc_get_price_decimals(); $out['recorded_at'] = gmdate('c'); $out['version'] = 1; $out['shipping_rule_review'] = !empty($rules['shipping_rule_review']); // Retain stored configuration without silently substituting global defaults. $stored = (array) get_option('owwc_pro_rules', []); $out['conditional_rules_unavailable'] = (!empty($stored['payments']) || !empty($stored['shipping'])) && !apply_filters('owwc_conditional_rules_available', false); return apply_filters('owwc_assumptions', $out, $order); } /** Translate pure calculation-library errors at the WordPress presentation boundary. */ public static function translate_error(string $message): string { switch ($message) { case 'Invalid monetary amount.': return __('Invalid monetary amount.', 'orderworth'); case 'Unsupported currency precision.': return __('Unsupported currency precision.', 'orderworth'); case 'Use a percentage from 0 to 100 with at most 12 decimal places.': return __('Use a percentage from 0 to 100 with at most 12 decimal places.', 'orderworth'); case 'Every required money input must be a non-negative integer.': return __('Every required money input must be a non-negative integer.', 'orderworth'); case 'Invalid percentage.': return __('Invalid percentage.', 'orderworth'); case 'Invalid payment cost.': return __('Invalid payment cost.', 'orderworth'); case 'Allocation amount is outside the supported range.': return __('Allocation amount is outside the supported range.', 'orderworth'); case 'Allocation weights must be non-negative integers.': return __('Allocation weights must be non-negative integers.', 'orderworth'); case 'Allocation weights exceed the supported range.': return __('Allocation weights exceed the supported range.', 'orderworth'); default: return $message; } } public static function messages(): array { return [ 'fx_missing'=>__('Exchange rate unavailable or historical context changed. This order is excluded from normalized totals. Open Exchange Rates to inspect or retry.', 'orderworth'), 'fx_cost_basis_missing'=>__('The historical currency of these costs is unconfirmed. Review and record base-currency costs before including this order.', 'orderworth'), 'fx_fee_incomplete'=>__('The recorded payment fee currency or historical fee rate is unavailable. Confirm a base-currency payment expense.', 'orderworth'), 'fx_refund_currency'=>__('Refund currency differs from the original order. Resolve the inconsistency before reporting.', 'orderworth'), 'conditional_rules_unavailable' => __('Saved conditional expense rules need the compatible Pro add-on. Recorded snapshots remain included. For an order without a snapshot, enter both manual payment and shipping expenses before including it.', 'orderworth'), 'setup_required' => __('Confirm your cost assumptions in Settings.', 'orderworth'), 'missing_cogs' => __('One or more items lack a confirmed cost. Enter an order-level product-cost total, or correct native order costs.', 'orderworth'), 'unsupported_product' => __('This order contains a product type or extension outside the tested scope.', 'orderworth'), 'currency_mismatch' => __('Different currency or currency precision: excluded to avoid mixing amounts.', 'orderworth'), 'unpaid_order' => __('Only processing, completed and refunded orders are eligible.', 'orderworth'), 'refund_needs_review' => __('Review this refund and explicitly enter recoverable product costs, including zero.', 'orderworth'), 'refund_or_tax_mismatch' => __('Refund and tax totals do not reconcile. Review the order.', 'orderworth'), 'recovery_exceeds_cost' => __('Recovered product cost exceeds the original cost.', 'orderworth'), 'recovery_without_refund' => __('Product cost recovery requires a recorded refund.', 'orderworth'), 'invalid_input' => __('A required amount is invalid. Review the recorded values.', 'orderworth'), 'no_items' => __('No supported product line items were found.', 'orderworth'), 'order_unavailable' => __('This order became unavailable during the scan.', 'orderworth'), 'order_date_changed' => __('The order date moved outside the selected report period.', 'orderworth'), 'order_date_unavailable' => __('This order has no valid creation date and is excluded from the selected period.', 'orderworth'), 'saved_adjustment_unavailable' => __('This order has saved add-on expenses or return adjustments whose processing is unavailable. Its records are preserved and its profit is excluded. Reactivate a compatible OrderWorth Pro add-on, then run a fresh report.', 'orderworth'), 'actual_expense_basis_mismatch' => __('The saved actual expenses no longer match this order or reporting currency. Review them before using this estimate.', 'orderworth'), 'returns_review_stale' => __('The refund or item details changed after the returns review. Review the current returned items before using their profit allocation.', 'orderworth'), 'payment_cost_basis_mismatch' => __('The saved payment expense uses a different or invalid currency or precision. Review and save this payment expense in the current reporting currency before using its profit estimate.', 'orderworth'), 'shipping_rule_review' => __('Multiple shipping methods: enter an explicit total shipping expense for this order.', 'orderworth'), ]; } }