diff --git a/app/Http/Controllers/Oasis/InvoiceController.php b/app/Http/Controllers/Oasis/InvoiceController.php index 22f54be5..c5187499 100644 --- a/app/Http/Controllers/Oasis/InvoiceController.php +++ b/app/Http/Controllers/Oasis/InvoiceController.php @@ -3,10 +3,20 @@ namespace App\Http\Controllers\Oasis; use App\Http\Controllers\Controller; +use App\Http\Requests\Oasis\StoreInvoiceRequest; use App\Http\Resources\Oasis\OasisInvoiceCollection; +use App\Http\Resources\Oasis\OasisInvoiceResource; +use App\Models\Oasis\Client; use App\Models\Oasis\Invoice; +use App\Notifications\Oasis\InvoiceDeliveryNotification; +use App\Notifications\SharedSendViaEmail; use Auth; +use Illuminate\Contracts\Foundation\Application; +use Illuminate\Contracts\Routing\ResponseFactory; use Illuminate\Http\Request; +use Illuminate\Http\Response; +use Illuminate\Support\Facades\Notification; +use Illuminate\Support\Str; class InvoiceController extends Controller { @@ -46,4 +56,72 @@ class InvoiceController extends Controller new OasisInvoiceCollection($results), 200 ); } + + + /** + * @param StoreInvoiceRequest $request + * @return Application|ResponseFactory|Response + */ + public function store(StoreInvoiceRequest $request) + { + $user = Auth::user(); + + // Store client + if (! Str::isUuid($request->client) && $request->store_client) { + + $client = $user->clients()->create([ + 'name' => $request->client_name, + 'address' => $request->client_address, + 'city' => $request->client_city, + 'postal_code' => $request->client_postal_code, + 'country' => $request->client_country, + 'ico' => $request->client_ico, + + 'avatar' => store_avatar($request, 'client_avatar') ?? null, + 'email' => $request->client_email ?? null, + 'phone_number' => $request->client_phone_number ?? null, + 'dic' => $request->client_dic ?? null, + 'ic_dph' => $request->client_ic_dph ?? null, + ]); + } + + // Get client + if (Str::isUuid($request->client)) { + + $client = Client::whereUserIdAndId($user->id, $request->client)->first(); + } + + // Store invoice + $invoice = Invoice::create([ + 'user_id' => $user->id, + 'invoice_type' => $request->invoice_type, + 'invoice_number' => $request->invoice_number, + 'variable_number' => $request->variable_number, + 'discount_type' => $request->discount_type ?? null, + 'discount_rate' => $request->discount_rate ?? null, + 'delivery_at' => $request->delivery_at, + 'items' => $request->items, + 'client_id' => $client->id ?? null, + 'client' => [ + 'email' => $client->email ?? $request->client_email, + 'name' => $client->name ?? $request->client_name, + 'address' => $client->address ?? $request->client_address, + 'city' => $client->city ?? $request->client_city, + 'postal_code' => $client->postal_code ?? $request->client_postal_code, + 'country' => $client->country ?? $request->client_country, + 'ico' => $client->ico ?? $request->client_ico, + 'dic' => $client->dic ?? $request->client_dic ?? null, + 'ic_dph' => $client->ic_dph ?? $request->client_ic_dph ?? null, + ], + ]); + + if ($request->send_invoice && $invoice->client['email']) { + Notification::route('mail', $invoice->client['email']) + ->notify(new InvoiceDeliveryNotification($user)); + } + + return response( + new OasisInvoiceResource($invoice), 201 + ); + } } diff --git a/app/Http/Requests/Oasis/StoreInvoiceRequest.php b/app/Http/Requests/Oasis/StoreInvoiceRequest.php new file mode 100644 index 00000000..6daa9f77 --- /dev/null +++ b/app/Http/Requests/Oasis/StoreInvoiceRequest.php @@ -0,0 +1,37 @@ + 'required|string', + 'invoice_number' => 'required|string', + 'variable_number' => 'required|string', + 'client' => 'required', + 'items' => 'required|array', + 'discount_type' => 'sometimes|string', + 'discount_rate' => 'sometimes|integer', + 'delivery_at' => 'required|date', + ]; + } +} diff --git a/app/Models/Oasis/Client.php b/app/Models/Oasis/Client.php index 45aeaa54..d012c119 100644 --- a/app/Models/Oasis/Client.php +++ b/app/Models/Oasis/Client.php @@ -5,6 +5,7 @@ namespace App\Models\Oasis; use App\Models\User; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; use Laravel\Scout\Searchable; use TeamTNT\TNTSearch\Indexer\TNTIndexer; @@ -19,6 +20,26 @@ class Client extends Model protected $keyType = 'string'; + /** + * Format avatar to full url + * + * @return \Illuminate\Contracts\Routing\UrlGenerator|string + */ + public function getAvatarAttribute() + { + // Get avatar from external storage + if ($this->attributes['avatar'] && ! is_storage_driver('local')) { + return Storage::temporaryUrl($this->attributes['avatar'], now()->addDay()); + } + + // Get avatar from local storage + if ($this->attributes['avatar']) { + return url('/' . $this->attributes['avatar']); + } + + return url('/assets/images/default-avatar.png'); + } + public function user() { return $this->hasOne(User::class, 'id', 'user_id'); diff --git a/app/Models/Oasis/Invoice.php b/app/Models/Oasis/Invoice.php index 3205efa4..33f45576 100644 --- a/app/Models/Oasis/Invoice.php +++ b/app/Models/Oasis/Invoice.php @@ -3,6 +3,7 @@ namespace App\Models\Oasis; use App\Models\User; +use Auth; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -69,6 +70,26 @@ class Invoice extends Model $invoice->total_discount = invoice_total_discount($invoice); $invoice->total_net = invoice_total_net($invoice); $invoice->total_tax = invoice_total_tax($invoice); + + $invoice->currency = 'CZK'; + + $user = Auth::user(); + + $invoice->author_name = $user->settings->name ?? null; + $invoice->author_stamp = ''; // TODO: doplnit + + $invoice->user = [ + 'name' => $user->settings->name ?? null, + 'address' => $user->settings->address ?? null, + 'state' => $user->settings->state ?? null, + 'city' => $user->settings->city ?? null, + 'postal_code' => $user->settings->postcode ?? null, + 'country' => $user->settings->country ?? null, + 'phone_number' => $user->settings->phoneNumber ?? null, + 'bank_name' => $user->settings->bank_name ?? null, + 'iban' => $user->settings->iban ?? null, + 'swift' => $user->settings->swift ?? null, + ]; }); } } diff --git a/app/Notifications/Oasis/InvoiceDeliveryNotification.php b/app/Notifications/Oasis/InvoiceDeliveryNotification.php new file mode 100644 index 00000000..8d5e991c --- /dev/null +++ b/app/Notifications/Oasis/InvoiceDeliveryNotification.php @@ -0,0 +1,62 @@ +user = $user; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->subject(__t('New invoice')) + ->greeting(__t('mail_greeting')) + ->line($this->user->settings->name . ' sent you an invoice.') + ->salutation(__t('mail_salutation')); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } +} diff --git a/database/migrations/oasis/2021_04_21_060812_create_invoices_table.php b/database/migrations/oasis/2021_04_21_060812_create_invoices_table.php index 4a9bda7a..f4fbc9b6 100644 --- a/database/migrations/oasis/2021_04_21_060812_create_invoices_table.php +++ b/database/migrations/oasis/2021_04_21_060812_create_invoices_table.php @@ -16,7 +16,7 @@ class CreateInvoicesTable extends Migration Schema::create('invoices', function (Blueprint $table) { $table->uuid('id')->primary()->index(); $table->uuid('user_id')->index(); - $table->uuid('client_id')->index(); + $table->uuid('client_id')->nullable()->index(); $table->enum('invoice_type', ['regular-invoice', 'advance-invoice']); diff --git a/routes/oasis.php b/routes/oasis.php index e0b579cf..6491c1c7 100644 --- a/routes/oasis.php +++ b/routes/oasis.php @@ -26,6 +26,8 @@ Route::group(['middleware' => 'api', 'prefix' => '/api/oasis'], function () { Route::get('/regular', [InvoiceController::class, 'get_all_regular_invoices']); Route::get('/advance', [InvoiceController::class, 'get_all_advance_invoices']); Route::get('/search', [InvoiceController::class, 'search']); + + Route::post('/', [InvoiceController::class, 'store']); }); // Clients diff --git a/tests/Feature/Oasis/OasisInvoiceTest.php b/tests/Feature/Oasis/OasisInvoiceTest.php index b5a7b287..eda24bc5 100644 --- a/tests/Feature/Oasis/OasisInvoiceTest.php +++ b/tests/Feature/Oasis/OasisInvoiceTest.php @@ -2,9 +2,15 @@ namespace Tests\Feature\Oasis; +use App\Models\Oasis\Client; +use App\Notifications\Oasis\InvoiceDeliveryNotification; +use Carbon\Carbon; use Illuminate\Foundation\Testing\DatabaseMigrations; use App\Models\Oasis\Invoice; use Illuminate\Bus\Queueable; +use Illuminate\Http\UploadedFile; +use Illuminate\Support\Facades\Notification; +use Illuminate\Support\Facades\Storage; use Laravel\Sanctum\Sanctum; use App\Models\User; use Tests\TestCase; @@ -26,6 +32,256 @@ class OasisInvoiceTest extends TestCase ]); } + /** + * @test + */ + public function user_create_new_invoice_with_storing_new_client() + { + Notification::fake(); + Storage::fake('local'); + + $user = User::factory(User::class) + ->create(['role' => 'user']); + + Sanctum::actingAs($user); + + $avatar = UploadedFile::fake() + ->image('fake-image.jpg'); + + $this->postJson('/api/oasis/invoices', [ + 'invoice_type' => 'regular-invoice', + 'invoice_number' => '2120001', + 'variable_number' => '2120001', + 'client' => 'others', + 'client_avatar' => $avatar, + 'client_name' => 'VueFileManager Inc.', + 'client_email' => 'howdy@hi5ve.digital', + 'client_phone_number' => '+421 950 123 456', + 'client_address' => 'Does 12', + 'client_city' => 'Bratislava', + 'client_postal_code' => '076 54', + 'client_country' => 'SK', + 'client_ico' => 11111111, + 'client_dic' => 11111111, + 'client_ic_dph' => 'SK11111111', + 'items' => [ + [ + 'description' => 'Test 1', + 'amount' => 1, + 'tax_rate' => 20, + 'price' => 200, + ], + [ + 'description' => 'Test 2', + 'amount' => 3, + 'tax_rate' => 20, + 'price' => 500, + ], + ], + 'discount_type' => 'percent', + 'discount_rate' => 10, + 'delivery_at' => Carbon::now()->addWeek(), + 'store_client' => true, + 'send_invoice' => true, + ])->assertStatus(201); + + $this->assertDatabaseHas('invoices', [ + 'invoice_number' => '2120001', + 'user_id' => $user->id, + 'items' => json_encode([ + [ + 'description' => 'Test 1', + 'amount' => 1, + 'tax_rate' => 20, + 'price' => 200, + ], + [ + 'description' => 'Test 2', + 'amount' => 3, + 'tax_rate' => 20, + 'price' => 500, + ], + ]), + 'client' => json_encode([ + 'email' => 'howdy@hi5ve.digital', + 'name' => 'VueFileManager Inc.', + 'address' => 'Does 12', + 'city' => 'Bratislava', + 'postal_code' => '076 54', + 'country' => 'SK', + 'ico' => 11111111, + 'dic' => 11111111, + 'ic_dph' => 'SK11111111', + ]), + ]); + + $this->assertDatabaseHas('clients', [ + 'user_id' => $user->id, + 'name' => 'VueFileManager Inc.', + 'email' => 'howdy@hi5ve.digital', + ]); + + Storage::disk('local') + ->assertExists(Client::first()->getRawOriginal('avatar')); + + Notification::assertTimesSent(1, InvoiceDeliveryNotification::class); + } + + /** + * @test + */ + public function user_create_new_invoice_without_storing_client() + { + Notification::fake(); + Storage::fake('local'); + + $user = User::factory(User::class) + ->create(['role' => 'user']); + + Sanctum::actingAs($user); + + $this->postJson('/api/oasis/invoices', [ + 'invoice_type' => 'regular-invoice', + 'invoice_number' => '2120001', + 'variable_number' => '2120001', + 'client' => 'others', + 'client_name' => 'VueFileManager Inc.', + 'client_email' => 'howdy@hi5ve.digital', + 'client_phone_number' => '+421 950 123 456', + 'client_address' => 'Does 12', + 'client_city' => 'Bratislava', + 'client_postal_code' => '076 54', + 'client_country' => 'SK', + 'client_ico' => 11111111, + 'client_dic' => 11111111, + 'client_ic_dph' => 'SK11111111', + 'items' => [ + [ + 'description' => 'Test 1', + 'amount' => 1, + 'tax_rate' => 20, + 'price' => 200, + ], + [ + 'description' => 'Test 2', + 'amount' => 3, + 'tax_rate' => 20, + 'price' => 500, + ], + ], + 'discount_type' => 'percent', + 'discount_rate' => 10, + 'delivery_at' => Carbon::now()->addWeek(), + 'store_client' => false, + 'send_invoice' => true, + ])->assertStatus(201); + + $this->assertDatabaseHas('invoices', [ + 'invoice_number' => '2120001', + 'user_id' => $user->id, + 'items' => json_encode([ + [ + 'description' => 'Test 1', + 'amount' => 1, + 'tax_rate' => 20, + 'price' => 200, + ], + [ + 'description' => 'Test 2', + 'amount' => 3, + 'tax_rate' => 20, + 'price' => 500, + ], + ]), + 'client' => json_encode([ + 'email' => 'howdy@hi5ve.digital', + 'name' => 'VueFileManager Inc.', + 'address' => 'Does 12', + 'city' => 'Bratislava', + 'postal_code' => '076 54', + 'country' => 'SK', + 'ico' => 11111111, + 'dic' => 11111111, + 'ic_dph' => 'SK11111111', + ]), + ]); + + $this->assertDatabaseMissing('clients', [ + 'name' => 'VueFileManager Inc.', + 'email' => 'howdy@hi5ve.digital', + ]); + + Notification::assertTimesSent(1, InvoiceDeliveryNotification::class); + } + + /** + * @test + */ + public function user_create_new_invoice_with_existing_client() + { + Notification::fake(); + Storage::fake('local'); + + $user = User::factory(User::class) + ->create(['role' => 'user']); + + Sanctum::actingAs($user); + + $client = Client::factory(Client::class) + ->create([ + 'user_id' => $user->id, + 'name' => 'VueFileManager Inc.', + 'email' => 'howdy@hi5ve.digital', + ]); + + $this->postJson('/api/oasis/invoices', [ + 'invoice_type' => 'regular-invoice', + 'invoice_number' => '2120001', + 'variable_number' => '2120001', + 'client' => $client->id, + 'items' => [ + [ + 'description' => 'Test 1', + 'amount' => 1, + 'tax_rate' => 20, + 'price' => 200, + ], + [ + 'description' => 'Test 2', + 'amount' => 3, + 'tax_rate' => 20, + 'price' => 500, + ], + ], + 'discount_type' => 'percent', + 'discount_rate' => 10, + 'delivery_at' => Carbon::now()->addWeek(), + 'send_invoice' => true, + ])->assertStatus(201); + + $this->assertDatabaseHas('invoices', [ + 'invoice_number' => '2120001', + 'user_id' => $user->id, + 'client_id' => $client->id, + 'items' => json_encode([ + [ + 'description' => 'Test 1', + 'amount' => 1, + 'tax_rate' => 20, + 'price' => 200, + ], + [ + 'description' => 'Test 2', + 'amount' => 3, + 'tax_rate' => 20, + 'price' => 500, + ], + ]), + ]); + + Notification::assertTimesSent(1, InvoiceDeliveryNotification::class); + } + /** * @test */