Compare commits
3 Commits
3ff34af7f0
...
3ec2276455
Author | SHA1 | Date | |
---|---|---|---|
3ec2276455 | |||
2629b76ec3 | |||
7af09fd078 |
@ -7,7 +7,7 @@ APP_KEY=
|
||||
APP_TIMEZONE=Australia/Melbourne
|
||||
APP_URL=https://www.graytech.net.au
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_CHANNEL=daily
|
||||
|
||||
DB_CONNECTION=pgsql
|
||||
DB_HOST=postgres
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Awobaz\Compoships\Compoships;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Leenooks\Traits\ScopeActive;
|
||||
|
||||
use App\Models\Scopes\SiteScope;
|
||||
use App\Interfaces\IDs;
|
||||
use App\Traits\SiteID;
|
||||
|
||||
/**
|
||||
* Class Account
|
||||
@ -20,12 +20,19 @@ use App\Traits\SiteID;
|
||||
* + sid : System ID for account
|
||||
* + name : Account Name
|
||||
* + taxes : Taxes Applicable to this account
|
||||
*
|
||||
* @package App\Models
|
||||
*/
|
||||
class Account extends Model implements IDs
|
||||
{
|
||||
use Compoships,HasFactory,ScopeActive,SiteID;
|
||||
use HasFactory,ScopeActive;
|
||||
|
||||
/* STATIC */
|
||||
|
||||
public static function InvoicesDue(Collection $invoices=NULL): Collection
|
||||
{
|
||||
return (new self)
|
||||
->invoiceSummaryDue($invoices,TRUE)
|
||||
->get();
|
||||
}
|
||||
|
||||
/* INTERFACES */
|
||||
|
||||
@ -41,13 +48,18 @@ class Account extends Model implements IDs
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
/**
|
||||
* Charges assigned to this account
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function charges()
|
||||
{
|
||||
return $this->hasMany(Charge::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the country the user belongs to
|
||||
* Country this account belongs to
|
||||
*/
|
||||
public function country()
|
||||
{
|
||||
@ -65,28 +77,47 @@ class Account extends Model implements IDs
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* Invoices created for this account
|
||||
*
|
||||
* @todo This needs to be optimised, to only return outstanding invoices and invoices for a specific age (eg: 2 years worth)
|
||||
*/
|
||||
public function invoices()
|
||||
{
|
||||
return $this->hasMany(Invoice::class)
|
||||
->active()
|
||||
->with(['items.taxes','paymentitems.payment']);
|
||||
}
|
||||
|
||||
public function language()
|
||||
/**
|
||||
* Relation to only return active invoices
|
||||
*
|
||||
* @todo Only return active invoice_items
|
||||
*/
|
||||
public function invoices_active()
|
||||
{
|
||||
return $this->belongsTo(Language::class);
|
||||
return $this->invoices()
|
||||
->active();
|
||||
}
|
||||
|
||||
/**
|
||||
* Payments received and assigned to this account
|
||||
*/
|
||||
public function payments()
|
||||
{
|
||||
return $this->hasMany(Payment::class)
|
||||
->active()
|
||||
->with(['items']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Relation to only return active payments
|
||||
*
|
||||
* @todo Only return active payment_items
|
||||
*/
|
||||
public function payments_active()
|
||||
{
|
||||
return $this->payments()
|
||||
->active();
|
||||
}
|
||||
|
||||
public function providers()
|
||||
{
|
||||
return $this->belongsToMany(ProviderOauth::class,'account__provider')
|
||||
@ -94,25 +125,33 @@ class Account extends Model implements IDs
|
||||
->withPivot('ref','synctoken','created_at','updated_at');
|
||||
}
|
||||
|
||||
public function services($active=FALSE)
|
||||
/**
|
||||
* Services assigned to this account
|
||||
*/
|
||||
public function services()
|
||||
{
|
||||
$query = $this->hasMany(Service::class,['account_id','site_id'],['id','site_id'])
|
||||
->withoutGlobalScope(SiteScope::class)
|
||||
->with(['product.translate','invoice_items']);
|
||||
|
||||
return $active ? $query->active() : $query;
|
||||
return $this->hasMany(Service::class)
|
||||
->with(['product.translate','product.type.supplied']);
|
||||
}
|
||||
|
||||
public function site()
|
||||
/**
|
||||
* Relation to only return active services
|
||||
*/
|
||||
public function services_active()
|
||||
{
|
||||
return $this->belongsTo(Site::class);
|
||||
return $this->services()
|
||||
->active();
|
||||
}
|
||||
|
||||
public function taxes()
|
||||
{
|
||||
return $this->hasMany(Tax::class,'country_id','country_id');
|
||||
return $this->hasMany(Tax::class,'country_id','country_id')
|
||||
->select(['id','zone','rate','country_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* User that owns this account
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
@ -149,30 +188,27 @@ class Account extends Model implements IDs
|
||||
* Get the address for the account
|
||||
*
|
||||
* @return array
|
||||
* @todo Change this to return a collection
|
||||
*/
|
||||
public function getAddressAttribute(): array
|
||||
{
|
||||
return [
|
||||
$this->address1,
|
||||
$this->address2,
|
||||
sprintf('%s %s %s',$this->city.(($this->state OR $this->zip) ? ',' : ''),$this->state,$this->zip)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Account breadcrumb to render on pages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBreadcrumbAttribute(): array
|
||||
{
|
||||
return [$this->name => url('u/home',$this->user_id)];
|
||||
return collect([
|
||||
'address1' => $this->address1,
|
||||
'address2' => $this->address2,
|
||||
'location' => sprintf('%s %s %s',
|
||||
$this->city.(($this->state || $this->zip) ? ',' : ''),
|
||||
$this->state,
|
||||
$this->zip)
|
||||
])
|
||||
->filter()
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the account name
|
||||
*
|
||||
* @return mixed|string
|
||||
* @return string
|
||||
*/
|
||||
public function getNameAttribute(): string
|
||||
{
|
||||
@ -184,7 +220,7 @@ class Account extends Model implements IDs
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTypeAttribute()
|
||||
public function getTypeAttribute(): string
|
||||
{
|
||||
return $this->company ? 'Business' : 'Private';
|
||||
}
|
||||
@ -195,6 +231,7 @@ class Account extends Model implements IDs
|
||||
* Get the due invoices on an account
|
||||
*
|
||||
* @return mixed
|
||||
* @deprecated use invoiceSummary->filter(_balance > 0)
|
||||
*/
|
||||
public function dueInvoices()
|
||||
{
|
||||
@ -203,6 +240,83 @@ class Account extends Model implements IDs
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* List of invoices (summary) for this account
|
||||
*
|
||||
* @param Collection|NULL $invoices
|
||||
* @return Collection
|
||||
*/
|
||||
public function invoiceSummary(Collection $invoices=NULL,bool $all=FALSE): Builder
|
||||
{
|
||||
return (new Invoice)
|
||||
->select([
|
||||
'invoices.account_id',
|
||||
'invoices.id as id',
|
||||
DB::raw('SUM(item) AS _item'),
|
||||
DB::raw('SUM(tax) AS _tax'),
|
||||
DB::raw('SUM(payments) AS _payment'),
|
||||
DB::raw('SUM(discount) AS _discount'),
|
||||
DB::raw('SUM(item_total) AS _item_total'),
|
||||
DB::raw('SUM(payment_fees) AS _payment_fee'),
|
||||
DB::raw('ROUND(CAST(SUM(item_total)-SUM(COALESCE(discount,0))+COALESCE(invoices.discount_amt,0) AS NUMERIC),2) AS _total'),
|
||||
DB::raw('ROUND(CAST(SUM(item_total)-SUM(COALESCE(discount,0))+COALESCE(invoices.discount_amt,0)-SUM(payments) AS NUMERIC),2) AS _balance'),
|
||||
'invoices.due_at',
|
||||
'invoices.created_at',
|
||||
])
|
||||
->from(
|
||||
(new Payment)
|
||||
->select([
|
||||
'invoice_id',
|
||||
DB::raw('0 as item'),
|
||||
DB::raw('0 as tax'),
|
||||
DB::raw('0 as discount'),
|
||||
DB::raw('0 as item_total'),
|
||||
DB::raw('SUM(amount) AS payments'),
|
||||
DB::raw('SUM(fees_amt) AS payment_fees'),
|
||||
])
|
||||
->join('payment_items',['payment_items.payment_id'=>'payments.id'])
|
||||
->where('payments.active',TRUE)
|
||||
->where('payment_items.active',TRUE)
|
||||
->groupBy(['payment_items.invoice_id'])
|
||||
->union(
|
||||
(new InvoiceItem)
|
||||
->select([
|
||||
'invoice_id',
|
||||
DB::raw('ROUND(CAST(SUM(quantity*price_base) AS NUMERIC),2) AS item'),
|
||||
DB::raw('ROUND(CAST(SUM(amount) AS NUMERIC),2) AS tax'),
|
||||
DB::raw('SUM(COALESCE(invoice_items.discount_amt,0)) AS discount'),
|
||||
DB::raw('ROUND(CAST(SUM(ROUND(CAST(quantity*price_base AS NUMERIC),2))+SUM(ROUND(CAST(amount AS NUMERIC),2))-SUM(COALESCE(invoice_items.discount_amt,0)) AS NUMERIC),2) AS item_total'),
|
||||
DB::raw('0 as payments'),
|
||||
DB::raw('0 as payment_fees'),
|
||||
])
|
||||
->leftjoin('invoice_item_taxes',['invoice_item_taxes.invoice_item_id'=>'invoice_items.id'])
|
||||
->rightjoin('invoices',['invoices.id'=>'invoice_items.invoice_id'])
|
||||
->where('invoice_items.active',TRUE)
|
||||
->where('invoices.active',TRUE)
|
||||
->groupBy(['invoice_items.invoice_id']),
|
||||
),'p')
|
||||
->join('invoices',['invoices.id'=>'invoice_id'])
|
||||
->when(($all === FALSE),fn($query)=>$query->where('invoices.account_id',$this->id))
|
||||
->orderBy('due_at')
|
||||
->groupBy(['invoices.account_id','invoices.id','invoices.created_at','invoices.due_at','invoices.discount_amt'])
|
||||
->with(['account']);
|
||||
}
|
||||
|
||||
public function invoiceSummaryDue(Collection $invoices=NULL,bool $all=FALSE): Builder
|
||||
{
|
||||
return $this->invoiceSummary($invoices,$all)
|
||||
->havingRaw('ROUND(CAST(SUM(item_total)-SUM(COALESCE(discount,0))+COALESCE(invoices.discount_amt,0)-SUM(payments) AS NUMERIC),2) > 0');
|
||||
}
|
||||
|
||||
public function invoiceSummaryPast(Collection $invoices=NULL,bool $all=FALSE): Builder
|
||||
{
|
||||
return $this->invoiceSummary($invoices,$all)
|
||||
->join('payment_items',['payment_items.invoice_id'=>'invoices.id'])
|
||||
->join('payments',['payments.id'=>'payment_items.payment_id'])
|
||||
->addSelect(DB::raw('max(paid_at) as _paid_at'))
|
||||
->havingRaw('ROUND(CAST(SUM(item_total)-SUM(COALESCE(discount,0))+COALESCE(invoices.discount_amt,0)-SUM(payments) AS NUMERIC),2) <= 0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the taxed value of a value
|
||||
*
|
||||
|
@ -57,16 +57,18 @@ class Charge extends Model
|
||||
|
||||
/* SCOPES */
|
||||
|
||||
/** @deprecated use pending */
|
||||
public function scopeUnprocessed($query)
|
||||
{
|
||||
return $this->scopePending();
|
||||
}
|
||||
|
||||
public function scopePending($query) {
|
||||
return $query
|
||||
->where('active',TRUE)
|
||||
->active()
|
||||
->whereNotNull('charge_at')
|
||||
->whereNotNull('type')
|
||||
->where(function($q) {
|
||||
return $q->where('processed',FALSE)
|
||||
->orWhereNull('processed');
|
||||
});
|
||||
->where(fn($query)=>$query->where('processed',FALSE)->orWhereNull('processed'));
|
||||
}
|
||||
|
||||
/* ATTRIBUTES */
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Awobaz\Compoships\Compoships;
|
||||
use Carbon\Carbon;
|
||||
use Clarkeash\Doorman\Facades\Doorman;
|
||||
use Clarkeash\Doorman\Models\Invite;
|
||||
@ -11,7 +10,7 @@ use Illuminate\Support\Arr;
|
||||
use Leenooks\Traits\ScopeActive;
|
||||
|
||||
use App\Interfaces\IDs;
|
||||
use App\Traits\{PushNew,SiteID};
|
||||
use App\Traits\PushNew;
|
||||
|
||||
/**
|
||||
* Class Invoice
|
||||
@ -34,14 +33,13 @@ use App\Traits\{PushNew,SiteID};
|
||||
*/
|
||||
class Invoice extends Model implements IDs
|
||||
{
|
||||
use Compoships,PushNew,ScopeActive,SiteID;
|
||||
use PushNew,ScopeActive;
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime:Y-m-d',
|
||||
'due_at' => 'datetime:Y-m-d',
|
||||
'reminders'=>'json',
|
||||
];
|
||||
|
||||
protected $dates = [
|
||||
'due_at',
|
||||
'_paid_at' => 'datetime:Y-m-d',
|
||||
];
|
||||
|
||||
public const BILL_WEEKLY = 0;
|
||||
@ -105,11 +103,6 @@ class Invoice extends Model implements IDs
|
||||
];
|
||||
*/
|
||||
|
||||
// Caching variables
|
||||
private int $_paid = 0;
|
||||
private int $_total = 0;
|
||||
private int $_total_tax = 0;
|
||||
|
||||
/* STATIC METHODS */
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Awobaz\Compoships\Compoships;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Casts\AsCollection;
|
||||
@ -19,10 +18,8 @@ use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Leenooks\Carbon as LeenooksCarbon;
|
||||
|
||||
use App\Models\Product\Type;
|
||||
use App\Models\Scopes\SiteScope;
|
||||
use App\Interfaces\IDs;
|
||||
use App\Traits\ScopeServiceUserAuthorised;
|
||||
use App\Traits\SiteID;
|
||||
|
||||
/**
|
||||
* Class Service
|
||||
@ -60,24 +57,23 @@ use App\Traits\SiteID;
|
||||
*/
|
||||
class Service extends Model implements IDs
|
||||
{
|
||||
use HasFactory,ScopeServiceUserAuthorised,SiteID,Compoships;
|
||||
use HasFactory,ScopeServiceUserAuthorised;
|
||||
|
||||
protected $casts = [
|
||||
'order_info'=>AsCollection::class,
|
||||
];
|
||||
|
||||
protected $dates = [
|
||||
'invoice_last_at',
|
||||
'invoice_next_at',
|
||||
'start_at',
|
||||
'stop_at',
|
||||
'order_info' => AsCollection::class,
|
||||
'invoice_last_at' => 'datetime:Y-m-d', // @todo Can these be removed, since we can work out invoice dynamically now
|
||||
'invoice_next_at' => 'datetime:Y-m-d', // @todo Can these be removed, since we can work out invoice dynamically now
|
||||
'stop_at' => 'datetime:Y-m-d',
|
||||
'start_at' => 'datetime:Y-m-d',
|
||||
];
|
||||
|
||||
/** @deprecated */
|
||||
protected $appends = [
|
||||
'category_name',
|
||||
'name_short',
|
||||
];
|
||||
|
||||
/** @deprecated */
|
||||
protected $visible = [
|
||||
// 'account_name',
|
||||
// 'admin_service_id_url',
|
||||
@ -95,8 +91,6 @@ class Service extends Model implements IDs
|
||||
];
|
||||
|
||||
protected $with = [
|
||||
//'invoice_items',
|
||||
//'product.type.supplied',
|
||||
'type',
|
||||
];
|
||||
|
||||
@ -332,8 +326,7 @@ class Service extends Model implements IDs
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class,['account_id','site_id'],['id','site_id'])
|
||||
->withoutGlobalScope(SiteScope::class);
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -353,12 +346,31 @@ class Service extends Model implements IDs
|
||||
*/
|
||||
public function charges()
|
||||
{
|
||||
return $this->hasMany(Charge::class,['service_id','site_id'],['id','site_id'])
|
||||
->withoutGlobalScope(SiteScope::class)
|
||||
->where('active','=',TRUE)
|
||||
return $this->hasMany(Charge::class)
|
||||
->orderBy('created_at');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return only the active charges
|
||||
*/
|
||||
public function charges_active()
|
||||
{
|
||||
return $this->charges()
|
||||
->active();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return only the charges not yet processed
|
||||
*/
|
||||
public function charges_pending()
|
||||
{
|
||||
return $this->charges()
|
||||
->pending();
|
||||
}
|
||||
|
||||
/**
|
||||
* Product changes for this service
|
||||
*/
|
||||
public function changes()
|
||||
{
|
||||
return $this->belongsToMany(Product::class,'service__change','service_id','product_id','id','id')
|
||||
@ -367,53 +379,80 @@ class Service extends Model implements IDs
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
// @todo changed to invoiced_items
|
||||
/**
|
||||
* @deprecated use invoiced_items
|
||||
*/
|
||||
public function invoice_items($active=TRUE)
|
||||
{
|
||||
$query = $this->hasMany(InvoiceItem::class,['service_id','site_id'],['id','site_id'])
|
||||
->withoutGlobalScope(SiteScope::class)
|
||||
->where('item_type','=',0)
|
||||
->orderBy('start_at');
|
||||
|
||||
// @todo Change to $query->active();
|
||||
if ($active)
|
||||
$query->where('active','=',TRUE);
|
||||
|
||||
return $query;
|
||||
return $this->invoiced_items_active();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoices for this service
|
||||
* Invoices that this service is itemised on
|
||||
*/
|
||||
public function invoices($active=TRUE)
|
||||
public function invoiced_items()
|
||||
{
|
||||
$query = $this->hasManyThrough(Invoice::class,InvoiceItem::class,NULL,'id',NULL,'invoice_id')
|
||||
->when($this->site_id,function($q) {
|
||||
return $q->where('invoices.site_id', $this->site_id)
|
||||
->withoutGlobalScope(SiteScope::class);
|
||||
})
|
||||
->distinct('id')
|
||||
->where('invoices.site_id','=',$this->site_id)
|
||||
->where('invoice_items.site_id','=',$this->site_id)
|
||||
->orderBy('created_at')
|
||||
->orderBy('due_at');
|
||||
|
||||
if ($active)
|
||||
$query->where('invoice_items.active','=',TRUE)
|
||||
->where('invoices.active','=',TRUE);
|
||||
|
||||
return $query;
|
||||
return $this->hasMany(InvoiceItem::class)
|
||||
->with(['taxes']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Account that ordered the service
|
||||
* Invoices that this service is itemised on that is active
|
||||
*/
|
||||
public function invoiced_items_active()
|
||||
{
|
||||
return $this->invoiced_items()
|
||||
->where('active',TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the extra charged items for this service (ie: item_type != 0)
|
||||
*/
|
||||
public function invoiced_extra_items()
|
||||
{
|
||||
return $this->hasMany(InvoiceItem::class)
|
||||
->where('item_type','<>',0)
|
||||
->orderBy('service_id')
|
||||
->orderBy('start_at');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return active extra items charged
|
||||
*/
|
||||
public function invoiced_extra_items_active()
|
||||
{
|
||||
return $this->invoiced_extra_items()
|
||||
->where('active',TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the service charged items for this service (ie: item_type == 0)
|
||||
*/
|
||||
public function invoiced_service_items()
|
||||
{
|
||||
return $this->hasMany(InvoiceItem::class)
|
||||
->where('item_type','=',0)
|
||||
->orderBy('service_id')
|
||||
->orderBy('start_at');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return active service items charged
|
||||
*/
|
||||
public function invoiced_service_items_active()
|
||||
{
|
||||
return $this->invoiced_service_items()
|
||||
->where('active',TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* User that ordered the service
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function orderedby()
|
||||
{
|
||||
return $this->belongsTo(Account::class,['ordered_by','site_id'],['id','site_id'])
|
||||
->withoutGlobalScope(SiteScope::class);
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -423,8 +462,7 @@ class Service extends Model implements IDs
|
||||
*/
|
||||
public function product()
|
||||
{
|
||||
return $this->belongsTo(Product::class,['product_id','site_id'],['id','site_id'])
|
||||
->withoutGlobalScope(SiteScope::class);
|
||||
return $this->belongsTo(Product::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -444,10 +482,11 @@ class Service extends Model implements IDs
|
||||
*/
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where(function () use ($query) {
|
||||
$query->where($this->getTable().'.active',TRUE)
|
||||
->orWhereNotIn('order_status',self::INACTIVE_STATUS);
|
||||
});
|
||||
return $query->where(
|
||||
fn($query)=>
|
||||
$query->where($this->getTable().'.active',TRUE)
|
||||
->orWhereNotIn('order_status',self::INACTIVE_STATUS)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -458,20 +497,11 @@ class Service extends Model implements IDs
|
||||
*/
|
||||
public function scopeInActive($query)
|
||||
{
|
||||
return $query->where(function () use ($query) {
|
||||
$query->where($this->getTable().'.active',FALSE)
|
||||
->orWhereIn('order_status',self::INACTIVE_STATUS);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable to perform queries without eager loading
|
||||
*
|
||||
* @param $query
|
||||
* @return mixed
|
||||
*/
|
||||
public function scopeNoEagerLoads($query){
|
||||
return $query->setEagerLoads([]);
|
||||
return $query->where(
|
||||
fn($query)=>
|
||||
$query->where($this->getTable().'.active',FALSE)
|
||||
->orWhereIn('order_status',self::INACTIVE_STATUS)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -723,7 +753,7 @@ class Service extends Model implements IDs
|
||||
if (! $this->product->price_recur_strict)
|
||||
return 1;
|
||||
|
||||
$n = $this->invoice_next->diff($this->invoice_next_end)->days+1;
|
||||
$n = round($this->invoice_next->diffInDays($this->invoice_next_end),0);
|
||||
|
||||
switch ($this->recur_schedule) {
|
||||
case Invoice::BILL_WEEKLY:
|
||||
@ -735,7 +765,7 @@ class Service extends Model implements IDs
|
||||
break;
|
||||
|
||||
case Invoice::BILL_QUARTERLY:
|
||||
$d = $this->invoice_next->addQuarter()->startOfQuarter()->diff($this->invoice_next_end->startOfQuarter())->days;
|
||||
$d = round($this->invoice_next_end->startOfQuarter()->diffInDays($this->invoice_next->addQuarter()->startOfQuarter()),1);
|
||||
break;
|
||||
|
||||
case Invoice::BILL_SEMI_YEARLY:
|
||||
@ -845,19 +875,15 @@ class Service extends Model implements IDs
|
||||
*/
|
||||
public function getPaidToAttribute(): ?Carbon
|
||||
{
|
||||
if (! $this->invoices->count())
|
||||
return NULL;
|
||||
// Last paid invoice
|
||||
$lastpaid = $this
|
||||
->invoices()
|
||||
->filter(fn($item)=>$item->_balance <= 0)
|
||||
->last();
|
||||
|
||||
foreach ($this->invoices->reverse() as $o)
|
||||
if ($o->due == 0)
|
||||
break;
|
||||
|
||||
return $o->items
|
||||
->filter(function($item) {
|
||||
return $item->item_type === 0;
|
||||
})
|
||||
->last()
|
||||
->stop_at;
|
||||
return $lastpaid
|
||||
? $this->invoiced_service_items_active->where('invoice_id',$lastpaid->id)->where('type',0)->max('stop_at')
|
||||
: NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1148,6 +1174,16 @@ class Service extends Model implements IDs
|
||||
return $this->product->hasUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this service invoices
|
||||
*/
|
||||
public function invoices()
|
||||
{
|
||||
return $this->account
|
||||
->invoiceSummary($this->invoiced_service_items_active->pluck('invoice_id'))
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a service is active. It is active, if active=1, or the order_status is not in self::INACTIVE_STATUS[]
|
||||
*
|
||||
|
@ -20,10 +20,6 @@ class Broadband extends Type implements ServiceUsage
|
||||
{
|
||||
private const LOGKEY = 'MSB';
|
||||
|
||||
protected $casts = [
|
||||
'connect_at'=>'datetime:Y-m-d',
|
||||
'expire_at'=>'datetime:Y-m-d',
|
||||
];
|
||||
protected $table = 'service_broadband';
|
||||
|
||||
/* INTERFACES */
|
||||
|
@ -14,7 +14,9 @@ class Domain extends Type
|
||||
use ServiceDomains;
|
||||
|
||||
protected $table = 'service_domain';
|
||||
protected $with = ['tld'];
|
||||
protected $with = [
|
||||
'tld',
|
||||
];
|
||||
|
||||
/* OVERRIDES */
|
||||
|
||||
|
@ -13,5 +13,7 @@ class Email extends Type
|
||||
use ServiceDomains;
|
||||
|
||||
protected $table = 'service_email';
|
||||
protected $with = ['tld'];
|
||||
protected $with = [
|
||||
'tld',
|
||||
];
|
||||
}
|
@ -14,7 +14,9 @@ class Host extends Type
|
||||
use ServiceDomains;
|
||||
|
||||
protected $table = 'service_host';
|
||||
protected $with = ['tld'];
|
||||
protected $with = [
|
||||
'tld',
|
||||
];
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
|
@ -8,10 +8,6 @@ namespace App\Models\Service;
|
||||
*/
|
||||
class Phone extends Type
|
||||
{
|
||||
protected $dates = [
|
||||
'connect_at',
|
||||
'expire_at',
|
||||
];
|
||||
protected $table = 'service_phone';
|
||||
|
||||
/* INTERFACES */
|
||||
|
@ -14,9 +14,11 @@ abstract class Type extends Model implements ServiceItem
|
||||
{
|
||||
use ScopeServiceActive,ScopeServiceUserAuthorised;
|
||||
|
||||
protected $dates = [
|
||||
'expire_at',
|
||||
protected $casts = [
|
||||
'connect_at' => 'datetime:Y-m-d',
|
||||
'expire_at' => 'datetime:Y-m-d',
|
||||
];
|
||||
|
||||
public $timestamps = FALSE;
|
||||
|
||||
/* RELATIONS */
|
||||
|
@ -9,6 +9,7 @@
|
||||
"ext-curl": "*",
|
||||
"ext-pdo": "*",
|
||||
"barryvdh/laravel-snappy": "^1.0",
|
||||
"clarkeash/doorman": "^9.0",
|
||||
"eduardokum/laravel-mail-auto-embed": "^2.0",
|
||||
"laravel/dreamscape": "^0.1.0",
|
||||
"laravel/framework": "^11.0",
|
||||
|
58
composer.lock
generated
58
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "65656094f15929a06f03ee8719037bdd",
|
||||
"content-hash": "b84c28d616dc200d6583006761ae9e0d",
|
||||
"packages": [
|
||||
{
|
||||
"name": "barryvdh/laravel-snappy",
|
||||
@ -213,6 +213,62 @@
|
||||
],
|
||||
"time": "2024-02-09T16:56:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "clarkeash/doorman",
|
||||
"version": "v9.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/clarkeash/doorman.git",
|
||||
"reference": "e94b6be6a0996b0fd457a4165c4a45016969ba5e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/clarkeash/doorman/zipball/e94b6be6a0996b0fd457a4165c4a45016969ba5e",
|
||||
"reference": "e94b6be6a0996b0fd457a4165c4a45016969ba5e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"laravel/framework": "^11.0",
|
||||
"php": "^8.2",
|
||||
"ramsey/uuid": "^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.4",
|
||||
"orchestra/testbench": "^9.0",
|
||||
"phpunit/phpunit": "^11.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Clarkeash\\Doorman\\Providers\\DoormanServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Doorman": "Clarkeash\\Doorman\\Facades\\Doorman"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Clarkeash\\Doorman\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ashley Clarke",
|
||||
"email": "me@ashleyclarke.me"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/clarkeash/doorman/issues",
|
||||
"source": "https://github.com/clarkeash/doorman/tree/v9.0.0"
|
||||
},
|
||||
"time": "2024-03-25T20:55:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "defuse/php-encryption",
|
||||
"version": "v2.4.0",
|
||||
|
@ -0,0 +1,16 @@
|
||||
<!-- $o = Account::class -->
|
||||
<!-- Show outstanding invoices -->
|
||||
<div class="card card-warning">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Invoices Due</h3>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
@if(($list=$o->invoiceSummaryDue()->get())->count())
|
||||
@include('theme.backend.adminlte.invoice.widget.due')
|
||||
|
||||
@else
|
||||
<p>No invoice due</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
@ -6,7 +6,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
@if(($x=$o->invoices->where('created_at','>',\Carbon\Carbon::now()->subMonths(12))->where('due','<=',0))->count())
|
||||
@if(($list=$o->invoiceSummaryPast()->where('invoices.created_at','>=',\Carbon\Carbon::now()->subYears(2)->startOfYear())->get())->count())
|
||||
<table class="table table-bordered w-100" id="invoices_past_{{ $o->id }}">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -19,13 +19,13 @@
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach ($x as $oo)
|
||||
@foreach ($list as $oo)
|
||||
<tr>
|
||||
<td>{{ $oo->account->name }}</td>
|
||||
<td><a href="{{ url('u/invoice',$oo->id) }}">{{ $oo->sid }}</a></td>
|
||||
<td><a href="{{ url('u/invoice',$oo->id) }}">{{ $oo->lid }}</a></td>
|
||||
<td>{{ $oo->created_at->format('Y-m-d') }}</td>
|
||||
<td>{{ $oo->paid_date ? $oo->paid_date->format('Y-m-d') : '' }}</td>
|
||||
<td class="text-right">${{ number_format($oo->total,2) }}</td>
|
||||
<td>{{ $oo->_paid_at?->format('Y-m-d') }}</td>
|
||||
<td class="text-right">${{ number_format($oo->_total,2) }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
@ -37,12 +37,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section('page-scripts')
|
||||
@section('page-styles')
|
||||
@css(datatables,bootstrap4|rowgroup)
|
||||
@append
|
||||
|
||||
@section('page-scripts')
|
||||
@js(datatables,bootstrap4|rowgroup)
|
||||
|
||||
<script type="text/javascript">
|
||||
@if ($x->count())
|
||||
@if ($list->count())
|
||||
$(document).ready(function() {
|
||||
$('#invoices_past_{{ $o->id }}').DataTable({
|
||||
order: [[2,'desc'],[0,'asc']],
|
@ -20,13 +20,13 @@
|
||||
|
||||
<tbody>
|
||||
@foreach ($x as $so)
|
||||
<tr>
|
||||
<td><a href="{{ url('u/service',[$so->id]) }}">{{ $so->sid }}</a></td>
|
||||
<td>{{ $so->product->category_name }}</td>
|
||||
<td>{{ $so->name_short }}</td>
|
||||
<td>{{ $so->product->name }}</td>
|
||||
<td>{{ $so->external_billing ? '-' : $so->invoice_next->format('Y-m-d') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="{{ url('u/service',[$so->id]) }}">{{ $so->sid }}</a></td>
|
||||
<td>{{ $so->product->category_name }}</td>
|
||||
<td>{{ $so->name_short }}</td>
|
||||
<td>{{ $so->product->name }}</td>
|
||||
<td>{{ $so->external_billing ? '-' : $so->invoice_next->format('Y-m-d') }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
<tfoot>
|
||||
@ -43,8 +43,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section('page-scripts')
|
||||
@section('page-styles')
|
||||
@css(datatables,bootstrap4|rowgroup)
|
||||
@append
|
||||
|
||||
@section('page-scripts')
|
||||
@js(datatables,bootstrap4|rowgroup)
|
||||
|
||||
<script type="text/javascript">
|
||||
@ -64,10 +67,6 @@
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
$('#services_active_{{ $ao->id }} tbody').on('click','tr', function () {
|
||||
$(this).toggleClass('selected');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@append
|
@ -45,7 +45,7 @@
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Account Balance</span>
|
||||
<span class="info-box-number"><small>$</small> {{ number_format(($x=$o->invoices()->with(['items.taxes','paymentitems.payment','account'])->get()->where('due','>',0))->sum('due'),2) }}</span>
|
||||
<span class="info-box-number"><small>$</small> {{ number_format(($x=$o->accounts->map(fn($item)=>$item->invoiceSummaryDue()->get()->pluck('_balance'))->flatten())->sum(),2) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -17,7 +17,7 @@
|
||||
@section('main-content')
|
||||
<!-- Our Summary Home Page Boxes -->
|
||||
<div class="row">
|
||||
@include('theme.backend.adminlte.common.account.widget.summary')
|
||||
@include('theme.backend.adminlte.account.widget.summary_boxes')
|
||||
</div>
|
||||
|
||||
<div class="card card-light card-tabs">
|
||||
@ -49,10 +49,6 @@
|
||||
<div class="card-header bg-white">
|
||||
<ul class="nav nav-pills">
|
||||
<li class="nav-item"><a class="nav-link {{ (! session()->has('supplier_update')) ? 'active' : '' }}" href="#tab-services" data-toggle="tab">Services</a></li>
|
||||
{{--
|
||||
<!-- @todo this is not working -->
|
||||
<li class="nav-item"><a class="nav-link" href="#tab-nextinvoice" data-toggle="tab">Next Invoice</a></li>
|
||||
--}}
|
||||
<li class="nav-item"><a class="nav-link" href="#tab-futureinvoice" data-toggle="tab">Future Invoice</a></li>
|
||||
@canany('reseller','wholesaler')
|
||||
<li class="nav-item ml-auto">
|
||||
@ -67,30 +63,16 @@
|
||||
<div class="tab-pane {{ (! session()->has('supplier_update')) ? 'active' : '' }}" id="tab-services">
|
||||
<div class="row">
|
||||
<div class="col-12 col-xl-7">
|
||||
@include('theme.backend.adminlte.service.widget.active',['o'=>$ao])
|
||||
@include('theme.backend.adminlte.account.widget.service_active',['o'=>$ao])
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-xl-5">
|
||||
@include('theme.backend.adminlte.invoice.widget.due',['o'=>$ao])
|
||||
|
||||
@include('theme.backend.adminlte.invoice.widget.list',['o'=>$ao])
|
||||
|
||||
@include('theme.backend.adminlte.payment.widget.list',['o'=>$ao])
|
||||
@include('theme.backend.adminlte.account.widget.invoice_due',['o'=>$ao])
|
||||
@include('theme.backend.adminlte.account.widget.invoice_past',['o'=>$ao])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{--
|
||||
<!-- @todo this is not working -->
|
||||
<div class="tab-pane" id="tab-nextinvoice">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@include('theme.backend.adminlte.u.invoice.widgets.next',['future'=>FALSE])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
--}}
|
||||
|
||||
<div class="tab-pane" id="tab-futureinvoice">
|
||||
<div class="row">
|
||||
<div class="col-12 col-xl-9">
|
||||
@ -121,18 +103,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('page-scripts')
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('body')
|
||||
.addClass('sidebar-collapse')
|
||||
.delay(500)
|
||||
.queue(function () {
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
$(this).dequeue();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@append
|
||||
@endsection
|
@ -1,62 +1,53 @@
|
||||
<!-- $o = Account::class -->
|
||||
<!-- Show outstanding invoices -->
|
||||
<div class="card card-warning">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Invoices Due</h3>
|
||||
</div>
|
||||
<table class="table table-bordered w-100" id="invoices_due_{{ $o->id }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Account</th>
|
||||
<th>#</th>
|
||||
<th>Due</th>
|
||||
<th class="text-right">Total</th>
|
||||
<th class="text-right">Outstanding</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<div class="card-body">
|
||||
@if(($x=$o->invoices->where('due','>',0))->count())
|
||||
<table class="table table-bordered w-100" id="invoices_due_{{ $o->id }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Account</th>
|
||||
<th>#</th>
|
||||
<th>Due</th>
|
||||
<th class="text-right">Total</th>
|
||||
<th class="text-right">Outstanding</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($list as $oo)
|
||||
<tr @if ($oo->due_at->isPast()) class="table-danger" @endif>
|
||||
<td>{{ $oo->account->name }}</td>
|
||||
<td><a href="{{ url('u/invoice',$oo->id) }}">{{ $oo->lid }}</a></td>
|
||||
<td>{{ $oo->due_at->format('Y-m-d') }}</td>
|
||||
<td class="text-right">${{ number_format($oo->_total,2) }}</td>
|
||||
<td class="text-right">${{ number_format($oo->_balance,2) }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<tbody>
|
||||
@foreach ($x as $oo)
|
||||
<tr @if ($oo->due_at->isPast()) class="table-danger" @endif>
|
||||
<td>{{ $oo->account->name }}</td>
|
||||
<td><a href="{{ url('u/invoice',$oo->id) }}">{{ $oo->lid }}</a></td>
|
||||
<td>{{ $oo->due_at->format('Y-m-d') }}</td>
|
||||
<td class="text-right">${{ number_format($oo->total,2) }}</td>
|
||||
<td class="text-right">${{ number_format($oo->due,2) }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@else
|
||||
<p>No invoice due</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@section('page-styles')
|
||||
@css(datatables,bootstrap4|rowgroup)
|
||||
@append
|
||||
|
||||
@section('page-scripts')
|
||||
@css(datatables,bootstrap4|rowgroup)
|
||||
@js(datatables,bootstrap4|rowgroup)
|
||||
|
||||
<script type="text/javascript">
|
||||
@if ($x->count())
|
||||
$(document).ready(function() {
|
||||
$('#invoices_due_{{ $o->id }}').DataTable({
|
||||
order: [[0,'asc'],[3,'desc']],
|
||||
rowGroup: {
|
||||
dataSrc: 0,
|
||||
},
|
||||
columnDefs: [
|
||||
{
|
||||
targets: [0],
|
||||
visible: false,
|
||||
}
|
||||
],
|
||||
});
|
||||
@if ($list->count())
|
||||
$(document).ready(function() {
|
||||
$('#invoices_due_{{ $o->id }}').DataTable({
|
||||
// If we have more than 1 account id, order by account
|
||||
order: [[0,'asc'],[2,'asc'],[1,'desc']],
|
||||
rowGroup: {
|
||||
dataSrc: 0,
|
||||
},
|
||||
columnDefs: [
|
||||
{
|
||||
targets: [0],
|
||||
visible: false,
|
||||
}
|
||||
],
|
||||
});
|
||||
});
|
||||
@endif
|
||||
</script>
|
||||
@append
|
@ -1,11 +1,11 @@
|
||||
<!-- @todo These needs to be optimised, and change for $o = Account::class -->
|
||||
<!-- Show next items for an invoice -->
|
||||
@if ($o->next_invoice_items($future)->count())
|
||||
@if (($x=$o->next_invoice_items($future))->count())
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<!-- Group by Account -->
|
||||
@foreach (($x=$o->next_invoice_items($future))->groupBy('product_id') as $id => $oo)
|
||||
@foreach ($x->groupBy('product_id') as $id => $oo)
|
||||
<tr>
|
||||
<th colspan="4">{{ $oo->first()->product->name }}</th>
|
||||
<th class="text-right">${{ number_format($oo->sum('total'),2) }}</th>
|
||||
|
@ -1,71 +0,0 @@
|
||||
<!-- $o = Account::class -->
|
||||
<!-- Show past 12 months payments -->
|
||||
<div class="card card-success">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Past Payments</h3>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
@if(($x=$o->payments->where('created_at','>',\Carbon\Carbon::now()->subMonths(12)))->count())
|
||||
<table class="table table-bordered w-100" id="payments_past">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Account</th>
|
||||
<th>#</th>
|
||||
<th>Received</th>
|
||||
<th class="text-right">Total</th>
|
||||
{{--<th class="text-right">Balance</th>--}}
|
||||
<th>Invoice(s)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach ($x as $oo)
|
||||
<tr>
|
||||
<td>{{ $oo->account->name }}</td>
|
||||
<td>{{ $oo->lid }}</td>
|
||||
<td>{{ $oo->paid_at->format('Y-m-d') }}</td>
|
||||
<td class="text-right">${{ number_format($oo->total,2) }}</td>
|
||||
{{--<td class="text-right">${{ number_format($oo->balance,2) }}</td>--}}
|
||||
<td>
|
||||
{!! join(', ',$oo->items
|
||||
->filter(function($item) { return $item->invoice_id; })
|
||||
->transform(function($item) { return sprintf('<a href="%s">%s</a>',url('u/invoice',$item->invoice_id),$item->invoice_id); })
|
||||
->toArray()) !!}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@else
|
||||
<p>No payments to list</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section('page-scripts')
|
||||
@css(datatables,bootstrap4|rowgroup)
|
||||
@js(datatables,bootstrap4|rowgroup)
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#payments_past').DataTable({
|
||||
order: [2,'desc'],
|
||||
rowGroup: {
|
||||
dataSrc: 0,
|
||||
},
|
||||
columnDefs: [
|
||||
{
|
||||
targets: [0],
|
||||
visible: false,
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
$('#payments_past tbody').on('click','tr', function () {
|
||||
$(this).toggleClass('selected');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@append
|
@ -6,63 +6,11 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
@if($o->query_invoice_summary()->havingRaw('invoice_total-payments > 0')->count())
|
||||
<table class="table table-bordered w-100" id="reseller_invoices_due">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Account</th>
|
||||
<th>#</th>
|
||||
<th>Issued</th>
|
||||
<th>Due</th>
|
||||
<th class="text-right">Total</th>
|
||||
<th class="text-right">Payments</th>
|
||||
<th class="text-right">Outstanding</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach ($o->query_invoice_summary()->havingRaw('invoice_total-payments > 0')->get() as $oo)
|
||||
<tr @if ($oo->due_at->isPast()) class="table-danger" @endif>
|
||||
<td>{{ $oo->account->name }}</td>
|
||||
<td><a href="{{ url('u/invoice',$oo->id) }}">{{ $oo->sid }}</a></td>
|
||||
<td>{{ $oo->created_at->format('Y-m-d') }}</td>
|
||||
<td>{{ $oo->due_at->format('Y-m-d') }}</td>
|
||||
<td class="text-right">${{ number_format($oo->total,2) }}</td>
|
||||
<td class="text-right">${{ number_format($oo->paid,2) }}</td>
|
||||
<td class="text-right">${{ number_format($oo->due,2) }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@if(($list=\App\Models\Account::InvoicesDue())->count())
|
||||
@include('theme.backend.adminlte.invoice.widget.due')
|
||||
|
||||
@else
|
||||
<p>No invoice due</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section('page-scripts')
|
||||
@css(datatables,bootstrap4|rowgroup)
|
||||
@js(datatables,bootstrap4|rowgroup)
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#reseller_invoices_due').DataTable({
|
||||
order: [[0,'asc'],[3,'desc']],
|
||||
rowGroup: {
|
||||
dataSrc: 0,
|
||||
},
|
||||
columnDefs: [
|
||||
{
|
||||
targets: [0],
|
||||
visible: false,
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
$('#invoices_due tbody').on('click','tr', function () {
|
||||
$(this).toggleClass('selected');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@append
|
||||
</div>
|
@ -0,0 +1,45 @@
|
||||
<table class="table table-sm" id="svc_bill_hist">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Invoice</th>
|
||||
<th>Start</th>
|
||||
<th>Stop</th>
|
||||
<th>Service</th>
|
||||
<th>Extras</th>
|
||||
<th class="text-right">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach($o->invoiced_items_active->groupBy('invoice_id') as $oo)
|
||||
<tr>
|
||||
<td><a href="{{ url('u/invoice',($x=$oo->first())->invoice_id) }}">{{ $x->invoice_id }}</a></td>
|
||||
<td>{{ ($y=$oo->where('item_type',0))->min('start_at')->format('Y-m-d') }}</td>
|
||||
<td>{{ $y->max('stop_at')->format('Y-m-d') }}</td>
|
||||
<th>{{ number_format($y->sum('total'),2) }}</th>
|
||||
<th>{{ number_format($oo->where('item_type','<>',0)->sum('total'),2) }}</th>
|
||||
<td class="text-right">{{ number_format($oo->sum('total'),2) }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@section('page-styles')
|
||||
@css(datatables,bootstrap4)
|
||||
@append
|
||||
|
||||
@section('page-scripts')
|
||||
@js(datatables,bootstrap4)
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#svc_bill_hist').DataTable({
|
||||
language: {
|
||||
emptyTable: "No Invoices"
|
||||
},
|
||||
order: [1,'desc'],
|
||||
pageLength: 10
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@append
|
Loading…
Reference in New Issue
Block a user