From 3865d7193ede697fbd189431e8b7b9447b5163e0 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 21 Sep 2022 17:00:49 +1000 Subject: [PATCH] Refactor for bank transactions --- .../Yodlee/Transformer/IncomeTransformer.php | 4 +- app/Jobs/Bank/MatchBankTransactions.php | 114 +++++++++++++----- app/Services/Bank/BankService.php | 9 +- tests/Feature/Bank/YodleeApiTest.php | 8 +- .../Bank/YodleeBankTransactionTest.php | 18 ++- 5 files changed, 104 insertions(+), 49 deletions(-) diff --git a/app/Helpers/Bank/Yodlee/Transformer/IncomeTransformer.php b/app/Helpers/Bank/Yodlee/Transformer/IncomeTransformer.php index e992055bb5da..6da207fb3e6e 100644 --- a/app/Helpers/Bank/Yodlee/Transformer/IncomeTransformer.php +++ b/app/Helpers/Bank/Yodlee/Transformer/IncomeTransformer.php @@ -132,7 +132,7 @@ class IncomeTransformer implements BankRevenueInterface public function transformTransaction($transaction) { - nlog($transaction); + return [ 'transaction_id' => $transaction->id, 'amount' => $transaction->amount->amount, @@ -143,7 +143,7 @@ class IncomeTransformer implements BankRevenueInterface 'date' => $transaction->date, 'bank_account_id' => $transaction->accountId, 'description' => $transaction->description->original, - 'base_type' => $transaction->baseType, + 'base_type' => property_exists($transaction, 'baseType') ? $transaction->baseType : '', ]; } diff --git a/app/Jobs/Bank/MatchBankTransactions.php b/app/Jobs/Bank/MatchBankTransactions.php index 4bff84816d6b..5f6d7b0527b7 100644 --- a/app/Jobs/Bank/MatchBankTransactions.php +++ b/app/Jobs/Bank/MatchBankTransactions.php @@ -29,6 +29,7 @@ use App\Models\Payment; use App\Services\Bank\BankService; use App\Utils\Ninja; use App\Utils\Traits\GeneratesCounter; +use App\Utils\Traits\MakesHash; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; @@ -39,7 +40,7 @@ use Illuminate\Support\Carbon; class MatchBankTransactions implements ShouldQueue { - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, GeneratesCounter; + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, GeneratesCounter, MakesHash; private int $company_id; @@ -54,6 +55,11 @@ class MatchBankTransactions implements ShouldQueue private BankTransaction $bt; private $categories; + + private float $available_balance = 0; + + private array $attachable_invoices = []; + /** * Create a new job instance. */ @@ -75,7 +81,6 @@ class MatchBankTransactions implements ShouldQueue */ public function handle() { -nlog("match bank transactions"); MultiDB::setDb($this->db); @@ -88,11 +93,9 @@ nlog("match bank transactions"); if($_categories) $this->categories = collect($_categories->transactionCategory); -nlog($this->input); - foreach($this->input as $match) { - if(array_key_exists('invoice_id', $match) && strlen($match['invoice_id']) > 1) + if(array_key_exists('invoice_ids', $match) && strlen($match['invoice_ids']) > 1) $this->matchInvoicePayment($match); else $this->matchExpense($match); @@ -100,24 +103,54 @@ nlog($this->input); } + private function getInvoices(string $invoice_hashed_ids) + { + $collection = collect(); + + $invoices = explode(",", $invoice_hashed_ids); + + if(count($invoices) >= 1) + { + + foreach($invoices as $invoice){ + + if(is_string($invoice) && strlen($invoice) > 1) + $collection->push($this->decodePrimaryKey($invoice)); + } + + } + + return $collection; + } + + private function checkPayable($invoices) :bool + { + + foreach($invoices as $invoice){ + + if(!$invoice->isPayable()) + return false; + + } + + return true; + + } + private function matchInvoicePayment(array $match) :void { $this->bt = BankTransaction::find($match['id']); - nlog($this->bt->toArray()); - - $_invoice = Invoice::withTrashed()->find($match['invoice_id']); - -nlog($_invoice->toArray()); + $_invoices = Invoice::withTrashed()->find($this->getInvoices($match['invoice_ids'])); if(array_key_exists('amount', $match) && $match['amount'] > 0) $amount = $match['amount']; else $amount = $this->bt->amount; - if($_invoice && $_invoice->isPayable()){ + if($_invoices && $this->checkPayable($_invoices)){ - $this->createPayment($match['invoice_id'], $amount); + $this->createPayment($_invoices, $amount); } @@ -139,21 +172,41 @@ nlog($_invoice->toArray()); } - private function createPayment(int $invoice_id, float $amount) :void + private function createPayment($invoices, float $amount) :void { -nlog("creating payment"); + $this->available_balance = $amount; - \DB::connection(config('database.default'))->transaction(function () use($invoice_id, $amount) { + \DB::connection(config('database.default'))->transaction(function () use($invoices) { - $this->invoice = Invoice::withTrashed()->where('id', $invoice_id)->lockForUpdate()->first(); + $invoices->each(function ($invoice) use ($invoices){ + + $this->invoice = Invoice::withTrashed()->where('id', $invoice->id)->lockForUpdate()->first(); - $this->invoice - ->service() - ->setExchangeRate() - ->updateBalance($amount * -1) - ->updatePaidToDate($amount) - ->setCalculatedStatus() - ->save(); + if($invoices->count() == 1){ + $_amount = $this->available_balance; + } + elseif($invoices->count() > 1 && floatval($this->invoice->balance) < floatval($this->available_balance) && $this->available_balance > 0) + { + $_amount = $this->invoice->balance; + $this->available_balance = $this->available_balance - $this->invoice->balance; + } + elseif($invoices->count() > 1 && floatval($this->invoice->balance) > floatval($this->available_balance) && $this->available_balance > 0) + { + $_amount = $this->available_balance; + $this->available_balance = 0; + } + + $this->attachable_invoices[] = ['id' => $this->invoice->id, 'amount' => $_amount]; + + $this->invoice + ->service() + ->setExchangeRate() + ->updateBalance($_amount * -1) + ->updatePaidToDate($_amount) + ->setCalculatedStatus() + ->save(); + + }); }, 1); @@ -164,7 +217,7 @@ nlog("creating payment"); $payment->applied = $amount; $payment->status_id = Payment::STATUS_COMPLETED; $payment->client_id = $this->invoice->client_id; - $payment->transaction_reference = $this->bt->transaction_id; + $payment->transaction_reference = $this->bt->description; $payment->transaction_id = $this->bt->transaction_id; $payment->currency_id = $this->harvestCurrencyId(); $payment->is_manual = false; @@ -176,8 +229,6 @@ nlog("creating payment"); $payment->saveQuietly(); -nlog($payment->toArray()); - $payment->service()->applyNumber()->save(); if($payment->client->getSetting('send_email_on_mark_paid')) @@ -186,9 +237,14 @@ nlog($payment->toArray()); $this->setExchangeRate($payment); /* Create a payment relationship to the invoice entity */ - $payment->invoices()->attach($this->invoice->id, [ - 'amount' => $amount, - ]); + foreach($this->attachable_invoices as $attachable_invoice) + { + + $payment->invoices()->attach($attachable_invoice['id'], [ + 'amount' => $attachable_invoice['amount'], + ]); + + } event('eloquent.created: App\Models\Payment', $payment); diff --git a/app/Services/Bank/BankService.php b/app/Services/Bank/BankService.php index 9397409eb4aa..ad15ddd21352 100644 --- a/app/Services/Bank/BankService.php +++ b/app/Services/Bank/BankService.php @@ -33,6 +33,8 @@ class BankService implements ShouldQueue private $invoices; + public $deleteWhenMissingModels = true; + public function __construct($company_id, $db) { $this->company_id = $company_id; @@ -58,8 +60,7 @@ class BankService implements ShouldQueue { BankTransaction::where('company_id', $this->company->id) - ->where('is_matched', false) - ->where('provisional_match', false) + ->where('status_id', BankTransaction::STATUS_UNMATCHED) ->cursor() ->each(function ($bt){ @@ -71,8 +72,8 @@ class BankService implements ShouldQueue if($invoice) { - $bt->invoice_id = $invoice->id; - $bt->provisional_match = true; + $bt->invoice_ids = $invoice->hashed_id; + $bt->status_id = BankTransaction::STATUS_MATCHED; $bt->save(); } diff --git a/tests/Feature/Bank/YodleeApiTest.php b/tests/Feature/Bank/YodleeApiTest.php index 1ddf3b23bc2f..0f4ed89cffb9 100644 --- a/tests/Feature/Bank/YodleeApiTest.php +++ b/tests/Feature/Bank/YodleeApiTest.php @@ -121,13 +121,13 @@ class YodleeApiTest extends TestCase $data = [ [ 'id' => $bt->id, - 'invoice_id' => $invoice->id + 'invoice_ids' => $invoice->hashed_id ] ]; MatchBankTransactions::dispatchSync($this->company->id, $this->company->db, $data); - $payment = Payment::where('transaction_reference', '123456')->first(); + $payment = Payment::where('transaction_reference', $bt->description)->first(); $this->assertNotNull($payment); @@ -202,11 +202,11 @@ class YodleeApiTest extends TestCase BankService::dispatchSync($this->company->id, $this->company->db); - $bt = BankTransaction::where('invoice_id', $this->invoice->id)->first(); + $bt = BankTransaction::where('invoice_ids', $this->invoice->hashed_id)->first(); $this->assertNotNull($bt); - $this->assertEquals(1, $bt->provisional_match); + $this->assertEquals(BankTransaction::STATUS_MATCHED, $bt->status_id); } diff --git a/tests/Feature/Bank/YodleeBankTransactionTest.php b/tests/Feature/Bank/YodleeBankTransactionTest.php index d563a2e1eec0..dbd56ebffcb9 100644 --- a/tests/Feature/Bank/YodleeBankTransactionTest.php +++ b/tests/Feature/Bank/YodleeBankTransactionTest.php @@ -54,8 +54,7 @@ class YodleeBankTransactionTest extends TestCase $invoices = Invoice::where('company_id', $this->company->id)->get(); BankTransaction::where('company_id', $this->company->id) - ->where('is_matched', false) - ->where('provisional_match', false) + ->where('status_id', BankTransaction::STATUS_UNMATCHED) ->cursor() ->each(function ($bt) use($invoices){ @@ -67,15 +66,15 @@ class YodleeBankTransactionTest extends TestCase if($invoice) { - $bt->invoice_id = $invoice->id; - $bt->provisional_match = $invoice->id; + $bt->invoice_ids = $invoice->hashed_id; + $bt->status_id = BankTransaction::STATUS_MATCHED; $bt->save(); } }); - $this->assertTrue(BankTransaction::where('invoice_id', $this->invoice->id)->exists()); + $this->assertTrue(BankTransaction::where('invoice_ids', $this->invoice->hashed_id)->exists()); } @@ -96,8 +95,7 @@ class YodleeBankTransactionTest extends TestCase $invoices = Invoice::where('company_id', $this->company->id)->get(); BankTransaction::where('company_id', $this->company->id) - ->where('is_matched', false) - ->where('provisional_match', false) + ->where('status_id', BankTransaction::STATUS_UNMATCHED) ->cursor() ->each(function ($bt) use($invoices){ @@ -109,15 +107,15 @@ class YodleeBankTransactionTest extends TestCase if($invoice) { - $bt->invoice_id = $invoice->id; - $bt->provisional_match = $invoice->id; + $bt->invoice_ids = $invoice->hashed_id; + $bt->status_id = BankTransaction::STATUS_MATCHED; $bt->save(); } }); - $this->assertTrue(BankTransaction::where('invoice_id', $this->invoice->id)->exists()); + $this->assertTrue(BankTransaction::where('invoice_ids', $this->invoice->hashed_id)->exists()); }