Rework payment tables, enable payment editing
This commit is contained in:
@@ -66,7 +66,7 @@ class PaymentsEzypayImport extends Command
|
||||
}
|
||||
|
||||
// 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([
|
||||
'customerId'=>$c->Id,
|
||||
@@ -87,13 +87,13 @@ class PaymentsEzypayImport extends Command
|
||||
$pd = Carbon::createFromFormat('Y-m-d?H:i:s.u',$p->Date);
|
||||
$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;
|
||||
|
||||
// New Payment
|
||||
$po = new Payment;
|
||||
$po->site_id = 1; // @todo
|
||||
$po->date_payment = $pd;
|
||||
$po->payment_date = $pd;
|
||||
$po->checkout_id = '999'; // @todo
|
||||
$po->checkout_data = $p->Id;
|
||||
$po->total_amt = $p->Amount;
|
||||
|
@@ -21,12 +21,12 @@ class AdminController extends Controller
|
||||
* @param Payment $o
|
||||
* @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()) {
|
||||
$validation = $request->validate([
|
||||
'account_id' => 'required|exists:accounts,id',
|
||||
'date_payment' => 'required|date',
|
||||
'payment_date' => 'required|date',
|
||||
'checkout_id' => 'required|exists:ab_checkout,id',
|
||||
'total_amt' => 'required|numeric|min:0.01',
|
||||
'fees_amt' => 'nullable|numeric|lt:total_amt',
|
||||
@@ -41,36 +41,64 @@ class AdminController extends Controller
|
||||
'invoices.*.id' => 'nullable|exists:ab_invoice,id',
|
||||
]);
|
||||
|
||||
$oo = new Payment;
|
||||
$oo->forceFill($request->only(['account_id','date_payment','checkout_id','checkout_id','total_amt','fees_amt','source_id','pending','notes','ip']));
|
||||
$oo->site_id = config('SITE')->site_id;
|
||||
$oo->save();
|
||||
if (! $o->exists) {
|
||||
$o->forceFill($request->only(['account_id','payment_date','checkout_id','checkout_id','total_amt','fees_amt','source_id','pending','notes','ip']));
|
||||
$o->site_id = config('SITE')->site_id;
|
||||
$o->save();
|
||||
}
|
||||
|
||||
foreach ($validation['invoices'] as $id => $amount) {
|
||||
$ooo = new PaymentItem;
|
||||
$ooo->invoice_id = $id;
|
||||
$ooo->alloc_amt = $amount;
|
||||
$ooo->site_id = config('SITE')->site_id;
|
||||
$oo->items()->save($ooo);
|
||||
|
||||
// See if we already have a payment item that we need to update
|
||||
$items = $o->items->filter(function($item) use ($id) { return $item->invoice_id == $id; });
|
||||
|
||||
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()
|
||||
->with('success','Payment recorded');
|
||||
}
|
||||
|
||||
return view('a.payment.add')
|
||||
return view('a.payment.addedit')
|
||||
->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
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Account $o
|
||||
* @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')
|
||||
->with('pid',$request->pid)
|
||||
->with('o',$o);
|
||||
}
|
||||
|
||||
|
@@ -217,7 +217,7 @@ class PaypalController extends Controller
|
||||
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_data = $cap->id;
|
||||
|
||||
|
@@ -5,8 +5,9 @@ namespace App\Http\Controllers;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
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
|
||||
{
|
||||
@@ -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']);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@@ -95,7 +95,7 @@ class Account extends Model implements IDs
|
||||
*
|
||||
* @param $query
|
||||
* @param string $term
|
||||
* @return
|
||||
* @return mixed
|
||||
*/
|
||||
public function scopeSearch($query,string $term)
|
||||
{
|
||||
|
@@ -106,7 +106,7 @@ class Invoice extends Model implements IDs
|
||||
*/
|
||||
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
|
||||
{
|
||||
if (! $this->_paid)
|
||||
$this->_paid = $this->currency()->round(
|
||||
$this->paymentitems
|
||||
->filter(function($item) { return ! $item->payment->pending_status; })
|
||||
->sum('alloc_amt'));
|
||||
|
||||
return $this->_paid;
|
||||
return $this->paymentitems
|
||||
->filter(function($item) { return ! $item->payment->pending_status; })
|
||||
->sum('alloc_amt');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function getPaidDateAttribute(): ?Carbon
|
||||
{
|
||||
if ($this->getDueAttribute())
|
||||
return NULL;
|
||||
|
||||
$o = $this->payments
|
||||
->filter(function($item) { return ! $item->pending_status; })
|
||||
->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
|
||||
{
|
||||
return $this->currency()->round(
|
||||
$this->paymentitems
|
||||
->filter(function($item) { return $item->payment->pending_status; })
|
||||
->sum('alloc_amt'));
|
||||
return $this->paymentitems
|
||||
->filter(function($item) { return $item->payment->pending_status; })
|
||||
->sum('alloc_amt');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,7 +236,7 @@ class Invoice extends Model implements IDs
|
||||
*/
|
||||
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
|
||||
{
|
||||
if (! $this->_total_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);
|
||||
return $this->items->where('active',TRUE)->sum('tax');
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoice total due
|
||||
*
|
||||
* @return string
|
||||
* @return float
|
||||
*/
|
||||
public function getTotalAttribute(): float
|
||||
{
|
||||
if (! $this->_total)
|
||||
foreach ($this->items as $o) {
|
||||
if ($o->active)
|
||||
$this->_total += $this->currency()->round($o->total);
|
||||
}
|
||||
|
||||
return sprintf('%3.'.$this->currency()->rounding.'f',$this->_total);
|
||||
return $this->getTotalSubAttribute()+$this->getTotalTaxAttribute();
|
||||
}
|
||||
|
||||
public function currency()
|
||||
|
@@ -10,6 +10,17 @@ use App\Traits\NextKey;
|
||||
use App\Traits\PushNew;
|
||||
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
|
||||
{
|
||||
use NextKey,PushNew;
|
||||
@@ -26,7 +37,7 @@ class InvoiceItem extends Model
|
||||
// Array of items that can be updated with PushNew
|
||||
protected $pushable = ['taxes'];
|
||||
|
||||
private $_tax = 0;
|
||||
/* RELATIONS */
|
||||
|
||||
public function invoice()
|
||||
{
|
||||
@@ -53,7 +64,7 @@ class InvoiceItem extends Model
|
||||
return $this->hasMany(InvoiceItemTax::class);
|
||||
}
|
||||
|
||||
/** ATTRIBUTES **/
|
||||
/* ATTRIBUTES */
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
foreach ($this->taxes as $o)
|
||||
{
|
||||
$this->_tax += $o->amount;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_tax;
|
||||
return sprintf('%3.2f',$this->taxes->sum('amount'));
|
||||
}
|
||||
|
||||
public function getTotalAttribute()
|
||||
/**
|
||||
* Total including taxes
|
||||
*
|
||||
* @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
|
||||
|
@@ -5,6 +5,8 @@ namespace App\Models;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
use App\Interfaces\IDs;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Leenooks\Traits\ScopeActive;
|
||||
use App\Traits\{NextKey,PushNew};
|
||||
|
||||
/**
|
||||
@@ -16,27 +18,26 @@ use App\Traits\{NextKey,PushNew};
|
||||
* + payment_date : Date payment received
|
||||
* + sid : System ID for payment
|
||||
* + total : Payment total
|
||||
* + balance : Remaining credit on payment
|
||||
*
|
||||
* @package App\Models
|
||||
*/
|
||||
class Payment extends Model implements IDs
|
||||
{
|
||||
use NextKey,PushNew;
|
||||
|
||||
const RECORD_ID = 'payment';
|
||||
public $incrementing = FALSE;
|
||||
use PushNew;
|
||||
|
||||
const CREATED_AT = 'date_orig';
|
||||
const UPDATED_AT = 'date_last';
|
||||
|
||||
protected $table = 'ab_payment';
|
||||
protected $dates = ['date_payment'];
|
||||
protected $dates = ['payment_date'];
|
||||
protected $dateFormat = 'U';
|
||||
//protected $with = ['account.country.currency','items'];
|
||||
|
||||
// Array of items that can be updated with PushNew
|
||||
protected $pushable = ['items'];
|
||||
|
||||
// Any balance below this we'll assume its all used.
|
||||
private const threshold = 0.05;
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
public function account()
|
||||
@@ -44,20 +45,49 @@ class Payment extends Model implements IDs
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
public function checkout()
|
||||
{
|
||||
return $this->belongsTo(Checkout::class);
|
||||
}
|
||||
|
||||
public function items()
|
||||
{
|
||||
return $this->hasMany(PaymentItem::class);
|
||||
}
|
||||
|
||||
/* ATTRIBUTES */
|
||||
/* SCOPES */
|
||||
|
||||
/**
|
||||
* Search for a record
|
||||
*
|
||||
* @param $query
|
||||
* @param string $term
|
||||
* @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());
|
||||
}
|
||||
|
||||
public function getTotalAttribute()
|
||||
public function getTotalAttribute(): float
|
||||
{
|
||||
return sprintf('%3.'.$this->currency()->rounding.'f',$this->total_amt);
|
||||
}
|
||||
|
||||
public function currency()
|
||||
{
|
||||
return $this->account->country->currency;
|
||||
return sprintf('%3.2f',$this->total_amt);
|
||||
}
|
||||
}
|
@@ -8,19 +8,29 @@ use App\Traits\{NextKey,PushNew};
|
||||
|
||||
class PaymentItem extends Model
|
||||
{
|
||||
use NextKey,PushNew;
|
||||
use PushNew;
|
||||
const RECORD_ID = 'payment_item';
|
||||
public $incrementing = FALSE;
|
||||
|
||||
protected $dateFormat = 'U';
|
||||
const CREATED_AT = 'date_orig';
|
||||
const UPDATED_AT = 'date_last';
|
||||
|
||||
protected $table = 'ab_payment_item';
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
public function payment() {
|
||||
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;
|
||||
}
|
||||
}
|
@@ -241,7 +241,7 @@ class User extends Authenticatable
|
||||
public function getPaymentHistoryAttribute()
|
||||
{
|
||||
return $this->payments
|
||||
->sortBy('date_payment')
|
||||
->sortBy('payment_date')
|
||||
->reverse();
|
||||
}
|
||||
|
||||
@@ -367,6 +367,8 @@ class User extends Authenticatable
|
||||
$result->push($o);
|
||||
}
|
||||
|
||||
$result->load('user.accounts');
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -623,7 +625,7 @@ class User extends Authenticatable
|
||||
->select([
|
||||
DB::raw('ab_payment.id AS id'),
|
||||
'date_orig',
|
||||
'date_payment',
|
||||
'payment_date',
|
||||
'total_amt',
|
||||
//'fees_amt',
|
||||
DB::raw('total_amt-allocate AS balance'),
|
||||
|
Reference in New Issue
Block a user