| @@ -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,10 +202,19 @@ | ||
| 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', | |
| 212 | + [ | |
| 213 | + 'key' => '_easy_invoice_client_id', | |
| 214 | + 'value' => (int) $customer_id, | |
| 215 | + 'compare' => '=', | |
| 216 | + ], | |
| 191 | 217 | [ |
| 192 | 218 | 'key' => '_easy_invoice_customer_id', |
| 193 | 219 | 'value' => $customer_id, |
| 194 | 220 | 'compare' => '=', |