applyFieldSanitization($field, $raw_value); // Handle currency fields with "global" option - save "global" as the value if (in_array($field_name, ['currency_code', 'currency_position']) && $processed_value === 'global') { $processed_value = 'global'; } // Apply field-specific processing if defined $processed_value = $this->applyFieldProcessing($field, $processed_value); // Apply field-specific validation if defined $validation_result = $this->applyFieldValidation($field, $processed_value); if ($validation_result !== true) { $errors[$field_name] = $validation_result; continue; } $processed_data[$field_name] = $processed_value; } // Always include payment_gateways in processed data $processed_data['payment_gateways'] = $payment_gateways_value; // Allow plugins to modify the processed data $processed_data = apply_filters('easy_invoice_form_processor_data', $processed_data, $raw_data); return [ 'data' => $processed_data, 'errors' => $errors ]; } /** * Process item data * * @since 1.0.0 * @param array $raw_item_data Raw item data * @param array $item_field_definitions Item field definitions * @return array Processed item data */ public function processItemData(array $raw_item_data, array $item_field_definitions): array { $processed_item = []; // Debug: Log the raw item data error_log("Processing item data: " . print_r($raw_item_data, true)); foreach ($item_field_definitions as $field) { $field_name = $field['name'] ?? ''; $required = $field['required'] ?? false; if (empty($field_name)) { continue; } $raw_value = $raw_item_data[$field_name] ?? ''; // Check required fields if ($required && empty($raw_value) && $field['type'] !== 'checkbox') { // For items, we'll skip invalid items rather than throwing errors error_log("Skipping required field {$field_name} - empty value"); continue; } // For non-required fields, include them even if empty (but not for checkboxes) if ($field['type'] === 'checkbox') { // For checkboxes, only include if they have a value if (isset($raw_item_data[$field_name])) { $processed_value = $this->applyFieldSanitization($field, $raw_value); $processed_item[$field_name] = $processed_value; } } else { // For non-checkbox fields, include them even if empty $processed_value = $this->applyFieldSanitization($field, $raw_value); $processed_item[$field_name] = $processed_value; } } // Debug: Log the processed item error_log("Processed item: " . print_r($processed_item, true)); return $processed_item; } /** * Process items data * * @since 1.0.0 * @param array $raw_items_data Raw items data * @param array $item_field_definitions Item field definitions * @return array Processed items data */ public function processItemsData(array $raw_items_data, array $item_field_definitions): array { $processed_items = []; foreach ($raw_items_data as $item_index => $raw_item_data) { // Ensure all checkbox fields are set foreach ($item_field_definitions as $field) { if (($field['type'] ?? '') === 'checkbox') { $field_name = $field['name'] ?? ''; if ($field_name && !isset($raw_item_data[$field_name])) { $raw_item_data[$field_name] = '0'; } } } $processed_item = $this->processItemData($raw_item_data, $item_field_definitions); if (!empty($processed_item)) { $processed_items[] = $processed_item; } } return $processed_items; } /** * Save form data to database using field configuration * * @since 1.0.0 * @param array $form_data Processed form data * @param array $field_definitions Field definitions for reference * @param object $model The model object (Invoice, Quote, etc.) * @return void */ public function saveFormDataToDatabase(array $form_data, array $field_definitions, $model): void { // Process each field from the configuration foreach ($field_definitions as $field) { $field_name = $field['name'] ?? ''; if (empty($field_name)) { continue; } // Generate database key with _easy_invoice_ prefix $db_key = '_easy_invoice_' . $field_name; // Get value from form data $value = $form_data[$field_name] ?? null; // Only save if value exists and is not empty (or is 0) if ($value !== null && $value !== '') { // Use custom save callback if provided if (isset($field['save_callback']) && is_callable($field['save_callback'])) { $field['save_callback']($value, $model); } else { // Default save to meta data if (method_exists($model, 'setMetaData')) { $model->setMetaData($db_key, $value); } } } } // Also process any extra fields that might not be in the configuration foreach ($form_data as $field_name => $value) { if ($value !== null && $value !== '') { $db_key = '_easy_invoice_' . $field_name; // Check if this field wasn't already processed above $already_processed = false; foreach ($field_definitions as $field) { if (($field['name'] ?? '') === $field_name) { $already_processed = true; break; } } if (!$already_processed && method_exists($model, 'setMetaData')) { $model->setMetaData($db_key, $value); } } } } /** * Generate database key from field name * * @since 1.0.0 * @param string $field_name The field name * @return string Database key with _easy_invoice_ prefix */ public function generateDatabaseKey(string $field_name): string { return '_easy_invoice_' . $field_name; } /** * Process field value based on field type * * @since 1.0.0 * @param array $field Field configuration * @param mixed $value Raw value * @return mixed Processed value */ private function processFieldValue(array $field, $value) { $type = $field['type'] ?? 'text'; switch ($type) { case 'number': return is_numeric($value) ? floatval($value) : 0; case 'checkbox': if (is_string($value)) { $value = strtolower($value); return ($value === '1' || $value === 'true' || $value === 'yes' || $value === 'on') ? '1' : '0'; } return ($value == '1' || $value === true) ? '1' : '0'; case 'payment_gateways': // Handle array of selected payment gateways if (is_array($value)) { // Sanitize each gateway ID and filter out empty values $sanitized_gateways = array_filter(array_map('sanitize_text_field', $value)); return implode(',', $sanitized_gateways); } elseif (is_string($value)) { // If it's already a string (comma-separated), sanitize it return sanitize_text_field($value); } return ''; case 'textarea': return sanitize_textarea_field($value); case 'email': return sanitize_email($value); case 'url': return esc_url_raw($value); case 'date': return sanitize_text_field($value); case 'select': case 'text': default: return sanitize_text_field($value); } } /** * Apply field-specific sanitization if defined * * @since 1.0.0 * @param array $field Field configuration * @param mixed $value Raw value * @return mixed Processed value */ private function applyFieldSanitization(array $field, $value) { // Check for sanitize_callback in field configuration $sanitize_callback = $field['sanitize_callback'] ?? null; if (is_callable($sanitize_callback)) { return $sanitize_callback($value); } // Fallback to basic field type processing return $this->processFieldValue($field, $value); } /** * Apply field-specific processing if defined * * @since 1.0.0 * @param array $field Field configuration * @param mixed $value Processed value * @return mixed Processed value */ private function applyFieldProcessing(array $field, $value) { // Check for process_callback in field configuration $process_callback = $field['process_callback'] ?? null; if (is_callable($process_callback)) { return $process_callback($value); } return $value; } /** * Apply field-specific validation if defined * * @since 1.0.0 * @param array $field Field configuration * @param mixed $value Processed value * @return bool|string True if valid, error message if invalid */ private function applyFieldValidation(array $field, $value) { // Check for validate_callback in field configuration $validate_callback = $field['validate_callback'] ?? null; if (is_callable($validate_callback)) { return $validate_callback($value); } return true; } /** * Get global currency value for a specific field * * @since 1.0.0 * @param string $field_name Field name (currency_code or currency_position) * @return string Global currency value */ private function getGlobalCurrencyValue(string $field_name): string { switch ($field_name) { case 'currency_code': return get_option('easy_invoice_currency_code', 'USD'); case 'currency_position': $position = get_option('easy_invoice_currency_position', 'left'); // Convert from old format if needed if ($position === 'l') return 'before'; if ($position === 'r') return 'after'; return $position; default: return ''; } } }