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')
+
+
+
+
+
+
+@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') }}
+
+
+
+
+ | # |
+ Deskripsi |
+ Biaya |
+
+
+
+ @forelse($draft->items() as $key => $item)
+
+ | {{ $key + 1 }} |
+ {{ $item->description }} |
+ {{ formatRp($item->amount) }} |
+
+ @empty
+ @endforelse
+
+
+
+ | {{ 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 @@
+
+
+
+
+
+
+
+ | # |
+ Deskripsi |
+ Biaya |
+ Action |
+
+
+
+
+ @forelse($draft->items() as $key => $item)
+
+ | {{ $no }} |
+ {{ Form::open(['route' => ['cart.update-draft-item', $draft->draftKey], 'method' => 'patch']) }}
+ {{ Form::hidden('item_key', $key) }}
+ {{ Form::text('description', $item->description, ['id' => 'description-' . $key]) }} |
+
+ {{ Form::number('amount', $item->amount, ['id' => 'amount-' . $key]) }}
+ |
+ {{ Form::submit('update-item-' . $key, ['style'=>'display:none']) }}
+ {{ Form::close() }}
+
+ {!! 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]) !!}
+ |
+
+ @empty
+ @endforelse
+
+ |
+ {{ Form::open(['route' => ['cart.add-draft-item', $draft->draftKey]]) }}
+ {{ Form::text('description', null, ['id' => 'description']) }} |
+
+ {{ Form::number('amount', null, ['id' => 'amount']) }}
+ |
+ {{ Form::submit('add-item', ['class' => 'btn btn-primary']) }} |
+ {{ Form::close() }}
+
+
+
+
+ | {{ 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 @@
+
+{{ 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());
+ }
+}