- Invoice listing in frontend

This commit is contained in:
Peter Papp
2021-04-21 16:53:39 +02:00
parent 5a9583be5b
commit eae212ac5d
22 changed files with 279 additions and 99 deletions

View File

@@ -6,6 +6,7 @@ use App\Models\File;
use App\Models\Folder;
use App\Models\Share;
use App\Services\HelperService;
use App\Services\Oasis\OasisDevService;
use App\Services\SetupService;
use App\Models\Setting;
use App\Models\User;
@@ -77,6 +78,9 @@ class SetupDevEnvironment extends Command
'--stop-when-empty' => true,
]);
// Oasis demo content generator
resolve(OasisDevService::class)->create_demo_content();
$this->info('Everything is done, congratulations! 🥳🥳🥳');
}

View File

@@ -3,9 +3,16 @@
namespace App\Http\Controllers\Oasis;
use App\Http\Controllers\Controller;
use Auth;
use Illuminate\Http\Request;
class ClientController extends Controller
{
//
/**
* @return mixed
*/
public function index()
{
return response(Auth::user()->clients, 200);
}
}

View File

@@ -3,9 +3,25 @@
namespace App\Http\Controllers\Oasis;
use App\Http\Controllers\Controller;
use App\Http\Resources\Oasis\OasisInvoiceCollection;
use Auth;
use Illuminate\Http\Request;
class InvoiceController extends Controller
{
//
/**
* @return mixed
*/
public function get_all_regular_invoices()
{
return response(new OasisInvoiceCollection(Auth::user()->regularInvoices), 200);
}
/**
* @return mixed
*/
public function get_all_advance_invoices()
{
return response(new OasisInvoiceCollection(Auth::user()->advanceInvoices), 200);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Http\Resources\Oasis;
use Illuminate\Http\Resources\Json\ResourceCollection;
class OasisInvoiceCollection extends ResourceCollection
{
public $collects = OasisInvoiceResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Support\Collection
*/
public function toArray($request)
{
return $this->collection;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Resources\Oasis;
use Illuminate\Http\Resources\Json\JsonResource;
class OasisInvoiceResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->client['name'] . ' ' . format_to_currency($this->total_net, $this->currency),
'invoiceNumber' => $this->invoice_number,
'total' => format_to_currency($this->total_net, $this->currency),
'file_url' => '',
'clientName' => $this->client['name'],
'mimetype' => 'pdf',
'created_at' => format_date($this->created_at, '%d. %B. %Y'),
];
}
}

View File

@@ -70,4 +70,15 @@ function invoice_total_tax($invoice, $format = false)
}
return $total;
}
/**
* @param $value
* @param $currency
* @param string $locale
* @return string
*/
function format_to_currency($value, $currency, $locale = 'cs')
{
return Cashier::formatAmount(($value * 100), $currency, $locale);
}

View File

