From e4cdf89ed9637119aea07b55ec24800aac876876 Mon Sep 17 00:00:00 2001 From: Dominik Hebeler <dominik@suma-ev.de> Date: Wed, 21 Aug 2024 16:52:21 +0200 Subject: [PATCH] Add invoice data to account --- .../Http/Controllers/LogsApiController.php | 117 +++++++++++++++++- metager/composer.json | 1 + metager/composer.lock | 47 ++++++- metager/config/metager/invoiceninja.php | 7 ++ metager/lang/de/logs.php | 28 +++++ metager/lang/en/logs.php | 7 ++ .../resources/less/metager/pages/logs.less | 23 ++++ metager/resources/views/logs/login.blade.php | 13 +- .../resources/views/logs/overview.blade.php | 9 +- .../views/logs/parts/invoice-data.blade.php | 82 ++++++++++++ metager/routes/logs.php | 3 +- 11 files changed, 326 insertions(+), 11 deletions(-) create mode 100644 metager/config/metager/invoiceninja.php create mode 100644 metager/lang/de/logs.php create mode 100644 metager/lang/en/logs.php create mode 100644 metager/resources/views/logs/parts/invoice-data.blade.php diff --git a/metager/app/Http/Controllers/LogsApiController.php b/metager/app/Http/Controllers/LogsApiController.php index fe4bfc9f2..db3c4beb8 100644 --- a/metager/app/Http/Controllers/LogsApiController.php +++ b/metager/app/Http/Controllers/LogsApiController.php @@ -8,14 +8,83 @@ use Auth; use DB; use Illuminate\Http\Request; use Illuminate\Support\MessageBag; +use InvoiceNinja\Sdk\InvoiceNinja; use Mail; +use Spatie\LaravelIgnition\Exceptions\InvalidConfig; use Validator; class LogsApiController extends Controller { public function overview(Request $request) { - return view('logs.overview', ['title' => __('titles.logs.overview')]); + if (empty(config("metager.invoiceninja.access_token"))) { + throw new InvalidConfig("Invoiceninja is not configured on this system", 500); + } + $invoice = [ + "email" => Auth::guard("logs")->user()->getAuthIdentifier(), + "company" => "", + "first_name" => "", + "last_name" => "", + "street" => "", + "postal_code" => "", + "city" => "", + ]; + + $client = $this->getOrCreateClient($invoice["email"]); + $contact = $this->getContactFromClient($client, $invoice["email"]); + + if (!empty($client["name"])) { + $invoice["company"] = $client["name"]; + } + if (!empty($client["address1"])) { + $invoice["street"] = $client["address1"]; + } + if (!empty($client["postal_code"])) { + $invoice["postal_code"] = $client["postal_code"]; + } + if (!empty($client["city"])) { + $invoice["city"] = $client["city"]; + } + if (!empty($contact["first_name"])) { + $invoice["first_name"] = $contact["first_name"]; + } + if (!empty($contact["last_name"])) { + $invoice["last_name"] = $contact["last_name"]; + } + + $edit_invoice = $request->filled("edit_invoice"); + if (empty($invoice["first_name"]) || empty($invoice["last_name"]) || empty($invoice["street"]) || empty($invoice["postal_code"]) || empty($invoice["city"])) { + $edit_invoice = true; + } + + return view('logs.overview', ['title' => __('titles.logs.overview')])->with(["css" => [mix("/css/logs.css")], "invoice" => $invoice, "edit_invoice" => $edit_invoice]); + } + + public function updateInvoiceData(Request $request) + { + $email = Auth::guard("logs")->user()->getAuthIdentifier(); + $client = $this->getOrCreateClient($email); + + $client["name"] = $request->input("company") ?? ""; + $client["address1"] = $request->input("street") ?? ""; + $client["postal_code"] = $request->input("postal_code") ?? ""; + $client["city"] = $request->input("city") ?? ""; + + for ($i = 0; $i < sizeof($client["contacts"]); $i++) { + if ($client["contacts"][$i]["email"] !== $email) + continue; + $client["contacts"][$i]["first_name"] = $request->input("first_name") ?? ""; + $client["contacts"][$i]["last_name"] = $request->input("last_name") ?? ""; + } + $invoice_client = $this->getInvoiceNinjaClient(); + $invoice_client->clients->update($client["id"], [ + "name" => $client["name"], + "address1" => $client["address1"], + "postal_code" => $client["postal_code"], + "city" => $client["city"], + "contacts" => $client["contacts"] + ]); + return redirect(route("logs:overview")); } public function admin(Request $request) @@ -113,4 +182,50 @@ class LogsApiController extends Controller } return redirect(route("logs:login"))->with(["email" => $validated["email"]]); } + + private function getInvoiceNinjaClient() + { + $invoice_client = new InvoiceNinja(config("metager.invoiceninja.access_token")); + $invoice_client->setUrl(config("metager.invoiceninja.url")); + return $invoice_client; + } + + private function getOrCreateClient(string $email) + { + // Check if there is already an Invoicing Account for this client + $invoice_client = $this->getInvoiceNinjaClient(); + + $client = null; + $clients = $invoice_client->clients->all([ + "email" => $email + ]); + foreach ($clients["data"] as $tmp_client) { + if ($tmp_client["group_settings_id"] === config("metager.invoiceninja.logs_group_id")) { + $client = $tmp_client; + } + } + + if (is_null($client)) { + $invoice_client->clients->create([ + "group_settings_id" => config("metager.invoiceninja.logs_group_id"), + "contacts" => [ + [ + 'send_email' => true, + 'email' => $email, + ] + ] + ]); + return $this->getOrCreateClient($email); + } + return $client; + } + + private function getContactFromClient($client, $email) + { + foreach ($client["contacts"] as $contact) { + if ($contact["email"] === $email) { + return $contact; + } + } + } } diff --git a/metager/composer.json b/metager/composer.json index 1bf939e8c..e4d5af138 100644 --- a/metager/composer.json +++ b/metager/composer.json @@ -11,6 +11,7 @@ "php": "^8.2", "endroid/qr-code": "^5.0", "globalcitizen/php-iban": "^4.2.3", + "invoiceninja/sdk-php": "^1.1", "jenssegers/agent": "^2.6.4", "laravel/framework": "^11.0", "laravel/sanctum": "^4.0", diff --git a/metager/composer.lock b/metager/composer.lock index 2ee4ec69f..7d781b75a 100644 --- a/metager/composer.lock +++ b/metager/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "791aa52ccb009d4044584609ff1b78d9", + "content-hash": "22c44237fc58e695c49ae267e11dfb46", "packages": [ { "name": "bacon/bacon-qr-code", @@ -1345,6 +1345,51 @@ ], "time": "2022-05-21T17:30:32+00:00" }, + { + "name": "invoiceninja/sdk-php", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/invoiceninja/sdk-php.git", + "reference": "dac1b77453e5ffc475b8755da6df000ab77106d1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/invoiceninja/sdk-php/zipball/dac1b77453e5ffc475b8755da6df000ab77106d1", + "reference": "dac1b77453e5ffc475b8755da6df000ab77106d1", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.3", + "php": "^7.4|^8.0|^8.1" + }, + "require-dev": { + "fakerphp/faker": "^1.16", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "InvoiceNinja\\Sdk\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David Bomba", + "email": "turbo124@gmail.com" + } + ], + "description": "The Invoice Ninja v5 PHP SDK", + "support": { + "issues": "https://github.com/invoiceninja/sdk-php/issues", + "source": "https://github.com/invoiceninja/sdk-php/tree/v1.1.0" + }, + "time": "2023-01-13T23:39:10+00:00" + }, { "name": "jaybizzle/crawler-detect", "version": "v1.2.118", diff --git a/metager/config/metager/invoiceninja.php b/metager/config/metager/invoiceninja.php new file mode 100644 index 000000000..e3bf2b8b7 --- /dev/null +++ b/metager/config/metager/invoiceninja.php @@ -0,0 +1,7 @@ +<?php + +return [ + "url" => env("INVOICENINJA_URL", "https://invoicing.co"), + "access_token" => env("INVOICENINJA_TOKEN", ""), + "logs_group_id" => env("INVOICENINJA_LOGS_GROUP_ID", ""), +]; \ No newline at end of file diff --git a/metager/lang/de/logs.php b/metager/lang/de/logs.php new file mode 100644 index 000000000..da4780cca --- /dev/null +++ b/metager/lang/de/logs.php @@ -0,0 +1,28 @@ +<?php + +return [ + 'login' => [ + 'hint' => 'Bitte melde dich an, um Zugriff auf dein Konto zu erhalten.', + 'email' => 'Email Addresse', + 'code' => 'Login Code', + 'email_sent' => 'Falls dieser Account bereits registriert ist, haben wir dir einen Login Code per Email gesendet. Bitte trage diesen ein um dich anzumelden.', + 'submit' => 'Abschicken', + 'restart' => 'Neu Anmelden' + ], + 'overview' => [ + "hint" => 'Hier findest du eine Übersicht über deine Bestellungen und Informationen zur Verwendung der API. Bitte stelle sicher, dass nachfolgende Rechnungsdaten aktuell und korrekt sind', + 'invoice-data' => [ + "heading" => "Rechnungsdaten", + "email" => "Email Addresse", + "company" => "Firma", + "full_name" => "Name", + "first_name" => "Vorname", + "last_name" => "Nachname", + "street" => "Straße u. Hausnummer", + "postal_code" => "Postleitzahl", + "city" => "Stadt", + "save" => "Speichern", + "update" => "Rechnungsdaten aktualisieren" + ], + ] +]; \ No newline at end of file diff --git a/metager/lang/en/logs.php b/metager/lang/en/logs.php new file mode 100644 index 000000000..34534eecd --- /dev/null +++ b/metager/lang/en/logs.php @@ -0,0 +1,7 @@ +<?php + +return [ + 'login' => [ + + ], +]; \ No newline at end of file diff --git a/metager/resources/less/metager/pages/logs.less b/metager/resources/less/metager/pages/logs.less index 66df7c5f9..b9e8c866b 100644 --- a/metager/resources/less/metager/pages/logs.less +++ b/metager/resources/less/metager/pages/logs.less @@ -18,4 +18,27 @@ font-size: .8rem; } } +} + +#overview { + >div#invoice-data { + display: grid; + row-gap: 0.5rem; + width: 50%; + min-width: 320px; + + >.input-group { + display: grid; + + >label { + margin: 0; + font-weight: normal; + font-size: .8rem; + } + } + + >h3 { + margin: 0; + } + } } \ No newline at end of file diff --git a/metager/resources/views/logs/login.blade.php b/metager/resources/views/logs/login.blade.php index 7aa08dea2..3e3cf25ab 100644 --- a/metager/resources/views/logs/login.blade.php +++ b/metager/resources/views/logs/login.blade.php @@ -14,14 +14,14 @@ </ul> </div> @endif - <p>Bitte melde dich an, um Zugriff auf dein Konto zu erhalten.</p> + <p>@lang('logs.login.hint')</p> <form action="{{ route("logs:login:post")}}" method="post"> <input type="hidden" name="_token" value="{{ csrf_token() }}" /> @if(session("email")) <input type="hidden" name="email" value="{{session('email')}}"> @endif <div class="input-group"> - <label for="email">Email Addresse</label> + <label for="email">@lang('logs.login.email')</label> @if(session("email")) <input type="email" name="email" id="email" value="{{session('email')}}" placeholder="max@mustermann.de" required disabled> @@ -32,15 +32,14 @@ </div> @if(session("email")) <div class="input-group"> - <label for="code">Login Code</label> + <label for="code">@lang('logs.login.code')</label> <input type="text" name="code" id="code" placeholder="123456" required> </div> - <p>Falls dieser Account bereits registriert ist, haben wir dir einen Login Code per Email gesendet. Bitte - trage diesen ein um dich anzumelden.</p> + <p>@lang('logs.login.email_sent')</p> @endif - <button class="btn btn-default" type="submit">Abschicken</button> + <button class="btn btn-default" type="submit">@lang('logs.login.submit')</button> @if(session("email")) - <a class="reset" href="{{ route('logs:login', ['reset' => '1']) }}">Neu Anmelden</a> + <a class="reset" href="{{ route('logs:login', ['reset' => '1']) }}">@lang('logs.login.restart')</a> @endif </form> </div> diff --git a/metager/resources/views/logs/overview.blade.php b/metager/resources/views/logs/overview.blade.php index 9ce1577b0..41e661c55 100644 --- a/metager/resources/views/logs/overview.blade.php +++ b/metager/resources/views/logs/overview.blade.php @@ -3,5 +3,12 @@ @section('title', $title) @section('content') -<h1>MetaGer Logs API</h1> +<div id="overview"> + <h1>MetaGer Logs API</h1> + <p>@lang('logs.overview.hint')</p> + @include("logs..parts.invoice-data") + @if(!$edit_invoice) + + @endif +</div> @endsection \ No newline at end of file diff --git a/metager/resources/views/logs/parts/invoice-data.blade.php b/metager/resources/views/logs/parts/invoice-data.blade.php new file mode 100644 index 000000000..c43f979aa --- /dev/null +++ b/metager/resources/views/logs/parts/invoice-data.blade.php @@ -0,0 +1,82 @@ +<div id="invoice-data"> + <h3>@lang('logs.overview.invoice-data.heading')</h3> + <div class="input-group"> + <label for="email">@lang('logs.overview.invoice-data.email')</label> + @if($edit_invoice) + <input type="email" name="email" id="email" placeholder="max@mustermann.de" value="{{ $invoice['email'] }}" + disabled> + @else + <span>{{ $invoice["email"] }}</span> + @endif + </div> + @if($edit_invoice || !empty($invoice["company"])) + <div class="input-group"> + <label for="company">@lang('logs.overview.invoice-data.company')</label> + @if($edit_invoice) + <input type="text" name="company" id="company" value="{{ $invoice['company'] }}" placeholder="Musterfirma" + form="update-invoice-data"> + @else + <span>{{ $invoice["company"] }}</span> + @endif + </div> + @endif + @if($edit_invoice) + <div class="input-group"> + <label for="first_name">@lang('logs.overview.invoice-data.first_name')</label> + <input type="text" name="first_name" id="first_name" value="{{ $invoice['first_name'] }}" placeholder="Max" + form="update-invoice-data"> + </div> + <div class="input-group"> + <label for="last_name">@lang('logs.overview.invoice-data.last_name')</label> + <input type="text" name="last_name" id="last_name" value="{{ $invoice['last_name'] }}" placeholder="Mustermann" + form="update-invoice-data"> + </div> + @elseif(!empty($invoice["first_name"]) && !empty($invoice["last_name"])) + <div class="input-group"> + <label for="full_name">@lang('logs.overview.invoice-data.full_name')</label> + <span>{{ $invoice["first_name"] }} {{ $invoice["last_name"] }}</span> + </div> + @endif + @if($edit_invoice || !empty($invoice["street"])) + <div class="input-group"> + <label for="street">@lang('logs.overview.invoice-data.street')</label> + @if($edit_invoice) + <input type="text" name="street" id="street" value="{{ $invoice['street'] }}" placeholder="Musterstraße 3" + form="update-invoice-data"> + @else + <span>{{ $invoice["street"] }}</span> + @endif + </div> + @endif + @if($edit_invoice || !empty($invoice["postal_code"])) + <div class="input-group"> + <label for="postal_code">@lang('logs.overview.invoice-data.postal_code')</label> + @if($edit_invoice) + <input type="text" name="postal_code" id="postal_code" value="{{ $invoice['postal_code'] }}" placeholder="12345" + form="update-invoice-data"> + @else + <span>{{ $invoice["postal_code"] }}</span> + @endif + </div> + @endif + @if($edit_invoice || !empty($invoice["city"])) + <div class="input-group"> + <label for="city">@lang('logs.overview.invoice-data.city')</label> + @if($edit_invoice) + <input type="text" name="city" id="city" value="{{ $invoice['city'] }}" placeholder="Musterstadt" + form="update-invoice-data"> + @else + <span>{{ $invoice["city"] }}</span> + @endif + </div> + @endif + @if($edit_invoice) + <form id="update-invoice-data" method="post" action="{{ route('logs:update_invoice_data') }}"> + <input type="hidden" name="_token" value="{{ csrf_token() }}" /> + <button type="submit" class="btn btn-default" + href="{{ route('logs:overview', ['edit_invoice' => '1']) }}">@lang("logs.overview.invoice-data.save")</button> + </form> + @else + <a href="{{ route('logs:overview', ["edit_invoice" => "1"]) }}">@lang("logs.overview.invoice-data.update")</a> + @endif +</div> \ No newline at end of file diff --git a/metager/routes/logs.php b/metager/routes/logs.php index afd8995af..2ab54de6e 100644 --- a/metager/routes/logs.php +++ b/metager/routes/logs.php @@ -12,5 +12,6 @@ Route::get("login", [LogsApiController::class, 'login'])->name("logs:login"); Route::post("login", [LogsApiController::class, "login_post"])->middleware("throttle:logs_login")->name("logs:login:post"); Route::middleware(LogsAuthentication::class)->group(function () { - Route::get("/", [LogsApiController::class, "overview"]); + Route::get("/", [LogsApiController::class, "overview"])->name("logs:overview"); + Route::post("update-invoice-data", [LogsApiController::class, "updateInvoiceData"])->name("logs:update_invoice_data"); }); \ No newline at end of file -- GitLab