diff --git a/app/Factory/AccountFactory.php b/app/Factory/AccountFactory.php index 5780142e00..dd9cf10589 100644 --- a/app/Factory/AccountFactory.php +++ b/app/Factory/AccountFactory.php @@ -162,9 +162,14 @@ class AccountFactory if ($accountTypeId > 0) { return AccountType::find($accountTypeId); } - $type = config('firefly.accountTypeByIdentifier.' . strval($accountType)); + $type = config('firefly.accountTypeByIdentifier.' . strval($accountType)); + $result = AccountType::whereType($type)->first(); + if (is_null($result) && !is_null($accountType)) { + // try as full name: + $result = AccountType::whereType($accountType)->first(); + } - return AccountType::whereType($type)->first(); + return $result; } diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index 9985526124..c96a1304d4 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -93,7 +93,7 @@ class TransactionJournalFactory // store date meta fields (if present): $fields = ['sepa-cc', 'sepa-ct-op', 'sepa-ct-id', 'sepa-db', 'sepa-country', 'sepa-ep', 'sepa-ci', 'interest_date', 'book_date', 'process_date', - 'due_date', 'payment_date', 'invoice_date', 'internal_reference',]; + 'due_date', 'payment_date', 'invoice_date', 'internal_reference','bunq_payment_id']; foreach ($fields as $field) { $this->storeMeta($journal, $data, $field); diff --git a/app/Factory/TransactionJournalMetaFactory.php b/app/Factory/TransactionJournalMetaFactory.php index 4e0e2a18df..6984975aff 100644 --- a/app/Factory/TransactionJournalMetaFactory.php +++ b/app/Factory/TransactionJournalMetaFactory.php @@ -56,7 +56,7 @@ class TransactionJournalMetaFactory if ($data['data'] instanceof Carbon) { $value = $data['data']->toW3cString(); } - if (strlen($value) === 0) { + if (strlen(strval($value)) === 0) { // don't store blank strings. if (!is_null($entry)) { try { diff --git a/app/Http/Controllers/Import/StatusController.php b/app/Http/Controllers/Import/StatusController.php index 4ae0e0a751..d476cd3578 100644 --- a/app/Http/Controllers/Import/StatusController.php +++ b/app/Http/Controllers/Import/StatusController.php @@ -26,6 +26,7 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Middleware\IsDemoUser; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use Log; /** * Class StatusController @@ -117,6 +118,7 @@ class StatusController extends Controller $result['running'] = true; } $result['percentage'] = $result['percentage'] > 100 ? 100 : $result['percentage']; + Log::debug(sprintf('JOB STATUS: %d/%d', $result['done'], $result['steps'])); return response()->json($result); } diff --git a/app/Http/Middleware/Installer.php b/app/Http/Middleware/Installer.php index 9bc82a4759..f5ff6ec02c 100644 --- a/app/Http/Middleware/Installer.php +++ b/app/Http/Middleware/Installer.php @@ -37,7 +37,7 @@ class Installer return $next($request); } - Log::debug(sprintf('URL is %s, will run installer middleware', $url)); + // Log::debug(sprintf('URL is %s, will run installer middleware', $url)); // no tables present? try { diff --git a/app/Import/Configuration/BunqConfigurator.php b/app/Import/Configuration/BunqConfigurator.php index e52455401c..8dbf9255f2 100644 --- a/app/Import/Configuration/BunqConfigurator.php +++ b/app/Import/Configuration/BunqConfigurator.php @@ -193,7 +193,7 @@ class BunqConfigurator implements ConfiguratorInterface // set default extended status: $extendedStatus = $this->repository->getExtendedStatus($job); - $extendedStatus['steps'] = 6; + $extendedStatus['steps'] = 8; // save to job: $job = $this->repository->setConfiguration($job, $finalConfig); diff --git a/app/Import/Routine/BunqRoutine.php b/app/Import/Routine/BunqRoutine.php index 1921d31cbb..50fefe2e88 100644 --- a/app/Import/Routine/BunqRoutine.php +++ b/app/Import/Routine/BunqRoutine.php @@ -23,13 +23,25 @@ declare(strict_types=1); namespace FireflyIII\Import\Routine; +use Carbon\Carbon; +use DB; use Exception; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Factory\AccountFactory; +use FireflyIII\Factory\TransactionJournalFactory; +use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; use FireflyIII\Models\ImportJob; +use FireflyIII\Models\TransactionJournalMeta; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Services\Bunq\Id\DeviceServerId; use FireflyIII\Services\Bunq\Object\DeviceServer; +use FireflyIII\Services\Bunq\Object\LabelMonetaryAccount; use FireflyIII\Services\Bunq\Object\MonetaryAccountBank; +use FireflyIII\Services\Bunq\Object\Payment; use FireflyIII\Services\Bunq\Object\ServerPublicKey; use FireflyIII\Services\Bunq\Object\UserCompany; use FireflyIII\Services\Bunq\Object\UserPerson; @@ -80,9 +92,14 @@ class BunqRoutine implements RoutineInterface public $journals; /** @var int */ public $lines = 0; + /** @var AccountFactory */ + private $accountFactory; + /** @var AccountRepositoryInterface */ + private $accountRepository; /** @var ImportJob */ private $job; - + /** @var TransactionJournalFactory */ + private $journalFactory; /** @var ImportJobRepositoryInterface */ private $repository; @@ -140,9 +157,15 @@ class BunqRoutine implements RoutineInterface */ public function setJob(ImportJob $job) { - $this->job = $job; - $this->repository = app(ImportJobRepositoryInterface::class); + $this->job = $job; + $this->repository = app(ImportJobRepositoryInterface::class); + $this->accountRepository = app(AccountRepositoryInterface::class); + $this->accountFactory = app(AccountFactory::class); + $this->journalFactory = app(TransactionJournalFactory::class); $this->repository->setUser($job->user); + $this->accountRepository->setUser($job->user); + $this->accountFactory->setUser($job->user); + $this->journalFactory->setUser($job->user); } /** @@ -176,27 +199,13 @@ class BunqRoutine implements RoutineInterface // do nothing in this stage. Job should revert to config routine. break; case 'have-account-mapping': + $this->setStatus('running'); $this->runStageHaveAccountMapping(); break; default: throw new FireflyException(sprintf('No action for stage %s!', $stage)); break; - // case 'has-token': - - // // import routine does nothing at this point: - // break; - // case 'user-logged-in': - // $this->runStageLoggedIn(); - // break; - // case 'have-account-mapping': - // $this->runStageHaveMapping(); - // break; - // default: - // throw new FireflyException(sprintf('Cannot handle stage %s', $stage)); - // } - // - // return true; } } @@ -205,17 +214,18 @@ class BunqRoutine implements RoutineInterface */ protected function runStageInitial() { + $this->addStep(); Log::debug('In runStageInitial()'); $this->setStatus('running'); // register the device at Bunq: $serverId = $this->registerDevice(); - $this->addStep(); Log::debug(sprintf('Found device server with id %d', $serverId->getId())); $config = $this->getConfig(); $config['stage'] = 'registered'; $this->setConfig($config); + $this->addStep(); return; } @@ -227,6 +237,7 @@ class BunqRoutine implements RoutineInterface */ protected function runStageRegistered(): void { + $this->addStep(); Log::debug('Now in runStageRegistered()'); $apiKey = Preferences::getForUser($this->job->user, 'bunq_api_key')->data; $serverPublicKey = Preferences::getForUser($this->job->user, 'bunq_server_public_key')->data; @@ -239,7 +250,8 @@ class BunqRoutine implements RoutineInterface $request->call(); $this->addStep(); - // todo store objects in job! + Log::debug('Requested new session.'); + $deviceSession = $request->getDeviceSessionId(); $userPerson = $request->getUserPerson(); $userCompany = $request->getUserCompany(); @@ -254,6 +266,8 @@ class BunqRoutine implements RoutineInterface $this->setConfig($config); $this->addStep(); + Log::debug('Session stored in job.'); + return; } @@ -262,7 +276,17 @@ class BunqRoutine implements RoutineInterface */ private function addStep() { - $this->repository->addStepsDone($this->job, 1); + $this->addSteps(1); + } + + /** + * Shorthand method. + * + * @param int $count + */ + private function addSteps(int $count) + { + $this->repository->addStepsDone($this->job, $count); } /** @@ -275,6 +299,71 @@ class BunqRoutine implements RoutineInterface $this->repository->addTotalSteps($this->job, $steps); } + /** + * @param int $paymentId + * + * @return bool + */ + private function alreadyImported(int $paymentId): bool + { + $count = TransactionJournalMeta::where('name', 'bunq_payment_id') + ->where('data', json_encode($paymentId))->count(); + + Log::debug(sprintf('Transaction #%d is %d time(s) in the database.', $paymentId, $count)); + + return $count > 0; + } + + /** + * @param LabelMonetaryAccount $party + * @param string $expectedType + * + * @return Account + */ + private function convertToAccount(LabelMonetaryAccount $party, string $expectedType): Account + { + Log::debug('in convertToAccount()'); + // find opposing party by IBAN first. + $result = $this->accountRepository->findByIbanNull($party->getIban(), [$expectedType]); + if (!is_null($result)) { + Log::debug(sprintf('Search for %s resulted in account %s (#%d)', $party->getIban(), $result->name, $result->id)); + + return $result; + } + + // try to find asset account just in case: + if ($expectedType !== AccountType::ASSET) { + $result = $this->accountRepository->findByIbanNull($party->getIban(), [AccountType::ASSET]); + if (!is_null($result)) { + Log::debug(sprintf('Search for Asset "%s" resulted in account %s (#%d)', $party->getIban(), $result->name, $result->id)); + + return $result; + } + } + // create new account: + $data = [ + 'user_id' => $this->job->user_id, + 'iban' => $party->getIban(), + 'name' => $party->getLabelUser()->getDisplayName(), + 'account_type_id' => null, + 'accountType' => $expectedType, + 'virtualBalance' => null, + 'active' => true, + + ]; + $account = $this->accountFactory->create($data); + Log::debug( + sprintf( + 'Converted label monetary account %s to %s account %s (#%d)', + $party->getLabelUser()->getDisplayName(), + $expectedType, + $account->name, $account->id + ) + ); + + return $account; + } + /** * This method creates a new public/private keypair for the user. This isn't really secure, since the key is generated on the fly with * no regards for HSM's, smart cards or other things. It would require some low level programming to get this right. But the private key @@ -498,6 +587,144 @@ class BunqRoutine implements RoutineInterface return $this->repository->getStatus($this->job); } + /** + * Import the transactions that were found. + * + * @param array $payments + * + * @throws FireflyException + */ + private function importPayments(array $payments): void + { + Log::debug('Going to run importPayments()'); + $journals = new Collection; + $config = $this->getConfig(); + foreach ($payments as $accountId => $data) { + Log::debug(sprintf('Now running for bunq account #%d with %d payment(s).', $accountId, count($data['payments']))); + /** @var Payment $payment */ + foreach ($data['payments'] as $index => $payment) { + Log::debug(sprintf('Now at payment #%d with ID #%d', $index, $payment->getId())); + // store or find counter party: + $counterParty = $payment->getCounterParty(); + $amount = $payment->getAmount(); + $paymentId = $payment->getId(); + if ($this->alreadyImported($paymentId)) { + Log::error(sprintf('Already imported bunq payment with id #%d', $paymentId)); + + // add three steps to keep up + $this->addSteps(3); + continue; + } + Log::debug(sprintf('Amount is %s %s', $amount->getCurrency(), $amount->getValue())); + $expected = AccountType::EXPENSE; + if (bccomp($amount->getValue(), '0') === 1) { + // amount + means that its a deposit. + $expected = AccountType::REVENUE; + Log::debug('Will make opposing account revenue.'); + } + $opposing = $this->convertToAccount($counterParty, $expected); + $account = $this->accountRepository->findNull($config['accounts-mapped'][$accountId]); + $type = TransactionType::WITHDRAWAL; + + $this->addStep(); + + Log::debug(sprintf('Will store withdrawal between "%s" (%d) and "%s" (%d)', $account->name, $account->id, $opposing->name, $opposing->id)); + + // start storing stuff: + $source = $account; + $destination = $opposing; + if (bccomp($amount->getValue(), '0') === 1) { + // its a deposit: + $source = $opposing; + $destination = $account; + $type = TransactionType::DEPOSIT; + Log::debug('Will make it a deposit.'); + } + if ($account->accountType->type === AccountType::ASSET && $opposing->accountType->type === AccountType::ASSET) { + $type = TransactionType::TRANSFER; + Log::debug('Both are assets, will make transfer.'); + } + + $storeData = [ + 'user' => $this->job->user_id, + 'type' => $type, + 'date' => $payment->getCreated(), + 'description' => $payment->getDescription(), + 'piggy_bank_id' => null, + 'piggy_bank_name' => null, + 'bill_id' => null, + 'bill_name' => null, + 'tags' => [$payment->getType(), $payment->getSubType()], + 'internal_reference' => $payment->getId(), + 'notes' => null, + 'bunq_payment_id' => $payment->getId(), + 'transactions' => [ + // single transaction: + [ + 'description' => null, + 'amount' => $amount->getValue(), + 'currency_id' => null, + 'currency_code' => $amount->getCurrency(), + 'foreign_amount' => null, + 'foreign_currency_id' => null, + 'foreign_currency_code' => null, + 'budget_id' => null, + 'budget_name' => null, + 'category_id' => null, + 'category_name' => null, + 'source_id' => $source->id, + 'source_name' => null, + 'destination_id' => $destination->id, + 'destination_name' => null, + 'reconciled' => false, + 'identifier' => 0, + ], + ], + ]; + $journal = $this->journalFactory->create($storeData); + Log::debug(sprintf('Stored journal with ID #%d', $journal->id)); + $this->addStep(); + $journals->push($journal); + + } + } + + // link to tag + /** @var TagRepositoryInterface $repository */ + $repository = app(TagRepositoryInterface::class); + $repository->setUser($this->job->user); + $data = [ + 'tag' => trans('import.import_with_key', ['key' => $this->job->key]), + 'date' => new Carbon, + 'description' => null, + 'latitude' => null, + 'longitude' => null, + 'zoomLevel' => null, + 'tagMode' => 'nothing', + ]; + $tag = $repository->store($data); + $extended = $this->getExtendedStatus(); + $extended['tag'] = $tag->id; + $this->setExtendedStatus($extended); + + Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag)); + Log::debug('Looping journals...'); + $tagId = $tag->id; + + foreach ($journals as $journal) { + Log::debug(sprintf('Linking journal #%d to tag #%d...', $journal->id, $tagId)); + DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journal->id, 'tag_id' => $tagId]); + $this->addStep(); + } + Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $journals->count(), $tag->id, $tag->tag)); + + // set status to "finished"? + // update job: + $this->setStatus('finished'); + + return; + } + /** * To install Firefly III as a new device: * - Send an installation token request. @@ -576,13 +803,17 @@ class BunqRoutine implements RoutineInterface $count = 0; if (0 === $user->getId()) { $user = new UserCompany($config['user_company']); + Log::debug(sprintf('Will try to get transactions for company #%d', $user->getId())); } + $this->addTotalSteps(count($config['accounts']) * 2); + foreach ($config['accounts'] as $accountData) { + $this->addStep(); $account = new MonetaryAccountBank($accountData); $importId = $account->getId(); if (1 === $mapping[$importId]) { - // grab all transactions + Log::debug(sprintf('Will grab payments for account %s', $account->getDescription())); $request = new ListPaymentRequest(); $request->setPrivateKey($this->getPrivateKey()); $request->setServerPublicKey($this->getServerPublicKey()); @@ -590,22 +821,24 @@ class BunqRoutine implements RoutineInterface $request->setUserId($user->getId()); $request->setAccount($account); $request->call(); - - exit; + $payments = $request->getPayments(); // store in array $all[$account->getId()] = [ - 'account' => $account, - 'import_id' => $importId, - 'transactions' => $transactions, + 'account' => $account, + 'import_id' => $importId, + 'payments' => $payments, ]; - $count += count($transactions); + $count += count($payments); } - Log::debug(sprintf('Total number of transactions: %d', $count)); + Log::debug(sprintf('Total number of payments: %d', $count)); $this->addStep(); - //$this->importTransactions($all); + // add steps for import: + $this->addTotalSteps($count * 3); + $this->importPayments($all); } - exit; + + // update job to be complete, I think? } /** @@ -613,6 +846,7 @@ class BunqRoutine implements RoutineInterface */ private function runStageLoggedIn(): void { + $this->addStep(); // grab new session token: $config = $this->getConfig(); $token = new SessionToken($config['session_token']); @@ -631,6 +865,7 @@ class BunqRoutine implements RoutineInterface $accounts = $request->getMonetaryAccounts(); $arr = []; Log::debug(sprintf('Get monetary accounts, found %d accounts.', $accounts->count())); + $this->addStep(); /** @var MonetaryAccountBank $account */ foreach ($accounts as $account) { @@ -645,6 +880,7 @@ class BunqRoutine implements RoutineInterface // once the accounts are stored, go to configuring stage: // update job, set status to "configuring". $this->setStatus('configuring'); + $this->addStep(); return; } diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 14b4e67ac4..f837262b3b 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -302,4 +302,5 @@ class AccountRepository implements AccountRepositoryInterface return $journal; } + } diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index e7e00f60dd..6066ae969d 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -66,6 +66,7 @@ interface AccountRepositoryInterface * @param string $number * @param array $types * + * @deprecated * @return Account */ public function findByAccountNumber(string $number, array $types): Account; @@ -74,14 +75,24 @@ interface AccountRepositoryInterface * @param string $iban * @param array $types * + * @deprecated * @return Account */ public function findByIban(string $iban, array $types): Account; + /** + * @param string $iban + * @param array $types + * + * @return Account|null + */ + public function findByIbanNull(string $iban, array $types): ?Account; + /** * @param string $name * @param array $types * + * @deprecated * @return Account|null */ public function findByName(string $name, array $types): ?Account; diff --git a/app/Repositories/Account/FindAccountsTrait.php b/app/Repositories/Account/FindAccountsTrait.php index 62e30db382..2aba29be10 100644 --- a/app/Repositories/Account/FindAccountsTrait.php +++ b/app/Repositories/Account/FindAccountsTrait.php @@ -41,6 +41,7 @@ trait FindAccountsTrait /** * @param $accountId * + * @deprecated * @return Account */ public function find(int $accountId): Account @@ -58,6 +59,7 @@ trait FindAccountsTrait * @param string $number * @param array $types * + * * @deprecated * @return Account */ @@ -109,10 +111,37 @@ trait FindAccountsTrait return new Account; } + /** + * @param string $iban + * @param array $types + * + * @return Account|null + */ + public function findByIbanNull(string $iban, array $types): ?Account + { + $query = $this->user->accounts()->where('iban', '!=', '')->whereNotNull('iban'); + + if (count($types) > 0) { + $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); + $query->whereIn('account_types.type', $types); + } + + $accounts = $query->get(['accounts.*']); + /** @var Account $account */ + foreach ($accounts as $account) { + if ($account->iban === $iban) { + return $account; + } + } + + return null; + } + /** * @param string $name * @param array $types * + * @deprecated * @return Account|null */ public function findByName(string $name, array $types): ?Account diff --git a/app/Repositories/ImportJob/ImportJobRepository.php b/app/Repositories/ImportJob/ImportJobRepository.php index 9eee6714e5..fc63e67c99 100644 --- a/app/Repositories/ImportJob/ImportJobRepository.php +++ b/app/Repositories/ImportJob/ImportJobRepository.php @@ -311,19 +311,11 @@ class ImportJobRepository implements ImportJobRepositoryInterface */ public function setExtendedStatus(ImportJob $job, array $array): ImportJob { - // remove 'errors' because it gets larger and larger and larger... - $display = $array; - unset($display['errors']); - Log::debug(sprintf('Incoming extended status for job "%s" is (except errors): ', $job->key), $display); $currentStatus = $job->extended_status; $newStatus = array_merge($currentStatus, $array); $job->extended_status = $newStatus; $job->save(); - // remove 'errors' because it gets larger and larger and larger... - unset($newStatus['errors']); - Log::debug(sprintf('Set extended status of job "%s" to (except errors): ', $job->key), $newStatus); - return $job; } diff --git a/app/Services/Bunq/Object/Avatar.php b/app/Services/Bunq/Object/Avatar.php index 67048f95ff..7231c763b3 100644 --- a/app/Services/Bunq/Object/Avatar.php +++ b/app/Services/Bunq/Object/Avatar.php @@ -27,4 +27,30 @@ namespace FireflyIII\Services\Bunq\Object; */ class Avatar extends BunqObject { + /** @var string */ + private $anchorUuid; + /** @var Image */ + private $image; + /** @var string */ + private $uuid; + + /** + * Avatar constructor. + * + * @param array $data + */ + public function __construct(array $data) + { + $this->uuid = $data['uuid']; + $this->anchorUuid = $data['anchor_uuid']; + $this->image = new Image($data['image']); + } + + /** + * @return array + */ + public function toArray(): array + { + die(sprintf('Cannot convert %s to array.', get_class($this))); + } } diff --git a/app/Services/Bunq/Object/BunqObject.php b/app/Services/Bunq/Object/BunqObject.php index 81af013f22..41b1d4a855 100644 --- a/app/Services/Bunq/Object/BunqObject.php +++ b/app/Services/Bunq/Object/BunqObject.php @@ -25,6 +25,12 @@ namespace FireflyIII\Services\Bunq\Object; /** * Class BunqObject. */ -class BunqObject +abstract class BunqObject { + /** + * Convert this object to array. + * + * @return array + */ + abstract public function toArray(): array; } diff --git a/app/Services/Bunq/Object/DeviceServer.php b/app/Services/Bunq/Object/DeviceServer.php index 44617cd66c..33fb1a9f94 100644 --- a/app/Services/Bunq/Object/DeviceServer.php +++ b/app/Services/Bunq/Object/DeviceServer.php @@ -75,4 +75,12 @@ class DeviceServer extends BunqObject { return $this->ip; } + + /** + * @return array + */ + public function toArray(): array + { + die(sprintf('Cannot convert %s to array.', get_class($this))); + } } diff --git a/app/Services/Bunq/Object/Image.php b/app/Services/Bunq/Object/Image.php new file mode 100644 index 0000000000..dc4370fcc6 --- /dev/null +++ b/app/Services/Bunq/Object/Image.php @@ -0,0 +1,62 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Bunq\Object; + + +/** + * Class Image + */ +class Image extends BunqObject +{ + /** @var string */ + private $attachmentPublicUuid; + /** @var string */ + private $contentType; + /** @var int */ + private $height; + /** @var int */ + private $width; + + /** + * Image constructor. + * + * @param array $data + */ + public function __construct(array $data) + { + $this->attachmentPublicUuid = $data['attachment_public_uuid'] ?? null; + $this->height = $data['height'] ?? null; + $this->width = $data['width'] ?? null; + $this->contentType = $data['content_type'] ?? null; + } + + /** + * @return array + */ + public function toArray(): array + { + die(sprintf('Cannot convert %s to array.', get_class($this))); + } + +} \ No newline at end of file diff --git a/app/Services/Bunq/Object/LabelMonetaryAccount.php b/app/Services/Bunq/Object/LabelMonetaryAccount.php index f203315318..a8023dbd7c 100644 --- a/app/Services/Bunq/Object/LabelMonetaryAccount.php +++ b/app/Services/Bunq/Object/LabelMonetaryAccount.php @@ -29,5 +29,54 @@ namespace FireflyIII\Services\Bunq\Object; */ class LabelMonetaryAccount extends BunqObject { + /** @var Avatar */ + private $avatar; + /** @var string */ + private $country; + /** @var string */ + private $iban; + /** @var bool */ + private $isLight; + /** @var LabelUser */ + private $labelUser; + + /** + * @return LabelUser + */ + public function getLabelUser(): LabelUser + { + return $this->labelUser; + } + + + /** + * LabelMonetaryAccount constructor. + * + * @param array $data + */ + public function __construct(array $data) + { + $this->iban = $data['iban']; + $this->isLight = $data['is_light']; + $this->avatar = new Avatar($data['avatar']); + $this->labelUser = new LabelUser($data['label_user']); + $this->country = $data['country']; + } + + /** + * @return string + */ + public function getIban(): string + { + return $this->iban; + } + + /** + * @return array + */ + public function toArray(): array + { + die(sprintf('Cannot convert %s to array.', get_class($this))); + } } diff --git a/app/Services/Bunq/Object/LabelUser.php b/app/Services/Bunq/Object/LabelUser.php new file mode 100644 index 0000000000..2ced566153 --- /dev/null +++ b/app/Services/Bunq/Object/LabelUser.php @@ -0,0 +1,90 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Bunq\Object; + + +/** + * Class LabelUser + */ +class LabelUser extends BunqObject +{ + /** @var Avatar */ + private $avatar; + /** @var string */ + private $country; + /** @var string */ + private $displayName; + /** @var string */ + private $publicNickName; + /** @var string */ + private $uuid; + + /** + * @return Avatar + */ + public function getAvatar(): Avatar + { + return $this->avatar; + } + + /** + * @return string + */ + public function getDisplayName(): string + { + return $this->displayName; + } + + /** + * @return string + */ + public function getPublicNickName(): string + { + return $this->publicNickName; + } + + + + /** + * LabelUser constructor. + * + * @param array $data + */ + public function __construct(array $data) + { + $this->uuid = $data['uuid']; + $this->displayName = $data['display_name']; + $this->country = $data['country']; + $this->publicNickName = $data['public_nick_name']; + $this->avatar = new Avatar($data['avatar']); + } + + /** + * @return array + */ + public function toArray(): array + { + die(sprintf('Cannot convert %s to array.', get_class($this))); + } +} \ No newline at end of file diff --git a/app/Services/Bunq/Object/Payment.php b/app/Services/Bunq/Object/Payment.php index 8a548ddb52..b64ba2ce92 100644 --- a/app/Services/Bunq/Object/Payment.php +++ b/app/Services/Bunq/Object/Payment.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace FireflyIII\Services\Bunq\Object; + use Carbon\Carbon; @@ -30,28 +31,30 @@ use Carbon\Carbon; */ class Payment extends BunqObject { - /** @var int */ - private $id; - /** @var Carbon */ - private $created; - /** @var Carbon */ - private $updated; - /** @var int */ - private $monetaryAccountId; + /** @var LabelMonetaryAccount */ + private $alias; /** @var Amount */ private $amount; - /** @var string */ - private $description; - /** @var string */ - private $type; - /** @var string */ - private $merchantReference; - /** @var LabelMonetaryAccount */ - private $counterParty; /** @var array */ private $attachments = []; + /** @var LabelMonetaryAccount */ + private $counterParty; + /** @var Carbon */ + private $created; + /** @var string */ + private $description; + /** @var int */ + private $id; + /** @var string */ + private $merchantReference; + /** @var int */ + private $monetaryAccountId; /** @var string */ private $subType; + /** @var string */ + private $type; + /** @var Carbon */ + private $updated; /** * Payment constructor. @@ -60,11 +63,81 @@ class Payment extends BunqObject */ public function __construct(array $data) { - $this->id = $data['id']; - $this->created = new Carbon(); + $this->id = $data['id']; + $this->created = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['created']); + $this->updated = Carbon::createFromFormat('Y-m-d H:i:s.u', $data['updated']); + $this->monetaryAccountId = (int)$data['monetary_account_id']; + $this->amount = new Amount($data['amount']); + $this->description = $data['description']; + $this->type = $data['type']; + $this->merchantReference = $data['merchant_reference']; + $this->alias = new LabelMonetaryAccount($data['alias']); + $this->counterParty = new LabelMonetaryAccount($data['counterparty_alias']); + $this->subType = $data['sub_type']; + } - var_dump($data); - exit; + /** + * @return Amount + */ + public function getAmount(): Amount + { + return $this->amount; + } + + /** + * @return LabelMonetaryAccount|null + */ + public function getCounterParty(): ?LabelMonetaryAccount + { + return $this->counterParty; + } + + /** + * @return Carbon + */ + public function getCreated(): Carbon + { + return $this->created; + } + + /** + * @return string + */ + public function getDescription(): string + { + return $this->description; + } + + /** + * @return int + */ + public function getId(): int + { + return $this->id; + } + + /** + * @return string + */ + public function getSubType(): string + { + return $this->subType; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @return array + */ + public function toArray(): array + { + die(sprintf('Cannot convert %s to array.', get_class($this))); } } diff --git a/app/Services/Bunq/Object/ServerPublicKey.php b/app/Services/Bunq/Object/ServerPublicKey.php index 89e16e1c27..0a89187145 100644 --- a/app/Services/Bunq/Object/ServerPublicKey.php +++ b/app/Services/Bunq/Object/ServerPublicKey.php @@ -55,4 +55,12 @@ class ServerPublicKey extends BunqObject { $this->publicKey = $publicKey; } + + /** + * @return array + */ + public function toArray(): array + { + die(sprintf('Cannot convert %s to array.', get_class($this))); + } } diff --git a/app/Services/Bunq/Object/UserLight.php b/app/Services/Bunq/Object/UserLight.php index 576b32b641..a889ff04f4 100644 --- a/app/Services/Bunq/Object/UserLight.php +++ b/app/Services/Bunq/Object/UserLight.php @@ -74,4 +74,12 @@ class UserLight extends BunqObject $this->legalName = $data['legal_name']; // aliases } + + /** + * @return array + */ + public function toArray(): array + { + die(sprintf('Cannot convert %s to array.', get_class($this))); + } } diff --git a/app/Services/Bunq/Request/DeviceServerRequest.php b/app/Services/Bunq/Request/DeviceServerRequest.php index d343dd0211..7e8600d7c9 100644 --- a/app/Services/Bunq/Request/DeviceServerRequest.php +++ b/app/Services/Bunq/Request/DeviceServerRequest.php @@ -48,14 +48,9 @@ class DeviceServerRequest extends BunqRequest Log::debug('Now in DeviceServerRequest::call()'); $uri = 'device-server'; $data = ['description' => $this->description, 'secret' => $this->secret, 'permitted_ips' => $this->permittedIps]; - - Log::debug('Data we send along: ', $data); - $headers = $this->getDefaultHeaders(); $headers['X-Bunq-Client-Authentication'] = $this->installationToken->getToken(); - Log::debug('Headers we send along: ', $headers); - $response = $this->sendSignedBunqPost($uri, $data, $headers); $deviceServerId = new DeviceServerId; $deviceServerId->setId(intval($response['Response'][0]['Id']['id'])); diff --git a/app/Services/Bunq/Request/ListPaymentRequest.php b/app/Services/Bunq/Request/ListPaymentRequest.php index 0dbcd08dea..04565a292f 100644 --- a/app/Services/Bunq/Request/ListPaymentRequest.php +++ b/app/Services/Bunq/Request/ListPaymentRequest.php @@ -51,24 +51,36 @@ class ListPaymentRequest extends BunqRequest */ public function call(): void { + $break = false; $this->payments = new Collection; $uri = sprintf('user/%d/monetary-account/%d/payment', $this->userId, $this->account->getId()); - $data = []; $headers = $this->getDefaultHeaders(); $headers['X-Bunq-Client-Authentication'] = $this->sessionToken->getToken(); - $response = $this->sendSignedBunqGet($uri, $data, $headers); + while ($break === false) { + $response = $this->sendSignedBunqGet($uri, [], $headers); + $uri = str_replace('/v1/', '', $response['Pagination']['future_url']); + $break = true; - // create payment objects: - $raw = $this->getArrayFromResponse('Payment', $response); - foreach ($raw as $entry) { - $account = new Payment($entry); - $this->payments->push($account); + // create payment objects: + $raw = $this->getArrayFromResponse('Payment', $response); + foreach ($raw as $entry) { + $payment = new Payment($entry); + $this->payments->push($payment); + } } return; - } + /** + * @return Collection + */ + public function getPayments(): Collection + { + return $this->payments; + } + + /** * @param MonetaryAccountBank $account */ diff --git a/resources/lang/en_US/import.php b/resources/lang/en_US/import.php index e79150807a..9ad5972637 100644 --- a/resources/lang/en_US/import.php +++ b/resources/lang/en_US/import.php @@ -163,6 +163,9 @@ return [ // bunq 'bunq_prerequisites_title' => 'Prerequisites for an import from bunq', 'bunq_prerequisites_text' => 'In order to import from bunq, you need to obtain an API key. You can do this through the app.', + 'bunq_do_import' => 'Yes, import from this account', + 'bunq_accounts_title' => 'Bunq accounts', + 'bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.', // Spectre 'spectre_title' => 'Import using Spectre', diff --git a/resources/lang/en_US/list.php b/resources/lang/en_US/list.php index 250e846177..d8da5cb39e 100644 --- a/resources/lang/en_US/list.php +++ b/resources/lang/en_US/list.php @@ -110,4 +110,5 @@ return [ 'sepa-cc' => 'SEPA Clearing Code', 'sepa-ep' => 'SEPA External Purpose', 'sepa-ci' => 'SEPA Creditor Identifier', + 'account_at_bunq' => 'Account with bunq', ];