Rework payment tables, enable payment editing

This commit is contained in:
Deon George 2021-07-23 17:25:26 +10:00
parent fa62a47680
commit d463239b17
No known key found for this signature in database
GPG Key ID: 7670E8DC27415254
19 changed files with 352 additions and 116 deletions

View File

@ -1,5 +1,5 @@
test: test:
image: ${CI_REGISTRY}/leenooks/php:8.0-fpm-ext-test image: ${CI_REGISTRY}/leenooks/php:8.0-fpm-image-test
stage: test stage: test

View File

@ -66,7 +66,7 @@ class PaymentsEzypayImport extends Command
} }
// Find the last payment logged // Find the last payment logged
$last = Carbon::createFromTimestamp(Payment::whereIN('checkout_id',$cos)->where('account_id',$ao->id)->max('date_payment')); $last = Carbon::createFromTimestamp(Payment::whereIN('checkout_id',$cos)->where('account_id',$ao->id)->max('payment_date'));
$o = $poo->getDebits([ $o = $poo->getDebits([
'customerId'=>$c->Id, 'customerId'=>$c->Id,
@ -87,13 +87,13 @@ class PaymentsEzypayImport extends Command
$pd = Carbon::createFromFormat('Y-m-d?H:i:s.u',$p->Date); $pd = Carbon::createFromFormat('Y-m-d?H:i:s.u',$p->Date);
$lp = $ao->payments->last(); $lp = $ao->payments->last();
if ($lp AND (($pd == $lp->date_payment) OR ($p->Id == $lp->checkout_data))) if ($lp AND (($pd == $lp->payment_date) OR ($p->Id == $lp->checkout_data)))
continue; continue;
// New Payment // New Payment
$po = new Payment; $po = new Payment;
$po->site_id = 1; // @todo $po->site_id = 1; // @todo
$po->date_payment = $pd; $po->payment_date = $pd;
$po->checkout_id = '999'; // @todo $po->checkout_id = '999'; // @todo
$po->checkout_data = $p->Id; $po->checkout_data = $p->Id;
$po->total_amt = $p->Amount; $po->total_amt = $p->Amount;

View File

@ -21,12 +21,12 @@ class AdminController extends Controller
* @param Payment $o * @param Payment $o
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
*/ */
public function pay_add(Request $request,Payment $o) public function pay_addedit(Request $request,Payment $o)
{ {
if ($request->post()) { if ($request->post()) {
$validation = $request->validate([ $validation = $request->validate([
'account_id' => 'required|exists:accounts,id', 'account_id' => 'required|exists:accounts,id',
'date_payment' => 'required|date', 'payment_date' => 'required|date',
'checkout_id' => 'required|exists:ab_checkout,id', 'checkout_id' => 'required|exists:ab_checkout,id',
'total_amt' => 'required|numeric|min:0.01', 'total_amt' => 'required|numeric|min:0.01',
'fees_amt' => 'nullable|numeric|lt:total_amt', 'fees_amt' => 'nullable|numeric|lt:total_amt',
@ -41,36 +41,64 @@ class AdminController extends Controller
'invoices.*.id' => 'nullable|exists:ab_invoice,id', 'invoices.*.id' => 'nullable|exists:ab_invoice,id',
]); ]);
$oo = new Payment; if (! $o->exists) {
$oo->forceFill($request->only(['account_id','date_payment','checkout_id','checkout_id','total_amt','fees_amt','source_id','pending','notes','ip'])); $o->forceFill($request->only(['account_id','payment_date','checkout_id','checkout_id','total_amt','fees_amt','source_id','pending','notes','ip']));
$oo->site_id = config('SITE')->site_id; $o->site_id = config('SITE')->site_id;
$oo->save(); $o->save();
}
foreach ($validation['invoices'] as $id => $amount) { foreach ($validation['invoices'] as $id => $amount) {
$ooo = new PaymentItem;
$ooo->invoice_id = $id; // See if we already have a payment item that we need to update
$ooo->alloc_amt = $amount; $items = $o->items->filter(function($item) use ($id) { return $item->invoice_id == $id; });
$ooo->site_id = config('SITE')->site_id;
$oo->items()->save($ooo); if ($items->count() == 1) {
$oo = $items->pop();
if (! $amount) {
$oo->delete();
continue;
}
} else {
$oo = new PaymentItem;
$oo->invoice_id = $id;
}
$oo->alloc_amt = $amount;
$oo->site_id = config('SITE')->site_id;
$o->items()->save($oo);
} }
return redirect()->back() return redirect()->back()
->with('success','Payment recorded'); ->with('success','Payment recorded');
} }
return view('a.payment.add') return view('a.payment.addedit')
->with('o',$o); ->with('o',$o);
} }
/**
* List unapplied payments
*
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
*/
public function pay_unapplied()
{
return view('a.payment.unapplied');
}
/** /**
* Show a list of invoices to apply payments to * Show a list of invoices to apply payments to
* *
* @param Request $request
* @param Account $o * @param Account $o
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
*/ */
public function pay_invoices(Account $o) public function pay_invoices(Request $request,Account $o)
{ {
return view('a.payment.widgets.invoices') return view('a.payment.widgets.invoices')
->with('pid',$request->pid)
->with('o',$o); ->with('o',$o);
} }

View File

@ -217,7 +217,7 @@ class PaypalController extends Controller
break; break;
} }
$po->date_payment = Carbon::parse($cap->create_time); $po->payment_date = Carbon::parse($cap->create_time);
$po->checkout_id = $this->o->id; $po->checkout_id = $this->o->id;
$po->checkout_data = $cap->id; $po->checkout_data = $cap->id;

View File

@ -5,8 +5,9 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use App\Models\{Account,Invoice,Service,Service\Adsl,Service\Voip,User}; use App\Models\{Account,Invoice,Payment,Service,Service\Adsl,Service\Voip,User};
class SearchController extends Controller class SearchController extends Controller
{ {
@ -90,6 +91,16 @@ class SearchController extends Controller
$result->push(['name'=>sprintf('%s (%s)',$o->service_name,$o->service->sid),'value'=>'/u/service/'.$o->id,'category'=>'Domains']); $result->push(['name'=>sprintf('%s (%s)',$o->service_name,$o->service->sid),'value'=>'/u/service/'.$o->id,'category'=>'Domains']);
} }
if (Gate::any(['wholesaler'],new Payment)) {
# Look for Payments
foreach (Payment::Search($request->input('term'))
->whereIN('account_id',$accounts)
->limit(10)->get() as $o)
{
$result->push(['name'=>sprintf('%s ($%s)',$o->id,number_format($o->total,2)),'value'=>'/a/payment/edit'.$o->id,'category'=>'Payments']);
}
}
return $result; return $result;
} }
} }

