diff --git a/.gitignore b/.gitignore index 9dd097b7..dbcf5907 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ yarn-error.log .gitignore .env.dev .env.testing +storage/invoices.zip +storage/app/public/logo.png diff --git a/app/Http/Controllers/Admin/PaymentController.php b/app/Http/Controllers/Admin/PaymentController.php index 4ce1ac88..6d48de09 100644 --- a/app/Http/Controllers/Admin/PaymentController.php +++ b/app/Http/Controllers/Admin/PaymentController.php @@ -5,11 +5,11 @@ namespace App\Http\Controllers\Admin; use App\Events\UserUpdateCreditsEvent; use App\Http\Controllers\Controller; use App\Models\Configuration; +use App\Models\InvoiceSettings; use App\Models\Payment; use App\Models\PaypalProduct; -use App\Models\Product; use App\Models\User; -use App\Notifications\ConfirmPaymentNotification; +use App\Notifications\InvoiceNotification; use Exception; use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\View\Factory; @@ -18,6 +18,11 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Storage; +use LaravelDaily\Invoices\Classes\Buyer; +use LaravelDaily\Invoices\Classes\InvoiceItem; +use LaravelDaily\Invoices\Classes\Party; +use LaravelDaily\Invoices\Invoice; use PayPalCheckoutSdk\Core\PayPalHttpClient; use PayPalCheckoutSdk\Core\ProductionEnvironment; use PayPalCheckoutSdk\Core\SandboxEnvironment; @@ -46,10 +51,10 @@ class PaymentController extends Controller public function checkOut(Request $request, PaypalProduct $paypalProduct) { return view('store.checkout')->with([ - 'product' => $paypalProduct, - 'taxvalue' => $paypalProduct->getTaxValue(), - 'taxpercent' => $paypalProduct->getTaxPercent(), - 'total' => $paypalProduct->getTotalPrice() + 'product' => $paypalProduct, + 'taxvalue' => $paypalProduct->getTaxValue(), + 'taxpercent' => $paypalProduct->getTaxPercent(), + 'total' => $paypalProduct->getTotalPrice() ]); } @@ -68,12 +73,12 @@ class PaymentController extends Controller [ "reference_id" => uniqid(), "description" => $paypalProduct->description, - "amount" => [ - "value" => $paypalProduct->getTotalPrice(), + "amount" => [ + "value" => $paypalProduct->getTotalPrice(), 'currency_code' => strtoupper($paypalProduct->currency_code), - 'breakdown' =>[ + 'breakdown' => [ 'item_total' => - [ + [ 'currency_code' => strtoupper($paypalProduct->currency_code), 'value' => $paypalProduct->price, ], @@ -89,8 +94,8 @@ class PaymentController extends Controller "application_context" => [ "cancel_url" => route('payment.cancel'), "return_url" => route('payment.success', ['product' => $paypalProduct->id]), - 'brand_name' => config('app.name', 'Laravel'), - 'shipping_preference' => 'NO_SHIPPING' + 'brand_name' => config('app.name', 'Laravel'), + 'shipping_preference' => 'NO_SHIPPING' ] @@ -186,15 +191,75 @@ class PaymentController extends Controller 'payer' => json_encode($response->result->payer), ]); - //payment notification - $user->notify(new ConfirmPaymentNotification($payment)); event(new UserUpdateCreditsEvent($user)); + //create invoice + $lastInvoiceID = \App\Models\Invoice::where("invoice_name", "like", "%" . now()->format('mY') . "%")->count("id"); + $newInvoiceID = $lastInvoiceID + 1; + $InvoiceSettings = InvoiceSettings::all()->first(); + $logoPath = storage_path('app/public/logo.png'); + + $seller = new Party([ + 'name' => $InvoiceSettings->company_name, + 'phone' => $InvoiceSettings->company_phone, + 'address' => $InvoiceSettings->company_adress, + 'vat' => $InvoiceSettings->company_vat, + 'custom_fields' => [ + 'E-Mail' => $InvoiceSettings->company_mail, + "Web" => $InvoiceSettings->company_web + ], + ]); + + + $customer = new Buyer([ + 'name' => $user->name, + 'custom_fields' => [ + 'E-Mail' => $user->email, + 'Client ID' => $user->id, + ], + ]); + $item = (new InvoiceItem()) + ->title($paypalProduct->description) + ->pricePerUnit($paypalProduct->price); + + $invoice = Invoice::make() + ->template('controlpanel') + ->buyer($customer) + ->seller($seller) + ->discountByPercent(0) + ->taxRate(floatval($paypalProduct->getTaxPercent())) + ->shipping(0) + ->addItem($item) + ->status(__('invoices::invoice.paid')) + ->series(now()->format('mY')) + ->delimiter("-") + ->sequence($newInvoiceID) + ->serialNumberFormat($InvoiceSettings->invoice_prefix . '{DELIMITER}{SERIES}{SEQUENCE}'); + + if (file_exists($logoPath)) { + $invoice->logo($logoPath); + } + //Save the invoice in "storage\app\invoice\USER_ID\YEAR" + $invoice->filename = $invoice->getSerialNumber() . '.pdf'; + $invoice->render(); + Storage::disk("local")->put("invoice/" . $user->id . "/" . now()->format('Y') . "/" . $invoice->filename, $invoice->output); + + + \App\Models\Invoice::create([ + 'invoice_user' => $user->id, + 'invoice_name' => $invoice->getSerialNumber(), + 'payment_id' => $payment->payment_id, + ]); + + //Send Invoice per Mail + $user->notify(new InvoiceNotification($invoice, $user, $payment)); + //redirect back to home return redirect()->route('home')->with('success', __('Your credit balance has been increased!')); } + // If call returns body in response, you can get the deserialized version from the result attribute of the response if (env('APP_ENV') == 'local') { dd($response); diff --git a/app/Http/Controllers/Admin/SettingsController.php b/app/Http/Controllers/Admin/SettingsController.php index d4d82fbe..94875bd1 100644 --- a/app/Http/Controllers/Admin/SettingsController.php +++ b/app/Http/Controllers/Admin/SettingsController.php @@ -3,11 +3,13 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; +use App\Models\InvoiceSettings; use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; use Illuminate\Http\Request; use Illuminate\Http\Response; +use ZipArchive; class SettingsController extends Controller { @@ -18,7 +20,16 @@ class SettingsController extends Controller */ public function index() { - return view('admin.settings.index'); + return view('admin.settings.index', + [ + 'company_name' => InvoiceSettings::get()->first()->company_name, + 'company_adress' => InvoiceSettings::get()->first()->company_adress, + 'company_phone' => InvoiceSettings::get()->first()->company_phone, + 'company_vat' => InvoiceSettings::get()->first()->company_vat, + 'company_mail' => InvoiceSettings::get()->first()->company_mail, + 'company_web' => InvoiceSettings::get()->first()->company_web, + 'invoice_prefix' => InvoiceSettings::get()->first()->invoice_prefix + ]); } public function updateIcons(Request $request) @@ -39,4 +50,53 @@ class SettingsController extends Controller return redirect()->route('admin.settings.index')->with('success', __('Icons updated!')); } + public function updateInvoiceSettings(Request $request) + { + $request->validate([ + 'logo' => 'nullable|max:10000|mimes:jpg,png,jpeg', + ]); + + InvoiceSettings::updateOrCreate(['id' => "1"], ['company_name' => $request->get('company-name')]); + InvoiceSettings::updateOrCreate(['id' => "1",], ['company_adress' => $request->get('company-adress')]); + InvoiceSettings::updateOrCreate(['id' => "1",], ['company_phone' => $request->get('company-phone')]); + InvoiceSettings::updateOrCreate(['id' => "1",], ['company_mail' => $request->get('company-mail')]); + InvoiceSettings::updateOrCreate(['id' => "1",], ['company_vat' => $request->get('company-vat')]); + InvoiceSettings::updateOrCreate(['id' => "1",], ['company_web' => $request->get('company-web')]); + InvoiceSettings::updateOrCreate(['id' => "1",], ['invoice_prefix' => $request->get('invoice-prefix')]); + + if ($request->hasFile('logo')) { + $request->file('logo')->storeAs('public', 'logo.png'); + } + + + return redirect()->route('admin.settings.index')->with('success', 'Invoice settings updated!'); + } + + public function downloadAllInvoices() + { + $zip = new ZipArchive; + $zip_safe_path = storage_path('invoices.zip'); + $res = $zip->open($zip_safe_path, ZipArchive::CREATE | ZipArchive::OVERWRITE); + $result = $this::rglob(storage_path('app/invoice/*')); + if ($res === TRUE) { + $zip->addFromString("1. Info.txt", "This Archive contains all Invoices from all Users!\nIf there are no Invoices here, no Invoices have ever been created!"); + foreach ($result as $file) { + if (file_exists($file) && is_file($file)) { + $zip->addFile($file, basename($file)); + } + } + $zip->close(); + } + return response()->download($zip_safe_path); + } + + public function rglob($pattern, $flags = 0) + { + $files = glob($pattern, $flags); + foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) { + $files = array_merge($files, $this::rglob($dir . '/' . basename($pattern), $flags)); + } + return $files; + } + } diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php new file mode 100644 index 00000000..c0618ab5 --- /dev/null +++ b/app/Models/Invoice.php @@ -0,0 +1,18 @@ +invoice = $invoice; + $this->user = $user; + $this->payment = $payment; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['mail']; + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->subject('Your Payment was successful!') + ->greeting('Hello,') + ->line("Your payment was processed successfully!") + ->line('Status: ' . $this->payment->status) + ->line('Price: ' . $this->payment->formatToCurrency($this->payment->total_price)) + ->line('Type: ' . $this->payment->type) + ->line('Amount: ' . $this->payment->amount) + ->line('Balance: ' . number_format($this->user->credits,2)) + ->line('User ID: ' . $this->payment->user_id) + ->attach(storage_path('app/invoice/' . $this->user->id . '/' . now()->format('Y') . '/' . $this->invoice->filename)); + } +} diff --git a/composer.json b/composer.json index 141a00d3..adee2497 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "laravel/framework": "^8.12", "laravel/tinker": "^2.5", "laravel/ui": "^3.2", + "laraveldaily/laravel-invoices": "^2.0", "paypal/paypal-checkout-sdk": "^1.0", "paypal/rest-api-sdk-php": "^1.14", "socialiteproviders/discord": "^4.1", diff --git a/composer.lock b/composer.lock index 87256edf..08e79a9d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c3168498b058b38657de646f5cddb531", + + "content-hash": "51c5797dc1629fe1f42b1fdc91c6e5d8", "packages": [ { "name": "asm89/stack-cors", @@ -62,6 +63,72 @@ }, "time": "2021-03-11T06:42:03+00:00" }, + { + "name": "barryvdh/laravel-dompdf", + "version": "v0.9.0", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-dompdf.git", + "reference": "5b99e1f94157d74e450f4c97e8444fcaffa2144b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/5b99e1f94157d74e450f4c97e8444fcaffa2144b", + "reference": "5b99e1f94157d74e450f4c97e8444fcaffa2144b", + "shasum": "" + }, + "require": { + "dompdf/dompdf": "^1", + "illuminate/support": "^5.5|^6|^7|^8", + "php": "^7.1 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9-dev" + }, + "laravel": { + "providers": [ + "Barryvdh\\DomPDF\\ServiceProvider" + ], + "aliases": { + "PDF": "Barryvdh\\DomPDF\\Facade" + } + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\DomPDF\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "A DOMPDF Wrapper for Laravel", + "keywords": [ + "dompdf", + "laravel", + "pdf" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-dompdf/issues", + "source": "https://github.com/barryvdh/laravel-dompdf/tree/v0.9.0" + }, + "funding": [ + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2020-12-27T12:05:53+00:00" + }, { "name": "biscolab/laravel-recaptcha", "version": "5.0.1", @@ -780,6 +847,73 @@ ], "time": "2020-05-25T17:44:05+00:00" }, + { + "name": "dompdf/dompdf", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/dompdf/dompdf.git", + "reference": "de4aad040737a89fae2129cdeb0f79c45513128d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/de4aad040737a89fae2129cdeb0f79c45513128d", + "reference": "de4aad040737a89fae2129cdeb0f79c45513128d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "phenx/php-font-lib": "^0.5.2", + "phenx/php-svg-lib": "^0.3.3", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5 || ^8 || ^9", + "squizlabs/php_codesniffer": "^3.5" + }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-gmagick": "Improves image processing performance", + "ext-imagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" + }, + "type": "library", + "autoload": { + "psr-4": { + "Dompdf\\": "src/" + }, + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + }, + { + "name": "Brian Sweeney", + "email": "eclecticgeek@gmail.com" + }, + { + "name": "Gabriel Bull", + "email": "me@gabrielbull.com" + } + ], + "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", + "homepage": "https://github.com/dompdf/dompdf", + "support": { + "issues": "https://github.com/dompdf/dompdf/issues", + "source": "https://github.com/dompdf/dompdf/tree/v1.1.1" + }, + "time": "2021-11-24T00:45:04+00:00" + }, { "name": "dragonmantank/cron-expression", "version": "v3.1.0", @@ -1763,6 +1897,71 @@ }, "time": "2021-05-25T16:45:33+00:00" }, + { + "name": "laraveldaily/laravel-invoices", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/LaravelDaily/laravel-invoices.git", + "reference": "88c472680951acc57ccf179711add7d8dda36821" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/LaravelDaily/laravel-invoices/zipball/88c472680951acc57ccf179711add7d8dda36821", + "reference": "88c472680951acc57ccf179711add7d8dda36821", + "shasum": "" + }, + "require": { + "barryvdh/laravel-dompdf": "^0.9", + "illuminate/http": "^5.5|^6|^7|^8", + "illuminate/support": "^5.5|^6|^7|^8", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.4", + "symfony/var-dumper": "^5.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelDaily\\Invoices\\InvoiceServiceProvider" + ], + "aliases": { + "Invoice": "LaravelDaily\\Invoices\\Facades\\Invoice" + } + } + }, + "autoload": { + "psr-4": { + "LaravelDaily\\Invoices\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Lun", + "email": "mysticcode@gmail.com", + "homepage": "https://lun.lt", + "role": "Developer" + } + ], + "description": "Missing invoices for Laravel", + "homepage": "https://github.com/LaravelDaily/laravel-invoices", + "keywords": [ + "invoice", + "invoices", + "laravel" + ], + "support": { + "issues": "https://github.com/LaravelDaily/laravel-invoices/issues", + "source": "https://github.com/LaravelDaily/laravel-invoices/tree/2.2.0" + }, + "time": "2021-09-29T08:31:40+00:00" + }, { "name": "league/commonmark", "version": "1.6.2", @@ -2601,6 +2800,92 @@ "abandoned": true, "time": "2019-01-04T20:04:25+00:00" }, + { + "name": "phenx/php-font-lib", + "version": "0.5.2", + "source": { + "type": "git", + "url": "https://github.com/PhenX/php-font-lib.git", + "reference": "ca6ad461f032145fff5971b5985e5af9e7fa88d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/ca6ad461f032145fff5971b5985e5af9e7fa88d8", + "reference": "ca6ad461f032145fff5971b5985e5af9e7fa88d8", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5 || ^6 || ^7" + }, + "type": "library", + "autoload": { + "psr-4": { + "FontLib\\": "src/FontLib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + } + ], + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/PhenX/php-font-lib", + "support": { + "issues": "https://github.com/PhenX/php-font-lib/issues", + "source": "https://github.com/PhenX/php-font-lib/tree/0.5.2" + }, + "time": "2020-03-08T15:31:32+00:00" + }, + { + "name": "phenx/php-svg-lib", + "version": "0.3.4", + "source": { + "type": "git", + "url": "https://github.com/PhenX/php-svg-lib.git", + "reference": "f627771eb854aa7f45f80add0f23c6c4d67ea0f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/f627771eb854aa7f45f80add0f23c6c4d67ea0f2", + "reference": "f627771eb854aa7f45f80add0f23c6c4d67ea0f2", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "sabberworm/php-css-parser": "^8.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Svg\\": "src/Svg" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Fabien Ménager", + "email": "fabien.menager@gmail.com" + } + ], + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/PhenX/php-svg-lib", + "support": { + "issues": "https://github.com/PhenX/php-svg-lib/issues", + "source": "https://github.com/PhenX/php-svg-lib/tree/0.3.4" + }, + "time": "2021-10-18T02:13:32+00:00" + }, { "name": "phpoption/phpoption", "version": "1.7.5", @@ -3262,6 +3547,55 @@ ], "time": "2020-08-18T17:17:46+00:00" }, + { + "name": "sabberworm/php-css-parser", + "version": "8.3.1", + "source": { + "type": "git", + "url": "https://github.com/sabberworm/PHP-CSS-Parser.git", + "reference": "d217848e1396ef962fb1997cf3e2421acba7f796" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/d217848e1396ef962fb1997cf3e2421acba7f796", + "reference": "d217848e1396ef962fb1997cf3e2421acba7f796", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "codacy/coverage": "^1.4", + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-0": { + "Sabberworm\\CSS": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raphael Schweikert" + } + ], + "description": "Parser for CSS Files written in PHP", + "homepage": "http://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "keywords": [ + "css", + "parser", + "stylesheet" + ], + "support": { + "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues", + "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.3.1" + }, + "time": "2020-06-01T09:10:00+00:00" + }, { "name": "socialiteproviders/discord", "version": "4.1.1", diff --git a/config/app.php b/config/app.php index 6340c6ff..f9cca409 100644 --- a/config/app.php +++ b/config/app.php @@ -83,6 +83,7 @@ return [ 'locale' => env('LOCALE', 'en'), + /* |-------------------------------------------------------------------------- | Datatable Language Setting @@ -96,6 +97,7 @@ return [ 'datatable_locale' => env('DATATABLE_LOCALE', 'en-gb'), + /* |-------------------------------------------------------------------------- | Application Fallback Locale diff --git a/config/invoices.php b/config/invoices.php new file mode 100644 index 00000000..772ef1fd --- /dev/null +++ b/config/invoices.php @@ -0,0 +1,97 @@ + [ + /* + * Carbon date format + */ + 'format' => 'Y-m-d', + /* + * Due date for payment since invoice's date. + */ + 'pay_until_days' => 7, + ], + + 'serial_number' => [ + 'series' => 'AA', + 'sequence' => 1, + /* + * Sequence will be padded accordingly, for ex. 00001 + */ + 'sequence_padding' => 5, + 'delimiter' => '.', + /* + * Supported tags {SERIES}, {DELIMITER}, {SEQUENCE} + * Example: AA.00001 + */ + 'format' => '{SERIES}{DELIMITER}{SEQUENCE}', + ], + + 'currency' => [ + 'code' => 'eur', + /* + * Usually cents + * Used when spelling out the amount and if your currency has decimals. + * + * Example: Amount in words: Eight hundred fifty thousand sixty-eight EUR and fifteen ct. + */ + 'fraction' => 'ct.', + 'symbol' => '€', + /* + * Example: 19.00 + */ + 'decimals' => 2, + /* + * Example: 1.99 + */ + 'decimal_point' => '.', + /* + * By default empty. + * Example: 1,999.00 + */ + 'thousands_separator' => '', + /* + * Supported tags {VALUE}, {SYMBOL}, {CODE} + * Example: 1.99 € + */ + 'format' => '{VALUE} {SYMBOL}', + ], + + 'paper' => [ + // A4 = 210 mm x 297 mm = 595 pt x 842 pt + 'size' => 'a4', + 'orientation' => 'portrait', + ], + + 'disk' => 'local', + + 'seller' => [ + /* + * Class used in templates via $invoice->seller + * + * Must implement LaravelDaily\Invoices\Contracts\PartyContract + * or extend LaravelDaily\Invoices\Classes\Party + */ + 'class' => \LaravelDaily\Invoices\Classes\Seller::class, + + /* + * Default attributes for Seller::class + */ + 'attributes' => [ + 'name' => 'Towne, Smith and Ebert', + 'address' => '89982 Pfeffer Falls Damianstad, CO 66972-8160', + 'code' => '41-1985581', + 'vat' => '123456789', + 'phone' => '760-355-3930', + 'custom_fields' => [ + /* + * Custom attributes for Seller::class + * + * Used to display additional info on Seller section in invoice + * attribute => value + */ + 'SWIFT' => 'BANK101', + ], + ], + ], +]; diff --git a/database/migrations/2021_11_27_014226_create_invoices.php b/database/migrations/2021_11_27_014226_create_invoices.php new file mode 100644 index 00000000..f2716c05 --- /dev/null +++ b/database/migrations/2021_11_27_014226_create_invoices.php @@ -0,0 +1,34 @@ +id(); + $table->string('invoice_name'); + $table->string('invoice_user'); + $table->string('payment_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('invoices'); + } +} diff --git a/database/migrations/2021_12_1_174440_invoice-settings.php b/database/migrations/2021_12_1_174440_invoice-settings.php new file mode 100644 index 00000000..8615d59b --- /dev/null +++ b/database/migrations/2021_12_1_174440_invoice-settings.php @@ -0,0 +1,48 @@ +id(); + $table->string('company_name')->nullable(); + $table->string('company_adress')->nullable(); + $table->string('company_phone')->nullable(); + $table->string('company_vat')->nullable(); + $table->string('company_mail')->nullable(); + $table->string('company_web')->nullable()->default(env("APP_URL","")); + $table->string('invoice_prefix')->nullable(); + $table->timestamps(); + }); + + DB::table('invoice_settings')->insert( + array( + 'company_name' => env("APP_NAME","MyCompany"), + 'company_web' => env("APP_URL",""), + 'invoice_prefix' => "INV" + + ) + ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('invoice_settings'); + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 3e532616..f275ede7 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -3,6 +3,7 @@ namespace Database\Seeders; use Database\Seeders\Seeds\ConfigurationSeeder; +use Database\Seeders\Seeds\InvoiceSettingsSeeder; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder diff --git a/resources/lang/de.json b/resources/lang/de.json index 7f4056de..367d1e24 100644 --- a/resources/lang/de.json +++ b/resources/lang/de.json @@ -358,5 +358,4 @@ "Login as User": "Als User anmelden", "Clone": "Klonen" - } diff --git a/resources/views/admin/settings/index.blade.php b/resources/views/admin/settings/index.blade.php index de71bc46..bdb040ff 100644 --- a/resources/views/admin/settings/index.blade.php +++ b/resources/views/admin/settings/index.blade.php @@ -39,6 +39,9 @@ + @@ -88,14 +91,123 @@ chrome hotkey) to reload without cache to see your changes appear :)

+ +
+
+ +
+ +
+ @csrf + @method('PATCH') + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ @error('logo') + + {{$message}} + + @enderror +
+
+
+ + + +
+ + - + + + @@ -103,7 +215,7 @@ + + diff --git a/resources/views/vendor/invoices/templates/default.blade.php b/resources/views/vendor/invoices/templates/default.blade.php new file mode 100644 index 00000000..f86f5bba --- /dev/null +++ b/resources/views/vendor/invoices/templates/default.blade.php @@ -0,0 +1,390 @@ + + + + {{ $invoice->name }} + + + + + + + {{-- Header --}} + @if($invoice->logo) + logo + @endif + + + + + + + + + +
+

+ {{ $invoice->name }} {{$invoice->getSerialNumber()}} +

+
+ @if($invoice->status) +

+ {{ $invoice->status }} +

+ @endif +

{{ __('invoices::invoice.serial') }} {{ $invoice->getSerialNumber() }}

+

{{ __('invoices::invoice.date') }}: {{ $invoice->getDate() }}

+
+ + {{-- Seller - Buyer --}} + + + + + + + + + + + + + + + +
+ {{ __('invoices::invoice.seller') }} + + {{ __('invoices::invoice.buyer') }} +
+ @if($invoice->seller->name) +

+ {{ $invoice->seller->name }} +

+ @endif + + @if($invoice->seller->address) +

+ {{ __('invoices::invoice.address') }}: {{ $invoice->seller->address }} +

+ @endif + + @if($invoice->seller->code) +

+ {{ __('invoices::invoice.code') }}: {{ $invoice->seller->code }} +

+ @endif + + @if($invoice->seller->vat) +

+ {{ __('invoices::invoice.vat') }}: {{ $invoice->seller->vat }} +

+ @endif + + @if($invoice->seller->phone) +

+ {{ __('invoices::invoice.phone') }}: {{ $invoice->seller->phone }} +

+ @endif + + @foreach($invoice->seller->custom_fields as $key => $value) +

+ {{ ucfirst($key) }}: {{ $value }} +

+ @endforeach +
+ @if($invoice->buyer->name) +

+ {{ $invoice->buyer->name }} +

+ @endif + + @if($invoice->buyer->address) +

+ {{ __('invoices::invoice.address') }}: {{ $invoice->buyer->address }} +

+ @endif + + @if($invoice->buyer->code) +

+ {{ __('invoices::invoice.code') }}: {{ $invoice->buyer->code }} +

+ @endif + + @if($invoice->buyer->vat) +

+ {{ __('invoices::invoice.vat') }}: {{ $invoice->buyer->vat }} +

+ @endif + + @if($invoice->buyer->phone) +

+ {{ __('invoices::invoice.phone') }}: {{ $invoice->buyer->phone }} +

+ @endif + + @foreach($invoice->buyer->custom_fields as $key => $value) +

+ {{ ucfirst($key) }}: {{ $value }} +

+ @endforeach +
+ + {{-- Table --}} + + + + + @if($invoice->hasItemUnits) + + @endif + + + @if($invoice->hasItemDiscount) + + @endif + @if($invoice->hasItemTax) + + @endif + + + + + {{-- Items --}} + @foreach($invoice->items as $item) + + + @if($invoice->hasItemUnits) + + @endif + + + @if($invoice->hasItemDiscount) + + @endif + @if($invoice->hasItemTax) + + @endif + + + + @endforeach + {{-- Summary --}} + @if($invoice->hasItemOrInvoiceDiscount()) + + + + + + @endif + @if($invoice->taxable_amount) + + + + + + @endif + @if($invoice->tax_rate) + + + + + + @endif + @if($invoice->hasItemOrInvoiceTax()) + + + + + + @endif + @if($invoice->shipping_amount) + + + + + + @endif + + + + + + +
{{ __('invoices::invoice.description') }}{{ __('invoices::invoice.units') }}{{ __('invoices::invoice.quantity') }}{{ __('invoices::invoice.price') }}{{ __('invoices::invoice.discount') }}{{ __('invoices::invoice.tax') }}{{ __('invoices::invoice.sub_total') }}
+ {{ $item->title }} + + @if($item->description) +

{{ $item->description }}

+ @endif +
{{ $item->units }}{{ $item->quantity }} + {{ $invoice->formatCurrency($item->price_per_unit) }} + + {{ $invoice->formatCurrency($item->discount) }} + + {{ $invoice->formatCurrency($item->tax) }} + + {{ $invoice->formatCurrency($item->sub_total_price) }} +
{{ __('invoices::invoice.total_discount') }} + {{ $invoice->formatCurrency($invoice->total_discount) }} +
{{ __('invoices::invoice.taxable_amount') }} + {{ $invoice->formatCurrency($invoice->taxable_amount) }} +
{{ __('invoices::invoice.tax_rate') }} + {{ $invoice->tax_rate }}% +
{{ __('invoices::invoice.total_taxes') }} + {{ $invoice->formatCurrency($invoice->total_taxes) }} +
{{ __('invoices::invoice.shipping') }} + {{ $invoice->formatCurrency($invoice->shipping_amount) }} +
{{ __('invoices::invoice.total_amount') }} + {{ $invoice->formatCurrency($invoice->total_amount) }} +
+ + @if($invoice->notes) +

+ {{ trans('invoices::invoice.notes') }}: {!! $invoice->notes !!} +

+ @endif + +

+ {{ trans('invoices::invoice.amount_in_words') }}: {{ $invoice->getTotalAmountInWords() }} +

+

+ {{ trans('invoices::invoice.pay_until') }}: {{ $invoice->getPayUntilDate() }} +

+ + + + diff --git a/routes/web.php b/routes/web.php index 18cadf8d..ffbadb29 100644 --- a/routes/web.php +++ b/routes/web.php @@ -123,6 +123,8 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () { Route::resource('configurations', ConfigurationController::class); Route::patch('settings/update/icons', [SettingsController::class, 'updateIcons'])->name('settings.update.icons'); + Route::patch('settings/update/invoice-settings', [SettingsController::class, 'updateInvoiceSettings'])->name('settings.update.invoicesettings'); + Route::get('settings/download-invoices', [SettingsController::class, 'downloadAllInvoices'])->name('settings.downloadAllInvoices');; Route::resource('settings', SettingsController::class)->only('index'); Route::get('usefullinks/datatable', [UsefulLinkController::class, 'datatable'])->name('usefullinks.datatable'); diff --git a/storage/app/public/.gitignore b/storage/app/public/.gitignore index d6b7ef32..2a984bb3 100755 --- a/storage/app/public/.gitignore +++ b/storage/app/public/.gitignore @@ -1,2 +1,3 @@ * !.gitignore +!logo.png \ No newline at end of file