post_type !== $this->post_type) { return null; } $invoice = new Invoice($post); // Ensure invoice has proper slug for pretty URLs $invoice->ensureProperSlug(); // Allow plugins to modify the found invoice return apply_filters('easy_invoice_invoice_found', $invoice, $id); } /** * Get all invoices * * @param array $args Optional arguments to filter the results * @return array Array of Invoice models */ public function all($args = []) { $default_args = [ 'post_type' => $this->post_type, 'posts_per_page' => -1, 'post_status' => 'publish', 'orderby' => 'date', 'order' => 'DESC', ]; $query_args = wp_parse_args($args, $default_args); // Allow plugins to modify query arguments $query_args = apply_filters('easy_invoice_invoice_query_args', $query_args); $query = new WP_Query($query_args); $invoices = []; if ($query->have_posts()) { foreach ($query->posts as $post) { $invoice = new Invoice($post); // Ensure invoice has proper slug for pretty URLs $invoice->ensureProperSlug(); $invoices[] = $invoice; } } // Allow plugins to modify the results return apply_filters('easy_invoice_invoices_found', $invoices, $query_args); } /** * Create a new invoice * * @param array $data Invoice data * @return Invoice|false The created invoice or false on failure */ public function create($data) { // Allow plugins to modify the data before creation $data = apply_filters('easy_invoice_invoice_create_data', $data); // Create a new invoice object $invoice = new Invoice(); // Set basic data $invoice->setTitle($data['title'] ?? 'New Invoice'); // Auto-generate invoice number if not provided if ((!isset($data['number']) || empty($data['number'])) && (!isset($data['invoice_number']) || empty($data['invoice_number']))) { $invoice_number_service = easy_invoice_get_invoice_number_service(); $data['number'] = $invoice_number_service->generateUniqueNumber(); } // Set invoice data $this->setInvoiceData($invoice, $data, false); // Allow plugins to modify the invoice before saving do_action('easy_invoice_invoice_before_create', $invoice, $data); // Save the invoice (this will create the post and ensure proper permalinks) if ($invoice->save()) { // Allow plugins to perform actions after creation do_action('easy_invoice_invoice_created', $invoice, $data); return $invoice; } return false; } /** * Update an existing invoice * * @param int $id The invoice ID * @param array $data The invoice data * @return Invoice|null The updated invoice model or null if not found */ public function update($id, $data) { try { $invoice = $this->find($id); if (!$invoice) { return null; } // Set invoice data $this->setInvoiceData($invoice, $data); // Save the invoice $result = $invoice->save(); return $result ? $invoice : null; } catch (\Exception $e) { return null; } } /** * Delete an invoice * * @param int $id The invoice ID * @return bool True if successful, false otherwise */ public function delete($id) { $invoice = $this->find($id); if (!$invoice) { return false; } // Allow plugins to perform actions before deletion do_action('easy_invoice_invoice_before_delete', $invoice); $result = wp_delete_post($id, true); if ($result) { // Allow plugins to perform actions after deletion do_action('easy_invoice_invoice_deleted', $id); } return $result instanceof WP_Post; } /** * Find invoices by customer ID * * @param int $customer_id The customer ID * @return array Array of Invoice models */ public function findByCustomer($customer_id) { $args = [ 'meta_query' => [ [ 'key' => '_easy_invoice_customer_id', 'value' => $customer_id, 'compare' => '=', ], ], ]; $invoices = $this->all($args); // Allow plugins to modify the filtered results return apply_filters('easy_invoice_invoices_by_customer', $invoices, $customer_id); } /** * Find invoices by status * * @param string $status The invoice status * @return array Array of Invoice models */ public function findByStatus($status) { $args = [ 'meta_query' => [ [ 'key' => '_easy_invoice_status', 'value' => $status, 'compare' => '=', ], ], ]; $invoices = $this->all($args); // Allow plugins to modify the filtered results return apply_filters('easy_invoice_invoices_by_status', $invoices, $status); } /** * Find invoices due within a date range * * @param string $start_date The start date in 'Y-m-d' format * @param string $end_date The end date in 'Y-m-d' format * @return array Array of Invoice models */ public function findByDueDate($start_date, $end_date = null) { $meta_query = [ [ 'key' => '_easy_invoice_due_date', 'value' => $start_date, 'compare' => '>=', 'type' => 'DATE', ], ]; if ($end_date) { $meta_query[] = [ 'key' => '_easy_invoice_due_date', 'value' => $end_date, 'compare' => '<=', 'type' => 'DATE', ]; } $args = [ 'meta_query' => $meta_query, ]; $invoices = $this->all($args); // Allow plugins to modify the filtered results return apply_filters('easy_invoice_invoices_by_due_date', $invoices, $start_date, $end_date); } /** * Count invoices * * @param array $args Optional arguments to filter the results * @return int Number of invoices */ public function count($args = []) { $default_args = [ 'post_type' => $this->post_type, 'post_status' => 'publish', ]; $query_args = wp_parse_args($args, $default_args); // Allow plugins to modify query arguments $query_args = apply_filters('easy_invoice_invoice_count_query_args', $query_args); $query = new WP_Query($query_args); $count = $query->found_posts; // Allow plugins to modify the count return apply_filters('easy_invoice_invoice_count', $count, $query_args); } /** * Find an invoice by its invoice number (stored in post meta) * * @param string $number Invoice number (exact match) * @return Invoice|null */ public function findByNumber(string $number) { $args = [ 'post_type' => $this->post_type, 'post_status' => ['publish', 'draft', 'private', 'pending'], 'posts_per_page' => 1, 'meta_query' => [ [ 'key' => \EasyInvoice\Constants\InvoiceMetaKeys::NUMBER, 'value' => $number, 'compare' => '=', ], ], 'orderby' => 'date', 'order' => 'DESC', ]; $query = new WP_Query($args); if (!empty($query->posts)) { $post = $query->posts[0]; if ($post && $post->post_type === $this->post_type) { $invoice = new Invoice($post); $invoice->ensureProperSlug(); return apply_filters('easy_invoice_invoice_found_by_number', $invoice, $number); } } return null; } /** * Find published invoice by ID * * @param int $id The invoice ID * @return Invoice|null The invoice model or null if not found */ public function findPublished(int $id) { $post = get_post($id); if (!$post || $post->post_type !== $this->post_type || $post->post_status !== 'publish') { return null; } $invoice = new Invoice($post); // Ensure invoice has proper slug for pretty URLs $invoice->ensureProperSlug(); return $invoice; } /** * Update all existing draft invoices to published status for proper permalinks * * @return int Number of invoices updated */ public function updateAllExistingInvoices(): int { global $wpdb; // Find all draft invoices $draft_invoices = $wpdb->get_col($wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND post_status = 'draft'", $this->post_type )); $updated_count = 0; foreach ($draft_invoices as $invoice_id) { $invoice = $this->find($invoice_id); if ($invoice) { // Save the invoice (this will publish it and ensure proper permalinks) if ($invoice->save()) { $updated_count++; } } } // Allow plugins to perform actions after bulk update do_action('easy_invoice_invoices_bulk_updated', $updated_count); return $updated_count; } /** * Set invoice data from array * * @param Invoice $invoice * @param array $data * @param bool $preserve_number * @return void */ protected function setInvoiceData(Invoice $invoice, array $data, $preserve_number = false) { // Allow plugins to modify the data setting process do_action('easy_invoice_invoice_set_data_before', $invoice, $data); // Set basic invoice fields (core fields that have dedicated methods) if (isset($data['title'])) { $invoice->setTitle($data['title']); } if (isset($data['invoice_title'])) { $invoice->setTitle($data['invoice_title']); } if (isset($data['description'])) { $invoice->setDescription($data['description']); } if (isset($data['invoice_description'])) { $invoice->setDescription($data['invoice_description']); } // Set invoice number (only if not preserving existing) if (isset($data['invoice_number']) && !empty($data['invoice_number'])) { $invoice->setNumber($data['invoice_number']); } elseif (isset($data['number']) && !empty($data['number'])) { $invoice->setNumber($data['number']); } // Set dates if (isset($data['issue_date'])) { $invoice->setIssueDate($data['issue_date']); } if (isset($data['invoice_date'])) { $invoice->setIssueDate($data['invoice_date']); } if (isset($data['issue-date'])) { $invoice->setIssueDate($data['issue-date']); } if (isset($data['due_date'])) { $invoice->setDueDate($data['due_date']); } if (isset($data['due-date'])) { $invoice->setDueDate($data['due-date']); } // Set status if (isset($data['status'])) { $invoice->setStatus($data['status']); } if (isset($data['invoice_status'])) { $invoice->setStatus($data['invoice_status']); } if (isset($data['payment_status'])) { $invoice->setStatus($data['payment_status']); } // Set notes and terms if (isset($data['notes'])) { $invoice->setNotes($data['notes']); } if (isset($data['terms_and_conditions'])) { $invoice->setTerms($data['terms_and_conditions']); } if (isset($data['internal_notes'])) { $invoice->setInternalNotes($data['internal_notes']); } if (isset($data['payment_instructions'])) { $invoice->setPaymentInstructions($data['payment_instructions']); } // Set payment gateways if (isset($data['payment_gateways']) && is_array($data['payment_gateways'])) { $invoice->setPaymentGateways($data['payment_gateways']); } elseif (isset($data['payment_gateways']) && is_string($data['payment_gateways'])) { $gateways = array_filter(explode(',', $data['payment_gateways'])); $invoice->setPaymentGateways($gateways); } else { $invoice->setPaymentGateways([]); } // Set payment gateways toggle state if (isset($data['payment_gateways_toggle_state'])) { $invoice->setPaymentGatewaysToggleState((string)$data['payment_gateways_toggle_state']); } // Set template if (isset($data['invoice_template'])) { $invoice->setTemplate($data['invoice_template']); } // Set client information if (isset($data['customer_name'])) { $invoice->setCustomerName($data['customer_name']); } if (isset($data['customer_address'])) { $invoice->setCustomerAddress($data['customer_address']); } if (isset($data['customer_email'])) { $invoice->setCustomerEmail($data['customer_email']); } // Set discount and tax settings if (isset($data['discount_type'])) { $invoice->setDiscountType($data['discount_type']); } if (isset($data['discount_value'])) { $invoice->setDiscountValue($data['discount_value']); } if (isset($data['discount'])) { $invoice->setDiscountValue($data['discount']); } if (isset($data['tax_rate'])) { $invoice->setTaxRate($data['tax_rate']); } if (isset($data['calculation_method'])) { $invoice->setCalculationMethod($data['calculation_method']); } if (isset($data['prices_include_tax'])) { $invoice->setPricesIncludeTax($data['prices_include_tax']); } // Set currency settings if (isset($data['currency_code'])) { $invoice->setCurrencyCode($data['currency_code']); } if (isset($data['currency_position'])) { $invoice->setCurrencyPosition($data['currency_position']); } // Set footer text if (isset($data['footer_text'])) { $invoice->setFooterText($data['footer_text']); } // Set client ID if (isset($data['client_id']) && !empty($data['client_id'])) { $invoice->setClientId((int) $data['client_id']); } // Set items if (isset($data['items']) && is_array($data['items'])) { $invoice->setItems($data['items']); } // Set custom fields if (isset($data['custom_fields']) && is_array($data['custom_fields'])) { $invoice->setCustomFields($data['custom_fields']); } // Set recurring fields if (isset($data['recurring_enabled'])) { $invoice->setRecurringEnabled((bool) $data['recurring_enabled']); } if (isset($data['recurring_frequency'])) { $invoice->setRecurringFrequency($data['recurring_frequency']); } if (isset($data['recurring_interval'])) { $invoice->setRecurringInterval((int) $data['recurring_interval']); } if (isset($data['recurring_start_date'])) { $invoice->setRecurringStartDate($data['recurring_start_date']); } // Use configuration-driven approach for all other fields $form_processor = new \EasyInvoice\Forms\FormProcessor(); $field_registration = new \EasyInvoice\Forms\Invoice\InvoiceFieldRegistration(); // Get all field definitions from all tabs $field_definitions = []; $tabs = $field_registration->getTabs(); foreach ($tabs as $tab_id => $tab) { $tab_fields = $field_registration->getFields($tab_id); foreach ($tab_fields as $field) { $field_definitions[] = $field; } } // Save form data using field configuration $form_processor->saveFormDataToDatabase($data, $field_definitions, $invoice); // Allow plugins to modify the data setting process do_action('easy_invoice_invoice_set_data_after', $invoice, $data); } }