View File

@ -95,7 +95,7 @@ class Account extends Model implements IDs
* *
* @param $query * @param $query
* @param string $term * @param string $term
* @return * @return mixed
*/ */
public function scopeSearch($query,string $term) public function scopeSearch($query,string $term)
{ {

View File

@ -106,7 +106,7 @@ class Invoice extends Model implements IDs
*/ */
public function getDueAttribute(): float public function getDueAttribute(): float
{ {
return sprintf('%4.'.$this->currency()->rounding.'f',$this->total-$this->paid); return sprintf('%3.2f',$this->getTotalAttribute()-$this->getPaidAttribute());
} }
/** /**
@ -162,28 +162,27 @@ class Invoice extends Model implements IDs
*/ */
public function getPaidAttribute(): float public function getPaidAttribute(): float
{ {
if (! $this->_paid) return $this->paymentitems
$this->_paid = $this->currency()->round(
$this->paymentitems
->filter(function($item) { return ! $item->payment->pending_status; }) ->filter(function($item) { return ! $item->payment->pending_status; })
->sum('alloc_amt')); ->sum('alloc_amt');
return $this->_paid;
} }
/** /**
* Get the date that the invoice was paid in full. * Get the date that the invoice was paid in full.
* We assume the last payment received pays it in full. * We assume the last payment received pays it in full, if its fully paid.
* *
* @return Carbon|null * @return Carbon|null
*/ */
public function getPaidDateAttribute(): ?Carbon public function getPaidDateAttribute(): ?Carbon
{ {
if ($this->getDueAttribute())
return NULL;
$o = $this->payments $o = $this->payments
->filter(function($item) { return ! $item->pending_status; }) ->filter(function($item) { return ! $item->pending_status; })
->last(); ->last();
return $o ? $o->date_payment : NULL; return $o ? $o->payment_date : NULL;
} }
/** /**
@ -193,10 +192,9 @@ class Invoice extends Model implements IDs
*/ */
public function getPaidPendingAttribute(): float public function getPaidPendingAttribute(): float
{ {
return $this->currency()->round( return $this->paymentitems
$this->paymentitems
->filter(function($item) { return $item->payment->pending_status; }) ->filter(function($item) { return $item->payment->pending_status; })
->sum('alloc_amt')); ->sum('alloc_amt');
} }
/** /**
@ -238,7 +236,7 @@ class Invoice extends Model implements IDs
*/ */
public function getTotalSubAttribute(): float public function getTotalSubAttribute(): float
{ {
return sprintf('%3.'.$this->currency()->rounding.'f',$this->total-$this->tax_total); return $this->items->where('active',TRUE)->sum('sub_total');
} }
/** /**
@ -259,29 +257,17 @@ class Invoice extends Model implements IDs
*/ */
public function getTotalTaxAttribute(): float public function getTotalTaxAttribute(): float
{ {
if (! $this->_total_tax) return $this->items->where('active',TRUE)->sum('tax');
foreach ($this->items as $o) {
if ($o->active)
$this->_total_tax += $this->currency()->round($o->tax);
}
return sprintf('%3.'.$this->currency()->rounding.'f',$this->_total_tax);
} }
/** /**
* Invoice total due * Invoice total due
* *
* @return string * @return float
*/ */
public function getTotalAttribute(): float public function getTotalAttribute(): float
{ {
if (! $this->_total) return $this->getTotalSubAttribute()+$this->getTotalTaxAttribute();
foreach ($this->items as $o) {
if ($o->active)
$this->_total += $this->currency()->round($o->total);
}
return sprintf('%3.'.$this->currency()->rounding.'f',$this->_total);
} }
public function currency() public function currency()

View File

@ -10,6 +10,17 @@ use App\Traits\NextKey;
use App\Traits\PushNew; use App\Traits\PushNew;
use Leenooks\Carbon; use Leenooks\Carbon;
/**
* Class Invoice Items
* Items that belong on an invoice
*
* Attributes for services:
* + date_start : Start date
* + date_stop : End date
* + sub_total : Value of item
* + tax : Total of all taxes
* + total : Total including taxes
*/
class InvoiceItem extends Model class InvoiceItem extends Model
{ {
use NextKey,PushNew; use NextKey,PushNew;
@ -26,7 +37,7 @@ class InvoiceItem extends Model
// Array of items that can be updated with PushNew // Array of items that can be updated with PushNew
protected $pushable = ['taxes']; protected $pushable = ['taxes'];
private $_tax = 0; /* RELATIONS */
public function invoice() public function invoice()
{ {
@ -53,7 +64,7 @@ class InvoiceItem extends Model
return $this->hasMany(InvoiceItemTax::class); return $this->hasMany(InvoiceItemTax::class);
} }
/** ATTRIBUTES **/ /* ATTRIBUTES */
/** /**
* Start date for the invoice item line * Start date for the invoice item line
@ -122,30 +133,37 @@ class InvoiceItem extends Model
} }
} }
public function getSubTotalAttribute() /**
* Sub total of item
*
* @return float
*/
public function getSubTotalAttribute(): float
{ {
return $this->quantity * $this->price_base; return sprintf('%3.2f',$this->quantity * $this->price_base);
} }
public function getTaxAttribute() /**
* Total of all taxes
*
* @return mixed
*/
public function getTaxAttribute(): float
{ {
if (! $this->_tax) return sprintf('%3.2f',$this->taxes->sum('amount'));
{
foreach ($this->taxes as $o)
{
$this->_tax += $o->amount;
}
} }
return $this->_tax; /**
} * Total including taxes
*
public function getTotalAttribute() * @return float
*/
public function getTotalAttribute(): float
{ {
return $this->tax + $this->sub_total; return sprintf('%3.2f',$this->getSubTotalAttribute()+$this->getTaxAttribute());
} }
/** FUNCTIONS **/ /* METHODS */
/** /**
* Add taxes to this record * Add taxes to this record

View File

@ -5,6 +5,8 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use App\Interfaces\IDs; use App\Interfaces\IDs;
use Illuminate\Support\Facades\DB;
use Leenooks\Traits\ScopeActive;
use App\Traits\{NextKey,PushNew}; use App\Traits\{NextKey,PushNew};
/** /**
@ -16,27 +18,26 @@ use App\Traits\{NextKey,PushNew};
* + payment_date : Date payment received * + payment_date : Date payment received
* + sid : System ID for payment * + sid : System ID for payment
* + total : Payment total * + total : Payment total
* + balance : Remaining credit on payment
* *
* @package App\Models * @package App\Models
*/ */
class Payment extends Model implements IDs class Payment extends Model implements IDs
{ {
use NextKey,PushNew; use PushNew;
const RECORD_ID = 'payment';
public $incrementing = FALSE;
const CREATED_AT = 'date_orig'; const CREATED_AT = 'date_orig';
const UPDATED_AT = 'date_last'; const UPDATED_AT = 'date_last';
protected $table = 'ab_payment'; protected $dates = ['payment_date'];
protected $dates = ['date_payment'];
protected $dateFormat = 'U'; protected $dateFormat = 'U';
//protected $with = ['account.country.currency','items'];
// Array of items that can be updated with PushNew // Array of items that can be updated with PushNew
protected $pushable = ['items']; protected $pushable = ['items'];
// Any balance below this we'll assume its all used.
private const threshold = 0.05;
/* RELATIONS */ /* RELATIONS */
public function account() public function account()
@ -44,20 +45,49 @@ class Payment extends Model implements IDs
return $this->belongsTo(Account::class); return $this->belongsTo(Account::class);
} }
public function checkout()
{
return $this->belongsTo(Checkout::class);
}
public function items() public function items()
{ {
return $this->hasMany(PaymentItem::class); return $this->hasMany(PaymentItem::class);
} }
/* ATTRIBUTES */ /* SCOPES */
/** /**
* Search for a record
*
* @param $query
* @param string $term
* @return mixed * @return mixed
* @deprecated use date_payment directly.
*/ */
public function getDatePaidAttribute() public function scopeSearch($query,string $term)
{ {
return $this->date_payment->format('Y-m-d'); // Build our where clause
$query->where('id','like','%'.$term.'%');
return $query;
}
public function scopeUnapplied($query)
{
return $query
->select([DB::raw('payment_id AS id'),'payment_date','account_id','checkout_id','total_amt',DB::raw("SUM(alloc_amt) as allocated")])
->join('payment_items',['payment_items.payment_id'=>'payments.id'])
->groupBy(['payment_id','payment_date','total_amt','account_id','checkout_id'])
->having(DB::raw("ROUND(total_amt-allocated,2)"),'>',self::threshold);
}
/* ATTRIBUTES */
public function getBalanceAttribute(): float
{
$balance = $this->getTotalAttribute()-$this->items->sum('alloc_amt');
return ($balance < self::threshold) ? 0 : $balance;
} }
/** /**
@ -80,13 +110,8 @@ class Payment extends Model implements IDs
return sprintf('%02s-%04s#%s',$this->site_id,$this->account_id,$this->getLIDattribute()); return sprintf('%02s-%04s#%s',$this->site_id,$this->account_id,$this->getLIDattribute());
} }
public function getTotalAttribute() public function getTotalAttribute(): float
{ {
return sprintf('%3.'.$this->currency()->rounding.'f',$this->total_amt); return sprintf('%3.2f',$this->total_amt);
}
public function currency()
{
return $this->account->country->currency;
} }
} }

View File

@ -8,19 +8,29 @@ use App\Traits\{NextKey,PushNew};
class PaymentItem extends Model class PaymentItem extends Model
{ {
use NextKey,PushNew; use PushNew;
const RECORD_ID = 'payment_item'; const RECORD_ID = 'payment_item';
public $incrementing = FALSE;
protected $dateFormat = 'U'; protected $dateFormat = 'U';
const CREATED_AT = 'date_orig'; const CREATED_AT = 'date_orig';
const UPDATED_AT = 'date_last'; const UPDATED_AT = 'date_last';
protected $table = 'ab_payment_item';
/* RELATIONS */ /* RELATIONS */
public function payment() { public function payment() {
return $this->belongsTo(Payment::class); return $this->belongsTo(Payment::class);
} }
/* ATTRIBUTES */
/**
* If our amount is negative, and invoice_id is null, then this is a reversal.
*
* @param $value
* @return float
*/
public function getAllocAmtAttribute($value): float
{
return (is_null($this->invoice_id) && $value < 0) ? -$value : $value;
}
} }

View File

@ -241,7 +241,7 @@ class User extends Authenticatable
public function getPaymentHistoryAttribute() public function getPaymentHistoryAttribute()
{ {
return $this->payments return $this->payments
->sortBy('date_payment') ->sortBy('payment_date')
->reverse(); ->reverse();
} }
@ -367,6 +367,8 @@ class User extends Authenticatable
$result->push($o); $result->push($o);
} }
$result->load('user.accounts');
return $result; return $result;
} }
@ -623,7 +625,7 @@ class User extends Authenticatable
->select([ ->select([
DB::raw('ab_payment.id AS id'), DB::raw('ab_payment.id AS id'),
'date_orig', 'date_orig',
'date_payment', 'payment_date',
'total_amt', 'total_amt',
//'fees_amt', //'fees_amt',
DB::raw('total_amt-allocate AS balance'), DB::raw('total_amt-allocate AS balance'),

View File

@ -0,0 +1,56 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ReworkPayments extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('ab_payment_item', function (Blueprint $table) {
$table->dropUnique('UNIQUE');
$table->dropForeign('fk_pi_pay');
$table->dropPrimary(['id','payment_id','site_id']);
$table->dropIndex('fk_pi_pay_idx');
});
DB::statement('ALTER TABLE ab_payment_item RENAME TO payment_items');
Schema::table('ab_payment', function (Blueprint $table) {
$table->dropPrimary(['id','checkout_id','account_id','site_id']);
$table->dropUnique('UNIQUE');
});
DB::statement('ALTER TABLE ab_payment RENAME TO payments');
DB::statement('ALTER TABLE payment_items ADD PRIMARY KEY (id,site_id), MODIFY COLUMN id bigint auto_increment');
DB::statement('ALTER TABLE payments ADD PRIMARY KEY (id,site_id), MODIFY COLUMN id bigint auto_increment');
DB::statement('ALTER TABLE payments RENAME COLUMN date_payment TO payment_date');
Schema::table('payment_items', function (Blueprint $table) {
$table->unique(['site_id','payment_id','invoice_id']);
$table->foreign(['payment_id','site_id'])->references(['id','site_id'])->on('payments');
});
Schema::table('payments', function (Blueprint $table) {
$table->unique(['site_id','id']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
abort(500,'cant go back');
}
}

View File

@ -32,14 +32,14 @@
<div class="row"> <div class="row">
<div class="col-4"> <div class="col-4">
<div class="form-group has-validation"> <div class="form-group has-validation">
<label for="date_payment">Date Received</label> <label for="payment_date">Date Received</label>
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-calendar"></i></span> <span class="input-group-text"><i class="fas fa-fw fa-calendar"></i></span>
</div> </div>
<input type="date" class="form-control @error('date_payment') is-invalid @enderror" id="date_payment" name="date_payment" value="{{ old('date_payment',$o->exists ? $o->date_payment : \Carbon\Carbon::now()->format('Y-m-d')) }}" required> <input type="date" class="form-control @error('payment_date') is-invalid @enderror" id="payment_date" name="payment_date" value="{{ old('payment_date',($o->exists ? $o->payment_date : \Carbon\Carbon::now())->format('Y-m-d')) }}" required>
<span class="invalid-feedback" role="alert"> <span class="invalid-feedback" role="alert">
@error('date_payment') @error('payment_date')
{{ $message }} {{ $message }}
@else @else
Payment Date is required. Payment Date is required.
@ -50,14 +50,14 @@
</div> </div>
</div> </div>
<div class="offset-1 col-4"> <div class="offset-4 col-4">
<div class="form-group has-validation"> <div class="form-group has-validation">
<label for="total_amt">Amount</label> <label for="total_amt">Amount</label>
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-dollar-sign"></i></span> <span class="input-group-text"><i class="fas fa-fw fa-dollar-sign"></i></span>
</div> </div>
<input type="text" class="form-control @error('total_amt') is-invalid @enderror" id="total_amt" name="total_amt" value="{{ old('total_amt',$o->exists ? $o->total_amt : 0) }}" required> <input type="text" class="text-right form-control @error('total_amt') is-invalid @enderror" id="total_amt" name="total_amt" value="{{ old('total_amt',$o->exists ? $o->total_amt : 0) }}" required>
<span class="invalid-feedback" role="alert"> <span class="invalid-feedback" role="alert">
@error('total_amt') @error('total_amt')
{{ $message }} {{ $message }}
@ -97,19 +97,19 @@
</div> </div>
</div> </div>
<div class="offset-1 col-4"> <div class="offset-4 col-4">
<div class="form-group has-validation"> <div class="form-group has-validation">
<label for="fees_amt">Fee</label> <label for="fees_amt">Fee</label>
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-fw fa-dollar-sign"></i></span> <span class="input-group-text"><i class="fas fa-fw fa-dollar-sign"></i></span>
</div> </div>
<input type="text" class="form-control @error('fees_amt') is-invalid @enderror" id="fees_amt" name="fees_amt" value="{{ old('fees_amt',$o->exists ? $o->fees_amt : 0) }}" required> <input type="text" class="text-right form-control @error('fees_amt') is-invalid @enderror" id="fees_amt" name="fees_amt" value="{{ old('fees_amt',$o->exists ? $o->fees_amt : 0) }}" required>
<span class="invalid-feedback" role="alert"> <span class="invalid-feedback" role="alert">
@error('fees_amt') @error('fees_amt')
{{ $message }} {{ $message }}
@else @else
Payment Amount is required. Total Fees is required.
@enderror @enderror
</span> </span>
</div> </div>
@ -146,10 +146,20 @@
<i class="fas fa-spinner d-none"></i> <i class="fas fa-spinner d-none"></i>
</div> </div>
</div> </div>
<div class="offset-2 col-4">
<label for="fees_amt">Balance</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('fees_amt') is-invalid @enderror" value="{{ $o->exists ? $o->balance : 0 }}" disabled>
</div>
</div>
</div> </div>
<div class="row mb-3"> <div class="row mt-3 mb-3">
<div class="col-6"> <div class="col-12">
<div id="invoices"></div> <div id="invoices"></div>
@error('invoices') @error('invoices')
<span class="invalid-feedback d-block mt-2 mb-2" role="alert"> <span class="invalid-feedback d-block mt-2 mb-2" role="alert">
@ -193,6 +203,7 @@
dataType: 'html', dataType: 'html',
cache: false, cache: false,
url: '{{ url('api/r/invoices') }}'+'/'+account, url: '{{ url('api/r/invoices') }}'+'/'+account,
data: {pid:{{ $o->id }}},
timeout: 2000, timeout: 2000,
error: function(x) { error: function(x) {
spinner.toggleClass('d-none').toggleClass('fa-spin'); spinner.toggleClass('d-none').toggleClass('fa-spin');

View File

@ -0,0 +1,72 @@
@extends('adminlte::layouts.app')
@section('htmlheader_title')
Unapplied Payments
@endsection
@section('page_title')
Unapplied
@endsection
@section('contentheader_title')
Unapplied Payments
@endsection
@section('contentheader_description')
@endsection
@section('main-content')
<div class="row">
<div class="col-6">
<div class="card">
<div class="card-body">
<table class="table table-striped table-hover" id="unapplied_payments">
<thead>
<tr>
<th>ID</th>
<th>Date Paid</th>
<th>Account</th>
<th>Method</th>
<th class="text-right">Total</th>
<th class="text-right">Balance</th>
<th>Invoices</th>
</tr>
</thead>
<tbody>
@foreach(\App\Models\Payment::unapplied()->with(['account.user','checkout','items'])->get() as $o)
@if (! $o->balance) @continue @endif
<tr>
<td><a href="{{ url('a/payment/addedit',$o->id) }}">{{ $o->id }}</td>
<td>{{ $o->payment_date->format('Y-m-d') }}</td>
<td>{{ $o->account->name }}</td>
<td>{{ $o->checkout->name }}</td>
<td class="text-right">{{ number_format($o->total_amt,2) }}</td>
<td class="text-right">{{ number_format($o->balance,2) }}</td>
<td>{!! $o->items->pluck('invoice_id')->map(function($item) { return sprintf('<a href="%s">%d</a>',url('u/invoice',$item),$item); })->join(', ') !!}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
@section('page-scripts')
@css('//cdn.datatables.net/1.10.25/css/dataTables.bootstrap4.min.css','jq-dt-css')
@js('//cdn.datatables.net/1.10.25/js/jquery.dataTables.min.js','jq-dt-js')
@js('//cdn.datatables.net/1.10.25/js/dataTables.bootstrap4.min.js','jq-dt-bs5-js','jq-dt-js')
<script type="text/javascript">
$(document).ready(function() {
$('#unapplied_payments').DataTable( {
order: [1,'desc'],
orderFixed: [1,'desc']
});
$('#unapplied_payments tbody').on('click','tr', function () {
$(this).toggleClass('selected');
});
});
</script>
@append

View File

@ -1,6 +1,11 @@
<div class="form-group mb-0"> <div class="form-group mb-0">
<label for="checkout_id">Invoices Due</label> <label for="checkout_id">Invoices Due</label>
@if(($x=$o->invoices()->where('active',TRUE)->orderBy('due_date')->with(['items.taxes','paymentitems.payment','account'])->get()->where('due','>',0))->count()) @if(($x=$o->invoices()
->where('active',TRUE)
->orderBy('due_date')
->with(['items.taxes','paymentitems.payment','account'])
->get()
->filter(function($item) use ($pid) { return $item->due > 0 || $item->payments->search(function($item) use ($pid) { return $item->id == $pid; }) !== FALSE; }))->count())
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
@ -9,7 +14,7 @@
<th>Date Due</th> <th>Date Due</th>
<th>Total</th> <th>Total</th>
<th>Due</th> <th>Due</th>
<th>Apply</th> <th class="text-right">Apply</th>
</tr> </tr>
</thead> </thead>
@ -21,7 +26,9 @@
<td>{{ $io->due_date->format('Y-m-d') }}</td> <td>{{ $io->due_date->format('Y-m-d') }}</td>
<td>{{ number_format($io->total,2) }}</td> <td>{{ number_format($io->total,2) }}</td>
<td>{{ number_format($io->due,2) }}</td> <td>{{ number_format($io->due,2) }}</td>
<td><input type="text" name="invoices[{{ $io->id }}]"></td> <td class="text-right">
<input type="text" class="text-right" name="invoices[{{ $io->id }}]" value="{{ ($x=$io->paymentitems->filter(function($item) use ($pid) { return $item->payment_id == $pid; })) ? $x->sum('alloc_amt') : 0}}">
</td>
</tr> </tr>
@endforeach @endforeach
</tbody> </tbody>

View File

@ -156,38 +156,39 @@
<div class="ml-auto col-4"> <div class="ml-auto col-4">
<table class="table"> <table class="table">
<tr> <tr>
<th colspan="2" style="width:50%">Subtotal:</th> <th colspan="3" style="width:50%">Subtotal:</th>
<td class="text-right">${{ number_format($o->total_sub,$o->currency()->rounding) }}</td> <td colspan="2" class="text-right">${{ number_format($o->total_sub,$o->currency()->rounding) }}</td>
</tr> </tr>
<tr> <tr>
<th>&nbsp;</th> <th>&nbsp;</th>
<th>Tax (GST 10%)</th> <th>Tax (GST 10%)</th>
<td class="text-right">${{ number_format($o->total_tax,$o->currency()->rounding) }}</td> <td colspan="2" class="text-right">${{ number_format($o->total_tax,$o->currency()->rounding) }}</td>
</tr> </tr>
<tr> <tr>
<th>&nbsp;</th> <th>&nbsp;</th>
<th>Other Charges:</th> <th>Other Charges:</th>
<!-- @todo --> <!-- @todo -->
<td class="text-right">$0.00</td> <td colspan="2" class="text-right">$0.00</td>
</tr> </tr>
<tr> <tr>
<th colspan="2">Total:</th> <th colspan="2">Total:</th>
<td class="text-right">${{ number_format($o->total,$o->currency()->rounding) }}</td> <td colspan="2" class="text-right">${{ number_format($o->total,$o->currency()->rounding) }}</td>
</tr> </tr>
@if($o->id) @if($o->id)
<tr> <tr>
<th>&nbsp;</th> <th>&nbsp;</th>
<th>Payments To Clear:</th> <th>Payments To Clear:</th>
<td class="text-right">${{ number_format($o->paid_pending,$o->currency()->rounding) }}</td> <td colspan="2" class="text-right">${{ number_format($o->paid_pending,$o->currency()->rounding) }}</td>
</tr> </tr>
<tr> <tr>
<th>&nbsp;</th> <th>&nbsp;</th>
<th>Payments:</th> <th>Payments:</th>
<td>#{{ $o->payments->pluck('id')->join(', #') }}</td>
<td class="text-right">${{ number_format($o->paid,$o->currency()->rounding) }}</td> <td class="text-right">${{ number_format($o->paid,$o->currency()->rounding) }}</td>
</tr> </tr>
<tr style="font-size: 145%"> <tr style="font-size: 145%">
<th colspan="2">Invoice Due:</th> <th colspan="2">Invoice Due:</th>
<td class="text-right">${{ number_format($o->due,$o->currency()->rounding) }}</td> <td colspan="2" class="text-right">${{ number_format($o->due,$o->currency()->rounding) }}</td>
</tr> </tr>
@endif @endif
</table> </table>

View File

@ -23,7 +23,7 @@
<tr> <tr>
<td>{{ $oo->account->name }}</td> <td>{{ $oo->account->name }}</td>
<td>{{ $oo->sid }}</td> <td>{{ $oo->sid }}</td>
<td>{{ $oo->date_payment->format('Y-m-d') }}</td> <td>{{ $oo->payment_date->format('Y-m-d') }}</td>
<td class="text-right">${{ number_format($oo->total,2) }}</td> <td class="text-right">${{ number_format($oo->total,2) }}</td>
{{--<td class="text-right">${{ number_format($oo->balance,2) }}</td>--}} {{--<td class="text-right">${{ number_format($oo->balance,2) }}</td>--}}
<td> <td>

View File

@ -32,10 +32,16 @@
<ul class="nav nav-treeview"> <ul class="nav nav-treeview">
<li class="nav-item"> <li class="nav-item">
<a href="{{ url('a/payment/add') }}" class="nav-link @if(Route::current()->uri() == 'payment/add') active @endif"> <a href="{{ url('a/payment/addedit') }}" class="nav-link @if(Route::current()->uri() == 'payment/addedit') active @endif">
<i class="fas fa-money-bill nav-icon"></i> <p>New Payment</p> <i class="fas fa-money-bill nav-icon"></i> <p>New Payment</p>
</a> </a>
</li> </li>
<li class="nav-item">
<a href="{{ url('a/payment/unapplied') }}" class="nav-link @if(Route::current()->uri() == 'payment/unapplied') active @endif">
<i class="fas fa-receipt nav-icon"></i> <p>Unapplied</p>
</a>
</li>
</ul> </ul>
</li> </li>
@endcan @endcan

View File

@ -43,7 +43,10 @@ Route::group(['middleware'=>['theme:adminlte-be','auth','role:wholesaler'],'pref
// Route::get('service/{o}','AdminHomeController@service'); // Route::get('service/{o}','AdminHomeController@service');
// Route::post('service/{o}','AdminHomeController@service_update'); // Route::post('service/{o}','AdminHomeController@service_update');
// Route::get('report/products','Wholesale\ReportController@products'); // Route::get('report/products','Wholesale\ReportController@products');
Route::match(['get','post'],'payment/add',[AdminController::class,'pay_add']);
Route::match(['get','post'],'payment/addedit/{o?}',[AdminController::class,'pay_addedit']);
Route::get('payment/unapplied',[AdminController::class,'pay_unapplied']);
Route::post('service/edit/{o}',[ServiceController::class,'domain_edit']) Route::post('service/edit/{o}',[ServiceController::class,'domain_edit'])
->where('o','[0-9]+') ->where('o','[0-9]+')
->middleware('can:update,o'); ->middleware('can:update,o');