@@ -3,6 +3,7 @@
namespace App\Models\Oasis;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
@@ -37,8 +38,15 @@ class Invoice extends Model
{
parent::boot();
static::creating(function ($order) {
$order->id = Str::uuid();
static::creating(function ($invoice) {
$invoice->id = Str::uuid();
$invoice->delivery_at = $invoice->created_at;
$invoice->due_at = Carbon::parse($invoice->created_at)->addWeeks(2);
$invoice->total_discount = invoice_total_discount($invoice);
$invoice->total_net = invoice_total_net($invoice);
$invoice->total_tax = invoice_total_tax($invoice);
});
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Services\Oasis;
use App\Models\Oasis\Client;
use App\Models\Oasis\Invoice;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;
class OasisDevService
{
public function create_demo_content()
{
$user = User::whereEmail('howdy@hi5ve.digital')
->first();
$clients = Client::factory(Client::class)
->count(6)
->create(['user_id' => $user->id]);
Invoice::factory(Invoice::class)
->state(new Sequence(
['client_id' => $clients[0]->id],
['client_id' => $clients[1]->id],
['client_id' => $clients[2]->id],
['client_id' => $clients[3]->id],
['client_id' => $clients[4]->id],
['client_id' => $clients[5]->id],
))->count(14)
->create([
'user_id' => $user->id,
'invoice_type' => 'regular_invoice'
]);
Invoice::factory(Invoice::class)
->count(14)
->state(new Sequence(
['client_id' => $clients[0]->id],
['client_id' => $clients[1]->id],
['client_id' => $clients[2]->id],
['client_id' => $clients[3]->id],
['client_id' => $clients[4]->id],
['client_id' => $clients[5]->id],
))->create([
'user_id' => $user->id,
'invoice_type' => 'advance_invoice'
]);
}
}

View File

@@ -23,8 +23,13 @@ trait Oasis
return $this->hasMany(Client::class);
}
public function createdInvoices()
public function regularInvoices()
{
return $this->hasMany(Invoice::class);
return $this->hasMany(Invoice::class)->whereInvoiceType('regular_invoice');
}
public function advanceInvoices()
{
return $this->hasMany(Invoice::class)->whereInvoiceType('advance_invoice');
}
}

View File

@@ -28,7 +28,7 @@ class InvoiceFactory extends Factory
'id' => $this->faker->uuid,
'user_id' => $this->faker->uuid,
'client_id' => $this->faker->uuid,
'invoice_type' => $this->faker->randomElement(['invoice', 'advance_invoice']),
'invoice_type' => $this->faker->randomElement(['regular_invoice', 'advance_invoice']),
'invoice_number' => $this->faker->numberBetween(2120001, 2120999),
'variable_number' => $this->faker->numberBetween(2120001, 2120999),
'currency' => $this->faker->randomElement(['CZK', 'EUR']),
@@ -94,10 +94,6 @@ class InvoiceFactory extends Factory
public function configure()
{
return $this->afterCreating(function (Invoice $invoice) {
$invoice->delivery_at = $invoice->created_at;
$invoice->due_at = Carbon::parse($invoice->created_at)->addWeeks(2);
if ($invoice->discount_type === 'percent') {
$invoice->discount_rate = $this->faker->randomElement([2, 5, 10, 15, 20]);
}
@@ -106,10 +102,6 @@ class InvoiceFactory extends Factory
$invoice->discount_rate = $this->faker->randomElement([20, 10]);
}
$invoice->total_discount = invoice_total_discount($invoice);
$invoice->total_net = invoice_total_net($invoice);
$invoice->total_tax = invoice_total_tax($invoice);
$invoice->save();
});
}

View File

@@ -18,7 +18,7 @@ class CreateInvoicesTable extends Migration
$table->uuid('user_id')->index();
$table->uuid('client_id')->index();
$table->enum('invoice_type', ['invoice', 'advance_invoice']);
$table->enum('invoice_type', ['regular_invoice', 'advance_invoice']);
$table->text('invoice_number')->nullable();
$table->text('variable_number')->nullable();

View File

