diff --git a/app/Entities/Invoices/Invoice.php b/app/Entities/Invoices/Invoice.php new file mode 100755 index 0000000..8a32c5b --- /dev/null +++ b/app/Entities/Invoices/Invoice.php @@ -0,0 +1,17 @@ + 'array']; + + public function getRouteKeyName() + { + return 'invoice_no'; + } +} diff --git a/app/Http/Controllers/InvoiceDraftController.php b/app/Http/Controllers/InvoiceDraftController.php new file mode 100644 index 0000000..9bd5c5f --- /dev/null +++ b/app/Http/Controllers/InvoiceDraftController.php @@ -0,0 +1,133 @@ +draftCollection = new InvoiceDraftCollection(); + } + + public function index(Request $request) + { + $draft = $this->draftCollection->content()->first(); + $projects = Project::pluck('name', 'id'); + + return view('invoices.create', compact('draft', 'projects')); + } + + public function show(Request $request, $draftKey = null) + { + $draft = $draftKey ? $this->draftCollection->get($draftKey) : $this->draftCollection->content()->first(); + if (is_null($draft)) { + flash(trans('invoice.draft_not_found'), 'danger'); + + return redirect()->route('invoices.create'); + } + + $projects = Project::pluck('name', 'id'); + return view('invoices.create', compact('draft', 'projects')); + } + + public function add(Request $request) + { + $this->draftCollection->add(new InvoiceDraft()); + + return redirect()->route('invoices.create', $this->draftCollection->content()->last()->draftKey); + } + + public function addDraftItem(Request $request, $draftKey) + { + $item = new Item(['description' => $request->description, 'amount' => $request->amount]); + $this->draftCollection->addItemToDraft($draftKey, $item); + + flash(trans('invoice.item_added')); + + return back(); + } + + public function updateDraftItem(Request $request, $draftKey) + { + $this->draftCollection->updateDraftItem($draftKey, $request->item_key, $request->only('description', 'amount')); + + return back(); + } + + public function removeDraftItem(Request $request, $draftKey) + { + $this->draftCollection->removeItemFromDraft($draftKey, $request->item_index); + + return back(); + } + + public function empty($draftKey) + { + $this->draftCollection->emptyDraft($draftKey); + + return redirect()->route('invoices.create', $draftKey); + } + + public function remove(Request $request) + { + $this->draftCollection->removeDraft($request->draft_key); + + if ($this->draftCollection->isEmpty()) { + return redirect()->route('invoices.create-empty'); + } + + $lastDraft = $this->draftCollection->content()->last(); + + return redirect()->route('invoices.create', $lastDraft->draftKey); + } + + public function destroy() + { + $this->draftCollection->destroy(); + flash(trans('invoice.draft_destroyed'), 'warning'); + + return redirect()->route('cart.index'); + } + + public function proccess(Request $request, $draftKey) + { + $this->validate($request, [ + 'project_id' => 'required|exists:projects,id', + 'notes' => 'nullable|string|max:100', + ]); + + $draft = $this->draftCollection->updateDraftAttributes($draftKey, $request->only('project_id', 'notes')); + + if ($draft->getItemsCount() == 0) { + flash(trans('invoice.item_list_empty'), 'warning')->important(); + + return redirect()->route('invoices.create', [$draftKey]); + } + + flash(trans('invoice.confirm_instruction', ['back_link' => link_to_route('invoices.create', trans('app.back'), $draftKey)]), 'warning')->important(); + + return redirect()->route('invoices.create', [$draftKey, 'action' => 'confirm']); + } + + public function store(Request $request, $draftKey) + { + $draft = $this->draftCollection->get($draftKey); + if (is_null($draft)) { + return redirect()->route('cart.index'); + } + + $invoice = $draft->store(); + $draft->destroy(); + flash(trans('invoice.created', ['invoice_no' => $invoice->invoice_no]), 'success')->important(); + + return redirect()->route('invoices.show', $invoice->invoice_no); + } +} diff --git a/app/Http/Controllers/InvoicesController.php b/app/Http/Controllers/InvoicesController.php new file mode 100644 index 0000000..d86c4f6 --- /dev/null +++ b/app/Http/Controllers/InvoicesController.php @@ -0,0 +1,14 @@ +items)->sortBy('name'); + } + + public function addItem(Item $item) + { + $this->items[] = $item; + + return $item; + } + + public function removeItem($itemKey) + { + unset($this->items[$itemKey]); + } + + public function empty() + { + $this->items = []; + } + + public function getTotal() + { + return $this->items()->sum('amount'); + } + + public function getItemsCount() + { + return $this->items()->count(); + } + + public function updateItem($itemKey, $newItemData) + { + if (!isset($this->items[$itemKey])) { + return; + } + + $item = $this->items[$itemKey]; + + $this->items[$itemKey] = $item->updateAttribute($newItemData); + + return $item; + } + + public function store() + { + $invoice = new Invoice(); + $invoice->invoice_no = $this->getNewInvoiceNo(); + $invoice->items = $this->getItemsArray(); + $invoice->project_id = $this->projectId; + $invoice->amount = $this->getTotal(); + $invoice->notes = $this->notes; + $invoice->status_id = 1; + $invoice->user_id = auth()->id() ?: 1; + + $invoice->save(); + + return $invoice; + } + + public function getNewInvoiceNo() + { + $prefix = date('ym'); + + $lastInvoice = Invoice::orderBy('invoice_no', 'desc')->first(); + + if (!is_null($lastInvoice)) { + $lastInvoiceNo = $lastInvoice->invoice_no; + if (substr($lastInvoiceNo, 0, 4) == $prefix) { + return ++$lastInvoiceNo; + } + } + + return $prefix.'0001'; + } + + protected function getItemsArray() + { + $items = []; + foreach ($this->items as $item) { + $items[] = [ + 'description' => $item->description, + 'amount' => $item->amount, + ]; + } + + return $items; + } + + public function destroy() + { + $cart = app(InvoiceDraftCollection::class); + + return $cart->removeDraft($this->draftKey); + } +} diff --git a/app/Services/InvoiceDraft/InvoiceDraftCollection.php b/app/Services/InvoiceDraft/InvoiceDraftCollection.php new file mode 100644 index 0000000..d20aa6e --- /dev/null +++ b/app/Services/InvoiceDraft/InvoiceDraftCollection.php @@ -0,0 +1,150 @@ +session = session(); + $this->instance('drafts'); + } + + public function instance($instance = null) + { + $instance = $instance ?: 'drafts'; + + $this->instance = sprintf('%s.%s', 'invoices', $instance); + + return $this; + } + + public function currentInstance() + { + return str_replace('invoices.', '', $this->instance); + } + + public function add(InvoiceDraft $draft) + { + $content = $this->getContent(); + $draft->draftKey = str_random(10); + $content->put($draft->draftKey, $draft); + + $this->session->put($this->instance, $content); + + return $draft; + } + + public function get($draftKey) + { + $content = $this->getContent(); + if (isset($content[$draftKey])) { + return $content[$draftKey]; + } + } + + public function updateDraftAttributes($draftKey, $draftAttributes) + { + $content = $this->getContent(); + + $content[$draftKey]->projectId = $draftAttributes['project_id']; + $content[$draftKey]->notes = $draftAttributes['notes']; + + $this->session->put($this->instance, $content); + + return $content[$draftKey]; + } + + public function emptyDraft($draftKey) + { + $content = $this->getContent(); + $content[$draftKey]->empty(); + $this->session->put($this->instance, $content); + } + + public function removeDraft($draftKey) + { + $content = $this->getContent(); + $content->pull($draftKey); + $this->session->put($this->instance, $content); + } + + public function content() + { + return $this->getContent(); + } + + protected function getContent() + { + $content = $this->session->has($this->instance) ? $this->session->get($this->instance) : collect([]); + + return $content; + } + + public function keys() + { + return $this->getContent()->keys(); + } + + public function destroy() + { + $this->session->remove($this->instance); + } + + public function addItemToDraft($draftKey, Item $item) + { + $content = $this->getContent(); + $content[$draftKey]->addItem($item); + + $this->session->put($this->instance, $content); + + return $item; + } + + public function draftHasItem(TrasactionDraft $draft, Product $product) + { + $item = $draft->search($product); + + return !is_null($item); + } + + public function updateDraftItem($draftKey, $itemKey, $newItemData) + { + $content = $this->getContent(); + $content[$draftKey]->updateItem($itemKey, $newItemData); + + $this->session->put($this->instance, $content); + } + + public function removeItemFromDraft($draftKey, $itemKey) + { + $content = $this->getContent(); + $content[$draftKey]->removeItem($itemKey); + + $this->session->put($this->instance, $content); + } + + public function count() + { + return $this->getContent()->count(); + } + + public function isEmpty() + { + return $this->count() == 0; + } + + public function hasContent() + { + return !$this->isEmpty(); + } +} diff --git a/app/Services/InvoiceDraft/Item.php b/app/Services/InvoiceDraft/Item.php new file mode 100644 index 0000000..e26d710 --- /dev/null +++ b/app/Services/InvoiceDraft/Item.php @@ -0,0 +1,30 @@ +description = $itemDetail['description']; + $this->amount = $itemDetail['amount']; + } + + public function updateAttribute(array $newItemData) + { + if (isset($newItemData['description'])) { + $this->description = $newItemData['description']; + } + if (isset($newItemData['amount'])) { + $this->amount = $newItemData['amount']; + } + + return $this; + } +} diff --git a/database/migrations/2017_10_05_162758_create_invoices_table.php b/database/migrations/2017_10_05_162758_create_invoices_table.php new file mode 100644 index 0000000..856adcc --- /dev/null +++ b/database/migrations/2017_10_05_162758_create_invoices_table.php @@ -0,0 +1,38 @@ +increments('id'); + $table->unsignedInteger('project_id'); + $table->string('invoice_no', 8); + $table->text('items'); + $table->unsignedInteger('amount'); + $table->string('notes'); + $table->unsignedTinyInteger('status_id'); + $table->unsignedInteger('user_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('invoices'); + } +} diff --git a/resources/lang/id/invoice.php b/resources/lang/id/invoice.php new file mode 100644 index 0000000..710cc6b --- /dev/null +++ b/resources/lang/id/invoice.php @@ -0,0 +1,31 @@ + 'Invoice', + 'list' => 'Daftar Invoice', + 'search' => 'Cari Invoice', + 'detail' => 'Detail Invoice', + 'not_found' => 'Invoice tidak ditemukan', + 'empty' => 'Belum ada Invoice', + 'back_to_index' => 'Kembali ke daftar Invoice', + + // Actions + 'proccess' => 'Proses Invoice', + 'create' => 'Input Invoice Baru', + 'created' => 'Input Invoice baru telah berhasil.', + 'show' => 'Detail Invoice', + 'edit' => 'Edit Invoice', + 'update' => 'Update Invoice', + 'updated' => 'Update data Invoice telah berhasil.', + 'delete' => 'Hapus Invoice', + 'delete_confirm' => 'Anda yakin akan menghapus Invoice ini?', + 'deleted' => 'Hapus data Invoice telah berhasil.', + 'undeleted' => 'Data Invoice gagal dihapus.', + 'undeleteable' => 'Data Invoice tidak dapat dihapus.', + + // Attributes + 'project' => 'Project', + 'items' => 'Item Invoice', + 'notes' => 'Catatan', +]; diff --git a/resources/views/invoices/create.blade.php b/resources/views/invoices/create.blade.php new file mode 100644 index 0000000..01d47f3 --- /dev/null +++ b/resources/views/invoices/create.blade.php @@ -0,0 +1,26 @@ +@extends('layouts.app') + +@section('title', 'Entry Invoice') + +@section('content') +
+
+ {{ csrf_field() }} + +
+
+

{{ trans('invoice.list') }}

+ + +@includeWhen(! InvoiceDraftCollection::isEmpty(), 'invoices.partials.invoice-draft-tabs') +@if ($draft) + @if (Request::get('action') == 'confirm') + @include('invoices.partials.draft-confirm') + @else +
+
@include('invoices.partials.draft-item-list')
+
@include('invoices.partials.form-draft-detail')
+
+ @endif +@endif +@endsection \ No newline at end of file diff --git a/resources/views/invoices/partials/draft-confirm.blade.php b/resources/views/invoices/partials/draft-confirm.blade.php new file mode 100644 index 0000000..d7d0350 --- /dev/null +++ b/resources/views/invoices/partials/draft-confirm.blade.php @@ -0,0 +1,59 @@ +
+
+
+

{{ trans('invoice.confirm') }}

+
+ + + + + + + + + + @forelse($draft->items() as $key => $item) + + + + + + @empty + @endforelse + + + + + + + +
#DeskripsiBiaya
{{ $key + 1 }}{{ $item->description }}{{ formatRp($item->amount) }}
{{ trans('invoice.total') }} :{{ formatRp($draft->getTotal()) }}
+
+
+
+
+
+

{{ trans('invoice.detail') }}

+
+ + + + + + + + + +
{{ trans('invoice.project') }} + {{ App\Entities\Projects\Project::findOrFail($draft->projectId)->name }} +
{{ trans('invoice.total') }}{{ formatRp($draft->getTotal()) }}
{{ trans('invoice.notes') }}{{ $draft->notes }}
+
+ +
+
+
\ No newline at end of file diff --git a/resources/views/invoices/partials/draft-item-list.blade.php b/resources/views/invoices/partials/draft-item-list.blade.php new file mode 100644 index 0000000..2c8512e --- /dev/null +++ b/resources/views/invoices/partials/draft-item-list.blade.php @@ -0,0 +1,62 @@ + + {{ trans('invoice.items') }} + + ({{ $draft->getItemsCount() }} Item) + + + +
+
+ + + + + + + + + + + + @forelse($draft->items() as $key => $item) + + + {{ Form::open(['route' => ['cart.update-draft-item', $draft->draftKey], 'method' => 'patch']) }} + {{ Form::hidden('item_key', $key) }} + + + {{ Form::submit('update-item-' . $key, ['style'=>'display:none']) }} + {{ Form::close() }} + + + @empty + @endforelse + + + {{ Form::open(['route' => ['cart.add-draft-item', $draft->draftKey]]) }} + + + + {{ Form::close() }} + + + + + + + + + +
#DeskripsiBiayaAction
{{ $no }} {{ Form::text('description', $item->description, ['id' => 'description-' . $key]) }} + {{ Form::number('amount', $item->amount, ['id' => 'amount-' . $key]) }} + + {!! FormField::delete([ + 'route' => ['cart.remove-draft-item', $draft->draftKey], + 'onsubmit' => 'Yakin ingin menghapus Item ini?', + 'class' => '', + ], 'x', ['id' => 'remove-item-' . $key, 'class' => 'btn btn-danger btn-xs show-on-hover','title' => 'Hapus item ini'], ['item_index' => $key]) !!} +
{{ Form::text('description', null, ['id' => 'description']) }} + {{ Form::number('amount', null, ['id' => 'amount']) }} + {{ Form::submit('add-item', ['class' => 'btn btn-primary']) }}
{{ trans('invoice.amount') }} :{{ formatRp($draft->getTotal()) }}
+
+
\ No newline at end of file diff --git a/resources/views/invoices/partials/form-draft-detail.blade.php b/resources/views/invoices/partials/form-draft-detail.blade.php new file mode 100644 index 0000000..feba000 --- /dev/null +++ b/resources/views/invoices/partials/form-draft-detail.blade.php @@ -0,0 +1,6 @@ +{{ trans('invoice.detail') }} +{{ Form::open(['route' => ['cart.draft-proccess', $draft->draftKey], 'method' => 'patch']) }} +{!! FormField::select('project_id', $projects, ['label' => trans('invoice.project'), 'required' => true]) !!} +{!! FormField::textarea('notes', ['label' => trans('invoice.notes'), 'value' => $draft->notes]) !!} +{{ Form::submit(trans('invoice.proccess'), ['class' => 'btn btn-info']) }} +{{ Form::close() }} \ No newline at end of file diff --git a/resources/views/invoices/partials/invoice-draft-tabs.blade.php b/resources/views/invoices/partials/invoice-draft-tabs.blade.php new file mode 100644 index 0000000..95efe9f --- /dev/null +++ b/resources/views/invoices/partials/invoice-draft-tabs.blade.php @@ -0,0 +1,17 @@ + + \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 7214f73..972acf5 100644 --- a/routes/web.php +++ b/routes/web.php @@ -11,5 +11,6 @@ require __DIR__ . '/web/projects.php'; require __DIR__ . '/web/payments.php'; require __DIR__ . '/web/subscriptions.php'; require __DIR__ . '/web/reports.php'; +require __DIR__ . '/web/invoices.php'; require __DIR__ . '/web/options-vue.php'; require __DIR__ . '/web/calendar.php'; \ No newline at end of file diff --git a/routes/web/invoices.php b/routes/web/invoices.php new file mode 100644 index 0000000..4f115c2 --- /dev/null +++ b/routes/web/invoices.php @@ -0,0 +1,23 @@ + ['web','role:admin']], function() { + /* + * Invoice Draft Routes + */ + Route::get('invoices/create', 'InvoiceDraftController@index')->name('invoices.create-empty'); + Route::get('invoices/create/{draftKey?}', 'InvoiceDraftController@show')->name('invoices.create'); + Route::post('invoices/create/{draftKey}', 'InvoiceDraftController@store')->name('invoices.store'); + Route::post('invoices/add-draft', 'InvoiceDraftController@add')->name('invoices.add'); + Route::post('cart/add-draft-item/{draftKey}', 'InvoiceDraftController@addDraftItem')->name('cart.add-draft-item'); + Route::patch('cart/update-draft-item/{draftKey}', 'InvoiceDraftController@updateDraftItem')->name('cart.update-draft-item'); + Route::patch('cart/{draftKey}/proccess', 'InvoiceDraftController@proccess')->name('cart.draft-proccess'); + Route::delete('cart/remove-draft-item/{draftKey}', 'InvoiceDraftController@removeDraftItem')->name('cart.remove-draft-item'); + Route::delete('cart/empty/{draftKey}', 'InvoiceDraftController@empty')->name('cart.empty'); + Route::delete('cart/remove', 'InvoiceDraftController@remove')->name('cart.remove'); + Route::delete('cart/destroy', 'InvoiceDraftController@destroy')->name('cart.destroy'); + + /* + * Invoices Routes + */ + Route::get('invoices/{invoice}', ['as' => 'invoices.show', 'uses' => 'InvoicesController@show']); +}); diff --git a/tests/Feature/InvoiceEntryTest.php b/tests/Feature/InvoiceEntryTest.php new file mode 100644 index 0000000..fba5024 --- /dev/null +++ b/tests/Feature/InvoiceEntryTest.php @@ -0,0 +1,169 @@ +adminUserSigningIn(); + + // Add new draft to collection + $cart = new InvoiceDraftCollection(); + $draft = $cart->add(new InvoiceDraft()); + + $this->visit(route('invoices.create')); + + $this->assertViewHas('draft', $draft); + } + + /** @test */ + public function user_can_create_invoice_draft_by_invoice_create_button() + { + $this->adminUserSigningIn(); + $this->visit(route('invoices.create')); + + $this->press(trans('invoice.create')); + $cart = new InvoiceDraftCollection(); + $draft = $cart->content()->last(); + $this->seePageIs(route('invoices.create', $draft->draftKey)); + } + + /** @test */ + public function user_can_add_item_to_cash_draft() + { + $this->adminUserSigningIn(); + + $cart = new InvoiceDraftCollection(); + $draft = new InvoiceDraft(); + $cart->add($draft); + + $this->visit(route('invoices.create', [$draft->draftKey])); + + $this->type('Testing deskripsi invoice item', 'description'); + $this->type(2000, 'amount'); + $this->press('add-item'); + + $this->see(trans('invoice.item_added')); + + $this->type('Testing deskripsi invoice item', 'description'); + $this->type(3000, 'amount'); + $this->press('add-item'); + + $this->seePageIs(route('invoices.create', $draft->draftKey)); + $this->assertEquals(5000, $draft->getTotal()); + $this->see(formatRp(5000)); + } + + /** @test */ + public function user_can_update_item_attribute() + { + $cart = new InvoiceDraftCollection(); + + $draft = $cart->add(new InvoiceDraft()); + + $item1 = new Item(['description' => 'Deskripsi item invoice', 'amount' => 1000]); + $item2 = new Item(['description' => 'Deskripsi item invoice', 'amount' => 2000]); + + // Add items to draft + $cart->addItemToDraft($draft->draftKey, $item1); + $cart->addItemToDraft($draft->draftKey, $item2); + + $this->adminUserSigningIn(); + $this->visit(route('invoices.create', $draft->draftKey)); + + $this->submitForm('update-item-0', [ + 'item_key' => 0, + 'description' => 'Testing deskripsi Update', + 'amount' => 100, + ]); + + $this->submitForm('update-item-1', [ + 'item_key' => 1, + 'description' => 'Testing deskripsi Update', + 'amount' => 100, + ]); + + $this->assertEquals(200, $draft->getTotal()); + + $this->see(formatRp($draft->getTotal())); + } + + /** @test */ + public function user_can_update_draft_transaction_detail_and_get_confirm_page() + { + $project = factory(Project::class)->create(); + $cart = new InvoiceDraftCollection(); + + $draft = $cart->add(new InvoiceDraft()); + + $item1 = new Item(['description' => 'Deskripsi item invoice', 'amount' => 1000]); + $item2 = new Item(['description' => 'Deskripsi item invoice', 'amount' => 2000]); + + // Add items to draft + $cart->addItemToDraft($draft->draftKey, $item1); + $cart->addItemToDraft($draft->draftKey, $item2); + + $this->adminUserSigningIn(); + $this->visit(route('invoices.create', $draft->draftKey)); + + $this->type($project->id, 'project_id'); + $this->type('catatan', 'notes'); + $this->press(trans('invoice.proccess')); + + $this->seePageIs(route('invoices.create', [$draft->draftKey, 'action' => 'confirm'])); + + $this->see(trans('invoice.confirm')); + $this->see($project->name); + $this->see($draft->notes); + $this->see(formatRp(3000)); + $this->seeElement('input', ['id' => 'save-invoice-draft']); + } + + /** @test */ + public function user_can_save_transaction_if_draft_is_completed() + { + $cart = new InvoiceDraftCollection(); + + $draft = $cart->add(new InvoiceDraft()); + + $item1 = new Item(['description' => 'Deskripsi item invoice', 'amount' => 1000]); + $item2 = new Item(['description' => 'Deskripsi item invoice', 'amount' => 2000]); + $project = factory(Project::class)->create(); + + // Add items to draft + $cart->addItemToDraft($draft->draftKey, $item1); + $cart->addItemToDraft($draft->draftKey, $item2); + + $draftAttributes = [ + 'project_id' => $project->id, + 'notes' => 'Catatan', + ]; + $cart->updateDraftAttributes($draft->draftKey, $draftAttributes); + + $user = $this->adminUserSigningIn(); + $this->visit(route('invoices.create', [$draft->draftKey, 'action' => 'confirm'])); + + $this->press(trans('invoice.save')); + + // $this->seePageIs(route('invoices.show', date('ym').'0001')); + // $this->see(trans('invoice.created', ['invoice_no' => date('ym').'0001'])); + + $this->seeInDatabase('invoices', [ + 'invoice_no' => date('ym').'0001', + 'items' => '[{"description":"Deskripsi item invoice","amount":1000},{"description":"Deskripsi item invoice","amount":2000}]', + 'project_id' => $project->id, + 'amount' => 3000, + 'notes' => 'Catatan', + 'user_id' => $user->id, + 'status_id' => 1, + ]); + } +} diff --git a/tests/Unit/InvoiceDraftCollectionTest.php b/tests/Unit/InvoiceDraftCollectionTest.php new file mode 100644 index 0000000..6ed8f35 --- /dev/null +++ b/tests/Unit/InvoiceDraftCollectionTest.php @@ -0,0 +1,152 @@ +assertEquals('drafts', $cart->currentInstance()); + } + + /** @test */ + public function it_can_have_multiple_instances() + { + $cart = new InvoiceDraftCollection(); + + $draft = new InvoiceDraft(); + + $cart->add($draft); + $cart->instance('wishlist')->add($draft); + + $this->assertCount(1, $cart->instance('drafts')->content()); + $this->assertCount(1, $cart->instance('wishlist')->content()); + } + + /** @test */ + public function cart_collection_consist_of_invoice_draft() + { + $cart = new InvoiceDraftCollection(); + $draft = new InvoiceDraft(); + + $cart->add($draft); + + $this->assertCount(1, $cart->content()); + $this->assertTrue($cart->hasContent()); + } + + /** @test */ + public function it_can_get_a_draft_by_key() + { + $draft = new InvoiceDraft(); + $cart = new InvoiceDraftCollection(); + + $cart->add($draft); + $gottenDraft = $cart->get($draft->draftKey); + $invalidDraft = $cart->get('random_key'); + + $this->assertEquals($draft, $gottenDraft); + $this->assertNull($invalidDraft); + } + + /** @test */ + public function it_can_remove_draft_from_draft_collection() + { + $cart = new InvoiceDraftCollection(); + $draft = new InvoiceDraft(); + + $cart->add($draft); + + $this->assertCount(1, $cart->content()); + $cart->removeDraft($cart->content()->keys()->last()); + $this->assertCount(0, $cart->content()); + } + + /** @test */ + public function it_can_be_empty_out() + { + $cart = new InvoiceDraftCollection(); + + $draft = new InvoiceDraft(); + + $cart->add($draft); + $cart->add($draft); + $cart->add($draft); + + $this->assertCount(3, $cart->content()); + $cart->destroy(); + + $this->assertCount(0, $cart->content()); + $this->assertTrue($cart->isEmpty()); + } + + /** @test */ + public function it_has_content_keys() + { + $cart = new InvoiceDraftCollection(); + + $draft = new InvoiceDraft(); + + $cart->add($draft); + + $this->assertCount(1, $cart->keys()); + $cart->removeDraft($cart->content()->keys()->last()); + $this->assertCount(0, $cart->keys()); + } + + /** @test */ + public function it_can_add_item_to_draft() + { + $cart = new InvoiceDraftCollection(); + + $draft = $cart->add(new InvoiceDraft()); + $item = new Item(['description' => 'Deskripsi item invoice', 'amount' => 1000]); + + $cart->addItemToDraft($draft->draftKey, $item); + $cart->addItemToDraft($draft->draftKey, $item); + $this->assertEquals(2000, $draft->getTotal()); + $this->assertEquals(2, $draft->getItemsCount()); + } + + /** @test */ + public function it_can_remove_item_from_draft() + { + $cart = new InvoiceDraftCollection(); + + $draft = $cart->add(new InvoiceDraft()); + $item = new Item(['description' => 'Deskripsi item invoice', 'amount' => 1000]); + + $cart->addItemToDraft($draft->draftKey, $item); + $this->assertCount(1, $draft->items()); + $cart->removeItemFromDraft($draft->draftKey, 0); + $this->assertCount(0, $draft->items()); + $this->assertEquals(0, $draft->getTotal()); + } + + /** @test */ + public function it_can_update_an_item_of_draft() + { + $cart = new InvoiceDraftCollection(); + + $draft = $cart->add(new InvoiceDraft()); + $item = new Item(['description' => 'Deskripsi item invoice', 'amount' => 1000]); + + $cart->addItemToDraft($draft->draftKey, $item); + $this->assertCount(1, $draft->items()); + $this->assertEquals(1000, $draft->getTotal()); + + $newItemData = [ + 'amount' => 100, + ]; + + $cart->updateDraftItem($draft->draftKey, 0, $newItemData); + $this->assertEquals(100, $draft->getTotal()); + } +}