Compare commits

...

4 Commits

Author SHA1 Message Date
9277d42196 Fix order URL
All checks were successful
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 35s
Create Docker Image / Final Docker Image Manifest (push) Successful in 9s
2024-07-25 14:09:24 +10:00
743374cb17 Move charge to under service 2024-07-25 14:08:26 +10:00
ddd44b643f Service display pricing, as a result of moving to psql. Service information updates 2024-07-24 20:17:23 +10:00
79237868cb Moved service.widget.status to a component 2024-07-24 17:45:32 +10:00
24 changed files with 495 additions and 547 deletions

View File

@ -30,58 +30,6 @@ class AdminController extends Controller
->with('o',$o);
}
// @todo Move to reseller
public function charge_addedit(Request $request,Charge $o)
{
if ($request->post()) {
$request->validate([
'account_id' => 'required|exists:accounts,id',
'charge_at' => 'required|date',
'service_id' => 'required|exists:services,id',
'quantity' => 'required|numeric|not_in:0',
'amount' => 'required|numeric|min:0.01',
'sweep_type' => 'required|numeric|in:'.implode(',',array_keys(Charge::sweep)),
'type' => 'required|numeric|in:'.implode(',',array_keys(InvoiceItem::type)),
'taxable' => 'nullable|boolean',
'description' => 'nullable|string|max:128',
]);
if (! $o->exists) {
$o->site_id = config('site')->site_id;
$o->user_id = Auth::id();
$o->active = TRUE;
}
$o->forceFill($request->only(['account_id','charge_at','service_id','quantity','amount','sweep_type','type','taxable','description']));
$o->save();
return redirect()
->back()
->with('success','Charge recorded: '.$o->id);
}
return view('theme.backend.adminlte.a.charge.addedit')
->with('o',$o);
}
// @todo Move to reseller
public function charge_pending_account(Request $request,Account $o)
{
return view('theme.backend.adminlte.a.charge.widgets.pending')
->with('list',$o->charges->where('active',TRUE)->where('processed',NULL)->except($request->exclude));
}
/**
* List unprocessed charges
*
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
*/
// @todo Move to reseller
public function charge_unprocessed()
{
return view('theme.backend.adminlte.a.charge.unprocessed');
}
/**
* Record payments on an account.
*

View File

@ -13,18 +13,6 @@ use App\Models\{Account,Product,Service,User};
class OrderController extends Controller
{
// @todo To check
public function __construct()
{
$this->middleware('auth');
}
// @todo To check
public function index()
{
return view('theme.backend.adminlte.order.home');
}
// @todo To check
public function product_order(Product $o)
{

View File

@ -8,6 +8,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session;
@ -17,7 +18,7 @@ use Illuminate\Validation\ValidationException;
use Illuminate\View\View;
use Symfony\Component\HttpKernel\Exception\HttpException;
use App\Http\Requests\ServiceChangeRequest;
use App\Http\Requests\{ChargeAdd,ServiceChangeRequest};
use App\Mail\{CancelRequest,ChangeRequest};
use App\Models\{Charge,Invoice,Product,Service};
@ -265,6 +266,49 @@ class ServiceController extends Controller
}
}
/**
* Add a charge to a service/account
*
* @param ChargeAdd $request
* @return RedirectResponse
*/
public function charge_addedit(ChargeAdd $request): RedirectResponse
{
$o = Charge::findOrNew(Arr::get($request->validated(),'id'));
// Dont update processed charges
if ($o->processed)
abort(403);
$o->forceFill(array_merge(Arr::except($request->validated(),['id']),['active'=>TRUE]));
$o->save();
return redirect()
->back()
->with('success',sprintf('Charge %s #%d',$o->wasRecentlyCreated ? 'Created' : 'Updated',$o->id));
}
/**
* Add a charge to a service/account
*
* @param Request $request
* @return View
*/
public function charge_edit(Request $request): View
{
$o = Charge::where('processed',FALSE)
->where('id',$request->id)
->firstOrFail();
if (Gate::allows('update',$o)) {
return view('theme.backend.adminlte.charge.widget.addedit')
->with('o',$o)
->with('so',$o->service);
}
abort(403);
}
/**
* List all the domains managed by the user
*

View File

@ -0,0 +1,46 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use App\Models\{Charge,InvoiceItem};
class ChargeAdd extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return Auth::user()
->accounts_all
->contains(request()->post('account_id'));
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
Session::put('charge_add',true);
return [
'id' => 'sometimes|exists:charges,id',
'account_id' => 'required|exists:accounts,id',
'charge_at' => 'required|date',
'service_id' => 'required|exists:services,id',
'site_id' => 'required|exists:sites,id',
'quantity' => 'required|numeric|not_in:0',
'amount' => 'required|numeric|min:0.01',
'sweep_type' => 'required|numeric|in:'.implode(',',array_keys(Charge::sweep)),
'type' => 'required|numeric|in:'.implode(',',array_keys(InvoiceItem::type)),
'taxable' => 'nullable|boolean',
'description' => 'nullable|string|min:5|max:128',
];
}
}

View File

@ -4,29 +4,24 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Leenooks\Traits\ScopeActive;
use App\Casts\CollectionOrNull;
use App\Traits\SiteID;
/**
* CLEANUP NOTES:
* + Charge Date should not be null
* + Attributes should be a collection array
* + type should not be null
* + It would be useful, given an array of Charges to call a function that renders them into invoice format. This may provide consistence and be the single view of how charges do look on an invoice.
*/
class Charge extends Model
{
use SiteID;
use ScopeActive;
protected $casts = [
'attributes' => CollectionOrNull::class,
];
protected $dates = [
'start_at',
'stop_at',
'charge_at', // The date the charge applies - since it can be different to created_at
'start_at' => 'datetime:Y-m-d',
'stop_at' => 'datetime:Y-m-d',
'charge_at' => 'datetime:Y-m-d', // The date the charge applies - since it can be different to created_at
];
public const sweep = [
@ -61,6 +56,7 @@ class Charge extends Model
/** @deprecated use pending */
public function scopeUnprocessed($query)
{
Log::alert('UMO:! Deprecated function scopeUnprocessed()');
return $this->scopePending();
}
@ -76,7 +72,21 @@ class Charge extends Model
public function getNameAttribute()
{
return sprintf('%s %s',$this->description,$this->getAttribute('attributes') ? join('|',unserialize($this->getAttribute('attributes'))) : '');
return sprintf('%s %s',
$this->description,
$this->getAttribute('attributes')
? join('|',unserialize($this->getAttribute('attributes')))
: '');
}
public function getSubTotalAttribute(): float
{
return $this->quantity*$this->amount;
}
public function getTotalAttribute(): float
{
return $this->account->taxed($this->getSubTotalAttribute());
}
public function getTypeNameAttribute(): string

View File

@ -0,0 +1,41 @@
<?php
namespace App\Models\Policies;
use App\Models\{Account,Charge,User};
class ChargePolicy
{
/**
* Determine whether the user can update the model.
*/
public function create(User $user,Account $account): bool
{
return $user->isReseller()
&& $user
->accounts_all
->contains($account->id);
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user,Charge $charge): bool
{
return $user->isReseller()
&& $user
->accounts_all
->contains($charge->account_id);
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user,Charge $charge): bool
{
return $user->isReseller()
&& $user
->accounts_all
->contains($charge->account_id);
}
}

View File

@ -13,9 +13,10 @@ use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use Leenooks\Traits\ScopeActive;
use App\Casts\CollectionOrNull;
use App\Http\Controllers\AccountingController;
use App\Interfaces\{IDs,ProductItem};
use App\Traits\{ProductDetails,ProviderRef,SiteID};
use App\Traits\{ProductDetails,ProviderRef};
/**
* Class Product
@ -65,10 +66,10 @@ use App\Traits\{ProductDetails,ProviderRef,SiteID};
*/
class Product extends Model implements IDs
{
use HasFactory,SiteID,ProductDetails,ScopeActive,ProviderRef;
use HasFactory,ProductDetails,ScopeActive,ProviderRef;
protected $casts = [
'pricing'=>'collection',
'pricing' => CollectionOrNull::class,
];
/* STATIC */

View File

@ -901,7 +901,9 @@ class Service extends Model implements IDs
*/
public function getStatusAttribute(): string
{
return $this->active ? $this->order_status : 'INACTIVE';
return $this->active
? strtolower($this->order_status)
: ((strtolower($this->order_status) === 'cancelled') ? 'cancelled' : 'inactive');
}
/**

28
composer.lock generated
View File

@ -975,16 +975,16 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "7.9.1",
"version": "7.9.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "a629e5b69db96eb4939c1b34114130077dd4c6fc"
"reference": "d281ed313b989f213357e3be1a179f02196ac99b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/a629e5b69db96eb4939c1b34114130077dd4c6fc",
"reference": "a629e5b69db96eb4939c1b34114130077dd4c6fc",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
"reference": "d281ed313b989f213357e3be1a179f02196ac99b",
"shasum": ""
},
"require": {
@ -1081,7 +1081,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.9.1"
"source": "https://github.com/guzzle/guzzle/tree/7.9.2"
},
"funding": [
{
@ -1097,7 +1097,7 @@
"type": "tidelift"
}
],
"time": "2024-07-19T16:19:57+00:00"
"time": "2024-07-24T11:22:20+00:00"
},
{
"name": "guzzlehttp/promises",
@ -2288,16 +2288,16 @@
},
{
"name": "league/commonmark",
"version": "2.5.0",
"version": "2.5.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
"reference": "0026475f5c9a104410ae824cb5a4d63fa3bdb1df"
"reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/0026475f5c9a104410ae824cb5a4d63fa3bdb1df",
"reference": "0026475f5c9a104410ae824cb5a4d63fa3bdb1df",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/ac815920de0eff6de947eac0a6a94e5ed0fb147c",
"reference": "ac815920de0eff6de947eac0a6a94e5ed0fb147c",
"shasum": ""
},
"require": {
@ -2390,7 +2390,7 @@
"type": "tidelift"
}
],
"time": "2024-07-22T18:18:14+00:00"
"time": "2024-07-24T12:52:09+00:00"
},
{
"name": "league/config",
@ -3056,11 +3056,11 @@
},
{
"name": "leenooks/laravel",
"version": "11.1.4",
"version": "11.1.5",
"source": {
"type": "git",
"url": "https://gitea.dege.au/laravel/leenooks.git",
"reference": "f393813311b912f77e4a7082498ed7511482b531"
"reference": "4a4cf3c5bf32f50dcdc8fdc6c3ff680d7f15d90d"
},
"type": "library",
"extra": {
@ -3093,7 +3093,7 @@
"laravel",
"leenooks"
],
"time": "2024-07-24T04:08:04+00:00"
"time": "2024-07-25T03:52:29+00:00"
},
{
"name": "leenooks/passkey",

View File

@ -22,9 +22,9 @@ return new class extends Migration
$table->boolean('processed')->default(false);
$table->integer('sweep_type')->nullable();
$table->integer('type')->nullable();
$table->float('amount', 10, 0)->nullable();
$table->float('quantity', 10, 0)->nullable();
$table->integer('type');
$table->float('amount', 10, 0);
$table->float('quantity', 10, 0);
$table->boolean('taxable')->default(true);
$table->jsonb('attributes')->nullable();
$table->string('description', 128)->nullable();

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
DB::update('ALTER TABLE charges ALTER COLUMN amount SET NOT NULL');
DB::update('ALTER TABLE charges ALTER COLUMN quantity SET NOT NULL');
DB::update('ALTER TABLE charges ALTER COLUMN type SET NOT NULL');
}
/**
* Reverse the migrations.
*/
public function down(): void
{
DB::update('ALTER TABLE charges ALTER COLUMN amount SET DEFAULT NULL');
DB::update('ALTER TABLE charges ALTER COLUMN quantity SET DEFAULT NULL');
DB::update('ALTER TABLE charges ALTER COLUMN type SET DEFAULT NULL'); }
};

View File

@ -0,0 +1,6 @@
<span {{ $attributes->class([
'badge',
'badge-success'=>($status === 'active'),
'badge-warning'=>($status === 'inactive'),
'badge-danger'=>($status === 'cancelled'),
])->except(['status','substatus']) }}>{{ $slot->isNotEmpty() ? $slot : strtoupper($status) }}</span> @if($slot->isEmpty() && (! in_array($status,['active','cancelled'])) && isset($substatus) && $substatus)<small>[{{ strtoupper($substatus) }}]</small> @endif

View File

@ -1,350 +0,0 @@
@extends('adminlte::layouts.app')
@section('htmlheader_title')
Charge {{ $o->id ? '#'. $o->id : '' }}
@endsection
@section('page_title')
Charge
@endsection
@section('contentheader_title')
Record Charge
@endsection
@section('contentheader_description')
@endsection
@section('main-content')
<div class="row">
<div class="col-6">
<div class="card card-dark">
<div class="card-header">
<h1 class="card-title">Record Charge {{ $o->id ? '#'. $o->id : '' }}</h1>
@if(session()->has('success'))
<span class="ml-3 pt-0 pb-0 pr-1 pl-1 btn btn-outline-success"><small>{{ session()->get('success') }}</small></span>
@endif
</div>
<div class="card-body">
<form class="g-0 needs-validation" method="POST" role="form">
@csrf
<div class="row">
<!-- DATE CHARGE -->
<div class="col-4">
<div class="form-group has-validation">
<label for="charge_at">Date Charge</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control @error('charge_at') is-invalid @enderror" id="charge_at" name="charge_at" value="{{ old('charge_at',($o->exists ? $o->charge_at : \Carbon\Carbon::now())->format('Y-m-d')) }}" required>
<span class="invalid-feedback" role="alert">
@error('charge_at')
{{ $message }}
@else
Charge Date is required.
@enderror
</span>
</div>
<span class="input-helper">Date of Charge</span>
</div>
</div>
<!-- QUANTITY -->
<div class="offset-6 col-2">
<div class="form-group has-validation">
<label class="float-right" for="quantity">Quantity</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-hashtag"></i></span>
</div>
<input type="text" class="text-right form-control @error('quantity') is-invalid @enderror" id="quantity" name="quantity" value="{{ old('quantity',$o->exists ? $o->quantity : 1) }}" required>
<span class="invalid-feedback" role="alert">
@error('quantity')
{{ $message }}
@else
Quantity is required.
@enderror
</span>
</div>
</div>
</div>
</div>
<div class="row">
<!-- ACCOUNTS -->
<div class="col-4">
<div class="form-group has-validation">
<label for="account_id">Account</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-user"></i></span>
</div>
<select class="form-control @error('account_id') is-invalid @enderror" id="account_id" name="account_id" required>
<option value=""></option>
@foreach (\App\Models\Account::active()->with(['user'])->get()->sortBy('name') as $ao)
<option value="{{ $ao->id }}" {{ $ao->id == old('account_id',$o->exists ? $o->account_id : NULL) ? 'selected' : '' }}>{{ $ao->name }}</option>
@endforeach
</select>
<span class="invalid-feedback" role="alert">
@error('account_id')
{{ $message }}
@else
Account is required.
@enderror
</span>
</div>
<span class="input-helper">Account to add charge to.</span>
</div>
</div>
<!-- SWEEP TYPE -->
<div class="offset-1 col-4">
<div class="form-group has-validation">
<label for="sweep_type">Sweep</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-dollar-sign"></i></span>
</div>
<select class="form-control @error('sweep_type') is-invalid @enderror" id="sweep_type" name="sweep_type" required>
@foreach (\App\Models\Charge::sweep as $k=>$v)
<option value="{{ $k }}" {{ $k == old('sweep_type',$o->exists ? $o->sweep_type : NULL) ? 'selected' : '' }}>{{ $v }}</option>
@endforeach
</select>
<span class="invalid-feedback" role="alert">
@error('sweep_type')
{{ $message }}
@else
Sweep Type is required.
@enderror
</span>
</div>
<span class="input-helper">When to add the charge to an invoice.</span>
</div>
</div>
<!-- TAXABLE -->
<div class="col-1">
<div class="form-check has-validation">
<label for="taxable">Taxable</label>
<div class="form-check text-right">
<input type="checkbox" class="form-check-input @error('taxable') is-invalid @enderror" id="taxable" name="taxable" value="1" {{ old('taxable',$o->exists ? $o->taxable : 1) ? 'checked' : '' }}>
</div>
</div>
</div>
<!-- AMOUNT -->
<div class="col-2">
<div class="form-group has-validation">
<label class="float-right" for="amount">Amount</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-dollar-sign"></i></span>
</div>
<input type="text" class="text-right form-control @error('amount') is-invalid @enderror" id="amount" name="amount" value="{{ number_format(old('amount',$o->exists ? $o->amount : 0),2) }}">
<span class="invalid-feedback" role="alert">
@error('amount')
{{ $message }}
@else
Amount is required.
@enderror
</span>
</div>
<span class="input-helper">Amount (ex Tax).</span>
</div>
</div>
</div>
<div class="row">
<!-- SERVICES -->
<div class="col-4">
<div class="form-group has-validation">
<label for="service_id">Services</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-bolt"></i></span>
</div>
<select class="form-control @error('service_id') is-invalid @enderror" id="service_id" name="service_id" required>
</select>
<span class="ml-2 pt-2"><i class="fas fa-spinner d-none"></i></span>
<span class="invalid-feedback" role="alert">
@error('service_id')
{{ $message }}
@else
Service is required.
@enderror
</span>
</div>
{{--
<!-- @todo -->
<span class="input-helper"><sup>**</sup>Service inactive.</span>
--}}
</div>
</div>
<!-- CHARGE TYPE -->
<div class="offset-1 col-4">
<div class="form-group has-validation">
<label for="type">Type</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-dollar-sign"></i></span>
</div>
<select class="form-control @error('type') is-invalid @enderror" id="type" name="type" required>
@foreach (collect(\App\Models\InvoiceItem::type)->sort() as $k=>$v)
<option value="{{ $k }}" {{ $k == old('type',$o->exists ? $o->type : NULL) ? 'selected' : '' }}>{{ $v }}</option>
@endforeach
</select>
<span class="invalid-feedback" role="alert">
@error('type')
{{ $message }}
@else
Type is required.
@enderror
</span>
</div>
<span class="input-helper">Charge type.</span>
</div>
</div>
<!-- TOTAL -->
<div class="offset-1 col-2">
<label class="float-right" for="fees_amt">Total</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-dollar-sign"></i></span>
</div>
<input type="text" class="text-right form-control" id="total" value="{{ number_format($o->exists ? $o->quantity*$o->amount : 0,2) }}" disabled>
</div>
<span class="input-helper">Total (ex Tax).</span>
</div>
</div>
<div class="row">
<!-- DESCRIPTION -->
<div class="col-12">
<div class="form-group has-validation">
<label for="description">Description</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-file-alt"></i></span>
</div>
<input type="text" class="form-control @error('description') is-invalid @enderror" id="description" name="description" value="{{ old('description',$o->exists ? $o->description : '') }}">
<span class="invalid-feedback" role="alert">
@error('description')
{{ $message }}
@enderror
</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<a href="{{ url('/home') }}" class="btn btn-danger">Cancel</a>
@can('wholesaler')
<button type="submit" name="submit" class="btn btn-success mr-0 float-right">@if ($site->exists)Save @else Add @endif</button>
@endcan
</div>
</div>
</form>
</div>
</div>
</div>
<div class="col-5">
<div id="pending"></div>
</div>
</div>
@endsection
@section('page-scripts')
@css(select2)
@js(select2,autofocus)
<script type="text/javascript">
function populate(account,spinner) {
spinner.toggleClass('d-none').toggleClass('fa-spin');
$.ajax({
type: 'GET',
dataType: 'json',
cache: false,
url: '{{ url('api/r/services') }}'+'/'+account,
data: {include: {{ $o->service_id ?: 'null' }} },
timeout: 2000,
error: function(x) {
spinner.toggleClass('d-none').toggleClass('fa-spin');
alert('Failed to submit');
},
success: function(data) {
$("select[name=service_id]").empty();
$.each(data,function(i,j) {
var row = '<option value="' + j.id + '" '+(j.id == {{ $o->service_id ?: 'null' }} ? 'selected' : '')+'>' + j.id + ': ' + j.category_name + ' ' + j.name_short + ((! j.active) ? ' **' : '') +'</option>';
$(row).appendTo("select[name=service_id]");
});
spinner.toggleClass('d-none').toggleClass('fa-spin');
}
});
$.ajax({
type: 'GET',
dataType: 'html',
data: {exclude: {{ $o->id ?: 'null' }}},
cache: false,
url: '{{ url('r/charges') }}'+'/'+account,
timeout: 2000,
error: function(x) {
spinner.toggleClass('d-none').toggleClass('fa-spin');
alert('Failed to submit');
},
success: function(data) {
$("div[id=pending]").empty().append(data);
}
});
}
function total() {
$('#total').val(($('#quantity').val()*$('#amount').val()).toFixed(2));
}
$(document).ready(function() {
var spinner = $('#service_id').parent().find('i.fas.fa-spinner');
if ($('#account_id').val()) {
populate($('#account_id').val(),spinner);
}
$('#account_id').select2({
sorter: data => data.sort((a, b) => a.text.localeCompare(b.text)),
})
.on('change',function(e) {
$("select[id=service_id]").empty();
if (! $(this).val()) {
return;
}
populate($(this).val(),spinner);
});
$('#service_id').select2();
$('#sweep_type').select2();
$('#type').select2();
if ($('#quantity').val() && $('#amount').val()) {
total();
}
$('#quantity').on('change',total);
$('#amount').on('change',total);
});
</script>
@append

View File

@ -1,3 +1,5 @@
@use(App\Models\Charge)
@extends('adminlte::layouts.app')
@section('htmlheader_title')
@ -32,17 +34,21 @@
</thead>
<tbody>
@foreach(\App\Models\Charge::unprocessed()->with(['account.user','service'])->get() as $o)
@forelse(Charge::pending()->with(['account.user','service'])->get() as $o)
<tr>
<td><a href="{{ url('a/charge/addedit',$o->id) }}">{{ $o->id }}</a></td>
<td>{{ $o->id }}</td>
<td>{{ $o->charge_at->format('Y-m-d') }}</td>
<td>{{ $o->created_at->format('Y-m-d') }}</td>
<td>{{ $o->account->name }}</td>
<td>{{ $o->service->name_short }}</td>
<td><a href="{{ url('u/service',$o->service_id) }}">{{ $o->service->name_short }}</a></td>
<td>{{ $o->description }}</td>
<td class="text-right">{{ number_format($o->quantity*$o->amount,2) }}</td>
</tr>
@endforeach
@empty
<tr>
<td colspan="7">Not charges unprocessed</td>
</tr>
@endforelse
</tbody>
</table>
</div>

View File

@ -0,0 +1,107 @@
<!-- $o=Charge::class -->
@use(Carbon\Carbon)
@use(App\Models\Charge)
@use(App\Models\InvoiceItem)
<form method="POST" action="{{ url('r/charge/addedit') }}">
@csrf
<input type="hidden" name="account_id" value="{{ old('account_id',$so->account_id) }}">
<input type="hidden" name="service_id" value="{{ old('service_id',$so->id) }}">
<input type="hidden" name="site_id" value="{{ old('site_id',$so->site_id) }}">
@if($id=old('id',isset($o) ? $o->id : FALSE))
<input type="hidden" name="id" value="{{ $id }}">
@endif
<div class="row">
<div class="col-12">
<h3>Charge @if($id)Update #{{ $id }}@else Add @endif
<x-leenooks::button.success class="float-right"/>
@error('id')
<x-leenooks::button.error class="float-right" name="id"/>
@enderror
</h3>
<hr>
</div>
</div>
<div class="row">
<div class="col-12 col-lg-6">
<div class="row">
<!-- DATE CHARGE -->
<div class="col-12 col-lg-10">
<x-leenooks::form.date name="charge_at" icon="fa-calendar" label="Date Charge" feedback="Charge Date is required" :value="($o->charge_at ?? Carbon::now())->format('Y-m-d')"/>
</div>
</div>
<div class="row">
<!-- SWEEP TYPE -->
<div class="col-12 col-lg-10">
<x-leenooks::form.select name="sweep_type" icon="fa-clock" label="Sweep" feedback="Sweep Type is required" helper="When to add the charge to an invoice." :options="collect(Charge::sweep)->map(fn($item,$key)=>['id'=>$key,'value'=>$item])" :value="$o->sweep_type ?? NULL"/>
</div>
</div>
<div class="row">
<!-- CHARGE TYPE -->
<div class="col-12 col-lg-10">
<x-leenooks::form.select name="type" icon="fa-archive" label="Type" feedback="Charge Type is required" helper="Charge Type." :options="collect(InvoiceItem::type)->sort()->map(fn($item,$key)=>['id'=>$key,'value'=>$item])" :value="$o->type ?? NULL"/>
</div>
</div>
</div>
<div class="col-12 col-lg-6">
<div class="row">
<!-- QUANTITY -->
<div class="col-12 offset-lg-6 col-lg-6">
<x-leenooks::form.text class="float-right text-right" id="quantity" name="quantity" icon="fa-hashtag" label="Quantity" feedback="Quantity is required" :value="$o->quantity ?? 1"/>
</div>
</div>
<div class="row">
<!-- TAXABLE -->
<div class="col-1 offset-lg-3 col-lg-1">
<x-leenooks::form.checkbox name="taxable" label="Taxable" value="1" checked/>
</div>
<!-- AMOUNT -->
<div class="col-11 col-lg-8">
<x-leenooks::form.text class="float-right text-right" id="amount" name="amount" icon="fa-dollar-sign" label="Amount" feedback="Amount is required" helper="Amount (ex Tax)." :value="$o->amount ?? 0.00"/>
</div>
</div>
<div class="row">
<!-- TOTAL -->
<div class="col-12 offset-lg-4 col-lg-8">
<x-leenooks::form.text class="float-right text-right" id="total" name="total" icon="fa-dollar-sign" label="Total" helper="Total (ex Tax)." :value="$o->subtotal ?? 0.00" disabled/>
</div>
</div>
</div>
</div>
<div class="row">
<!-- DESCRIPTION -->
<div class="col">
<x-leenooks::form.text name="description" icon="fa-file-alt" label="Description" helper="Description for Invoice" :value="$o->description ?? NULL"/>
</div>
</div>
<div class="row">
<div class="col">
<x-leenooks::button.reset/>
<x-leenooks::button.submit class="float-right">@if($id)Update @else Add @endif</x-leenooks::button.submit>
</div>
</div>
</form>
@section('page-scripts')
<script type="text/javascript">
function total() {
$('#total').val(($('#quantity').val()*$('#amount').val()).toFixed(2));
}
$(document).ready(function() {
$(document).on('change','#quantity',function() { total(); });
$(document).on('change','#amount',function() { total(); });
});
</script>
@append

View File

@ -29,17 +29,20 @@
<div class="card-header bg-dark d-flex p-0">
<ul class="nav nav-pills w-100 p-2">
@if($x=! ($o->suspend_billing || $o->external_billing))
<li class="nav-item"><a @class(['nav-link','active'=>! session()->has('service_update')]) href="#pending_items" data-toggle="tab">Pending Items</a></li>
<li class="nav-item"><a @class(['nav-link','active'=>! (session()->has('service_update') || session()->has('charge_add'))]) href="#pending_items" data-toggle="tab">Pending Items</a></li>
@endif
@if($o->product->hasUsage())
<li class="nav-item"><a @class(['nav-link','active'=>! ($x || session()->has('service_update'))]) href="#traffic" data-toggle="tab">Traffic</a></li>
<li class="nav-item"><a @class(['nav-link','active'=>! ($x || (session()->has('service_update') || session()->has('charge_add')))]) href="#traffic" data-toggle="tab">Traffic</a></li>
@endif
@can('wholesaler')
<li class="nav-item ml-auto"><a class="nav-link" href="#billing" data-toggle="tab">Billing History</a></li>
<li class="nav-item"><a class="nav-link" href="#internal" data-toggle="tab">Internal</a></li>
<li class="nav-item"><a @class(['nav-link','active'=>session()->has('service_update')]) href="#update" data-toggle="tab">Update</a></li>
@if($o->active || $o->isPending())
<li class="nav-item"><a @class(['nav-link','active'=>session()->has('charge_add')]) href="#charge" data-toggle="tab">Charge</a></li>
@endif
@endcan
</ul>
@ -59,16 +62,16 @@
@endcan
</div>
<div class="card-body">
<div class="card-body pt-2 pl-2 pr-2 pb-0">
<div class="tab-content">
@if($x=! ($o->suspend_billing || $o->external_billing))
<div @class(['tab-pane','fade','show active'=>! session()->has('service_update')]) id="pending_items">
<div @class(['tab-pane','fade','show active'=>! (session()->has('service_update') || session()->has('charge_add'))]) id="pending_items">
@include('theme.backend.adminlte.service.widget.invoice')
</div>
@endif
@if($o->product->hasUsage())
<div @class(['tab-pane','fade','show active'=>! ($x || session()->has('service_update'))]) id="traffic">
<div @class(['tab-pane','fade','show active'=>! ($x || (session()->has('service_update') || session()->has('charge_add')))]) id="traffic">
@if($o->type->usage(30)->count())
@include('theme.backend.adminlte.service.widget.'.$o->product->category.'.usagegraph',['o'=>$o->type])
@endif
@ -88,9 +91,15 @@
@endif
</div>
<div @class(['tab-pane','fade','show active'=>session()->pull('service_update')]) id="update">
<div @class(['tab-pane','fade','show active'=>session()->pull('service_update'),'p-2']) id="update">
@include('theme.backend.adminlte.service.widget.update')
</div>
@if($o->active || $o->isPending())
<div @class(['tab-pane','fade','show active'=>session()->pull('charge_add')]) id="charge">
@include('theme.backend.adminlte.service.widget.charge')
</div>
@endif
@endcan
</div>
</div>

View File

@ -0,0 +1,102 @@
@use(App\Models\Charge)
<div class="card card-secondary card-outline card-outline-tabs">
<div class="card-header p-0 border-bottom-0">
<ul class="nav nav-tabs">
<li class="nav-item"><a class="nav-link active" href="#charge_add" data-toggle="tab">Charge</a></li>
<li class="nav-item"><a class="nav-link" href="#charge_pending" data-toggle="tab">Pending</a></li>
</ul>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane fade show active" id="charge_add">
@include('theme.backend.adminlte.charge.widget.addedit',['o'=>NULL,'so'=>$o])
</div>
<div class="tab-pane fade" id="charge_pending">
<table class="table table-sm" id="svc_bill_hist">
<thead>
<tr>
<th>ID</th>
<th>Created</th>
<th>Charge</th>
<th>Type</th>
<th class="text-right">Q</th>
<th class="text-right">Total</th>
<th>Processed</th>
</tr>
</thead>
<tbody>
@forelse(Charge::where('service_id',$o->id)->active()->orderBy('charge_at','DESC')->with(['account'])->get() as $oo)
<tr>
<td>{{ $oo->id }}</td>
<td>{{ $oo->created_at->format('Y-m-d') }}</td>
<td>{{ $oo->charge_at?->format('Y-m-d') }}</td>
<td>{{ $oo->type_name }}</td>
<td class="text-right">{{ number_format($oo->quantity) }}</td>
<td class="text-right">{{ number_format($oo->total,2) }}</td>
<td>
{{ $oo->processed ? 'YES' : 'NO' }}
@if(! $oo->processed)
<span class="float-right">
<a class="charge_edit" id="{{ $oo->id }}" href="#"><i class="fas fa-fw fa-edit"></i></a>
<a class="charge_delete" id="{{ $oo->id }}" href="#"><i class="fas fa-fw fa-trash"></i></a>
</span>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="7">No Charges for this Service</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
<x-leenooks::modal.delete trigger="charge_delete"/>
@section('page-scripts')
<script type="text/javascript">
function total() {
$('#total').val(($('#quantity').val()*$('#amount').val()).toFixed(2));
}
$(document).ready(function() {
var loaded = [];
$('.charge_edit').on('click',function(item) {
// Open our charge add tab automatically
$('.nav-item a[href="#charge_add"]').tab('show');
if (loaded[item.currentTarget.id])
return false;
console.log(item.currentTarget.id);
// Prepopulate with the details of the charge
$.ajax({
url: '{{ url('r/charge/edit') }}',
method: 'POST',
data: { id: item.currentTarget.id },
dataType: 'html',
}).done(function(html) {
$('div[id="charge_add"]').empty().append(html);
loaded[item.currentTarget.id] = true;
}).fail(function() {
alert('Failed');
});
return false;
});
});
//<a href="{{ url('/api/charge/delete',$o->id) }}">
</script>
@append

View File

@ -1,4 +1,4 @@
<!-- $o = App\Models\Service -->
<!-- $o=Service::class -->
<div class="card">
@if($o->external_billing)
<div class="ribbon-wrapper ribbon-lg">
@ -20,7 +20,7 @@
</tr>
<tr>
<th>Status</th>
<td>@include('theme.backend.adminlte.service.widget.status')</td>
<td><x-button.status :status="$o->status" :substatus="$o->order_status"/></td>
</tr>
@if ($o->order_status == 'ORDER-SENT')
<tr>
@ -34,13 +34,7 @@
<td>{{ $o->start_at->format('Y-m-d') }}</td>
</tr>
@endif
@if ($o->stop_at)
<tr>
<th>Cancellation Date</th>
<td>{{ $o->stop_at->format('Y-m-d') }}</td>
</tr>
@endif
@if (($o->active OR $o->isPending()) AND ! $o->external_billing)
@if (($o->active || $o->isPending()) && (! $o->external_billing))
<tr>
<th>Billed</th>
<td>{{ $o->billing_interval_string }}</td>
@ -53,7 +47,7 @@
<td>${{ number_format($o->billing_charge,2) }}</td>
@endif
</tr>
@if($o->active AND $o->invoice_to)
@if($o->active && $o->invoice_to)
<tr>
<th>Invoiced To</th>
<td>{{ $o->invoice_to->format('Y-m-d') }}</td>
@ -67,7 +61,7 @@
@endif
<tr>
<th>Next Invoice</th>
<td>@if ($o->suspend_billing)<strike>@endif{{ $o->invoice_next->format('Y-m-d') }}@if ($o->suspend_billing)</strike> <strong>SUSPENDED</strong>@endif</td>
<td>@if ($o->suspend_billing)<del>@endif{{ $o->invoice_next->format('Y-m-d') }}@if ($o->suspend_billing)</del> <strong>SUSPENDED</strong>@endif</td>
</tr>
<tr>
<th>Next Estimated Invoice</th>
@ -81,14 +75,13 @@
@elseif($o->wasCancelled())
<tr>
<th>Cancelled</th>
<!-- @todo This should show the cancelled date -->
<td>{!! $o->stop_at ? $o->stop_at->format('Y-m-d') : ($o->paid_to ? $o->paid_to->format('Y-m-d') : '').'<sup>*</sup>' !!}</td>
<td>{{ ($o->stop_at ?: $o->paid_to)?->format('Y-m-d') }}</td>
</tr>
@endif
</table>
</div>
@if($o->active OR $o->isPending())
@if($o->active || $o->isPending())
<div class="card-footer sm">
<strong><sup>*</sup>NOTE:</strong> Estimated Invoice does not include any setup, connection nor all current billing cycle usage charges.
</div>

View File

@ -1,12 +0,0 @@
<!-- $o=Service::class -->
<span class="badge @if($o->isPending())badge-warning @else
@switch ($o->status)
@case('ACTIVE')
badge-success
@break;
@case('INACTIVE')
badge-danger
@break;
@endswitch
@endif
">{{ $o->status }}</span>

View File

@ -263,11 +263,10 @@ DO NOT request making changes to an active service here, it will not be processe
@endsection
@section('page-scripts')
<!-- @todo change to blade service provider -->
@themejs('plugin/jqBootstrapValidation/jqBootstrapValidation.js','jq-validation','jquery')
@themejs('//code.jquery.com/ui/1.12.1/jquery-ui.js','jquery-ui-js','jqery')
<script type="text/javascript" src="plugin/jqBootstrapValidation/jqBootstrapValidation.js"></script>
<script type="text/javascript" src="//code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
<script type="text/javascript">
$(function () { $("input,select,textarea").not("[type=submit]").jqBootstrapValidation(); } );
$(document).ready(function () {

View File

@ -6,5 +6,5 @@
</div>
<!-- Default to the left -->
<strong>&copy; {{ \Carbon\Carbon::now()->year }} <a href="#">Leenooks</a>.</strong> All rights reserved. [#{{ $site->site_id }}]
<strong>&copy; {{ \Carbon\Carbon::now()->year }} <a href="#">Leenooks</a>.</strong> All rights reserved. @isset($site)[#{{ $site->site_id }}]@endisset
</footer>

View File

@ -1,21 +1,21 @@
<li class="nav-header">MAIN</li>
<li class="nav-item">
<a href="{{ url('home') }}" class="nav-link @if(preg_match('#^u/home$#',$path=request()->path())) active @endif">
<a href="{{ url('home') }}" @class(['nav-link','active'=>($path=request()->path()) === 'u/home'])>
<i class="nav-icon fas fa-home"></i> <p>Home</p>
</a>
</li>
<!-- ORDERS -->
<li class="nav-item has-treeview @if(preg_match('#^order#',$path)) menu-open @endif">
<a href="#" class="nav-link @if(preg_match('#^order#',$path)) active @endif">
<li @class(['nav-item','has-treeview','menu-open'=>$x=preg_match('#^order#',$path),'menu-closed'=>! $x])>
<a href="#" @class(['nav-link','active'=>$path === 'order'])>
<i class="nav-icon fas fa-cash-register"></i>
<p>Orders <i class="fas fa-angle-left right"></i></p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="{{ url('order') }}" class="nav-link @if(preg_match('#^order$#',$path)) active @endif">
<a href="{{ url('order') }}" @class(['nav-link','active'=>$path === 'order'])>
<i class="fas fa-tag nav-icon"></i> <p>New Order</p>
</a>
</li>
@ -23,44 +23,22 @@
</li>
@can('wholesaler')
<!-- CHARGES -->
<li class="nav-item has-treeview @if(preg_match('#^a/charge/#',$path)) menu-open @endif">
<a href="#" class="nav-link @if(preg_match('#^a/charge/#',$path)) active @endif">
<i class="nav-icon fas fa-plus"></i>
<p>Charges <i class="fas fa-angle-left right"></i></p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="{{ url('a/charge/addedit') }}" class="nav-link @if(request()->path() == 'a/charge/addedit') active @endif">
<i class="fas fa-cart-plus nav-icon"></i> <p>New Charge</p>
</a>
</li>
<li class="nav-item">
<a href="{{ url('a/charge/unprocessed') }}" class="nav-link @if(request()->path() == 'a/charge/unprocessed') active @endif">
<i class="fas fa-list nav-icon"></i> <p>Unprocessed</p>
</a>
</li>
</ul>
</li>
<!-- PAYMENTS -->
<li class="nav-item has-treeview @if(preg_match('#^a/payment/#',$path)) menu-open @endif">
<a href="#" class="nav-link @if(preg_match('#^a/payment/#',$path)) active @endif">
<li @class(['nav-item','has-treeview','menu-open'=>$x=preg_match('#^a/payment/#',$path),'menu-closed'=>! $x])>
<a href="#" @class(['nav-link','active'=>preg_match('#^a/payment/#',$path)])>
<i class="nav-icon fas fa-receipt"></i>
<p>Payments <i class="fas fa-angle-left right"></i></p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="{{ url('a/payment/addedit') }}" class="nav-link @if(preg_match('#^a/payment/addedit#',$path)) active @endif">
<a href="{{ url('a/payment/addedit') }}" @class(['nav-link','active'=>$path === 'a/payment/addedit'])>
<i class="fas fa-money-bill nav-icon"></i> <p>New Payment</p>
</a>
</li>
<li class="nav-item">
<a href="{{ url('a/payment/unapplied') }}" class="nav-link @if(preg_match('#^a/payment/unapplied$#',$path)) active @endif">
<a href="{{ url('a/payment/unapplied') }}" @class(['nav-link','active'=>$path === 'a/payment/unapplied'])>
<i class="fas fa-receipt nav-icon"></i> <p>Unapplied</p>
</a>
</li>
@ -72,61 +50,61 @@
<li class="nav-header">ADMIN</li>
<li class="nav-item">
<a href="{{ url('auth/intuit') }}" class="nav-link @if(preg_match('#^auth/intuit#',$path)) active @endif">
<a href="{{ url('auth/intuit') }}" @class(['nav-link','active'=>preg_match('#^auth/intuit#',$path)])>
<i class="nav-icon fas fa-calculator"></i> <p>Quickbooks Link</p>
</a>
</li>
<li class="nav-item">
<a href="{{ url('a/setup') }}" class="nav-link @if(preg_match('#^a/setup#',$path)) active @endif">
<a href="{{ url('a/setup') }}" @class(['nav-link','active'=>preg_match('#^a/setup#',$path)])>
<i class="nav-icon fas fa-cogs"></i> <p>Site Setup</p>
</a>
</li>
<!-- CHECKOUT (PAYMENTS) -->
<li class="nav-item">
<a href="{{ url('a/checkout') }}" class="nav-link @if(preg_match('#^a/checkout#',$path)) active @endif">
<a href="{{ url('a/checkout') }}" @class(['nav-link','active'=>preg_match('#^a/checkout#',$path)])>
<i class="nav-icon fas fa-money-check-alt"></i> <p>Payments</p>
</a>
</li>
<!-- PRODUCTS -->
<li class="nav-item">
<a href="{{ url('a/product') }}" class="nav-link @if(preg_match('#^a/product#',$path)) active @endif">
<a href="{{ url('a/product') }}" @class(['nav-link','active'=>preg_match('#^a/product#',$path)])>
<i class="nav-icon fas fa-barcode"></i> <p>Products</p>
</a>
</li>
<!-- SUPPLIERS -->
<li class="nav-item has-treeview @if(preg_match('#^a/supplier#',$path)) menu-open @endif">
<a href="#" class="nav-link @if(preg_match('#^a/supplier#',$path)) active @endif">
<li @class(['nav-item','has-treeview','menu-open'=>$x=preg_match('#^a/supplier#',$path),'menu-closed'=>! $x])>
<a href="#" @class(['nav-link','active'=>preg_match('#^a/supplier#',$path)])>
<i class="nav-icon fas fa-user-tag"></i>
<p>Suppliers <i class="fas fa-angle-left right"></i></p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="{{ url('a/supplier') }}" class="nav-link @if(preg_match('#^a/supplier(/[cost|details]+/[0-9]+)?$#',$path)) active @endif">
<a href="{{ url('a/supplier') }}" @class(['nav-link','active'=>preg_match('#^a/supplier(/[cost|details]+/[0-9]+)?$#',$path)])>
<i class="nav-icon fas fa-cogs"></i> <p>Configuration</p>
</a>
</li>
<li class="nav-item">
<a href="{{ url('a/supplier/product/add') }}" class="nav-link @if(preg_match('#^a/supplier/product/(add|edit)#',$path)) active @endif">
<a href="{{ url('a/supplier/product/add') }}" @class(['nav-link','active'=>preg_match('#^a/supplier/product/(add|edit)#',$path)])>
<i class="nav-icon fas fa-barcode"></i> <p>New Product</p>
</a>
</li>
</ul>
</li>
<li class="nav-item has-treeview @if(preg_match('#^a/report/(accounts|products|services)#',$path))menu-open @else menu-closed @endif">
<a href="#" class="nav-link @if(preg_match('#^a/report/(accounts|products|services)#',$path)) active @endif">
<li @class(['nav-item','has-treeview','menu-open'=>$x=preg_match('#^a/report/(accounts|products|services)#',$path),'menu-closed'=>! $x])>
<a href="#" @class(['nav-link','active'=>preg_match('#^a/report/(accounts|products|services)#',$path)])>
<i class="nav-icon fas fa-list"></i> <p>REPORT<i class="fas fa-angle-left right"></i></p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="{{ url('a/report/accounts') }}" class="nav-link @if(preg_match('#^a/report/accounts#',$path))active @endif">
<a href="{{ url('a/report/accounts') }}" @class(['nav-link','active'=>$path === 'a/report/accounts'])>
<i class="nav-icon fas fa-users"></i> <p>Accounts</p>
</a>
</li>
@ -134,7 +112,7 @@
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="{{ url('a/report/products') }}" class="nav-link @if(preg_match('#^a/report/products#',$path))active @endif">
<a href="{{ url('a/report/products') }}" @class(['nav-link','active'=>$path === 'a/report/products'])>
<i class="nav-icon fas fa-barcode"></i> <p>Products</p>
</a>
</li>
@ -142,7 +120,7 @@
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="{{ url('a/report/services') }}" class="nav-link @if(preg_match('#^a/report/services#',$path))active @endif">
<a href="{{ url('a/report/services') }}" @class(['nav-link','active'=>$path === 'a/report/services'])>
<i class="nav-icon fas fa-layer-group"></i> <p>Services</p>
</a>
</li>
@ -153,26 +131,32 @@
@can('reseller')
<li class="nav-header">RESELLER</li>
<li class="nav-item has-treeview @if(preg_match('#^r/report/(domain|email|hosting)#',$path))menu-open @else menu-closed @endif">
<a href="#" class="nav-link @if(preg_match('#^r/report/(domain|email|hosting)#',$path)) active @endif">
<li @class(['nav-item','has-treeview','menu-open'=>$x=preg_match('#^r/report/(charge|domain|email|hosting)#',$path),'menu-closed'=>! $x])>
<a href="#" @class(['nav-link','active'=>preg_match('#^r/report/(charge|domain|email|hosting)#',$path)])>
<i class="nav-icon fas fa-list"></i> <p>REPORT<i class="fas fa-angle-left right"></i></p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="{{ url('r/report/domain') }}" class="nav-link @if(preg_match('#^r/report/domain$#',$path))active @endif">
<a href="{{ url('r/report/charge/pending') }}" @class(['nav-link','active'=>$path === 'r/report/charge/pending'])>
<i class="nav-icon fas fa-list"></i> <p>Charges Unprocessed</p>
</a>
</li>
<li class="nav-item">
<a href="{{ url('r/report/domain') }}" @class(['nav-link','active'=>$path === 'r/report/domain'])>
<i class="nav-icon fas fa-globe-asia"></i> <p>Domain Names</p>
</a>
</li>
<li class="nav-item">
<a href="{{ url('r/report/email') }}" class="nav-link @if(preg_match('#^r/report/email$#',$path))active @endif">
<a href="{{ url('r/report/email') }}" @class(['nav-link','active'=>$path === 'r/report/email'])>
<i class="nav-icon fas fa-envelope"></i> <p>Email Hosting</p>
</a>
</li>
<li class="nav-item">
<a href="{{ url('r/report/hosting') }}" class="nav-link @if(preg_match('#^r/report/hosting$#',$path))active @endif">
<a href="{{ url('r/report/hosting') }}" @class(['nav-link','active'=>$path === 'r/report/hosting'])>
<i class="nav-icon fas fa-sitemap"></i> <p>Web Hosting</p>
</a>
</li>

View File

@ -26,8 +26,6 @@ Route::group(['middleware'=>['auth:api','role:wholesaler']], function() {
// Reseller API calls
Route::group(['middleware'=>['auth:api','role:reseller']], function() {
Route::get('/r/services/{o}',[ResellerServicesController::class,'services'])
->where('o','[0-9]+');
Route::post('r/invoices/{o}',[AdminController::class,'pay_invoices'])
->where('o','[0-9]+')
->middleware(['theme:adminlte-be','role:wholesaler']);

View File

@ -121,9 +121,6 @@ Route::group(['middleware'=>['auth','role:wholesaler'],'prefix'=>'a'],function()
Route::get('report/accounts',[ReportController::class,'accounts']);
Route::get('report/products',[ReportController::class,'products']);
Route::get('report/services',[ReportController::class,'services']);
// Charges - @todo This should probably go to resellers
Route::match(['get','post'],'charge/addedit/{o?}',[AdminController::class,'charge_addedit']);
Route::get('charge/unprocessed',[AdminController::class,'charge_unprocessed']);
// Payments - @todo This should probably go to resellers
Route::match(['get','post'],'payment/addedit/{o?}',[AdminController::class,'pay_addedit']);
@ -155,13 +152,15 @@ Route::group(['middleware'=>['auth','role:reseller'],'prefix'=>'r'],function() {
});
// Charges
Route::get('charges/{o}',[AdminController::class,'charge_pending_account'])
->middleware('can:view,o')
->where('o','[0-9]+');
Route::post('charge/addedit',[ServiceController::class,'charge_addedit']);
Route::post('charge/edit',[ServiceController::class,'charge_edit']);
// Reseller API calls
Route::post('service_change_charges/{o}',[ServiceController::class,'service_change_charges_display'])
->where('o','[0-9]+');
// Charges
Route::view('report/charge/pending','theme.backend.adminlte.charge.pending');
});
// Our User Routes
@ -214,7 +213,7 @@ Route::group(['prefix'=>'u'],function() {
});
// Frontend
Route::get('order',[OrderController::class,'index']);
Route::view('order','theme.frontend.metronic.order.home');
Route::post('order',[OrderController::class,'submit']);
Route::get('product_order/{o}',[OrderController::class,'product_order']);