@@ -52,8 +52,8 @@
"/chunks/invoices.js": "/chunks/invoices.js?id=1deb187f6d3eb1e81ad0",
"/chunks/not-found-shared.js": "/chunks/not-found-shared.js?id=7fc7f9b6f10bdfac770e",
"/chunks/oasis/homepage.js": "/chunks/oasis/homepage.js?id=a1885dd8814f9eb63a1c",
"/chunks/oasis/invoices.js": "/chunks/oasis/invoices.js?id=468f5d8b279884496332",
"/chunks/oasis/invoices/list.js": "/chunks/oasis/invoices/list.js?id=5fc965d6fde96ed7ef00",
"/chunks/oasis/invoices.js": "/chunks/oasis/invoices.js?id=acaee920d77bcc1daca2",
"/chunks/oasis/invoices/list.js": "/chunks/oasis/invoices/list.js?id=7aab0c285898e97a29d8",
"/chunks/oasis/invoices~chunks/platform~chunks/shared.js": "/chunks/oasis/invoices~chunks/platform~chunks/shared.js?id=9ad184d8e96c7d04941c",
"/chunks/oasis/platba.js": "/chunks/oasis/platba.js?id=53d9f2a31b1dd5dad85e",
"/chunks/oasis/platba~chunks/oasis/upgrade-billing~chunks/oasis/upgrade-plan~chunks/upgrade-billing~ch~6880400b.js": "/chunks/oasis/platba~chunks/oasis/upgrade-billing~chunks/oasis/upgrade-plan~chunks/upgrade-billing~ch~6880400b.js?id=0c8d1c3cca084baa177f",
@@ -107,22 +107,5 @@
"/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~d5ccfc07.js": "/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~d5ccfc07.js?id=2b62263afbefba53c9c6",
"/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~f0e94b19.js": "/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~f0e94b19.js?id=d57479525cd6baaf09f3",
"/vendors~chunks/files~chunks/oasis/invoices~chunks/platform~chunks/shared~chunks/shared-files~chunks/~9b82b04f.js": "/vendors~chunks/files~chunks/oasis/invoices~chunks/platform~chunks/shared~chunks/shared-files~chunks/~9b82b04f.js?id=548bff41ffa0385b8f01",
"/vendors~chunks/oasis/invoices~chunks/platform~chunks/shared.js": "/vendors~chunks/oasis/invoices~chunks/platform~chunks/shared.js?id=03d3cf785cbafa4d31f0",
"/chunks/oasis/invoices.525fb5fc3996fd479e64.hot-update.js": "/chunks/oasis/invoices.525fb5fc3996fd479e64.hot-update.js",
"/chunks/oasis/invoices/list.f8b68fed096730458e93.hot-update.js": "/chunks/oasis/invoices/list.f8b68fed096730458e93.hot-update.js",
"/chunks/oasis/invoices.5bd21dc572fc69a3cc77.hot-update.js": "/chunks/oasis/invoices.5bd21dc572fc69a3cc77.hot-update.js",
"/js/main.0837bfb2fd90932547ee.hot-update.js": "/js/main.0837bfb2fd90932547ee.hot-update.js",
"/js/main.0905d00a6c5a085b2fe7.hot-update.js": "/js/main.0905d00a6c5a085b2fe7.hot-update.js",
"/js/main.16fd33b90c3edcb8b122.hot-update.js": "/js/main.16fd33b90c3edcb8b122.hot-update.js",
"/js/main.c4585c2affb9ad7faa80.hot-update.js": "/js/main.c4585c2affb9ad7faa80.hot-update.js",
"/js/main.cc9d6a982ece49bfd227.hot-update.js": "/js/main.cc9d6a982ece49bfd227.hot-update.js",
"/js/main.6973dc93adfa40ca5718.hot-update.js": "/js/main.6973dc93adfa40ca5718.hot-update.js",
"/js/main.178d7e57e604f65f98b2.hot-update.js": "/js/main.178d7e57e604f65f98b2.hot-update.js",
"/chunks/oasis/invoices/list.8578178491ad6c3c565f.hot-update.js": "/chunks/oasis/invoices/list.8578178491ad6c3c565f.hot-update.js",
"/chunks/oasis/invoices.0522bbd02c490c945440.hot-update.js": "/chunks/oasis/invoices.0522bbd02c490c945440.hot-update.js",
"/chunks/oasis/invoices/list.0522bbd02c490c945440.hot-update.js": "/chunks/oasis/invoices/list.0522bbd02c490c945440.hot-update.js",
"/chunks/oasis/invoices.722ea2e34cc7e8e95915.hot-update.js": "/chunks/oasis/invoices.722ea2e34cc7e8e95915.hot-update.js",
"/chunks/oasis/invoices.884a13144310550d496d.hot-update.js": "/chunks/oasis/invoices.884a13144310550d496d.hot-update.js",
"/chunks/oasis/invoices/list.31d8fd28372c5ca4c59f.hot-update.js": "/chunks/oasis/invoices/list.31d8fd28372c5ca4c59f.hot-update.js",
"/chunks/oasis/invoices/list.f2da41474e486bd5b136.hot-update.js": "/chunks/oasis/invoices/list.f2da41474e486bd5b136.hot-update.js"
"/vendors~chunks/oasis/invoices~chunks/platform~chunks/shared.js": "/vendors~chunks/oasis/invoices~chunks/platform~chunks/shared.js?id=03d3cf785cbafa4d31f0"
}

View File

@@ -91,13 +91,13 @@
}
},
methods: {
goTo(category) {
goTo(location) {
let routes = {
'invoices': 'getInvoices',
'advance-invoices': 'getAdvanceInvoices',
'clients': 'getClients',
}
this.$store.dispatch(routes[category])
this.$store.dispatch(routes[location])
}
},
mounted() {
@@ -105,6 +105,8 @@
events.$on('fileItem:deselect', () => this.isScaledDown = false)
events.$on('mobile-menu:hide', () => this.isScaledDown = false)
this.$store.dispatch('getInvoices')
}
}
</script>

