| @@ -177,8 +177,18 @@ | ||
| 177 | 177 | return apply_filters('easy_invoice_quotes_by_client', $filtered_quotes, $client_id); |
| 178 | 178 | } |
| 179 | 179 | |
| 180 | 180 | /** |
| 181 | + * Quotes for a client — the name QuoteService calls; it did not exist. | |
| 182 | + * | |
| 183 | + * @param int $customer_id Client id. | |
| 184 | + * @return array | |
| 185 | + */ | |
| 186 | + public function findByCustomer($customer_id): array { | |
| 187 | + return $this->findByClient((int) $customer_id); | |
| 188 | + } | |
| 189 | + | |
| 190 | + /** | |
| 181 | 191 | * Create new quote |
| 182 | 192 | * |
| 183 | 193 | * @since 1.0.0 |
| 184 | 194 | * @param array $data Quote data |
| @@ -188,9 +198,26 @@ | ||
| 188 | 198 | // Allow plugins to modify the data before creation |
| 189 | 199 | $data = apply_filters('easy_invoice_quote_create_data', $data); |
| 190 | 200 | |
| 191 | 201 | $quote = new Quote(); |
| 202 | + // Same default as invoices: discount before tax unless chosen otherwise. | |
| 203 | + if (empty($data['discount_calculation_method'])) { | |
| 204 | + $data['discount_calculation_method'] = 'before_tax'; | |
| 205 | + } | |
| 206 | + // Reserve a number under the number lock. Left to the form default, | |
| 207 | + // the model peeked at the next number without taking it, so quotes | |
| 208 | + // created at the same moment (or by two people with the builder | |
| 209 | + // open) shared one number. | |
| 210 | + $requested = (string) ($data['number'] ?? $data['quote_number'] ?? ''); | |
| 211 | + $data['number'] = (new \EasyInvoice\Services\QuoteNumberService())->claimOrGenerate($requested); | |
| 212 | + unset($data['quote_number']); | |
| 192 | 213 | $this->setQuoteData($quote, $data); |
| 214 | + | |
| 215 | + // Same as invoices: a client id without customer details is filled in | |
| 216 | + // from the client record before the first save. | |
| 217 | + if (($data['client_id'] ?? 0) > 0 && (empty($data['customer_email']) || empty($data['customer_name'])) && method_exists($quote, 'populateClientInfo')) { | |
| 218 | + $quote->populateClientInfo(); | |
| 219 | + } | |
| 193 | 220 | |
| 194 | 221 | // Allow plugins to modify the quote before saving |
| 195 | 222 | do_action('easy_invoice_quote_before_create', $quote, $data); |
| 196 | 223 | |
| @@ -493,10 +520,11 @@ | ||
| 493 | 520 | if (isset($data['notes'])) { |
| 494 | 521 | $quote->setNotes($data['notes']); |
| 495 | 522 | } |
| 496 | 523 | |
| 497 | - if (isset($data['description'])) { | |
| 498 | - $quote->setDescription($data['description']); | |
| 524 | + // Always update description if it exists in data (even if empty, to allow clearing) | |
| 525 | + if (array_key_exists('description', $data)) { | |
| 526 | + $quote->setDescription($data['description'] ?? ''); | |
| 499 | 527 | } |
| 500 | 528 | |
| 501 | 529 | if (isset($data['terms'])) { |
| 502 | 530 | $quote->setTerms($data['terms']); |
| @@ -535,8 +563,17 @@ | ||
| 535 | 563 | if (isset($data['discount_value'])) { |
| 536 | 564 | $quote->setDiscountValue($data['discount_value']); |
| 537 | 565 | } |
| 538 | 566 | |
| 567 | + // Per-quote tax switch. The invoice repository picks this up through the | |
| 568 | + // form processor's pass-through; quotes set every field by hand, and this | |
| 569 | + // one was missing, so a quote built with tax on lost it (and passed a | |
| 570 | + // tax-free invoice on conversion) whenever the site's global tax was off. | |
| 571 | + if (array_key_exists('tax_enabled', $data)) { | |
| 572 | + $enabled = $data['tax_enabled']; | |
| 573 | + $quote->setTaxEnabled(($enabled === true || $enabled === 1 || in_array(strtolower((string) $enabled), ['1', 'yes', 'true', 'on'], true)) ? 'yes' : 'no'); | |
| 574 | + } | |
| 575 | + | |
| 539 | 576 | if (isset($data['tax_rate'])) { |
| 540 | 577 | $quote->setTaxRate($data['tax_rate']); |
| 541 | 578 | } |
| 542 | 579 | |
| @@ -589,10 +626,10 @@ | ||
| 589 | 626 | $quote->setCustomFields($data['custom_fields']); |
| 590 | 627 | } |
| 591 | 628 | |
| 592 | 629 | // Populate client information if we have a client_id |
| 593 | - if ($quote->getClientId() > 0) { | |
| 594 | - $this->populateCustomerFromClient($quote, $quote->getClientId()); | |
| 630 | + if (is_numeric($quote->getClientId()) && (int) $quote->getClientId() > 0) { | |
| 631 | + $this->populateCustomerFromClient($quote, (int) $quote->getClientId()); | |
| 595 | 632 | } |
| 596 | 633 | |
| 597 | 634 | // Allow plugins to modify the data setting process |
| 598 | 635 | do_action('easy_invoice_quote_set_data_after', $quote, $data); |