PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.0
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.0
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
← All changes | includes/Repositories/InvoiceRepository.php +74 -11 2.1.12.4.0 View file →
@@ -103,16 +103,33 @@
103 103 // Set basic data
104 104 $invoice->setTitle($data['title'] ?? 'New Invoice');
105 105
106 106 // Auto-generate invoice number if not provided
107 - if ((!isset($data['number']) || empty($data['number'])) &&
108 - (!isset($data['invoice_number']) || empty($data['invoice_number']))) {
109 - $invoice_number_service = easy_invoice_get_invoice_number_service();
110 - $data['number'] = $invoice_number_service->generateUniqueNumber();
107 + // A number the form sent is kept only if nobody has it yet (two
108 + // builders opened at once are pre-filled with the same one); the
109 + // counter moves past it, or a fresh number is issued.
110 + $invoice_number_service = easy_invoice_get_invoice_number_service();
111 + $requested = (string) ($data['number'] ?? $data['invoice_number'] ?? '');
112 + $data['number'] = $invoice_number_service->claimOrGenerate($requested);
113 + unset($data['invoice_number']);
114 +
115 + // Discount timing defaults to "before tax", the first choice the builder
116 + // offers. Left empty, the model's arithmetic takes the after-tax branch,
117 + // which is what API- and import-created invoices used to get.
118 + if (empty($data['discount_calculation_method'])) {
119 + $data['discount_calculation_method'] = 'before_tax';
111 120 }
112 -
121 +
113 122 // Set invoice data
114 123 $this->setInvoiceData($invoice, $data, false);
124 +
125 + // A client was given but no customer details: take them from the
126 + // client record now, so the object handed back (and the meta saved)
127 + // already carries the name and email — callers that email straight
128 + // after creating would otherwise see "client email is missing".
129 + if (is_numeric($data['client_id'] ?? 0) && (int) ($data['client_id'] ?? 0) > 0 && (empty($data['customer_email']) || empty($data['customer_name'])) && method_exists($invoice, 'populateClientInfo')) {
130 + $invoice->populateClientInfo();
131 + }
115 132
116 133 // Allow plugins to modify the invoice before saving
117 134 do_action('easy_invoice_invoice_before_create', $invoice, $data);
118 135
@@ -185,11 +202,20 @@
185 202 * @param int $customer_id The customer ID
186 203 * @return array Array of Invoice models
187 204 */
188 205 public function findByCustomer($customer_id) {
206 + // Invoices record their client as _easy_invoice_client_id; the
207 + // _easy_invoice_customer_id key was never written, so this lookup
208 + // used to match nothing (the dashboard's "active clients" stayed 0).
189 209 $args = [
190 210 'meta_query' => [
211 + 'relation' => 'OR',
191 212 [
213 + 'key' => '_easy_invoice_client_id',
214 + 'value' => (int) $customer_id,
215 + 'compare' => '=',
216 + ],
217 + [
192 218 'key' => '_easy_invoice_customer_id',
193 219 'value' => $customer_id,
194 220 'compare' => '=',
195 221 ],
@@ -286,8 +312,42 @@
286 312 return apply_filters('easy_invoice_invoice_count', $count, $query_args);
287 313 }
288 314
289 315 /**
316 + * Find an invoice by its invoice number (stored in post meta)
317 + *
318 + * @param string $number Invoice number (exact match)
319 + * @return Invoice|null
320 + */
321 + public function findByNumber(string $number) {
322 + $args = [
323 + 'post_type' => $this->post_type,
324 + 'post_status' => ['publish', 'draft', 'private', 'pending'],
325 + 'posts_per_page' => 1,
326 + 'meta_query' => [
327 + [
328 + 'key' => \EasyInvoice\Constants\InvoiceMetaKeys::NUMBER,
329 + 'value' => $number,
330 + 'compare' => '=',
331 + ],
332 + ],
333 + 'orderby' => 'date',
334 + 'order' => 'DESC',
335 + ];
336 +
337 + $query = new WP_Query($args);
338 + if (!empty($query->posts)) {
339 + $post = $query->posts[0];
340 + if ($post && $post->post_type === $this->post_type) {
341 + $invoice = new Invoice($post);
342 + $invoice->ensureProperSlug();
343 + return apply_filters('easy_invoice_invoice_found_by_number', $invoice, $number);
344 + }
345 + }
346 + return null;
347 + }
348 +
349 + /**
290 350 * Find published invoice by ID
291 351 *
292 352 * @param int $id The invoice ID
293 353 * @return Invoice|null The invoice model or null if not found
@@ -361,14 +421,15 @@
361 421 if (isset($data['invoice_title'])) {
362 422 $invoice->setTitle($data['invoice_title']);
363 423 }
364 424
365 - if (isset($data['description'])) {
366 - $invoice->setDescription($data['description']);
425 + // Always update description if it exists in data (even if empty, to allow clearing)
426 + if (array_key_exists('description', $data)) {
427 + $invoice->setDescription($data['description'] ?? '');
367 428 }
368 429
369 - if (isset($data['invoice_description'])) {
370 - $invoice->setDescription($data['invoice_description']);
430 + if (array_key_exists('invoice_description', $data)) {
431 + $invoice->setDescription($data['invoice_description'] ?? '');
371 432 }
372 433
373 434 // Set invoice number (only if not preserving existing)
374 435 if (isset($data['invoice_number']) && !empty($data['invoice_number'])) {
@@ -477,10 +538,12 @@
477 538 if (isset($data['tax_rate'])) {
478 539 $invoice->setTaxRate($data['tax_rate']);
479 540 }
480 541
481 - if (isset($data['calculation_method'])) {
482 - $invoice->setCalculationMethod($data['calculation_method']);
542 + if (isset($data['discount_calculation_method'])) {
543 + $invoice->setDiscountCalculationMethod($data['discount_calculation_method']);
544 + } elseif (isset($data['calculation_method'])) {
545 + $invoice->setDiscountCalculationMethod($data['calculation_method']);
483 546 }
484 547
485 548 if (isset($data['prices_include_tax'])) {
486 549 $invoice->setPricesIncludeTax($data['prices_include_tax']);