View File

@@ -4,7 +4,7 @@
<!--Base location-->
<div class="mobile-actions">
<MobileActionButton @click.native="showLocations" icon="filter">
{{ $route.meta.title }}
{{ directoryName }}
</MobileActionButton>
<MobileActionButton @click.native="createButton" icon="file-plus">
Create
@@ -32,8 +32,13 @@
},
computed: {
...mapGetters([
//
'currentFolder'
]),
directoryName() {
return this.currentFolder
? this.currentFolder.name
: 'Invoices'
},
},
data() {
return {

View File

@@ -144,32 +144,6 @@
id: undefined,
location: 'invoices',
})
this.$store.commit('LOADING_STATE', {
isLoading: false,
data: [
{
id: 'c2fa9df5-se64-499b-b001-94691ccb14e2',
name: 'Nitex s.r.o. - 3260,00 EUR',
invoiceNumber: '2021001',
total: '3260,00 EUR',
created_at: '12. April. 2021',
clientName: 'Nitex s.r.o.',
file_url: 'http://192.168.1.198:8000/file/9ti6r4481cqtuk36-190001-FMkuIZM5TYWjp0TpxoTPjSYj96a5qMtM.pdf',
mimetype: 'pdf',
},
{
id: 'c3fa9df5-ee64-499b-b001-94691ccb14e1',
name: 'VueFileManager s.r.o. - 220,00 EUR',
invoiceNumber: '2021002',
total: '220,00 EUR',
created_at: '9. April. 2021',
clientName: 'VueFileManager s.r.o.',
file_url: 'http://192.168.1.198:8000/file/9ti6r4481cqtuk36-190001-FMkuIZM5TYWjp0TpxoTPjSYj96a5qMtM.pdf',
mimetype: 'pdf',
},
],
})
}
}
</script>

View File

@@ -6,7 +6,7 @@
<!--<chevron-left-icon :class="{'is-active': browseHistory.length > 1 }" class="icon-back" size="17" />-->
<span class="location-title">
Invoices
{{ directoryName }}
</span>
</div>
@@ -90,8 +90,14 @@
computed: {
...mapGetters([
'isVisibleSidebar',
'currentFolder',
'clipboard',
]),
directoryName() {
return this.currentFolder
? this.currentFolder.name
: this.homeDirectory.name
},
canActiveInView() {
let locations = [
'invoices',

View File

@@ -2,11 +2,11 @@
<MenuMobile name="invoice-filter">
<MenuMobileGroup>
<OptionGroup>
<Option @click.native="showLocation('invoices')" title="Invoices" icon="file-text" is-hover-disabled="true" />
<Option @click.native="showLocation('advance-invoices')" title="Advance Invoices" icon="clock" is-hover-disabled="true" />
<Option @click.native="showLocation('invoices')" :is-active="$isThisLocation('invoices')" title="Invoices" icon="file-text" is-hover-disabled="true" />
<Option @click.native="showLocation('advance-invoices')" :is-active="$isThisLocation('advance-invoices')" title="Advance Invoices" icon="clock" is-hover-disabled="true" />
</OptionGroup>
<OptionGroup>
<Option @click.native="showLocation('clients')" title="Clients" icon="users" is-hover-disabled="true" />
<Option @click.native="showLocation('clients')" :is-active="$isThisLocation('clients')" title="Clients" icon="users" is-hover-disabled="true" />
</OptionGroup>
</MenuMobileGroup>
</MenuMobile>
@@ -34,31 +34,13 @@ export default {
},
methods: {
showLocation(location) {
},
flushBrowseHistory() {
this.$store.commit('FLUSH_FOLDER_HISTORY')
},
goToFiles() {
this.$store.dispatch('getFolder', [{folder: this.homeDirectory, back: false, init: true}])
this.flushBrowseHistory()
},
goToLatest() {
this.$store.dispatch('getLatest')
this.flushBrowseHistory()
},
goToTrash() {
this.$store.dispatch('getTrash')
this.flushBrowseHistory()
},
goToShared() {
this.$store.dispatch('getShared', [{back: false, init: false}])
this.flushBrowseHistory()
},
goToParticipantUploads() {
this.$store.dispatch('getParticipantUploads')
this.flushBrowseHistory()
}
let routes = {
'invoices': 'getInvoices',
'advance-invoices': 'getAdvanceInvoices',
'clients': 'getClients',
}
this.$store.dispatch(routes[location])
}
}
}
</script>

View File

@@ -357,7 +357,7 @@ export default {
width: 100%;
display: flex;
align-items: center;
padding: 7px 7px 7px 15px;
padding: 12px 7px 12px 15px;
&.is-dragenter {
border-radius: 8px;

View File

@@ -15,7 +15,7 @@ const actions = {
})
axios
.get(`${getters.api}/invoices/invoice`)
.get('/api/oasis/invoices/regular')
.then(response => {
commit('LOADING_STATE', {loading: false, data: response.data})
})
@@ -36,7 +36,7 @@ const actions = {
})
axios
.get(`${getters.api}/invoices/advance-invoice`)
.get('/api/oasis/invoices/advance')
.then(response => {
commit('LOADING_STATE', {loading: false, data: response.data})
})
@@ -57,7 +57,7 @@ const actions = {
})
axios
.get(`${getters.api}/invoices/clients`)
.get('/api/oasis/invoices/clients')
.then(response => {
commit('LOADING_STATE', {loading: false, data: response.data})
})

