Minor job cleanup, import Ezypay payments update

This commit is contained in:
Deon George 2021-07-29 13:11:14 +10:00
parent 10e6c73b2b
commit 00f215b780
No known key found for this signature in database
GPG Key ID: 7670E8DC27415254
6 changed files with 134 additions and 123 deletions

View File

@ -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 public function getCustomers(): Collection
{ {
return Cache::remember(__METHOD__,86400,function() { 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 public function getDebits($opt=[]): Collection
{ {
return Cache::remember(__METHOD__.http_build_query($opt),86400,function() use ($opt) { 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) : ''))); return Collect($this->connect('settlements/'.config('services.ezypay.guid').($opt ? '?'.http_build_query($opt) : '')));
}); });
} }
} }

View File

@ -2,11 +2,10 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use App\Classes\External\Payments\Ezypay; use App\Classes\External\Payments\Ezypay;
use App\Models\{Account,Checkout,Payment}; use App\Jobs\PaymentsImport as Job;
class PaymentsEzypayImport extends Command class PaymentsEzypayImport extends Command
{ {
@ -24,16 +23,6 @@ class PaymentsEzypayImport extends Command
*/ */
protected $description = 'Retrieve payments from Ezypay'; protected $description = 'Retrieve payments from Ezypay';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/** /**
* Execute the console command. * Execute the console command.
* *
@ -41,67 +30,6 @@ class PaymentsEzypayImport extends Command
*/ */
public function handle() public function handle()
{ {
$poo = new Ezypay(); Job::dispatchSync(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));
}
}
}
} }
} }

View File

@ -2,6 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use App\Classes\External\Payments\Ezypay; 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'; 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. * Execute the console command.
* *
@ -40,12 +31,10 @@ class PaymentsEzypayNext extends Command
*/ */
public function handle() public function handle()
{ {
$poo = new Ezypay(); $poo = new Ezypay;
foreach ($poo->getCustomers() as $c) foreach ($poo->getCustomers() as $c) {
{ if ($c->BillingStatus == 'Inactive') {
if ($c->BillingStatus == 'Inactive')
{
$this->info(sprintf('Ignoring INACTIVE: [%s] %s %s',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname)); $this->info(sprintf('Ignoring INACTIVE: [%s] %s %s',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname));
continue; continue;
} }
@ -53,10 +42,9 @@ class PaymentsEzypayNext extends Command
// Load Account Details from ReferenceId // Load Account Details from ReferenceId
$ao = Account::where('site_id',(int)substr($c->ReferenceId,0,2)) $ao = Account::where('site_id',(int)substr($c->ReferenceId,0,2))
->where('id',(int)substr($c->ReferenceId,2,4)) ->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)); $this->warn(sprintf('Missing: [%s] %s %s (%s)',$c->EzypayReferenceNumber,$c->Firstname,$c->Surname,$c->ReferenceId));
continue; continue;
} }
@ -70,12 +58,33 @@ class PaymentsEzypayNext extends Command
'dateTo'=>now()->addQuarter()->format('Y-m-d'), 'dateTo'=>now()->addQuarter()->format('Y-m-d'),
])->reverse()->first(); ])->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) 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) 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 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)));
} }
} }
} }

View File

