diff --git a/app/Classes/External/Payments/Ezypay.php b/app/Classes/External/Payments/Ezypay.php index e567bfe..35d5be7 100644 --- a/app/Classes/External/Payments/Ezypay.php +++ b/app/Classes/External/Payments/Ezypay.php @@ -45,6 +45,56 @@ class Ezypay extends Payments }); } + /** + * Get a list of configured customers + * + * @return Collection + * @todo Hard coded at 100 clients - need to make this more dynamic + * + * {#1079 + * +"TermsAndConditions": true + * +"Account": {#1082 + * +"PaymentMethodId": 0 + * } + * +"Address1": "1 Road Street " + * +"BillingStatus": "Active" + * +"BusinessAccountReference": "12345" + * +"CountryCode": "AU" + * +"Email": "user@example.com" + * +"EzypayReferenceNumber": 12345678 + * +"Firstname": "Bart" + * +"Id": "6219c42b-8e56-4e4e-af3a-76ddf4b0e4e1" + * +"MobilePhone": "" + * +"Postcode": "1234" + * +"ReferenceId": "01nnnn" + * +"State": "VIC" + * +"Suburb": "TOWN" + * +"Towncity": "" + * +"Surname": "Simpson " + * +"PaymentPlanId": "00000000-0000-0000-0000-000000000000" + * +"DateOfBirth": "1970-01-01T00:00:00.000" + * +"Gender": "M" + * +"DebitType": 0 + * +"RecurringAmount": 0.0 + * +"Frequency": 0 + * +"FrequencyType": 0 + * +"StartDate": "0001-01-01T00:00:00.000" + * +"RecurringDebitEndType": 0 + * +"TotalAmountCollected": 0.0 + * +"MinimumNumberOfPayment": 0 + * +"RecurringWithDifferentFirstDebitAmount": 0.0 + * +"RecurringDebitFirstDebitDate": "0001-01-01T00:00:00.000" + * +"RecurringDebitAmount": 0.0 + * +"RecurringDebitFrequency": 0 + * +"RecurringDebitFrequencyType": 0 + * +"RecurringDebitStartDate": "0001-01-01T00:00:00.000" + * +"RecurringDebitDifferentFirstAmountEndType": 0 + * +"RecurringDebitTotalAmountCollected": 0.0 + * +"RecurringDebitMinimumNumberOfPayment": 0 + * +"OnceOffAmount": 0.0 + * +"OnceOffStartDate": "0001-01-01T00:00:00.000" + * } + */ public function getCustomers(): Collection { return Cache::remember(__METHOD__,86400,function() { @@ -52,6 +102,29 @@ class Ezypay extends Payments }); } + /** + * Get Specific debits for a client. + * + * @param array $opt + * @return Collection + * + * Illuminate\Support\Collection^ {#1077 + * #items: array:4 [ + * 0 => {#1826 + * +"Amount": 99.99 + * +"Id": "76666ef1-106c-458c-9162-e77fe746517c" + * +"CustomerId": "6219c42b-8e56-4e4e-af3a-76ddf4b0e4e1" + * +"Date": "2021-10-01T00:00:00.000" + * +"Status": "Pending" + * } + * 1 => {#2075 + * +"Amount": 99.99 + * +"Id": "80ba201d-fb6f-4700-b1b7-2b13fbfa91d5" + * +"CustomerId": "6219c42b-8e56-4e4e-af3a-76ddf4b0e4e1" + * +"Date": "2021-09-01T00:00:00.000" + * +"Status": "Pending" + * } + */ public function getDebits($opt=[]): Collection { return Cache::remember(__METHOD__.http_build_query($opt),86400,function() use ($opt) { @@ -65,4 +138,4 @@ class Ezypay extends Payments return Collect($this->connect('settlements/'.config('services.ezypay.guid').($opt ? '?'.http_build_query($opt) : ''))); }); } -} \ No newline at end of file +} diff --git a/app/Console/Commands/PaymentsEzypayImport.php b/app/Console/Commands/PaymentsEzypayImport.php index 2e215db..bfaae7d 100644 --- a/app/Console/Commands/PaymentsEzypayImport.php +++ b/app/Console/Commands/PaymentsEzypayImport.php @@ -2,11 +2,10 @@ namespace App\Console\Commands; -use Carbon\Carbon; use Illuminate\Console\Command; use App\Classes\External\Payments\Ezypay; -use App\Models\{Account,Checkout,Payment}; +use App\Jobs\PaymentsImport as Job; class PaymentsEzypayImport extends Command { @@ -24,16 +23,6 @@ class PaymentsEzypayImport extends Command */ protected $description = 'Retrieve payments from Ezypay'; - /** - * Create a new command instance. - * - * @return void - */ - public function __construct() - { - parent::__construct(); - } - /** * Execute the console command. * @@ -41,67 +30,6 @@ class PaymentsEzypayImport extends Command */ public function handle() { - $poo = new Ezypay(); - - // Get our checkout IDs for this plugin - $cos = Checkout::where('plugin',config('services.ezypay.plugin'))->pluck('id'); - - foreach ($poo->getCustomers() as $c) - { - if ($c->BillingStatus == 'Inactive') - { - $this->info(sprintf('Ignoring INACTIVE: [%s] %s %s',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname)); - continue; - } - - // Load Account Details from ReferenceId - $ao = Account::where('site_id',(int)substr($c->ReferenceId,0,2)) - ->where('id',(int)substr($c->ReferenceId,2,4)) - ->first(); - - if (! $ao) - { - $this->warn(sprintf('Missing: [%s] %s %s (%s)',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname,$c->ReferenceId)); - continue; - } - - // Find the last payment logged - $last = Carbon::createFromTimestamp(Payment::whereIN('checkout_id',$cos)->where('account_id',$ao->id)->max('payment_date')); - - $o = $poo->getDebits([ - 'customerId'=>$c->Id, - 'dateFrom'=>$last->format('Y-m-d'), - 'dateTo'=>$last->addQuarter()->format('Y-m-d'), - 'pageSize'=>100, - ]); - - // Load the payments - if ($o->count()) - { - foreach ($o->reverse() as $p) - { - // If not success, ignore it. - if ($p->Status != 'Success') - continue; - - $pd = Carbon::createFromFormat('Y-m-d?H:i:s.u',$p->Date); - $lp = $ao->payments->last(); - - 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->payment_date = $pd; - $po->checkout_id = '999'; // @todo - $po->checkout_data = $p->Id; - $po->total_amt = $p->Amount; - $ao->payments()->save($po); - - $this->info(sprintf('Recorded: Payment for [%s] %s %s (%s) on %s',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname,$po->id,$pd)); - } - } - } + Job::dispatchSync(new Ezypay); } } \ No newline at end of file diff --git a/app/Console/Commands/PaymentsEzypayNext.php b/app/Console/Commands/PaymentsEzypayNext.php index 6ec456f..1476d50 100644 --- a/app/Console/Commands/PaymentsEzypayNext.php +++ b/app/Console/Commands/PaymentsEzypayNext.php @@ -2,6 +2,7 @@ namespace App\Console\Commands; +use Carbon\Carbon; use Illuminate\Console\Command; use App\Classes\External\Payments\Ezypay; @@ -23,16 +24,6 @@ class PaymentsEzypayNext extends Command */ protected $description = 'Load next payments, and ensure they cover the next invoice'; - /** - * Create a new command instance. - * - * @return void - */ - public function __construct() - { - parent::__construct(); - } - /** * Execute the console command. * @@ -40,12 +31,10 @@ class PaymentsEzypayNext extends Command */ public function handle() { - $poo = new Ezypay(); + $poo = new Ezypay; - foreach ($poo->getCustomers() as $c) - { - if ($c->BillingStatus == 'Inactive') - { + foreach ($poo->getCustomers() as $c) { + if ($c->BillingStatus == 'Inactive') { $this->info(sprintf('Ignoring INACTIVE: [%s] %s %s',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname)); continue; } @@ -53,10 +42,9 @@ class PaymentsEzypayNext extends Command // Load Account Details from ReferenceId $ao = Account::where('site_id',(int)substr($c->ReferenceId,0,2)) ->where('id',(int)substr($c->ReferenceId,2,4)) - ->first(); + ->single(); - if (! $ao) - { + if (! $ao) { $this->warn(sprintf('Missing: [%s] %s %s (%s)',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname,$c->ReferenceId)); continue; } @@ -70,12 +58,33 @@ class PaymentsEzypayNext extends Command 'dateTo'=>now()->addQuarter()->format('Y-m-d'), ])->reverse()->first(); + if ($next_pay->Status !== 'Pending') { + $this->warn(sprintf('Next payment is not pending for (%s)',$ao->name)); + continue; + } + + $next_paydate = Carbon::createFromTimeString($next_pay->Date); + if ($next_pay->Amount < $account_due) - $this->warn(sprintf('Next payment for (%s) [%s] not sufficient for outstanding balance [%s]',$ao->name,number_format($next_pay->Amount,2),number_format($account_due,2))); + $this->warn(sprintf('Next payment on [%s] for (%s) [%s] not sufficient for outstanding balance [%s]', + $next_paydate->format('Y-m-d'), + $ao->name, + number_format($next_pay->Amount,2), + number_format($account_due,2))); + elseif ($next_pay->Amount > $account_due) - $this->warn(sprintf('Next payment for (%s) [%s] is too much for outstanding balance [%s]',$ao->name,number_format($next_pay->Amount,2),number_format($account_due,2))); + $this->warn(sprintf('Next payment on [%s] for (%s) [%s] is too much for outstanding balance [%s]', + $next_paydate->format('Y-m-d'), + $ao->name, + number_format($next_pay->Amount,2), + number_format($account_due,2))); + else - $this->info(sprintf('Next payment for (%s) [%s] will cover outstanding balance [%s]',$ao->name,number_format($next_pay->Amount,2),number_format($account_due,2))); + $this->info(sprintf('Next payment on [%s] for (%s) [%s] will cover outstanding balance [%s]', + $next_paydate->format('Y-m-d'), + $ao->name, + number_format($next_pay->Amount,2), + number_format($account_due,2))); } } } \ No newline at end of file diff --git a/app/Jobs/BroadbandTraffic.php b/app/Jobs/BroadbandTraffic.php index 18ef625..77fed58 100644 --- a/app/Jobs/BroadbandTraffic.php +++ b/app/Jobs/BroadbandTraffic.php @@ -5,18 +5,18 @@ namespace App\Jobs; use Carbon\Carbon; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Mail; use App\Mail\TrafficMismatch; use App\Models\Service\Adsl; use App\Models\Service\AdslTraffic; use App\Models\AdslSupplier; -use Illuminate\Support\Facades\Mail; /** * Class BroadbandTraffic @@ -24,18 +24,18 @@ use Illuminate\Support\Facades\Mail; * * @package App\Jobs */ -class BroadbandTraffic implements ShouldQueue +final class BroadbandTraffic implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; private const LOGKEY = 'JBT'; - protected $aso = NULL; // The supplier we are updating from - private $class_prefix = 'App\Classes\External\Supplier\\'; + protected Model $o; // The supplier we are updating from + private const class_prefix = 'App\Classes\External\Supplier\\'; public function __construct(AdslSupplier $o) { - $this->aso = $o; + $this->o = $o; } /** @@ -47,14 +47,14 @@ class BroadbandTraffic implements ShouldQueue */ public function handle() { - Log::info(sprintf('%s:Importing Broadband Traffic from [%s]',self::LOGKEY,$this->aso->name),['m'=>__METHOD__]); + Log::info(sprintf('%s:Importing Broadband Traffic from [%s]',self::LOGKEY,$this->o->name)); $u = 0; // Load our class for this supplier - $class = $this->class_prefix.$this->aso->name; + $class = self::class_prefix.$this->o->name; if (class_exists($class)) { - $o = new $class($this->aso); + $o = new $class($this->o); } else { Log::error(sprintf('%s: Class doesnt exist: %d',get_class($this),$class)); @@ -62,12 +62,12 @@ class BroadbandTraffic implements ShouldQueue } // Repeat pull traffic data until yesterday - while ($this->aso->stats_lastupdate < Carbon::now()->subDay()) { - Log::notice(sprintf('%s:Next update is [%s]',self::LOGKEY,$this->aso->stats_lastupdate->format('Y-m-d')),['m'=>__METHOD__]); + while ($this->o->stats_lastupdate < Carbon::now()->subDay()) { + Log::notice(sprintf('%s:Next update is [%s]',self::LOGKEY,$this->o->stats_lastupdate->format('Y-m-d'))); // Delete traffic, since we'll refresh it. - AdslTraffic::where('supplier_id',$this->aso->id) - ->where('date',$this->aso->stats_lastupdate) + AdslTraffic::where('supplier_id',$this->o->id) + ->where('date',$this->o->stats_lastupdate) ->delete(); $c = 0; @@ -100,8 +100,8 @@ class BroadbandTraffic implements ShouldQueue $to = new AdslTraffic; $to->site_id = 1; // @todo TO ADDRESS - $to->date = $this->aso->stats_lastupdate; - $to->supplier_id = $this->aso->id; + $to->date = $this->o->stats_lastupdate; + $to->supplier_id = $this->o->id; $to->up_peak = $row[$o->getColumnKey('Peak upload')]; $to->up_offpeak = $row[$o->getColumnKey('Off peak upload')]; $to->down_peak = $row[$o->getColumnKey('Peak download')]; @@ -112,7 +112,7 @@ class BroadbandTraffic implements ShouldQueue // If we have no records if ($oo->count() != 1) { - Log::error(sprintf('%s:Too many services return for [%s]',self::LOGKEY,$row[$o->getColumnKey('Login')]),['m'=>__METHOD__,'date'=>$date,'count'=>$oo->count()]); + Log::error(sprintf('%s:Too many services return for [%s]',self::LOGKEY,$row[$o->getColumnKey('Login')]),['date'=>$date,'count'=>$oo->count()]); $to->service = $row[$o->getColumnKey('Login')]; $to->save(); @@ -124,20 +124,20 @@ class BroadbandTraffic implements ShouldQueue $u++; } catch (\Exception $e) { - Log::error(sprintf('%s:Exception occurred when storing traffic record for [%s].',self::LOGKEY,$row[$o->getColumnKey('Login')]),['m'=>__METHOD__,'row'=>$row,'line'=>$line]); + Log::error(sprintf('%s:Exception occurred when storing traffic record for [%s].',self::LOGKEY,$row[$o->getColumnKey('Login')]),['row'=>$row,'line'=>$line]); throw new \Exception('Error while storing traffic date'); } } - Log::info(sprintf('%s: Records Imported [%d] for [%s]',self::LOGKEY,$u,$this->aso->stats_lastupdate->format('Y-m-d')),['m'=>__METHOD__]); + Log::info(sprintf('%s: Records Imported [%d] for [%s]',self::LOGKEY,$u,$this->o->stats_lastupdate->format('Y-m-d'))); if ($u) { - $this->aso->stats_lastupdate = $this->aso->stats_lastupdate->addDay(); - $this->aso->save(); + $this->o->stats_lastupdate = $this->o->stats_lastupdate->addDay(); + $this->o->save(); - if ($this->aso->trafficMismatch($date)->count()) + if ($this->o->trafficMismatch($date)->count()) Mail::to('deon@graytech.net.au') // @todo To change - ->send(new TrafficMismatch($this->aso,$date)); + ->send(new TrafficMismatch($this->o,$date)); } } } diff --git a/app/Models/Payment.php b/app/Models/Payment.php index c329c36..b4c92c8 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -74,11 +74,12 @@ class Payment extends Model implements IDs public function scopeUnapplied($query) { + //DB::enableQueryLog(); 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); + ->select(['payments.id','payment_date','account_id','checkout_id','total_amt',DB::raw("SUM(alloc_amt) as allocated")]) + ->leftJoin('payment_items',['payment_items.payment_id'=>'payments.id']) + ->groupBy(['payments.id','payment_date','total_amt','account_id','checkout_id']) + ->having(DB::raw('ROUND(total_amt-IFNULL(allocated,0),2)'),'>',self::threshold); } /* ATTRIBUTES */ diff --git a/resources/views/theme/backend/adminlte/a/payment/addedit.blade.php b/resources/views/theme/backend/adminlte/a/payment/addedit.blade.php index 6ea5c87..73661c4 100644 --- a/resources/views/theme/backend/adminlte/a/payment/addedit.blade.php +++ b/resources/views/theme/backend/adminlte/a/payment/addedit.blade.php @@ -104,7 +104,7 @@