request = $request; } public function execute() : CreateInvoiceResponse { $response = new CreateInvoiceResponse(); $rejection = $this->rejectTravelReason(); if ($rejection !== null) { $response->message = $rejection; return $response; } if ($this->request->accountIban === 'undefined') { $this->request->accountIban = null; } $travelReason = $this->travelReason(); $invoice = Invoice::create([ 'tenant' => currentTenant()->slug, 'cost_unit_id' => $this->request->costUnit->id, 'invoice_number' => $this->generateInvoiceNumber(), 'status' => InvoiceStatus::INVOICE_STATUS_NEW, 'type' => $this->request->invoiceType, 'type_other' => $this->request->invoiceTypeExtended, 'purpose' => $this->purpose($travelReason), 'donation' => $this->request->isDonation, 'user_id' => $this->request->paymentPurpose === null ? $this->request->userId : null, 'contact_name' => $this->request->contactName, 'contact_email' => $this->request->contactEmail, 'contact_phone' => $this->request->contactPhone, 'contact_bank_owner' => $this->request->accountOwner, 'contact_bank_iban' => $this->request->accountIban, 'amount' => $this->request->totalAmount, 'distance' => $this->request->distance, 'travel_direction' => $this->request->travelRoute, 'travel_reason' => $travelReason, 'payment_purpose' => $this->request->paymentPurpose, 'comment' => $this->request->notices, 'document_filename' => $this->request->receiptFile !== null ? $this->request->receiptFile->fullPath : null, ]); if ($invoice !== null) { $response->success = true; $response->invoice = $invoice; if ($invoice->contact_email !== null) { Mail::to($invoice->contact_email)->send(new InvoiceMailsSubmittedConfirmationMail( invoice: $invoice, costUnit: $this->request->costUnit, )); } } if ($this->request->costUnit->mail_on_new) { $recipients = [currentTenant()->email_finance]; foreach ($this->request->costUnit->treasurers()->get() as $treasurer) { if (!in_array($treasurer->email, $recipients)) { $recipients[] = $treasurer->email; } } foreach ($recipients as $recipient) { Mail::to($recipient)->send(new InvoiceMailsNewInvoiceMail( invoice: $invoice, costUnit: $this->request->costUnit, )); } } return $response; } /** * Der Zahlungsgrund, einmal beim Anlegen festgehalten. * * Danach ist er eine eigene Angabe: Die Kassenwart*in kann ihn korrigieren, und nichts schreibt ihn * mehr um. Bei Fahrtkosten setzt er sich aus Reisegrund und den gefahrenen Personen zusammen, sonst * trägt ihn "Was wurde eingekauft". Wo nichts erfasst wird -- Beitragserstattungen -- bleibt er leer. * * Steht er schon fest, wird er übernommen: Eine Abrechnungskorrektur kopiert den Beleg, und ein von * Hand gesetzter Grund darf dabei nicht verloren gehen. */ private function purpose(?string $travelReason) : ?string { if (trim((string) $this->request->purpose) !== '') { return $this->request->purpose; } $purpose = Invoice::joinPurposeParts( $this->request->invoiceType === InvoiceType::INVOICE_TYPE_TRAVELLING // Der Name des Grundes, nicht sein Schlüssel: In der Belegliste soll "Materialtransport" // stehen, nicht "material_transport". ? [TravelReason::text($travelReason), $this->request->travellers] : [$this->request->invoiceTypeExtended] ); return $purpose === '' ? null : $purpose; } /** * Was in `travel_reason` landet: der Schlüssel des gewählten Grundes -- oder, bei "Anderer Grund", * der Text selbst. Der Schlüssel `other` sagt für sich nichts aus, der Text alles. * * Ein unbekannter Wert ist deshalb kein Fehler, sondern genau dieser Fall: So kommen auch * Bestandsbelege und Kopien durch, die ihren Freitext schon mitbringen. */ private function travelReason() : ?string { $reason = TravelReason::find($this->request->travelReason); if ($reason === null) { return $this->request->travelReason; } return $reason->requires_note ? trim((string) $this->request->travelReasonNote) : $reason->slug; } /** * Sicherheitsnetz hinter der Oberfläche: Dort geht es erst weiter, wenn die Erläuterung steht. Über * einen direkten Aufruf ginge das sonst vorbei, und ein "Anderer Grund" ohne Text sagt nichts aus -- * gespeichert würde ein leerer Reisegrund. */ private function rejectTravelReason() : ?string { $reason = TravelReason::find($this->request->travelReason); if ($reason === null || !$reason->requires_note) { return null; } return trim((string) $this->request->travelReasonNote) === '' ? 'Bitte gib an, was der Grund für die Reise war.' : null; } private function generateInvoiceNumber() : string { $lastInvoiceNumber = Invoice::query() ->where('tenant', currentTenant()->slug) ->whereYear('created_at', date('Y')) ->count(); $invoiceNumber = $lastInvoiceNumber + 1; $invoiceNumber = str_pad($invoiceNumber, 4, '0', STR_PAD_LEFT); return sprintf('%1$s-%2$s', date('Y'), $invoiceNumber); } }