+
+ +
' : ''; !!}{!! nl2br($item->description) !!}
- -
-
+
+ +
-
-
- -
+
diff --git a/app/Http/Controllers/Api/InvoiceController.php b/app/Http/Controllers/Api/InvoiceController.php index ef3992f..8ebc652 100644 --- a/app/Http/Controllers/Api/InvoiceController.php +++ b/app/Http/Controllers/Api/InvoiceController.php @@ -2,10 +2,12 @@ namespace App\Http\Controllers\Api; +use App\Enum\InvoiceTypeCode; use App\Http\Controllers\Controller; use App\Models\Invoice; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Validation\Rule; class InvoiceController extends Controller { @@ -36,16 +38,21 @@ class InvoiceController extends Controller 'customer_id' => 'required|integer|exists:customers,id', 'address_id' => 'required|integer|exists:addresses,id', 'delivery_id' => 'nullable|integer|exists:addresses,id', + 'project_id' => 'nullable|integer|exists:projects,id', + 'currency_code' => 'required|string', + 'type' => [Rule::enum(InvoiceTypeCode::class)], + 'project_count' => 'nullable|integer', 'tax' => 'required|numeric', 'sum' => 'required|numeric', + 'due_date' => 'required|date', + 'cash_discount' => 'nullable|numeric', + 'cash_discount_date' => 'nullable|date', ]); + $invoiceData['user_id'] = auth()->id(); - $invoiceData['type'] = '380'; $invoiceData['status'] = 'created'; $invoiceData['invoice_number'] = Invoice::whereYear('created_at', now()->year)->count() + 1; - - $invoice = new Invoice($invoiceData); $invoice->save(); @@ -63,6 +70,9 @@ class InvoiceController extends Controller 'delivery_id' => 'nullable|integer|exists:addresses,id', 'tax' => 'required|numeric', 'sum' => 'required|numeric', + 'due_date' => 'required|date', + 'cash_discount' => 'nullable|numeric', + 'cash_discount_date' => 'nullable|date', ]); $invoice->update($invoiceData); diff --git a/app/Http/Controllers/Api/InvoiceitemController.php b/app/Http/Controllers/Api/InvoiceitemController.php index 2bca81c..55820ec 100644 --- a/app/Http/Controllers/Api/InvoiceitemController.php +++ b/app/Http/Controllers/Api/InvoiceitemController.php @@ -31,6 +31,7 @@ class InvoiceitemController extends Controller 'total' => 'required|numeric|min:0', 'name' => 'required|string', 'description' => 'nullable|string', + 'article_number' => 'nullable|string', ]); diff --git a/app/Http/Controllers/EController.php b/app/Http/Controllers/EController.php index bd51849..80b5d97 100644 --- a/app/Http/Controllers/EController.php +++ b/app/Http/Controllers/EController.php @@ -43,8 +43,8 @@ class EController extends Controller if (!isset($taxes[$item->tax])) { $taxes[$item->tax] = ['tax' => 0, 'taxable' => 0]; } - $taxes[$item->tax]['tax'] += round($item->price * $item->amount * $item->tax / 100, 2, PHP_ROUND_HALF_UP); - $taxes[$item->tax]['taxable'] += $item->price * $item->amount; + $taxes[$item->tax]['tax'] += round($item->price * $item->amount * ($item->tax / 100) * (100 - $item->discount) / 100, 2, PHP_ROUND_HALF_UP); + $taxes[$item->tax]['taxable'] += $item->price * $item->amount * (100 - $item->discount) / 100; } return $taxes; diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index ce64bff..60373ea 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Http\Option; use App\Models\Invoice; use Illuminate\Contracts\View\View; @@ -20,7 +21,7 @@ class InvoiceController extends Controller */ public function create(): View { - return view('invoice.create'); + return view('invoice.create', ['options' => Option::optionsAsObject()]); } /** diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 9d0cde4..4e94a75 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -23,6 +23,13 @@ class Invoice extends Model 'status', 'sum', 'tax', + 'project_id', + 'project_count', + 'is_template', + 'currency_code', + 'due_date', + 'cash_discount', + 'cash_discount_date', ]; /** @@ -36,6 +43,25 @@ class Invoice extends Model 'localized_state' ]; + /** + * Set the project_count variable automatically, if a project is related to this invoice. + */ + public static function boot(): void + { + parent::boot(); + self::creating(function (Invoice $invoice) { + if (is_null($invoice->project_id)) { + return true; + } + if ($invoice->type != '326') { + return true; + } + $projectMax = Invoice::where('project_id', '=', $invoice->project_id)->max('project_count') + 1; + $invoice->project_count = $projectMax; + return true; + }); + } + /** * Get the invoice state as translated string. */ @@ -107,4 +133,9 @@ class Invoice extends Model { return $this->hasMany(Payment::class); } + + public function project(): BelongsTo + { + return $this->belongsTo(Project::class); + } } diff --git a/app/Models/Invoiceitem.php b/app/Models/Invoiceitem.php index bd126a3..45fd349 100644 --- a/app/Models/Invoiceitem.php +++ b/app/Models/Invoiceitem.php @@ -14,6 +14,7 @@ class Invoiceitem extends Model */ protected $fillable = [ 'invoice_id', + 'article_number', 'amount', 'discount', 'tax', diff --git a/app/Models/Project.php b/app/Models/Project.php index 6db981a..5c15065 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -5,6 +5,7 @@ namespace App\Models; use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class Project extends Model @@ -33,8 +34,20 @@ class Project extends Model protected $appends = [ 'start', 'end', + 'customer_email', + 'customer_name', ]; + + public function getCustomerNameAttribute(): string + { + return $this->customer->name; + } + public function getCustomerEmailAttribute(): string + { + return $this->customer->email; + } + /** * Get the end_date attribute in local format. */ @@ -58,4 +71,9 @@ class Project extends Model { return $this->belongsTo(Customer::class); } + + public function invoices(): HasMany + { + return $this->hasMany(Invoice::class); + } } diff --git a/database/migrations/2025_01_23_143013_add_article_number_to_invoiceitems.php b/database/migrations/2025_01_23_143013_add_article_number_to_invoiceitems.php new file mode 100644 index 0000000..e363e40 --- /dev/null +++ b/database/migrations/2025_01_23_143013_add_article_number_to_invoiceitems.php @@ -0,0 +1,28 @@ +string('article_number')->after('invoice_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('invoiceitems', function (Blueprint $table) { + $table->dropColumn('article_number'); + }); + } +}; diff --git a/database/migrations/2025_01_30_141234_add_due_date_and_cash_discount_to_invoices.php b/database/migrations/2025_01_30_141234_add_due_date_and_cash_discount_to_invoices.php new file mode 100644 index 0000000..9adafa2 --- /dev/null +++ b/database/migrations/2025_01_30_141234_add_due_date_and_cash_discount_to_invoices.php @@ -0,0 +1,32 @@ +date('due_date')->nullable()->after('paid_at'); + $table->decimal('cash_discount', 8, 2)->default(0)->after('due_date'); + $table->date('cash_discount_date', 8, 2)->nullable()->after('cash_discount'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('invoices', function (Blueprint $table) { + $table->dropColumn('due_date'); + $table->dropColumn('cash_discount'); + $table->dropColumn('cash_discount_date'); + }); + } +}; diff --git a/lang/de/enum.php b/lang/de/enum.php new file mode 100644 index 0000000..5887e65 --- /dev/null +++ b/lang/de/enum.php @@ -0,0 +1,25 @@ + 'Abschlagsrechnung', + 'COMMERCIAL_INVOICE' => 'Rechnung', + 'CREDIT_NOTE' => 'Gutschrift', + 'CORRECTED_INVOICE' => 'Rechnungskorrektur', + 'SELF_BILLED_INVOICE' => 'Selbstfakturierte Rechnung', + 'PARTIAL_CONSTRUCTION_INVOICE' => 'Abschlagsrechnung $$ 14 und 16 VOB/B', + 'PARTIAL_FINAL_CONSTRUCTION_INVOICE' => 'Teilschlussrechnung $$ 14 und 16 VOB/B', + 'FINAL_CONSTRUCTION_INVOICE' => 'Schlussrechnung $$ 14 und 16 VOB/B', + +]; diff --git a/lang/de/invoice.php b/lang/de/invoice.php index 36f1c11..9327c7a 100644 --- a/lang/de/invoice.php +++ b/lang/de/invoice.php @@ -52,6 +52,7 @@ return [ 'Net long' => 'Netto zuzüglich MwSt.', 'Gross long' => 'Gesamtpreis inkl. gesetzlicher MwSt.', 'Final sentence' => 'Bitte überweisen Sie den fälligen Rechnungsbetrag in Höhe von :sum bis spätestens :date auf das unten genannte Konto.', + 'Discount sentence' => 'Bei Zahlung bis zum :date gewähren wir einen Skontoabzug von :discount, wodurch der Rechnungsbetrag auf :sum reduziert wird.', 'state_created' => 'Erstellt', 'state_sent' => 'Gesendet', 'state_paid' => 'Bezahlt', @@ -88,5 +89,19 @@ return [ 'Article number' => 'Artikelnummer', 'Discount' => 'Rabatt', 'Type code' => 'Rechnungstyp', + 'Select invoice basis' => 'Rechnungsgrundlage wählen', + 'Should this invoice be created for customer or project?' => 'Soll die Rechnung für einen Kunden oder ein Projekt erstellt werden?', + 'Select project' => 'Projekt auswählen', + 'Search project' => 'Projekt suchen', + 'Select your project' => 'Wähle ein Projekt für die Rechnung aus.', + 'Invoice options' => 'Rechnungsoptionen', + 'Select the options for this invoice.' => 'Wähle die Optionen für diese Rechnung.', + 'Partial invoice number' => 'Abschlagsrechnungs-Nr.', + 'Payment terms' => 'Zahlungsziel', + 'Payment terms in days' => 'Zahlungsziel in Tagen', + 'Cash discount' => 'Skonto', + 'Cash discount sum' => 'Skonto-Summe', + 'Cash discount until' => 'Skonto bis', + 'Cash discount terms in days' => 'Skontogültigkeit in Tagen', ]; diff --git a/resources/views/components/invoice-item.blade.php b/resources/views/components/invoice-item.blade.php index 366db80..61a2868 100644 --- a/resources/views/components/invoice-item.blade.php +++ b/resources/views/components/invoice-item.blade.php @@ -1,15 +1,16 @@
- {{ __("invoice.Select your customer and address") }} -
++ {{ __("invoice.Should this invoice be created for customer or project?") }} +
++ {{ __("invoice.Select your customer and address") }} +
++ {{ __("invoice.Select your project") }} +
+- {{ __("invoice.Enter your invoice items. Click add for an additional invoice item.") }} + {{ __("invoice.Select the options for this invoice.") }}
- {{ __("invoice.Select your customer and address") }} -
++ {{ __("invoice.Should this invoice be created for customer or project?") }} +
++ {{ __("invoice.Select your customer and address") }} +
++ {{ __("invoice.Select your project") }} +
+- {{ __("invoice.Enter your invoice items. Click add for an additional invoice item.") }} + {{ __("invoice.Select the options for this invoice.") }}
-
{{ __('form.Saved') }}
-