View File

@@ -1,6 +1,8 @@
<?php
use App\Http\Controllers\Oasis\AdminController;
use App\Http\Controllers\Oasis\ClientController;
use App\Http\Controllers\Oasis\InvoiceController;
use App\Http\Controllers\Oasis\SubscriptionController;
Route::group(['middleware' => 'api', 'prefix' => '/api/oasis'], function () {
@@ -18,6 +20,17 @@ Route::group(['middleware' => 'api', 'prefix' => '/api/oasis'], function () {
Route::get('/{order}', [SubscriptionController::class, 'get_subscription_request']);
Route::get('/{order}/setup-intent', [SubscriptionController::class, 'get_setup_intent']);
});
// Invoices
Route::group(['prefix' => 'invoices'], function () {
Route::get('/regular', [InvoiceController::class, 'get_all_regular_invoices']);
Route::get('/advance', [InvoiceController::class, 'get_all_advance_invoices']);
});
// Clients
Route::group(['prefix' => 'clients'], function () {
Route::get('/', [ClientController::class, 'index']);
});
});
// Web routes

View File

@@ -3,7 +3,9 @@
namespace Tests\Feature\Oasis;
use App\Models\Oasis\Client;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;
class OasisClientTest extends TestCase
@@ -22,4 +24,26 @@ class OasisClientTest extends TestCase
'name' => $client->name,
]);
}
/**
* @test
*/
public function it_get_all_clients()
{
$user = User::factory(User::class)
->create(['role' => 'user']);
Sanctum::actingAs($user);
$client = Client::factory(Client::class)
->create([
'user_id' => $user->id,
]);
$this->getJson('/api/oasis/clients')
->assertJsonFragment([
'id' => $client->id,
'user_id' => $user->id,
])->assertStatus(200);
}
}

View File

@@ -3,7 +3,9 @@
namespace Tests\Feature\Oasis;
use App\Models\Oasis\Invoice;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;
class OasisInvoiceTest extends TestCase
@@ -124,4 +126,50 @@ class OasisInvoiceTest extends TestCase
$this->assertEquals(40, invoice_total_tax($invoice));
$this->assertEquals('40,00 Kč', invoice_total_tax($invoice, true));
}
/**
* @test
*/
public function it_get_all_user_regular_invoices()
{
$user = User::factory(User::class)
->create(['role' => 'user']);
Sanctum::actingAs($user);
$invoice = Invoice::factory(Invoice::class)
->create([
'user_id' => $user->id,
'invoice_type' => 'regular_invoice'
]);
$response = $this->getJson('/api/oasis/invoices/regular')
->assertJsonFragment([
'id' => $invoice->id,
])->assertStatus(200);
dd(json_decode($response->content(), true));
}
/**
* @test
*/
public function it_get_all_user_advance_invoices()
{
$user = User::factory(User::class)
->create(['role' => 'user']);
Sanctum::actingAs($user);
$invoice = Invoice::factory(Invoice::class)
->create([
'user_id' => $user->id,
'invoice_type' => 'advance_invoice'
]);
$this->getJson('/api/oasis/invoices/advance')
->assertJsonFragment([
'id' => $invoice->id,
])->assertStatus(200);
}
}