Recurring Invoice ListView

This commit is contained in:
David Bomba 2019-08-15 21:10:02 +10:00
parent 03d67bbb52
commit 0892993afe
8 changed files with 202 additions and 23 deletions

View File

@ -11,7 +11,6 @@
namespace App\Http\Controllers\ClientPortal;
use App\Filters\InvoiceFilters;
use App\Http\Controllers\Controller;
use App\Models\RecurringInvoice;
use App\Utils\Traits\MakesHash;
@ -37,19 +36,24 @@ class RecurringInvoiceController extends Controller
*
* @return \Illuminate\Http\Response
*/
public function index(InvoiceFilters $filters, Builder $builder)
public function index(Builder $builder)
{
$invoices = Invoice::filter($filters);
$invoices = RecurringInvoice::whereClientId(auth()->user()->client->id)
->whereIn('status_id', [RecurringInvoice::STATUS_PENDING, RecurringInvoice::STATUS_ACTIVE, RecurringInvoice::STATUS_COMPLETED])
->orderBy('status_id', 'asc')
->get();
if (request()->ajax()) {
return DataTables::of($invoices)->addColumn('action', function ($invoice) {
return '<a href="/client/recurring_invoices/'. $invoice->hashed_id .'/edit" class="btn btn-xs btn-primary"><i class="glyphicon glyphicon-edit"></i>'.ctrans('texts.view').'</a>';
})->addColumn('frequency_id', function ($invoice) {
return RecurringInvoice::frequencyForKey($invoice->frequency_id);
})
->editColumn('status_id', function ($invoice){
return Invoice::badgeForStatus($invoice->status);
return RecurringInvoice::badgeForStatus($invoice->status);
})
->rawColumns(['checkbox', 'action', 'status_id'])
->rawColumns(['action', 'status_id'])
->make(true);
}

View File

@ -31,9 +31,9 @@ class RecurringInvoice extends BaseModel
*/
const STATUS_DRAFT = 2;
const STATUS_ACTIVE = 3;
const STATUS_CANCELLED = 4;
const STATUS_PENDING = -1;
const STATUS_COMPLETED = -2;
const STATUS_CANCELLED = -3;
/**
@ -85,9 +85,9 @@ class RecurringInvoice extends BaseModel
'line_items' => 'object',
];
protected $with = [
// 'client',
// 'company',
protected $appends = [
'hashed_id',
'status'
];
public function company()
@ -110,30 +110,42 @@ class RecurringInvoice extends BaseModel
$this->morphMany(RecurringInvoiceInvitation::class);
}
public function getStatusAttribute()
{
if($this->status_id == RecurringInvoice::STATUS_ACTIVE && $this->start_date > Carbon::now()) //marked as active, but yet to fire first cycle
return RecurringInvoice::STATUS_PENDING;
else if($this->status_id == RecurringInvoice::STATUS_ACTIVE && $this->next_send_date > Carbon::now())
return RecurringInvoice::STATUS_COMPLETED;
else
return $this->status_id;
}
public function nextSendDate() :?Carbon
{
switch ($this->frequency_id)
{
case FREQUENCY_WEEKLY:
case RecurringInvoice::FREQUENCY_WEEKLY:
return Carbon::parse($this->next_send_date->addWeek());
case FREQUENCY_TWO_WEEKS:
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
return Carbon::parse($this->next_send_date->addWeeks(2));
case FREQUENCY_FOUR_WEEKS:
case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
return Carbon::parse($this->next_send_date->addWeeks(4));
case FREQUENCY_MONTHLY:
case RecurringInvoice::FREQUENCY_MONTHLY:
return Carbon::parse($this->next_send_date->addMonth());
case FREQUENCY_TWO_MONTHS:
case RecurringInvoice::FREQUENCY_TWO_MONTHS:
return Carbon::parse($this->next_send_date->addMonths(2));
case FREQUENCY_THREE_MONTHS:
case RecurringInvoice::FREQUENCY_THREE_MONTHS:
return Carbon::parse($this->next_send_date->addMonths(3));
case FREQUENCY_FOUR_MONTHS:
case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
return Carbon::parse($this->next_send_date->addMonths(4));
case FREQUENCY_SIX_MONTHS:
case RecurringInvoice::FREQUENCY_SIX_MONTHS:
return Carbon::parse($this->next_send_date->addMonths(6));
case FREQUENCY_ANNUALLY:
case RecurringInvoice::FREQUENCY_ANNUALLY:
return Carbon::parse($this->next_send_date->addYear());
case FREQUENCY_TWO_YEARS:
case RecurringInvoice::FREQUENCY_TWO_YEARS:
return Carbon::parse($this->next_send_date->addYears(2));
default:
return null;
@ -160,4 +172,71 @@ class RecurringInvoice extends BaseModel
$this->save();
}
public static function badgeForStatus(int $status)
{
switch ($status) {
case RecurringInvoice::STATUS_DRAFT:
return '<h4><span class="badge badge-light">'.ctrans('texts.draft').'</span></h4>';
break;
case RecurringInvoice::STATUS_PENDING:
return '<h4><span class="badge badge-primary">'.ctrans('texts.sent').'</span></h4>';
break;
case RecurringInvoice::STATUS_ACTIVE:
return '<h4><span class="badge badge-primary">'.ctrans('texts.partial').'</span></h4>';
break;
case RecurringInvoice::STATUS_COMPLETED:
return '<h4><span class="badge badge-success">'.ctrans('texts.status_completed').'</span></h4>';
break;
case RecurringInvoice::STATUS_CANCELLED:
return '<h4><span class="badge badge-danger">'.ctrans('texts.overdue').'</span></h4>';
break;
default:
# code...
break;
}
}
public static function frequencyForKey(int $frequency_id) :string
{
switch ($frequency_id) {
case RecurringInvoice::FREQUENCY_WEEKLY:
return ctrans('texts.freq_weekly');
break;
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
return ctrans('texts.freq_two_weeks');
break;
case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
return ctrans('texts.freq_four_weeks');
break;
case RecurringInvoice::FREQUENCY_MONTHLY:
return ctrans('texts.freq_monthly');
break;
case RecurringInvoice::FREQUENCY_TWO_MONTHS:
return ctrans('texts.freq_two_months');
break;
case RecurringInvoice::FREQUENCY_THREE_MONTHS:
return ctrans('texts.freq_three_months');
break;
case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
return ctrans('texts.freq_four_months');
break;
case RecurringInvoice::FREQUENCY_SIX_MONTHS:
return ctrans('texts.freq_six_months');
break;
case RecurringInvoice::FREQUENCY_ANNUALLY:
return ctrans('texts.freq_annually');
break;
case RecurringInvoice::FREQUENCY_TWO_YEARS:
return ctrans('texts.freq_two_years');
break;
case RecurringInvoice::RECURS_INDEFINITELY:
return ctrans('texts.freq_indefinitely');
break;
default:
# code...
break;
}
}
}

View File

@ -6,7 +6,7 @@ use Faker\Generator as Faker;
$factory->define(App\Models\RecurringInvoice::class, function (Faker $faker) {
return [
'status_id' => App\Models\RecurringInvoice::STATUS_PENDING,
'status_id' => App\Models\RecurringInvoice::STATUS_ACTIVE,
'discount' => $faker->numberBetween(1,10),
'is_amount_discount' => $faker->boolean(),
'tax_name1' => 'GST',
@ -28,5 +28,7 @@ $factory->define(App\Models\RecurringInvoice::class, function (Faker $faker) {
'last_sent_date' => $faker->date(),
'next_send_date' => $faker->date(),
'remaining_cycles' => $faker->numberBetween(1,10),
'amount' => $faker->randomFloat(2, $min = 1, $max = 1000) // 48.8932
];
});

View File

@ -468,7 +468,7 @@ class CreateUsersTable extends Migration
$t->datetime('last_viewed')->nullable();
$t->unsignedInteger('frequency_id');
$t->date('start_date')->nullable();
$t->datetime('start_date')->nullable();
$t->datetime('last_sent_date')->nullable();
$t->datetime('next_send_date')->nullable();
$t->unsignedInteger('remaining_cycles')->nullable();

View File

@ -98,11 +98,16 @@ class RandomDataSeeder extends Seeder
/** Invoice Factory */
factory(\App\Models\Invoice::class,500)->create(['user_id' => $user->id, 'company_id' => $company->id, 'client_id' => $client->id]);
/** Recurring Invoice Factory */
factory(\App\Models\RecurringInvoice::class,20)->create(['user_id' => $user->id, 'company_id' => $company->id, 'client_id' => $client->id]);
$clients = Client::all();
foreach($clients as $client)
{
$client->getNextClientNumber($client);
//$client->getNextClientNumber($client);
$client->id_number = $client->getNextClientNumber($client);
$client->save();
}

View File

@ -3105,6 +3105,9 @@ $LANG = array(
'email_already_register' => 'This email is already linked to an account',
'create_account' => 'Create Account',
'locations' => 'Locations',
'freq_indefinitely' => 'Indefinitely',
'next_send_date' => 'Next send date',
'cycles_remaining' => 'Cycles remaining',
);
return $LANG;

View File

@ -0,0 +1,82 @@
@extends('portal.default.layouts.master')
@section('header')
@parent
<link href="//cdn.datatables.net/1.10.19/css/dataTables.bootstrap4.min.css" rel="stylesheet" type="text/css"/>
@stop
@section('body')
<main class="main">
<div class="container-fluid">
<div class="row" style="padding-top: 30px;">
<div class="col-lg-12" style="padding-bottom: 10px;">
<div class="animated fadeIn">
<div class="col-md-12 card">
{!! $html->table(['class' => 'table table-hover table-striped', 'id' => 'datatable'], true) !!}
</div>
</div>
</div>
</div>
</div>
</main>
</body>
@endsection
@push('scripts')
<script src="//cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js" type="text/javascript"></script>
<script src="//cdn.datatables.net/1.10.18/js/dataTables.bootstrap4.min.js"></script>
@endpush
@section('footer')
<script>
/*global json payload*/
var data;
var data_table;
$(function() {
data_table = $('#datatable').DataTable({
processing: true,
serverSide: true,
searching: false,
bLengthChange: false,
language: {
processing: " {{ trans('texts.processing_request') }}",
search: "{{ trans('texts.search') }}:",
// info: "{{ trans('texts.info') }}",
infoPostFix: "",
loadingRecords: "{{ trans('texts.loading') }}",
zeroRecords: "{{ trans('texts.no_records_found') }}"
},
ajax: {
url: '{!! route('client.recurring_invoices.index') !!}',
data: function(data) {
}
},
drawCallback: function(settings){
data = this.api().ajax.json().data;
},
columns: [
{data: 'frequency_id', name: 'frequency_id', title: '{{trans('texts.frequency')}}', visible: true},
{data: 'start_date', name: 'start_date', title: '{{trans('texts.start_date')}}', visible: true},
{data: 'next_send_date', name: 'next_send_date', title: '{{trans('texts.next_send_date')}}', visible: true},
{data: 'remaining_cycles', name: 'remaining_cycles', title: '{{trans('texts.cycles_remaining')}}', visible: true},
{data: 'amount', name: 'amount', title: '{{trans('texts.amount')}}', visible: true},
{data: 'action', name: 'action', title: '', searchable: false, orderable: false},
]
});
});
</script>
@endsection

View File

@ -15,9 +15,13 @@ Route::post('client/password/reset', 'Auth\ContactResetPasswordController@reset'
Route::group(['middleware' => ['auth:contact'], 'prefix' => 'client', 'as' => 'client.'], function () {
Route::get('dashboard', 'ClientPortal\DashboardController@index')->name('dashboard'); // name = (dashboard. index / create / show / update / destroy / edit
Route::get('invoices', 'ClientPortal\InvoiceController@index')->name('invoices.index');
Route::get('recurring_invoices', 'ClientPortal\RecurringInvoiceController@index')->name('recurring_invoices.index');
Route::post('invoices/payment', 'ClientPortal\InvoiceController@bulk')->name('invoices.bulk');
Route::get('recurring_invoices', 'ClientPortal\RecurringInvoiceController@index')->name('recurring_invoices.index');
Route::get('profile/{client_contact}/edit', 'ClientPortal\ProfileController@edit')->name('profile.edit');
Route::put('profile/{client_contact}/edit', 'ClientPortal\ProfileController@update')->name('profile.update');
Route::put('profile/{client_contact}/edit_client', 'ClientPortal\ProfileController@updateClient')->name('profile.edit_client');