mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Automating data pulls
This commit is contained in:
parent
ea611e9256
commit
3b1d0e07e2
@ -17,6 +17,7 @@ use App\Jobs\Cron\RecurringInvoicesCron;
|
||||
use App\Jobs\Cron\SubscriptionCron;
|
||||
use App\Jobs\Ledger\LedgerBalanceUpdate;
|
||||
use App\Jobs\Ninja\AdjustEmailQuota;
|
||||
use App\Jobs\Ninja\BankTransactionSync;
|
||||
use App\Jobs\Ninja\CompanySizeCheck;
|
||||
use App\Jobs\Ninja\QueueSize;
|
||||
use App\Jobs\Ninja\SystemMaintenance;
|
||||
@ -68,6 +69,8 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new SystemMaintenance)->weekly()->withoutOverlapping();
|
||||
|
||||
$schedule->job(new BankTransactionSync)->dailyAt('04:00')->withoutOverlapping();
|
||||
|
||||
if (Ninja::isSelfHost()) {
|
||||
$schedule->call(function () {
|
||||
Account::whereNotNull('id')->update(['is_scheduler_running' => true]);
|
||||
|
@ -30,6 +30,7 @@ class BankTransactionFactory
|
||||
$bank_transaction->date = now()->format('Y-m-d');
|
||||
$bank_transaction->description = '';
|
||||
$bank_transaction->is_matched = 0;
|
||||
$bank_transaction->base_type = 'CREDIT';
|
||||
|
||||
return $bank_transaction;
|
||||
}
|
||||
|
@ -134,18 +134,15 @@ class IncomeTransformer implements BankRevenueInterface
|
||||
{
|
||||
|
||||
return [
|
||||
'id' => $transaction->id,
|
||||
'transaction_id' => $transaction->id,
|
||||
'amount' => $transaction->amount->amount,
|
||||
'currency' => $transaction->amount->currency,
|
||||
'currency_code' => $transaction->amount->currency,
|
||||
'account_type' => $transaction->CONTAINER,
|
||||
'category_id' => $transaction->categoryId,
|
||||
'category_type' => $transaction->categoryType,
|
||||
'date' => $transaction->date,
|
||||
'account_id' => $transaction->accountId,
|
||||
'bank_account_id' => $transaction->accountId,
|
||||
'description' => $transaction->description->original,
|
||||
'invoice_id' => '',
|
||||
'expense_id' => '',
|
||||
'payment_id' => '',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -574,7 +574,8 @@ class BankIntegrationController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the remote list of accounts stored on the third part provider.
|
||||
* Return the remote list of accounts stored on the third party provider
|
||||
* and update our local cache.
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
@ -631,12 +632,49 @@ class BankIntegrationController extends BaseController
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the remote list of accounts stored on the third party provider
|
||||
* and update our local cache.
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/bank_integrations/get_transactions/account_id",
|
||||
* operationId="getAccountTransactions",
|
||||
* tags={"bank_integrations"},
|
||||
* summary="Retrieve transactions for a account",
|
||||
* description="Retrieve transactions for a account",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Retrieve transactions for a account",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/BankIntegration"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function getTransactions(AdminBankIntegrationRequest $request)
|
||||
{
|
||||
|
||||
$bank_account_id = auth()->user()->account->bank_integration_account_id;
|
||||
|
||||
$bank_account_id = 'sbMem62e1e69547bfb1';
|
||||
// $bank_account_id = 'sbMem62e1e69547bfb1';
|
||||
|
||||
if(!$bank_account_id)
|
||||
return response()->json(['message' => 'Not yet authenticated with Bank Integration service'], 400);
|
||||
@ -645,17 +683,15 @@ class BankIntegrationController extends BaseController
|
||||
$yodlee->setTestMode();
|
||||
|
||||
$data = [
|
||||
'CONTAINER' => 'bank',
|
||||
'categoryType' => 'INCOME, UNCATEGORIZE',
|
||||
'top' => 500,
|
||||
'fromDate' => '2000-10-10', /// YYYY-MM-DD
|
||||
];
|
||||
|
||||
$transactions = $yodlee->getTransactions($data);
|
||||
|
||||
$transactions = (new BankService(auth()->user()->company()))->match();
|
||||
BankService::dispatch(auth()->user()->company()->id, auth()->user()->company()->db));
|
||||
|
||||
return response()->json($transactions, 200, [], JSON_PRETTY_PRINT);
|
||||
return response()->json(['message' => 'Fetching transactions....'], 200);
|
||||
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
* @OA\Property(property="description", type="string", example="Potato purchases for kevin", description="The description of the transaction"),
|
||||
* @OA\Property(property="category_id", type="integer", example=1, description="The category id"),
|
||||
* @OA\Property(property="category_type", type="string", example="Expenses", description="The category description"),
|
||||
* @OA\Property(property="base_type", type="string", example="CREDIT", description="Either CREDIT or DEBIT"),
|
||||
* @OA\Property(property="date", type="string", example="2022-09-01", description="The date of the transaction"),
|
||||
* @OA\Property(property="bank_account_id", type="integer", example="1", description="The ID number of the bank account"),
|
||||
* )
|
||||
|
117
app/Jobs/Bank/ProcessBankTransactions.php
Normal file
117
app/Jobs/Bank/ProcessBankTransactions.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* Credit Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Credit Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Bank;
|
||||
|
||||
use App\Helpers\Bank\Yodlee\Yodlee;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Services\Bank\BankService;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ProcessBankTransactions implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private string $bank_integration_account_id;
|
||||
|
||||
private BankIntegration $bank_integration;
|
||||
|
||||
private ?string $from_date;
|
||||
|
||||
private string $default_date = '2022-01-01';
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(string $bank_integration_account_id, BankIntegration $bank_integration, ?string $from_date)
|
||||
{
|
||||
$this->bank_integration_account_id = $bank_integration_account_id;
|
||||
$this->bank_integration = $bank_integration;
|
||||
$this->from_date = $from_date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$yodlee = new Yodlee($this->bank_integration_account_id);
|
||||
$yodlee->setTestMode();
|
||||
|
||||
$data = [
|
||||
'baseType' => 'DEBIT', //CREDIT
|
||||
'top' => 500,
|
||||
'fromDate' => $this->from_date ?: $this->default_date, /// YYYY-MM-DD
|
||||
'accountId' => $this->bank_integration->bank_account_id,
|
||||
];
|
||||
|
||||
//expense transactions
|
||||
$transactions = $yodlee->getTransactions($data);
|
||||
|
||||
$company = $this->bank_integration->company;
|
||||
$user_id = $company->owner()->id;
|
||||
|
||||
foreach($transactions as $transaction)
|
||||
{
|
||||
|
||||
if(BankTransaction::where('transaction_id', $transaction['transaction_id'])->where('company_id', $company->id)->withTrashed()->exists())
|
||||
continue;
|
||||
|
||||
$bt = BankTransaction::create(
|
||||
$transaction
|
||||
);
|
||||
|
||||
$bt->company_id = $company->id;
|
||||
$bt->user_id = $user_id;
|
||||
$bt->base_type = 'DEBIT';
|
||||
$bt->save();
|
||||
}
|
||||
|
||||
$data = [
|
||||
'baseType' => 'CREDIT', //CREDIT
|
||||
'top' => 500,
|
||||
'fromDate' => $this->from_date ?: $this->default_date, /// YYYY-MM-DD
|
||||
'accountId' => $this->bank_integration->bank_account_id,
|
||||
];
|
||||
|
||||
//income transactions
|
||||
$transactions = $yodlee->getTransactions($data);
|
||||
|
||||
foreach($transactions as $transaction)
|
||||
{
|
||||
|
||||
if(BankTransaction::where('transaction_id', $transaction['transaction_id'])->where('company_id', $company->id)->withTrashed()->exists())
|
||||
continue;
|
||||
|
||||
$bt = BankTransaction::create(
|
||||
$transaction
|
||||
);
|
||||
|
||||
$bt->company_id = $company->id;
|
||||
$bt->user_id = $user_id;
|
||||
$bt->base_type = 'CREDIT';
|
||||
$bt->save();
|
||||
}
|
||||
|
||||
BankService::dispatch($company->id, $company->db);
|
||||
|
||||
}
|
||||
|
||||
}
|
79
app/Jobs/Ninja/BankTransactionSync.php
Normal file
79
app/Jobs/Ninja/BankTransactionSync.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Ninja;
|
||||
|
||||
use App\Jobs\Bank\ProcessBankTransactions;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class BankTransactionSync implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (! Ninja::isHosted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//multiDB environment, need to
|
||||
foreach (MultiDB::$dbs as $db) {
|
||||
MultiDB::setDB($db);
|
||||
|
||||
$this->syncTransactions();
|
||||
}
|
||||
}
|
||||
|
||||
public function syncTransactions()
|
||||
{
|
||||
$a = Account::with('bank_integrations')->whereNotNull('bank_integration_account_id')->cursor()->each(function ($account){
|
||||
|
||||
$queue = Ninja::isHosted() ? 'bank' : 'default';
|
||||
|
||||
if($account->isPaid())
|
||||
{
|
||||
|
||||
$account->bank_integrations->each(function ($bank_integration) use ($queue, $account){
|
||||
|
||||
ProcessBankTransactions::dispatch($account->bank_integration_account_id, $bank_integration)->onQueue($queue);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -138,6 +138,11 @@ class Account extends BaseModel
|
||||
return $this->hasMany(Company::class);
|
||||
}
|
||||
|
||||
public function bank_integrations()
|
||||
{
|
||||
return $this->hasMany(BankIntegration::class);
|
||||
}
|
||||
|
||||
public function company_users()
|
||||
{
|
||||
return $this->hasMany(CompanyUser::class);
|
||||
|
@ -11,18 +11,38 @@
|
||||
|
||||
namespace App\Services\Bank;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class BankService
|
||||
class BankService implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public Company $company;
|
||||
private $company_id;
|
||||
|
||||
private Company $company;
|
||||
|
||||
private $db;
|
||||
|
||||
private $invoices;
|
||||
|
||||
public function __construct(Company $company)
|
||||
public function __construct($company_id, $db)
|
||||
{
|
||||
$this->company = $company;
|
||||
$this->company_id = $company_id;
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
$this->company = Company::find($this->company_id);
|
||||
|
||||
$this->invoices = $this->company->invoices()->whereIn('status_id', [1,2,3])
|
||||
->where('is_deleted', 0)
|
||||
|
@ -60,6 +60,7 @@ class BankTransactionTransformer extends EntityTransformer
|
||||
'date' => (string) $bank_transaction->date ?: '',
|
||||
'bank_account_id' => (int) $bank_transaction->bank_account_id,
|
||||
'description' => (string) $bank_transaction->description ?: '',
|
||||
'base_type' => (string) $bank_transaction->base_type ?: '',
|
||||
'invoice_id' => (string) $this->encodePrimaryKey($bank_transaction->invoice_id) ?: '',
|
||||
'expense_id'=> (string) $this->encodePrimaryKey($bank_transaction->expense_id) ?: '',
|
||||
'is_matched'=> (bool) $bank_transaction->is_matched ?: '',
|
||||
|
@ -55,10 +55,11 @@ return new class extends Migration
|
||||
$table->unsignedInteger('company_id');
|
||||
$table->unsignedInteger('user_id');
|
||||
$table->unsignedBigInteger('bank_integration_id');
|
||||
$table->unsignedBigInteger('transaction_id')->nullable();
|
||||
$table->unsignedBigInteger('transaction_id')->index();
|
||||
$table->decimal('amount', 20, 6);
|
||||
$table->string('currency_code');
|
||||
$table->string('account_type');
|
||||
$table->string('base_type')->index();
|
||||
$table->unsignedInteger('category_id');
|
||||
$table->string('category_type');
|
||||
$table->date('date');
|
||||
|
@ -109,8 +109,9 @@ Route::group(['middleware' => ['throttle:300,1', 'api_db', 'token_auth', 'locale
|
||||
Route::put('accounts/{account}', [AccountController::class, 'update'])->name('account.update');
|
||||
Route::resource('bank_integrations', BankIntegrationController::class); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::post('bank_integrations/refresh_accounts', [BankIntegrationController::class, 'refreshAccounts'])->name('bank_integrations.refresh_accounts');
|
||||
Route::post('bank_integrations/transactions', [BankIntegrationController::class, 'getTransactions'])->name('bank_integrations.transactions');
|
||||
Route::post('bank_integrations/remove_account/{acc_id}', [BankIntegrationController::class, 'removeAccount'])->name('bank_integrations.remove_account');
|
||||
Route::post('bank_integrations/get_transactions/{acc_id}', [BankIntegrationController::class, 'getTransactions'])->name('bank_integrations.transactions');
|
||||
|
||||
Route::post('bank_integrations/bulk', [BankIntegrationController::class, 'bulk'])->name('bank_integrations.bulk');
|
||||
|
||||
Route::resource('bank_transactions', BankTransactionController::class); // name = (clients. index / create / show / update / destroy / edit
|
||||
|
Loading…
x
Reference in New Issue
Block a user