@ -5,18 +5,18 @@ namespace App\Jobs;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use App\Mail\TrafficMismatch; use App\Mail\TrafficMismatch;
use App\Models\Service\Adsl; use App\Models\Service\Adsl;
use App\Models\Service\AdslTraffic; use App\Models\Service\AdslTraffic;
use App\Models\AdslSupplier; use App\Models\AdslSupplier;
use Illuminate\Support\Facades\Mail;
/** /**
* Class BroadbandTraffic * Class BroadbandTraffic
@ -24,18 +24,18 @@ use Illuminate\Support\Facades\Mail;
* *
* @package App\Jobs * @package App\Jobs
*/ */
class BroadbandTraffic implements ShouldQueue final class BroadbandTraffic implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private const LOGKEY = 'JBT'; private const LOGKEY = 'JBT';
protected $aso = NULL; // The supplier we are updating from protected Model $o; // The supplier we are updating from
private $class_prefix = 'App\Classes\External\Supplier\\'; private const class_prefix = 'App\Classes\External\Supplier\\';
public function __construct(AdslSupplier $o) public function __construct(AdslSupplier $o)
{ {
$this->aso = $o; $this->o = $o;
} }
/** /**
@ -47,14 +47,14 @@ class BroadbandTraffic implements ShouldQueue
*/ */
public function handle() 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; $u = 0;
// Load our class for this supplier // Load our class for this supplier
$class = $this->class_prefix.$this->aso->name; $class = self::class_prefix.$this->o->name;
if (class_exists($class)) { if (class_exists($class)) {
$o = new $class($this->aso); $o = new $class($this->o);
} else { } else {
Log::error(sprintf('%s: Class doesnt exist: %d',get_class($this),$class)); 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 // Repeat pull traffic data until yesterday
while ($this->aso->stats_lastupdate < Carbon::now()->subDay()) { while ($this->o->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__]); 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. // Delete traffic, since we'll refresh it.
AdslTraffic::where('supplier_id',$this->aso->id) AdslTraffic::where('supplier_id',$this->o->id)
->where('date',$this->aso->stats_lastupdate) ->where('date',$this->o->stats_lastupdate)
->delete(); ->delete();
$c = 0; $c = 0;
@ -100,8 +100,8 @@ class BroadbandTraffic implements ShouldQueue
$to = new AdslTraffic; $to = new AdslTraffic;
$to->site_id = 1; // @todo TO ADDRESS $to->site_id = 1; // @todo TO ADDRESS
$to->date = $this->aso->stats_lastupdate; $to->date = $this->o->stats_lastupdate;
$to->supplier_id = $this->aso->id; $to->supplier_id = $this->o->id;
$to->up_peak = $row[$o->getColumnKey('Peak upload')]; $to->up_peak = $row[$o->getColumnKey('Peak upload')];
$to->up_offpeak = $row[$o->getColumnKey('Off peak upload')]; $to->up_offpeak = $row[$o->getColumnKey('Off peak upload')];
$to->down_peak = $row[$o->getColumnKey('Peak download')]; $to->down_peak = $row[$o->getColumnKey('Peak download')];
@ -112,7 +112,7 @@ class BroadbandTraffic implements ShouldQueue
// If we have no records // If we have no records
if ($oo->count() != 1) { 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->service = $row[$o->getColumnKey('Login')];
$to->save(); $to->save();
@ -124,20 +124,20 @@ class BroadbandTraffic implements ShouldQueue
$u++; $u++;
} catch (\Exception $e) { } 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'); 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) { if ($u) {
$this->aso->stats_lastupdate = $this->aso->stats_lastupdate->addDay(); $this->o->stats_lastupdate = $this->o->stats_lastupdate->addDay();
$this->aso->save(); $this->o->save();
if ($this->aso->trafficMismatch($date)->count()) if ($this->o->trafficMismatch($date)->count())
Mail::to('deon@graytech.net.au') // @todo To change Mail::to('deon@graytech.net.au') // @todo To change
->send(new TrafficMismatch($this->aso,$date)); ->send(new TrafficMismatch($this->o,$date));
} }
} }
} }

View File

@ -74,11 +74,12 @@ class Payment extends Model implements IDs
public function scopeUnapplied($query) public function scopeUnapplied($query)
{ {
//DB::enableQueryLog();
return $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")]) ->select(['payments.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']) ->leftJoin('payment_items',['payment_items.payment_id'=>'payments.id'])
->groupBy(['payment_id','payment_date','total_amt','account_id','checkout_id']) ->groupBy(['payments.id','payment_date','total_amt','account_id','checkout_id'])
->having(DB::raw("ROUND(total_amt-allocated,2)"),'>',self::threshold); ->having(DB::raw('ROUND(total_amt-IFNULL(allocated,0),2)'),'>',self::threshold);
} }
/* ATTRIBUTES */ /* ATTRIBUTES */

View File

@ -104,7 +104,7 @@
<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="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> <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) }}">
<span class="invalid-feedback" role="alert"> <span class="invalid-feedback" role="alert">
@error('fees_amt') @error('fees_amt')
{{ $message }} {{ $message }}