Added intuit payments

This commit is contained in:
2023-05-13 21:20:56 +10:00
parent a518158ccf
commit 8ad9e73abb
12 changed files with 347 additions and 22 deletions

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Console\Commands;
use GuzzleHttp\Exception\ConnectException;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Config;
use Intuit\Exceptions\ConnectionIssueException;
use App\Models\{ProviderOauth,Site,User};
class AccountingPaymentGet extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'accounting:payment:get'
.' {siteid : Site ID}'
.' {provider : Provider Name}'
.' {user : User Email}'
.' {id : Payment ID}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Get a payment from the accounting provider';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$site = Site::findOrFail($this->argument('siteid'));
Config::set('site',$site);
$uo = User::where('email',$this->argument('user'))->singleOrFail();
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
if (! ($to=$so->token($uo)))
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
try {
$api = $to->API();
dump($api->getPaymentQuery($this->argument('id')));
} catch (ConnectException|ConnectionIssueException $e) {
$this->error($e->getMessage());
return Command::FAILURE;
}
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Config;
use App\Models\{ProviderOauth,Site,User};
use App\Jobs\AccountingPaymentSync as Job;
class AccountingPaymentSync extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'accounting:payment:sync'
.' {siteid : Site ID}'
.' {provider : Provider Name}'
.' {user : User Email}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Synchronise payments with accounting system';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$site = Site::findOrFail($this->argument('siteid'));
Config::set('site',$site);
$uo = User::where('email',$this->argument('user'))->singleOrFail();
$so = ProviderOauth::where('name',$this->argument('provider'))->singleOrFail();
if (! ($to=$so->token($uo)))
abort(500,sprintf('Unknown Tokens for [%s]',$uo->email));
$api = $to->API();
foreach ($api->getPayments() as $acc)
Job::dispatchSync($to,$acc);
}
}

View File

@@ -2,9 +2,7 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use App\Models\ProviderOauth;
use App\Models\User;
@@ -38,9 +36,4 @@ class AccountingController extends Controller
->transform(function($item,$value) { return ['id'=>$value,'value'=>$item]; })
->values();
}
public function webhook(Request $request)
{
Log::channel('webhook')->debug('Webhook event',['request'=>$request]);
}
}

View File

@@ -0,0 +1,136 @@
<?php
namespace App\Jobs;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use App\Models\{Account,Invoice,Payment,PaymentItem,ProviderToken};
/**
* Synchronise payments with our payments.
*
* This will:
* + Create/Update the payment in our system
* + Assocate the payment to the same invoice in our system
*/
class AccountingPaymentSync implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private const LOGKEY = 'JPS';
private \Intuit\Models\Payment $pmi;
private ProviderToken $to;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(ProviderToken $to,\Intuit\Models\Payment $pmi)
{
$this->pmi = $pmi;
$this->to = $to;
}
/**
* Execute the job.
*
* @return void
* @throws \Exception
*/
public function handle()
{
// See if we are already linked
if (($x=$this->to->provider->payments->where('pivot.ref',$this->pmi->id))->count() === 1) {
$o = $x->pop();
} else {
// Find the account
$ao = Account::select('accounts.*')
->join('account__provider',['account__provider.account_id'=>'accounts.id'])
->where('provider_oauth_id',$this->to->provider_oauth_id)
->where('ref',$this->pmi->account_ref)
->single();
if (! $ao) {
Log::alert(sprintf('%s:Account not found for payment [%s:%d]',self::LOGKEY,$this->pmi->id,$this->pmi->account_ref));
return;
}
// Create the payment
$o = new Payment;
$o->account_id = $ao->id;
$o->site_id = $ao->site_id; // @todo Automatically determine
}
// Update the payment details
$o->paid_at = $this->pmi->date_paid;
$o->active = TRUE;
$o->checkout_id = 2; // @todo
$o->total_amt = $this->pmi->total_amt;
$o->notes = 'Imported from Intuit';
$o->save();
Log::info(sprintf('%s:Recording payment [%s:%3.2f]',self::LOGKEY,$this->pmi->id,$this->pmi->total_amt));
$o->providers()->syncWithoutDetaching([
$this->to->provider->id => [
'ref' => $this->pmi->id,
'synctoken' => $this->pmi->synctoken,
'created_at'=>Carbon::create($this->pmi->created_at),
'updated_at'=>Carbon::create($this->pmi->updated_at),
'site_id'=>$this->to->site_id,
],
]);
// Load the invoice that this payment pays
$invoices = collect();
foreach ($this->pmi->lines() as $item => $amount) {
$invoice = Invoice::select('invoices.*')
->join('invoice__provider',['invoice__provider.invoice_id'=>'invoices.id'])
->where('provider_oauth_id',$this->to->provider_oauth_id)
->where('ref',$item)
->single();
$invoices->put($item,$invoice);
}
// Delete existing paid invoices that are no longer paid
foreach ($o->items as $pio)
if ($invoices->pluck('id')->search($pio->invoice_id) === FALSE)
$pio->delete();
// Update payment items
foreach ($this->pmi->lines() as $item => $amount) {
if (! $invoices->has($item)) {
Log::alert(sprintf('%s:Invoice [%s] not recorded, payment not assigned',self::LOGKEY,$item));
continue;
}
$io = $invoices->get($item);
// If the payment item already exists
if (($x=$o->items->where('invoice_id',$io->id))->count()) {
$pio = $x->pop();
} else {
$pio = new PaymentItem;
$pio->invoice_id = $io->id;
$pio->site_id = $io->site_id;
}
$pio->amount = $amount;
$o->items()->save($pio);
}
Log::alert(sprintf('%s:Payment updated [%s:%s]',self::LOGKEY,$o->id,$this->pmi->id));
}
}

View File

@@ -7,6 +7,7 @@ use Illuminate\Support\Facades\DB;
use Leenooks\Traits\ScopeActive;
use App\Interfaces\IDs;
use App\Traits\ProviderRef;
use App\Traits\PushNew;
/**
@@ -24,7 +25,7 @@ use App\Traits\PushNew;
*/
class Payment extends Model implements IDs
{
use PushNew,ScopeActive;
use PushNew,ScopeActive,ProviderRef;
protected $dates = [
'paid_at',
@@ -75,6 +76,13 @@ class Payment extends Model implements IDs
return $this->hasMany(PaymentItem::class);
}
public function providers()
{
return $this->belongsToMany(ProviderOauth::class,'payment__provider')
->where('payment__provider.site_id',$this->site_id)
->withPivot('ref','synctoken','created_at','updated_at');
}
/* SCOPES */
/**

View File

@@ -31,6 +31,13 @@ class ProviderOauth extends Model
->withPivot('ref','synctoken','created_at','updated_at');
}
public function payments()
{
return $this->belongsToMany(Payment::class,'payment__provider')
->where('payment__provider.site_id',$this->site_id)
->withPivot('ref','synctoken','created_at','updated_at');
}
public function taxes()
{
return $this->belongsToMany(Tax::class,'tax__provider')